Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jetty on JDK11 SSL Handshake failure with PKIX KMF algorithm and valid SAN DNS Name used as FQDN #7691

Closed
ramtech123 opened this issue Mar 5, 2022 · 12 comments
Labels
Bug For general bugs on Jetty side

Comments

@ramtech123
Copy link

ramtech123 commented Mar 5, 2022

Jetty version(s)
9.4.45.v20220203

Java version/vendor (use: java -version)

openjdk version "11.0.14" 2022-01-18 LTS
OpenJDK Runtime Environment Zulu11.54+24-SA (build 11.0.14+9-LTS)
OpenJDK 64-Bit Server VM Zulu11.54+24-SA (build 11.0.14+9-LTS, mixed mode)

OS type/version

uname -a
Linux photonvm 4.19.229-1.ph3 #1-photon SMP Thu Feb 17 05:56:28 UTC 2022 x86_64 GNU/Linux

Set up
I have a Jetty server running on JRE 11 with BouncyCastle JCE provider and Sun JSSE provider.
The ssl.KeyManagerFactory.algorithm is set to PKIX.

The configured SSL certificate has below configuration
RSA 2048 bit key size,
SHA-256 with RSA signature algorithm
Multiple DNS Names under SAN attributes and appropriate KeyUsage, ExtendedKeyUsage and other extensions.

Description
When I use a valid DNS Name entry as the FQDN for accessing the jetty server, SSL handshake fails with errors that does not make sense. Below is the snippet of the java ssl handshake log showing the error.

javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.667 UTC|null:-1|KeyMgr: getting aliases (
[jetty-server-certs (verified: OK)]
)
javax.net.ssl|ALL|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|No X.509 cert selected for RSA
javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|Ignore alias jetty-server-certs: key algorithm does not match
javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|KeyMgr: no matching alias found
javax.net.ssl|ALL|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|KeyMgr: getting aliases (
[jetty-server-certs (verified: OK)]
)
javax.net.ssl|ALL|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|No X.509 cert selected for RSA
javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|Ignore alias jetty-server-certs: key algorithm does not match
javax.net.ssl|DEBUG|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|KeyMgr: no matching alias found
javax.net.ssl|ALL|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|ERROR|19|qtp373342545-25|2022-01-17 15:00:20.668 UTC|null:-1|Fatal (HANDSHAKE_FAILURE): no cipher suites in common (
"throwable" : {
  javax.net.ssl.SSLHandshakeException: no cipher suites in common
  	at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
  	at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
  	at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
  	at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
  	at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
  	at java.base/sun.security.ssl.ServerHello$T12ServerHelloProducer.chooseCipherSuite(Unknown Source)
  	at java.base/sun.security.ssl.ServerHello$T12ServerHelloProducer.produce(Unknown Source)

Known Workarounds
Making any of the below changes will make it work (with caveat):

  1. Access jetty server using IP address. (SSL warning for invalid host)
  2. Create an entry in hosts file for jetty server with a hostname that is not listed in the SSL certificate. Use this hostname in the FQDN. (SSL warning for invalid host)
  3. Build jetty server with ssl.KeyManagerFactory.alorithm=SunX509 (This is not what we need, especially if configured with FIPS mode)
  4. Use any other server instead of Jetty (tried with Netty based server)

Log
Attached excerpt from Java SSL debug log for reference
qtp373342545-25-scrub.log

How to reproduce?

  • Create a Jetty Server
  • Programmatically configure BouncyCastle security provider at index 1 at the beginning of the application
  • Programmatically configure SSL KMF algorithm to PKIX at the beginning of the application
Provider bcProvider = // Get an instance of BouncyCastle provider (BC or BCFIPS)
Security.insertProviderAt(bcProvider, 1);
Security.setProperty("ssl.KeyManagerFactory.algorithm", "PKIX");
  • Build and deploy the jetty server, configure it with SSL certificate that has DNS Names listed as SAN attributes
  • Create necessary host entries and access the server with listed DNS name in the FQDN.
@ramtech123 ramtech123 added the Bug For general bugs on Jetty side label Mar 5, 2022
@joakime
Copy link
Contributor

joakime commented Mar 5, 2022

On startup of the Jetty server, enable the "Dump After Start" boolean to true and report back the server dump.

Your server configuration is important to know now.
The dump will report all of the TLS protocols and cipher suites and their states (included/excluded, by config, or security policy of your JVM).
Also, configuring TLS via Security.setProperty("ssl.*", value) properties is not sufficient, you should properly configure the SslContextFactory.Server for your Connector to make things work. (example, the TrustManagerFactoryAlgorithm and/or KeyManagerFactoryAlgorithm needs to be set to PKIX)

Do you require the other PKIX configurations to work?

https://github.com/eclipse/jetty.project/blob/4a0c91c0be53805e3fcffdcdcc9587d5301863db/jetty-server/src/main/config/etc/jetty-ssl-context.xml#L45-L64

The most common reason for "no cipher suites in common" is that you either have a misconfiguration of the protocols/cipher suites on the server side, or some overlooked server certificate detail is being trapped by a JVM security policy that prevents the certificate from matching an available cipher suite. (eg: RSA at 2048 bits was at the low end of the JVM security policy back when JDK 11 was released, it wouldn't surprise me if your JDK 11.0.14 is now preventing your certificates from being used properly)

Keep an eye on the Java Crypto Roadmap
https://java.com/en/jre-jdk-cryptoroadmap.html

Finally, be aware of the bugs in the JVM and BouncyCastle when it comes to combination of PKIX and SNI ...
See: #5204

You might need to implement a custom SslContextFactory.Server to work around the internal alias mangling of BouncyCastle.
See SslContextFactoryTest at : https://github.com/eclipse/jetty.project/pull/5205/files#diff-765f2fde3d2f7a1acead27aef1db1618ffdad06db99a8a0361fb84ed459f933fR385-R401

@ramtech123
Copy link
Author

Hi @joakime ,

Thanks for the details, I will share some updates soon based on the pointers that you provided.

@ramtech123
Copy link
Author

ramtech123 commented Mar 9, 2022

Hello @joakime ,

Find some updates as below based on my verification with different Jetty versions:

  1. I tested my server with different versions of jetty-server 9.4.x. It turns out, the issue happens if I use v9.4.41 and above. With v9.4.40 and lower versions of jetty, I do not face this issue.

  2. Based on above, I guess it is something to do with fix Cipher preference may break SNI if certificates have different key types #6099 in jetty-9.4.41.v20210516, but in my case I have certificate with only RSA key

  3. I have enabled jetty debug logs as well as programmatically called dump() methods of SslContextFactory and SslContextFactory.Server and collected logs with 9.4.40 and 9.4.41. Refer the below:
    server-jetty-9.4.41-failed.log
    server-jetty-9.4.40-success.log

  4. Right now I can not test with 10.x or 11.x, but will verify when I get some time.

@ramtech123
Copy link
Author

ramtech123 commented Mar 9, 2022

You might need to implement a custom SslContextFactory.Server to work around the internal alias mangling of BouncyCastle.
See SslContextFactoryTest at : https://github.com/eclipse/jetty.project/pull/5205/files#diff-765f2fde3d2f7a1acead27aef1db1618ffdad06db99a8a0361fb84ed459f933fR385-R401

Tried the custom X509ExtendedKeyManager as indicated in above test. Its working fine with that workaround.

However, still trying to understand the differences between 9.4.40 and 9.4.41 indicated in the previous comment. Thanks.

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

3. I have enabled jetty debug logs as well as programmatically called dump() methods of SslContextFactory and SslContextFactory.Server and collected logs with 9.4.40 and 9.4.41. Refer the below:
server-jetty-9.4.41-failed.log
server-jetty-9.4.40-success.log

In the "failed" log you have ...

SslConnectionFactory@1c68d0db{SSL->HTTP/1.1} - STOPPED
|  +? Server@9be7319[provider=null,keyStore=null,trustStore=null] - STOPPED
|     +> trustAll=false
|     +> Protocol Selections
|     |  +> Enabled size=1
|     |  |  +> TLSv1.2
|     |  +> Disabled size=5
|     |     +> SSLv2Hello - ConfigExcluded:'SSLv2Hello', ConfigIncluded:NotSelected
|     |     +> SSLv3 - ConfigExcluded:'SSLv3', ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1 - ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1.1 - ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1.3 - ConfigIncluded:NotSelected
|     +> Cipher Suite Selections
|        +> Enabled size=2
|        |  +> TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|        |  +> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|        +> Disabled size=43
|           +> TLS_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_EMPTY_RENEGOTIATION_INFO_SCSV - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected

In the success log you have ...

+? SslConnectionFactory@4e50ae56{SSL->HTTP/1.1} - STOPPED
|  +? Server@1c68d0db[provider=null,keyStore=null,trustStore=null] - STOPPED
|     +> trustAll=false
|     +> Protocol Selections
|     |  +> Enabled size=1
|     |  |  +> TLSv1.2
|     |  +> Disabled size=5
|     |     +> SSLv2Hello - ConfigExcluded:'SSLv2Hello', ConfigIncluded:NotSelected
|     |     +> SSLv3 - ConfigExcluded:'SSLv3', ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1 - ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1.1 - ConfigIncluded:NotSelected JVM:disabled
|     |     +> TLSv1.3 - ConfigIncluded:NotSelected
|     +> Cipher Suite Selections
|        +> Enabled size=2
|        |  +> TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|        |  +> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|        +> Disabled size=43
|           +> TLS_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected
|           +> TLS_EMPTY_RENEGOTIATION_INFO_SCSV - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_128_GCM_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_CBC_SHA - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_CBC_SHA256 - ConfigIncluded:NotSelected
|           +> TLS_RSA_WITH_AES_256_GCM_SHA384 - ConfigIncluded:NotSelected

They appear to be similar.

In both, you only have 2 cipher suites enabled.
And you only have TLSv1.2 enabled.

|        |  +> TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|        |  +> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Which basically means your configuration only support key exchanges with ECDH256.
Both of those cipher suites are considered medium strength ciphers when used with an RSA certificate within Bouncycastle (bouncycastle's own documentation says this). You might want to review this configuration to include some stronger cipher suites.

With a default Jetty install, on OpenJDK 11.0.12 you have ...

|  += SslConnectionFactory@74d1dc36{SSL->http/1.1} - STARTED
|  |  += Server@18317edc[provider=null,keyStore=file:///home/joakim/code/jetty/distros/bases/ssl-base/etc/keystore,trustStore=file:///home/joakim/code/jetty/distros/bases/ssl-base/etc/keystore] - STARTED
|  |     +> trustAll=false
|  |     +> Protocol Selections
|  |     |  +> Enabled size=2
|  |     |  |  +> TLSv1.2
|  |     |  |  +> TLSv1.3
|  |     |  +> Disabled size=4
|  |     |     +> SSLv2Hello - ConfigExcluded:'SSLv2Hello'
|  |     |     +> SSLv3 - ConfigExcluded:'SSLv3' JVM:disabled
|  |     |     +> TLSv1 - JVM:disabled
|  |     |     +> TLSv1.1 - JVM:disabled
|  |     +> Cipher Suite Selections
|  |        +> Enabled size=27
|  |        |  +> TLS_AES_128_GCM_SHA256
|  |        |  +> TLS_AES_256_GCM_SHA384
|  |        |  +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_EMPTY_RENEGOTIATION_INFO_SCSV
|  |        +> Disabled size=18
|  |           +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$'
|  |           +> TLS_RSA_WITH_AES_128_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$', ConfigExcluded:'^TLS_RSA_.*$'
|  |           +> TLS_RSA_WITH_AES_128_CBC_SHA256 - ConfigExcluded:'^TLS_RSA_.*$'
|  |           +> TLS_RSA_WITH_AES_128_GCM_SHA256 - ConfigExcluded:'^TLS_RSA_.*$'
|  |           +> TLS_RSA_WITH_AES_256_CBC_SHA - ConfigExcluded:'^.*_(MD5|SHA|SHA1)$', ConfigExcluded:'^TLS_RSA_.*$'
|  |           +> TLS_RSA_WITH_AES_256_CBC_SHA256 - ConfigExcluded:'^TLS_RSA_.*$'
|  |           +> TLS_RSA_WITH_AES_256_GCM_SHA384 - ConfigExcluded:'^TLS_RSA_.*$'

Which has all of the following Cipher Suites enabled ...

|  |        |  +> TLS_AES_128_GCM_SHA256
|  |        |  +> TLS_AES_256_GCM_SHA384
|  |        |  +> TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
|  |        |  +> TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
|  |        |  +> TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
|  |        |  +> TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
|  |        |  +> TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
|  |        |  +> TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
|  |        |  +> TLS_EMPTY_RENEGOTIATION_INFO_SCSV

Did you mean to configured your SslContextFactory.Server to only include 2 Cipher Suites?

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

Also, both failed and success show ...

|  +? Server@9be7319[provider=null,keyStore=null,trustStore=null] - STOPPED

Your configuration seems to not be using a keystore/truststore.
Your configuration have decided to not configure the SslContextFactory.Server.setProvider(String) to use Bouncycastle.
That means default JVM Provider is used for ...

  • java.securit.cert.CertificateFactory.getInstance(String type, String provider)
  • java.securit.cert.CertStore.getInstance(String type, CertStoreParameters params, String provider)
  • javax.net.ssl.KeyManagerFactory.getInstance(String algorithm, String provider)
  • javax.net.ssl.TrustManagerFactory.getInstance(String algorithm, String provider)
  • java.security.SecureRandom.getInstance(String algorithm, String provider)

This kind of setup is possible, but it would require you to use Bouncycastle to create a javax.net.ssl.SSLContext and pass that created context into SslContextFactory.Server.setSslContext(Context context) before you start your Jetty server.

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

You might need to implement a custom SslContextFactory.Server to work around the internal alias mangling of BouncyCastle.
See SslContextFactoryTest at : https://github.com/eclipse/jetty.project/pull/5205/files#diff-765f2fde3d2f7a1acead27aef1db1618ffdad06db99a8a0361fb84ed459f933fR385-R401

Tried the custom X509ExtendedKeyManager as indicated in above test. Its working fine with that workaround.

However, still trying to understand the differences between 9.4.40 and 9.4.41 indicated in the previous comment. Thanks.

The relevant change is the SNI alias matching fix as part of #6099 - in 9.4.40 it would often select the wrong alias, either it would be first one, or it would pick a default one (if the hostnames are reported to Jetty as mangled, see JVM bug linked in that issue, along with explanation by @sbordet )

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

Also, your bouncycastle jars are incorrect for your JVM.

   |  +> jar:file:/opt/vmware/gateway/lib/admin-22.03.0.0-exec.jar!/BOOT-INF/lib/bcpkix-fips-1.0.5.jar!/
   |  +> jar:file:/opt/vmware/gateway/lib/admin-22.03.0.0-exec.jar!/BOOT-INF/lib/bc-fips-1.0.2.1.jar!/

See https://www.bouncycastle.org/latest_releases.html

Since you are using "OpenJDK Runtime Environment Zulu11.54+24-SA (build 11.0.14+9-LTS)", that
means you should be using the -jdk15on- classified artifacts from bouncycastle to have support for JDK 11.
This also means you need to be on Bouncycastle 1.46 (or newer) to have proper Java 11 support out of Bouncycastle.

@ramtech123
Copy link
Author

ramtech123 commented Mar 9, 2022

Thanks @joakime for detailed analysis. Please find below the responses:

Which basically means your configuration only support key exchanges with ECDH256.
Did you mean to configured your SslContextFactory.Server to only include 2 Cipher Suites?

Yeah, ciphhers are applied as below from a configurable list. So, this is not an issue.
sslContextFactory.setIncludeCipherSuites(serverSettings.getCipherSuites().split(","));

Your configuration seems to not be using a keystore/truststore.
Your configuration have decided to not configure the SslContextFactory.Server.setProvider(String) to use Bouncycastle.
That means default JVM Provider is used for ...

I have the JRE embedded in my product and java.security providers configuration overridden to set BouncyCastle as the first/default provider for the JVM, instead of passing provider as arguments all across the places.

you need to be on Bouncycastle 1.46 (or newer) to have proper Java 11 support out of Bouncycastle

Using FIPS variant of BC jars, so should be good

in 9.4.40 it would often select the wrong alias, either it would be first one, or it would pick a default one (if the hostnames are reported to Jetty as mangled

I went through the related JDK ticket, it reads the alias is manipulated from what's in the keystore (for example, "jetty") to an internal format such as "N.0.jetty" . So, I understand the workaround. Also the affected version seems to be Java 14.x, but may or may not be applicable to 11.x, so leave that.

What I'm still not able to understand is, with same Java + BouncyCastle version, and same SSL certificate with single alias having only RSA key, why would jetty 9.4.41+ should fail? As per the explanation, it should have been failing since 9.4.31 or so..which is not the case, even without overriding X509ExtendedKeyManager. Doesn't it mean some working behavior was broken while fixing the usecase of multi-key certificates in 9.4.41?

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

I went through the related JDK ticket, it reads the alias is manipulated from what's in the keystore (for example, "jetty") to an internal format such as "N.0.jetty" . So, I understand the workaround. Also the affected version seems to be Java 14.x, but may or may not be applicable to 11.x, so leave that.

The affected version is how the baseline normal JDK behaves.
You have Bouncycastle involved, so it has it's own bugs.

What I'm still not able to understand is, with same Java + BouncyCastle version, and same SSL certificate with single alias having only RSA key, why would jetty 9.4.41+ should fail? As per the explanation, it should have been failing since 9.4.31 or so..which is not the case, even without overriding X509ExtendedKeyManager. Doesn't it mean some working behavior was broken while fixing the usecase of multi-key certificates in 9.4.41?

The algorithm to select a certificate was wrong prior to 9.4.41.
If you had multiple certificates, we wouldn't know that, we would just get called multiple times and not know there were more possible certificates.
We wound up picking the first certificate in most cases. Even when blatantly wrong. Even when you have only 1 certificate.

@joakime
Copy link
Contributor

joakime commented Mar 9, 2022

I have the JRE embedded in my product and java.security providers configuration overridden to set BouncyCastle as the first/default provider for the JVM, instead of passing provider as arguments all across the places.

Still need to call SslContextFactory.Server.setSslContext(Context context) with an javax.net.ssl.SSLContext you created from Bouncycastle.

That technique of setting the first/default provider and hoping/praying it works everywhere in all situations is not considered best practice, and only applies to a limited number of scenarios, of the 5 JVM APIs we mentioned in an earlier comment on this issue, only 2 will work the way you expect with that technique (the KeyManagerFactory and TrustManagerFactory).

As far as BC and FIPS is concerned, you have 2 providers.
Now you have to split the behavior between the JSSE and JCE providers, which have different provider ids.

As <JRE_HOME>/lib/security/java.security entry

security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS

or in code ...

Security.addProvider(new BouncyCastleFipsProvider());
Security.addProvider(new BouncyCastleJsseProvider(“fips:BCFIPS”));

That's what's recommended by Bouncycastle documentation.

In either approach above (java.security or code) you are still expected to configure Jetty's SslContextFactory.Server properly.

SslContextFactory.Server serverSslFactory = new SslContextFactory.Server();
// This requirement will skip internal behaviors in SslContextFactory.Server attempting to create an SSLContext using JVM defaults.
// This will properly setup Jetty, the SSLContext, and SSLEngine it uses for BouncyCastle.
// It will also skip all of the internal behaviors based on JVM default assumption.
// (a requirement if you use anything but the JVM default providers, eg: Bouncycastle, or the IBM JDK)
// Past Jetty users of Bouncycastle report that this is not optional and required for success.
// Without this change, the SslContextFactory.Server will stomp on your Security.setProperty() settings in ways
// that counteract your desired end goal.
SSLContext fipsContext = SSLContext.getInstance("TLS", "BCJSSE");
serverSslFactory.setSslContext(fipsContext);
// more SslContextFactory.Server configuration here ...
// This will allow certificate handling and random number generation to use Bouncycastle specific behaviors, not JVM default behaviors.
// This is optional, but past Jetty users of Bouncycastle report this as necessary for their behaviors.
// What was seen in the past was a mix of usage between the certificate handling and the protocols and the cipher suites.
// Some behaviors were non-BC provider, some were BC, some were BCJSSE.
// Aligning the providers for each produced better and more consistent behavior.
// But again, this is technically optional.
serverSslFactory.setProvider("BCJSSE");
// the rest of the init of your ServerConnector / SslConnectionFactory
ServerConnector sslConnector = new ServerConnector(server,
            new SslConnectionFactory(serverSslFactory, HttpVersion.HTTP_1_1.asString()),
            new HttpConnectionFactory(httpsConfig));

@ramtech123
Copy link
Author

Thanks @joakime , its more clear now.
And also thanks for the observations and suggestions on sslContext and other APIs. Will look into that.

Closing the ticket as all the queries have been answered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side
Projects
None yet
Development

No branches or pull requests

2 participants