Как заставить OpenJDK 16 общаться с сервером nginx с поддержкой SSL?

Я запускаю приложение весенней загрузки, которому нужно сделать https-вызов на сервер nginx. Приложение работает на CentOS 7 с OpenJDK 16.

Примерно после это я собрал список всех шифров, доступных для JVM:

TLS_AES_256_GCM_SHA384
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_EMPTY_RENEGOTIATION_INFO_SCSV

В nginx у меня есть следующее:

    ssl_certificate /etc/ssl/cert;
    ssl_certificate_key /etc/ssl/key;
    ssl_protocols TLSv1.2 TLSv1.3;

    resolver 169.254.169.253;

    ssl_prefer_server_ciphers off;
    ssl_ciphers ...keys here...;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
    ssl_dhparam /etc/ssl/dhparam;

Я пробовал все шифры, доступные для моей JVM вместо ...keys here..., и все они приводят к тому, что nginx не запускается с:

nginx: [emerg] SSL_CTX_set_cipher_list("...") failed (SSL: error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match)

Я могу добавить список больше, чем я ожидал, например:

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

В этот момент запускается nginx — и большинство приложений, таких как Chrome, wget и т. д., с этим справляются. Oracle JDK 11 на моем ноутбуке также подключается к нему без проблем. Однако приложение на основе OpenJDK отказывается подключаться:

"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "1F F7 66 B2 EB 52 F0 3A 99 E6 9B A7 10 1A 85 E1 0C FF DC 36 06 C7 52 38 0C 8A 27 9F 21 AA 0E 7D",
  "session id"          : "70 E0 79 AB 78 7B 48 22 41 22 1E 38 64 01 BF E8 7D E0 2C DD BA 08 09 00 20 B2 39 8D 53 B4 65 A2",
  "cipher suites"       : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), TLS_DHE_RSA_
WITH_AES_128_GCM_SHA256(0x009E), TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2), TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), TLS_DHE_DSS_WITH_AE
S_128_CBC_SHA256(0x0040), TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), TLS_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038), TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032), TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D
), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_RSA_WITH_AES_256_CBC_SHA256(0x003D), TLS_RSA_WITH_AES_128_CBC_SHA256(0x003C), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F)]",
...
javax.net.ssl|DEBUG|1C|http-nio-8080-exec-7|2022-03-21 22:37:28.252 UTC|Alert.java:238|Received alert message (
"Alert": {
  "level"      : "fatal",
  "description": "handshake_failure"
}
)

Я также столкнулся с это — я предполагаю, что это означает, что шифр JDK с именем TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, например, должен быть DHE-RSA-AES256-GCM-SHA384 в nginx/OpenSSL. Итак, я попробовал модифицированный список шифров в nginx:

ssl_ciphers DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA;

но все тот же результат. В логах nginx:

2022/03/22 06:05:55 [info] 74#74: *22 SSL_do_handshake() failed (SSL: error:141F7065:SSL routines:final_key_share:no suitable key share) while SSL handshaking, client: #.#.#.#, server: 0.0.0.0:443

и в журналах службы Java:

javax.net.ssl|DEBUG|18|http-nio-8080-exec-3|2022-03-22 06:05:56.332 UTC|ClientHello.java:652|Produced ClientHello handshake message (
"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "A9 28 13 AB 6F 82 B6 F1 88 E9 2C C9 CE 84 55 15 84 9E 25 E9 57 72 C3 BA CF 1C 9B 45 3D 13 28 7F",
  "session id"          : "5E F8 0E 52 83 A9 C2 AF DE 6C BD E4 D7 3C A5 FD D9 00 6F 1C D7 CA 07 E0 63 EF C4 24 CF 57 9F A1",
  "cipher suites"       : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_CHACHA20_POLY1305_SHA256(0x1303), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCAA), TLS_DH
...
)
javax.net.ssl|DEBUG|18|http-nio-8080-exec-3|2022-03-22 06:05:56.334 UTC|Alert.java:238|Received alert message (
"Alert": {
  "level"      : "fatal",
  "description": "handshake_failure"
}
)
javax.net.ssl|ERROR|18|http-nio-8080-exec-3|2022-03-22 06:05:56.335 UTC|TransportContext.java:361|Fatal (HANDSHAKE_FAILURE): Received fatal alert: handshake_failure (
"throwable" : {
  javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
...

Кажется, я не могу найти никакой хорошей документации по добавлению дополнительных шифров в JVM, а только включить те, которые присутствуют, но по какой-то причине не включены. Я видел некоторые расплывчатые ссылки на BouncyCastle, но неясно, как правильно использовать это для этой цели, тем более что я не буду создавать сокеты сам (используя библиотеку, которая должна выполнять вызов).

Я также не могу понять, как настроить nginx/OpenSSL, чтобы включить дополнительные шифры для поддержки того, что ожидает приложение Java. Я использую nginx 1.21.6 и OpenSSL 1.1.1k.

Итак... Как мне добавить/настроить шифры, чтобы они хорошо работали вместе?


43
1

Ответ:

Решено

Для меня проблема сводилась к устаревшей установке OpenSSL.

Я по ошибке прочитал версию OpenSSL с хоста докера (1.1.1x), а не с контейнера (версию не помню, но была с 2017 года). К сожалению, это не может быть легко обновлено в CentOS 7 — ничего из менеджера пакетов, а компиляция из исходного кода приводит к всевозможным проблемам. Но после устранения этих проблем список шифров стал примерно в 5 раз длиннее, и соединение работало нормально.

Затем я переключился на Ubuntu 20.04, что значительно упростило установку OpenSSL (1.1.1f входит в стандартную комплектацию менеджера пакетов). Подтверждение SSL прошло, как и ожидалось.

Перейдем к следующей проблеме: даже при успешном согласовании SSL по какой-то причине время ожидания всех SSL-соединений истекает через 25 мс... но это будет другая тема (здесь для тех, кто спускается в ту же дыру, что и я).