如何让 OpenJDK 16 与启用 SSL 的 nginx 服务器通信?

How do I get OpenJDK 16 to talk with an SSL enabled nginx server?

我是 运行 一个 spring 启动应用程序,需要对 nginx 服务器进行 https 调用。该应用程序在 CentOS 7 上 运行,OpenJDK 16.

按照 this 或多或少,我收集了 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"
}
)

我也在 this 中 运行 - 我假设这意味着 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。

所以...我如何 add/configure 密码让两者一起玩?

对我来说,问题归结为 OpenSSL 安装过时。

我错误地从 docker 主机 (1.1.1x) 而不是容器中读取了 OpenSSL 版本(不记得版本了,但是是 2017 年的)。不幸的是,这在 CentOS 7 中无法轻松更新 - 包管理器没有任何内容,并且从源代码编译会遇到各种问题。但是一旦解决了这些问题,密码列表的长度就会增加大约 5 倍,并且连接工作正常。

然后我切换到 Ubuntu 20.04,这使得 OpenSSL 的安装更加容易(1.1.1f 是包管理器的标准配置)。 SSL 握手按预期进行。

关于下一个问题 - 即使 SSL 协商成功,出于某种原因所有 SSL 连接都会在 25 毫秒后超时...但这将是另一个话题(here和我一样的老鼠。