I have a simple java test program, that opens an SSL connection to a server. This is on a redhat linux system, actually a UBI 9.5 container. The container is running on a RHEL 9.5 system, with crypto-policy set to FIPS:NO-ENFORCE-EMS. I have tried the container built with crypto-policy set to FIPS and FIPS:NO-ENFORCE-EMS.
I understand that NO-ENFORCE-EMS
should only be needed trying to connect from RH linux 9.2, or maybe connecting to older systems, or something. For this debugging, I am trying to connect to a server in the container itself. The JVM is openjdk17
I understand that whether the crypto policy is FIPS or FIPS:NO-ENFORCE-EMS that a client will OFFER EMS. The Java client using JSSE should support EMS, which has been supported since JDK 11.
(1) Why is the java client not OFFERING the EMS extension?
BUT the java test client run with debugging enabled reports:
java -Djavax.net.debug=ssl,handshake -cp . SSLTest 127.0.0.1 8095
ExtendedMasterSecretExtension.java:123|Ignore unavailable extended_master_secret extension
Some more information:
$ java --version
openjdk 17.0.14 2025-01-21 LTS
OpenJDK Runtime Environment (Red_Hat-17.0.14.0.7-1) (build 17.0.14+7-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.14.0.7-1) (build 17.0.14+7-LTS, mixed mode,
sharing)
$ update-crypto-policies --show
FIPS
$ cat /etc/redhat-release
Red Hat Enterprise Linux release 9.5 (Plow)
The test program I am using is below. I compile it and transfer the class file into the container. It also needs a SSL server running. I can update this question with some commands to run an SSL server, but I'm hoping someone just has an explanation.
import javax.net.ssl.*;
public class SSLTest {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java -cp . SSLTest crowd 8095");
}
var host = args[0];
var port = Integer.parseInt(args[1]);
try {
System.out.println("Connecting to " + host + ":" + port);
// Use the default SSL socket factory (ensure your truststore is set up if needed)
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
try (SSLSocket socket = (SSLSocket) factory.createSocket(host, port)) {
// Start the handshake manually to see detailed debug info.
socket.startHandshake();
System.out.println("SSL handshake completed successfully.");
}
} catch (Exception e) {
System.err.println("SSL handshake failed: " + e.getMessage());
e.printStackTrace();
}
}
}
Running that like this, yields the ClientHello below. I reduced the schemes to make the ClientHello smaller, and to match the cipher suite and signature that openssl s_client succeeds in negotiating.
[root@ubi9fips-ssl-test-9-5 /]# java -Djdk.tls.client.protocols=TLSv1.2 -Djavax.net.debug=ssl,handshake -Djdk.tls.signatureSchemes=rsa_pkcs1_sha384 -Djdk.tls.client.cipherSuites=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -cp . SSLTest 127.0.0.1 4433 2>&1
<snip>
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.913 GMT|SupportedGroupsExtension.java:365|Ignore inactive or disabled named group: ffdhe6144
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.913 GMT|SupportedGroupsExtension.java:365|Ignore inactive or disabled named group: ffdhe8192
javax.net.ssl|INFO|10|main|2025-02-25 21:19:07.913 GMT|AlpnExtension.java:182|No available application protocols
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.913 GMT|SSLExtensions.java:272|Ignore, context unavailable extension: application_layer_protocol_negotiation
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.914 GMT|ExtendedMasterSecretExtension.java:123|Ignore unavailable extended_master_secret extension
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.914 GMT|SSLExtensions.java:272|Ignore, context unavailable extension: extended_master_secret
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.914 GMT|SessionTicketExtension.java:353|Stateless resumption supported
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.920 GMT|ClientHello.java:639|Produced ClientHello handshake message (
"ClientHello": {
"client version" : "TLSv1.2",
"random" : "6CBDF2CC3EA1A3DCBC9318AE39E2EE1B0CD2B1F4F5B6B8FD05EB5B5640FC2A81",
"session id" : "",
"cipher suites" : "[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030)]",
"compression methods" : "00",
"extensions" : [
"status_request (5)": {
"certificate status type": ocsp
"OCSP status request": {
"responder_id": <empty>
"request extensions": {
<empty>
}
}
},
"supported_groups (10)": {
"versions": [secp256r1, secp384r1, secp521r1]
},
"ec_point_formats (11)": {
"formats": [uncompressed]
},
"status_request_v2 (17)": {
"cert status request": {
"certificate status type": ocsp_multi
"OCSP status request": {
"responder_id": <empty>
"request extensions": {
<empty>
}
}
}
},
"session_ticket (35)": {
<empty>
},
"signature_algorithms (13)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha224, rsa_sha224]
},
"supported_versions (43)": {
"versions": [TLSv1.2]
},
"signature_algorithms_cert (50)": {
"signature schemes": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, ecdsa_sha224, rsa_sha224]
},
"renegotiation_info (65,281)": {
"renegotiated connection": [<no renegotiated connection>]
}
]
}
)
javax.net.ssl|DEBUG|10|main|2025-02-25 21:19:07.922 GMT|Alert.java:238|Received alert message (
"Alert": {
"level" : "fatal",
"description": "handshake_failure"
}
)
javax.net.ssl|ERROR|10|main|2025-02-25 21:19:07.922 GMT|TransportContext.java:375|Fatal (HANDSHAKE_FAILURE): Received fatal alert: handshake_failure
And although enforcing ems is not set, the handshake fails immediately after the hello.
Regarding why JSSE is not offering EMS in the ClientHello, here is the explanation.
When FIPS-mode is turned on, the Red Hat Build of OpenJDK ensures all the cryptographic primitives come from the SunPKCS11 security provider, configured with NSS as its PKCS #11 back-end. This is a FIPS requirement, since NSS acts as the cryptographic module, subject to the FIPS-validation process. Even though the EMS extension is implemented in the SunJSSE provider, it requires a specific key-derivation primitive to be available through the SunPKCS11-NSS-FIPS provider. Given this primitive is not currently available, SunJSSE automatically disables the extension.
Why is it not available? Although an NSS vendor-specific mechanism exists, this hasn't been implemented in SunPKCS11 due to the lack of a standard PKCS #11 way of doing it. This is expected to be fixed in PKCS #11 v3.2, and we will be able to implement the required SunPKCS11 enhancement allowing the support of EMS in FIPS-mode (when that version of the standard is released).