Bug #95039 KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used
Submitted: 16 Apr 2019 15:36 Modified: 13 Mar 16:35
Reporter: Przemyslaw Bielicki Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S1 (Critical)
Version:5.7.x, 8.0.x OS:Any
Assigned to: Filipe Silva CPU Architecture:Any

[16 Apr 2019 15:36] Przemyslaw Bielicki
Description:
When connecting to MySQL 5.7 using either JDBC driver 5.1.47 or 8.0.12 I get this exception:

java.sql.SQLNonTransientConnectionException: KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
        at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:70)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:832)
        at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
        at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240)
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:207)
        at java.sql.DriverManager.getConnection(DriverManager.java:664)
        at java.sql.DriverManager.getConnection(DriverManager.java:208)
        ... many more
Caused by: com.mysql.cj.exceptions.SSLParamsException: KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used
        at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:571)
        at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:301)
        at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:206)
        at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99)
        at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:347)
        at com.mysql.cj.protocol.a.NativeAuthenticationProvider.negotiateSSLConnection(NativeAuthenticationProvider.java:796)
        at com.mysql.cj.protocol.a.NativeAuthenticationProvider.proceedHandshakeWithPluggableAuthentication(NativeAuthenticationProvider.java:503)
        at com.mysql.cj.protocol.a.NativeAuthenticationProvider.connect(NativeAuthenticationProvider.java:220)
        at com.mysql.cj.protocol.a.NativeProtocol.connect(NativeProtocol.java:1443)
        at com.mysql.cj.NativeSession.connect(NativeSession.java:165)
        at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:952)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:822)
        ... 190 more
Caused by: java.security.KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used
        at sun.security.ssl.SSLContextImpl.chooseTrustManager(SSLContextImpl.java:120)
        at sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:83)
        at javax.net.ssl.SSLContext.init(SSLContext.java:282)
        at com.mysql.cj.protocol.ExportControlled.getSSLContext(ExportControlled.java:565)
        ... 201 more

IMO bug is in com.mysql.jdbc.ExportControlled class in this piece of code:

for (TrustManager tm : origTms) {
  // wrap X509TrustManager or put original if non-X509 TrustManager
  tms.add(tm instanceof X509TrustManager ? new X509TrustManagerWrapper((X509TrustManager) tm, verifyServerCert) : tm);
}

Later on SSLContext is initialized:

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms.toArray(new TrustManager[tms.size()]), null);

and the last call is finally ending up in sun.security.ssl.SSLContextImpl#chooseTrustManager method:

if (SunJSSE.isFIPS() &&
    !(tm[i] instanceof X509TrustManagerImpl)) {

    throw new KeyManagementException("FIPS mode: only SunJSSE TrustManagers may be used");
}

The problem is that we are in FIPS mode so SunJSSE.isFIPS() is True but (tm[i] instanceof X509TrustManagerImpl)) is always False as it is wrapped by X509TrustManagerWrapper class.
On the other hand X509TrustManagerWrapper.origTm is instance of X509TrustManagerImpl, so it's the wrapper that messes up the situation here.

How to repeat:
Modify standard JDK to be FIPS compliant with the following steps.
1. Modify the java.policy adding following lines:

        //FIPS and Bouncy castle required permissions
        permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec";
        permission org.bouncycastle.crypto.CryptoServicesPermission "tlsAlgorithmsEnabled";

2. Modify the java.security:

#
# FIPS mode provided by Bouncy Castle
#
security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
security.provider.2=com.sun.net.ssl.internal.ssl.Provider BCFIPS
security.provider.3=sun.security.provider.Sun

#
# Default keystore type.
#
keystore.type=bcfks

3. Install the Bouncy Castle FIPS provider by placing following JARs at $JAVA_HOME/jre/lib/ext/ 

https://downloads.bouncycastle.org/fips-java/bc-fips-1.0.0.jar
https://downloads.bouncycastle.org/fips-java/bcmail-fips-1.0.1.jar
https://downloads.bouncycastle.org/fips-java/bcpkix-fips-1.0.1.jar

