jetty.project icon indicating copy to clipboard operation
jetty.project copied to clipboard

SniX509ExtendedKeyManager causes exception: "FIPS mode: only SunJSSE KeyManagers may be used"

Open robertgrabowski opened this issue 7 years ago • 14 comments

We have set up SunJSSE with a FIPS-compliant cryptography library, as documented here: https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/FIPS.html Among others, this puts the SunJSSE library into a special "FIPS mode".

We use Jetty 9.4.6, and have configured an SslContextFactory for HTTPS with an appropriate FIPS-compliant key store. When we start an HTTP server, we get an exception:

java.security.KeyManagementException: FIPS mode: only SunJSSE KeyManagers may be used
	at sun.security.ssl.SSLContextImpl.chooseKeyManager(SSLContextImpl.java:149)
	at sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:66)
	at javax.net.ssl.SSLContext.init(SSLContext.java:282)
	at org.eclipse.jetty.util.ssl.SslContextFactory.load(SslContextFactory.java:306)
	at org.eclipse.jetty.util.ssl.SslContextFactory.doStart(SslContextFactory.java:220)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:113)
	at org.eclipse.jetty.server.SslConnectionFactory.doStart(SslConnectionFactory.java:72)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:131)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:113)
	at org.eclipse.jetty.server.AbstractConnector.doStart(AbstractConnector.java:270)
	at org.eclipse.jetty.server.AbstractNetworkConnector.doStart(AbstractNetworkConnector.java:81)
	at org.eclipse.jetty.server.ServerConnector.doStart(ServerConnector.java:236)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
	at org.eclipse.jetty.server.Server.doStart(Server.java:431)
	... 7 more

This is caused by the following check in SSLContextImpl in the SunJSSE library:

   if (SunJSSE.isFIPS()) {
        // In FIPS mode, require that one of SunJSSE's own keymanagers
        // is used. Otherwise, we cannot be sure that only keys from
        // the FIPS token are used.
        if ((km instanceof X509KeyManagerImpl)
                    || (km instanceof SunX509KeyManagerImpl)) {
            return (X509ExtendedKeyManager)km;
        } else {
            // throw exception, we don't want to silently use the
            // dummy keymanager without telling the user.
            throw new KeyManagementException
                ("FIPS mode: only SunJSSE KeyManagers may be used");
         }
    }

