Home > Back-end >  How do I get OpenJDK 16 to talk with an SSL enabled nginx server?
How do I get OpenJDK 16 to talk with an SSL enabled nginx server?

Time:03-25

I'm running a spring boot application that needs to make an https call to an nginx server. The application is running on CentOS 7 with OpenJDK 16.

Following this more or less, I gathered a list of all the ciphers available to the 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

In nginx, I have the following:

    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;

I've tried every one of the ciphers available to my JVM in place of ...keys here... and they all result in nginx failing to start with:

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

I can add a list more like I would expect, such as:

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 starts at this point - and most applications like Chrome, wget, etc are fine with it. Oracle's JDK 11 on my laptop also connects to it without issue. However the OpenJDK based application refuses to connect:

"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"
}
)

I also ran across this - I'm assuming this means that the JDK cipher named TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 for instance should be DHE-RSA-AES256-GCM-SHA384 in nginx/OpenSSL. So I tried a modified cipher list in nginx of:

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;

but still the same result. In the nginx logs:

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

and in the Java service logs:

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)
...

I can't seem to find any good documentation on adding additional ciphers to the JVM, only enabling ones that are present but not enabled for some reason. I've seen some vague references to BouncyCastle, but unclear how to properly use that for this purpose, especially since I won't be creating the sockets myself (using a library that needs to make the call).

I also can't seem to figure out how to configure nginx/OpenSSL to enable additional ciphers to support what the Java application is expecting. I'm using nginx 1.21.6 and OpenSSL 1.1.1k.

So... How do I add/configure ciphers to make the two play well together?

CodePudding user response:

For me, the issue came down to an outdated installation of OpenSSL.

I mistakenly read the OpenSSL version from the docker host (1.1.1x) instead of from the container (don't remember the version, but was from 2017). Unfortunately, this can't easily be updated in CentOS 7 - nothing from the package manager, and compiling from source runs into all sorts of issues. But once past those issues, the list of ciphers was about 5x as long and the connection worked fine.

I then switched to Ubuntu 20.04, which made the installation of OpenSSL much easier (1.1.1f comes standard from the package manager). SSL handshake went through as expected.

On to the next problem - even with a successful SSL negotiation, for some reason all SSL connections are timing out after 25ms... but that'll be another topic (here for anyone going down the same rathole as I am).

  • Related