4. Write simples possible Java application connecting to MySQL database using MySQL Connector / J (version 5.1.47 or 8.0.12)

Suggested fix:
Modify tms array filling it with wrappers only after SSLContext has been initialized.
I'm not sure why it's done before. If it's not needed this fix would work.
[17 Apr 2019 8:18] Przemyslaw Bielicki
It appears this problem came up in our staging environment where MySQL server had disabled SSL. 

It looks like it is working as expected with DB server with SSL enabled.
[18 Apr 2019 13:25] Przemyslaw Bielicki
In fact this is still a valid case.
It was working fine with SSL but only with MySQL server 5.6.
Starting with 5.7 (also checked with 8.0) it is failing with the exceptions above.
[18 Apr 2019 13:26] Przemyslaw Bielicki
updated affected versions
[29 Oct 2020 11:30] Charu Joshi
Is there any resolution / workaround to this issue?
[6 Nov 2020 19:34] Filipe Silva
Hi Przemyslaw,

Thank you for this bug report. My apologies for taking so long to verify it.

This is a very tricky issue. On one hand FIPS support on Sun's JSEE implementation has always been an experimental feature, and even removed in Java 9 (https://bugs.openjdk.java.net/browse/JDK-8217907), on the other hand, the default JSSE implementation doesn't offer means for validating server certificates thoroughly - a feature Connector/J needs to validate server identity, for example - so we had to implement our own TrustManagers. The problem arises when you combine both.

So, there's no easy solution. Neither we can stop using our X509TrustManagerWrapper nor it works with JSSE default implementation with FIPS enabled.

However, it is possible to replace the default JSSE provider by BouncyCastle's one, same as you already do with the main security provider:
- Get BuncyCastle's JSSE implementation: bctls-fips-1.0.10.jar
- Add these two lines to your java.security file:
    security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
    security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
- Reset Sun's JSSE provider to its original value:
    security.provider.{N}=com.sun.net.ssl.internal.ssl.Provider

I don't know about additional configurations you may have to do, but this should give you an option to get going.

Can you please confirm if this is a good solution for you?

Thank you,
[7 Dec 2020 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
[26 Jan 2021 7:27] Nagesh Kunchakarra
Hi,

we are facing the same issue in our environment. However, we are using safelogic's cryptocomply instead of bouncycastle as FIPS provider, although I believe the underlying component is bouncycastle itself. 

Did the suggestion of using bouncy castle's jsse provider work? please let us know.

Also, as przemyslaw suggested, isnt it possible to move the trustmanager warpping after the FIPS check?

Thanks,
Nagesh
[9 Mar 2021 13:01] neeraj kumar
These settings worked for me in my cassandra db connection in FIPS mode.

However, it is possible to replace the default JSSE provider by BouncyCastle's one, same as you already do with the main security provider:
- Get BuncyCastle's JSSE implementation: bctls-fips-1.0.10.jar
- Add these two lines to your java.security file:
    security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
    security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
- Reset Sun's JSSE provider to its original value:
    security.provider.{N}=com.sun.net.ssl.internal.ssl.Provider<Removed the BCFIPS from here actually works>

Thanks,
[11 Jul 2023 15:20] Daniel So
Posted by developer:
 
Added the following entry to the Connector/J 8.1.0 changelog: 

"When using a Java 8 to 12 JRE, if JSSE was configured to use a FIPS provider, attempts to establish secure connections to a MySQL Server failed with a KeyManagement Exception, complaining that "FIPS mode: only SunJSSE TrustManagers may be used." It was because a custom TrustManager implemented by Connector/J was invoked but, in that case, then rejected by the default implementation of SunJSSE. With this patch, a new connection property, fipsCompliantJsse, is created for users to instruct Connector/J not to use its custom TrustManager implementation. Additionally new connection properties are created for users to specify the security providers from which Connector/J will request the corresponding security materials such as key and trust manager factories or SSL context provider. See Issue with FIPS Mode for JSSE for details."