(see http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/sun/security/ssl/SSLContextImpl.java#l125)

Via debugging, we can see that SslContextFactory.getKeyManagers() obtains a SunX509KeyManagerImpl from the default KeyManagerFactory, which would pass the check above - but then it wraps it into an SniX509ExtendedKeyManager and returns it instead:

   if (!_certHosts.isEmpty() || !_certWilds.isEmpty())
    {
        for (int idx = 0; idx < managers.length; idx++)
        {
           if (managers[idx] instanceof X509ExtendedKeyManager)
                managers[idx] = new SniX509ExtendedKeyManager((X509ExtendedKeyManager)managers[idx]);
        }
    }
    ...
    return managers;

(see https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java#L1125)

Since the SniX509ExtendedKeyManager is not "approved" by SunJSSE in FIPS mode, the exception is thrown and the HTTP server cannot start.

Note that we were able to run a FIPS-compliant HTTP server with Jetty 8.x, because it does not wrap the KeyManager.

Is there any way to prevent Jetty from using the SniX509ExtendedKeyManager?

robertgrabowski avatar Nov 30 '17 15:11 robertgrabowski

Firstly it would be good if you could open an issue against the JVM asking them to provide a method that derived KeyManagers can be FIPs compliant.

So we wrap a KeyManager with an AliasesKeyManager if there are certificate aliases. We may further wrap it with an SNI manager if there are hosts or wildcards for the certificates. So you can prevent the wrapping by not having any certificates with hosts or wildcards. The later of these seams straight forward enough, but I think that all certificates will have a host (CN).

So we might be a bit overzealous with our SNI wrapping! I will investigate.

For now, you will have to override the getKeyManagers method... which is a bit of a hassle as you need the passwords and there are no getters... however something like the following should work:

    public static class FIPSSslContextFactor extends SslContextFactory
    {
        private Password _keyStorePassword;
        private Password _keyManagerPassword;
        
        @Override
        public void setKeyStorePassword(String password)
        {
            super.setKeyStorePassword(password);
            if (password != null)
                _keyStorePassword = newPassword(password);
        }

        @Override
        public void setKeyManagerPassword(String password)
        {
            super.setKeyManagerPassword(password);
            if (password != null)
                _keyManagerPassword = newPassword(password);
        }

        @Override
        protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
        {
            KeyManager[] managers = null;

            if (keyStore != null)
            {
                KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(getKeyManagerFactoryAlgorithm());
                keyManagerFactory.init(keyStore, _keyManagerPassword == null ? (_keyStorePassword == null ? null : _keyStorePassword.toString().toCharArray()) : _keyManagerPassword.toString().toCharArray());
                managers = keyManagerFactory.getKeyManagers();
            }
            return managers;
        }
    }

Of course certificate aliases and SNI will not work!

gregw avatar Nov 30 '17 17:11 gregw

This problem is pretty common in the Java world (since 2009 and the evolution of things like SSL/TLS, certificate minimums, certificate capabilities, etc. The FIPs standards just haven't kept up)

  • spring-projects/spring-boot#9351
  • http://tomcat.10.x6.nabble.com/Problem-in-configuring-tomcat-for-PKCS-11-for-HSM-td2162211.html

joakime avatar Nov 30 '17 18:11 joakime

@gregw, @joakime: Thank you for the very quick response! The proposed derived SslContextFactory class worked right out of the box! This solution is OK for us.

Of course, it is a bit dependent on the concrete Jetty implementation, which may change in future releases, and we would prefer a solution where you can configure SslContextFactory directly to not wrap the key managers. (Or at least have getters for the passwords, to reduce the dependency on the implementation.)

But I do see the point of the SniX509ExtendedKeyManager, and it would be great if it could be used as-is even in FIPS mode. Then again, I suppose the check in the JDK is so strict because they want to be on the safe side. Kind of a difficult situation...

robertgrabowski avatar Dec 01 '17 09:12 robertgrabowski

@robertgrabowski great! However I will keep this issue open as I agree it would be good to more simply allow wrapping to be prevented. I still thing it may be a bug that we are wrapping if certificates have CNs. Perhaps we should only SNI wrap if we have more than 1 CN or wildcards? @joakime thoughts?

gregw avatar Dec 01 '17 14:12 gregw

Team, would like to confirm what is the solution for SNI when running in Fips mode. It doesn't look like SNI works when we enable FIPs in Jetty? Any workarounds?

sirspud avatar Oct 30 '18 22:10 sirspud

This issue has been automatically marked as stale because it has been a full year without activit. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 20 '19 12:11 stale[bot]

I have a product that has two separate instances of Jetty (9.4.18) running (long history). One is embedded, so I created the override SslContextFactory class and it works fine. However, the other Jetty is a vanilla, unaltered copy of Jetty where we basically unpack the Jetty tarball and run start.jar, configuring everything via .xml and .ini files. Is there a way to configure Jetty to use the custom context factory from my .jar file instead of its own SslContextFactory?

Dean-J123 avatar Mar 12 '20 18:03 Dean-J123

@Dean-J123 be aware of the security reports against Jetty.

https://www.eclipse.org/jetty/security-reports.html

9.4.18 is subject to several of them.

joakime avatar Mar 12 '20 18:03 joakime

@Dean-J123 sorry but it's unclear what you are doing, it's unclear what does not work and from your comment not clear if it is related to this issue?

sbordet avatar Mar 18 '20 11:03 sbordet

Sorry - I'll clarify. We run two separate instances of Jetty servers. The first runs as part of our server software (on its own thread in the server process) providing a RESTful interface for managing our hardware. We are able to customize this progrommatically without much effort. The other is an unaltered copy of Jetty that runs as its own process and simply responds to requests on port 80 and 443 for an index.html "Welcome" page. To meet government requirements we have to run all Java RTEs in FIPS mode. This means when a certificate is uploaded that contains wild cards or multiple hosts in the CN/SAN, Java is throwning the exception "java.security.KeyManagementException: FIPS mode: only SunJSSE KeyManagers may be used". For the in-process server we were able to use the solution from gregw on Nov 30, 2017 above - we created a FIPSSslContextFactory class as described. However, for the standalone (unaltered) instance of Jetty, I do not find any .xml or .ini file that allows me to configure the server to use our custom class. So:

  1. If there is a way configure an unaltered Jetty server to use a custom SslContextFactory, please let me know.
  2. If there isn't such a way, could a modification be made to add this ability? For example, in the ssl.ini add: ## Specify a custom SSL context factory jetty.ssl.contextFactory=com.company.CustomSslContextFactory
  3. If the above isn't possible, then provide a way to disable SNI. This wouldn't meet all our needs (we also add custom syslogging to our Factory), but it would prevent Java from throwing an exception. For example, add the following line to ssl.ini: ## Allow disabling SNI. Required when running Java in FIPS mode and the server will ## hold certificates with wild cards or multiple hosts in the CN/SAN jetty.ssl.disableSNI=true

Dean-J123 avatar Mar 27 '20 22:03 Dean-J123

This issue has been automatically marked as stale because it has been a full year without activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 02 '21 17:06 stale[bot]

This issue has been automatically marked as stale because it has been a full year without activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Jun 06 '22 00:06 github-actions[bot]

This issue has been automatically marked as stale because it has been a full year without activity. It will be closed if no further activity occurs. Thank you for your contributions.

github-actions[bot] avatar Jun 15 '23 00:06 github-actions[bot]