Hi

After upgrade to 2.3.6 tomcat ssl keystore password no longer works

server:
  port: 10002
  ssl:
    key-store: xxx.jks
    key-password: xxxxx
Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
    at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:785)
    at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:56)
    at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:224)
    at sun.security.provider.JavaKeyStore$DualFormatJKS.engineLoad(JavaKeyStore.java:70)
    at java.security.KeyStore.load(KeyStore.java:1445)
    at org.apache.tomcat.util.security.KeyStoreUtil.load(KeyStoreUtil.java:69)
    at org.apache.tomcat.util.net.SSLUtilBase.getStore(SSLUtilBase.java:216)
    at org.apache.tomcat.util.net.SSLHostConfigCertificate.getCertificateKeystore(SSLHostConfigCertificate.java:207)
    at org.apache.tomcat.util.net.SSLUtilBase.getKeyManagers(SSLUtilBase.java:282)
    at org.apache.tomcat.util.net.SSLUtilBase.createSSLContext(SSLUtilBase.java:246)
    at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:97)

The configuration for keystore retrieved from a cloud config server, and works fine on 2.3.5 release

Probably related to the following fix in 2.3.6 but from the quick look I do not see an issue there https://github.com/spring-projects/spring-boot/issues/24052

Regards Alex M

Comment From: scottfrederick

java.io.IOException: Keystore was tampered with, or password was incorrect

This error is related to the keystore password, which you are not setting (i.e. with server.ssl.key-store-password).

When any of the keystore-related properties are set on the Tomcat connection, Tomcat initializes a new certificate object with a default keystore password value. Prior to the change in #24052, Spring Boot would override the Tomcat default keystore password with null when server.ssl.key-store-password was not set. After this change the default password value is retained, which doesn't match your keystore's password.

Can you try setting server.ssl.key-store-password to see if this fixes your problem?

Setting the keystore password seems like a best-practice when setting a keystore, so I'm not sure if there's anything more we should do here, but I'll mark this issue for team attention to discuss whether this new behavior is correct or if there are further changes that should be made in Spring Boot.

Comment From: alexmond

Hi Scott, thanks for a quick response.

Sorry for the delay, I had to test the issue locally. So looks like the issue is mostly affecting JKS format. Up to 2.3.5 version either key-password or key-store-password settings can be used for both store and key password, and It still works the same way in Netty.

Right now, just replacing the key-password with key-store-password (in a couple hundred yaml files) solves our issue.

Since the JKS format is deprecated we will be migrating to p12 where both passwords have to be set and this issue will get resolved, but I think some discussion about backward compatibility will still make sense.

You are welcome to close this issue Alex M

Comment From: scottfrederick

Up to 2.3.5 version either key-password or key-store-password settings can be used for both store and key password, and It still works the same way in Netty.

The Netty connection setup in Spring Boot uses the server.ssl.key-store-password property as-is, allowing it to be null if not set. server.ssl.key-password is not used for the store password. The inverse is true though - if server.ssl.key-password is not set then server.ssl.key-store-password is used for the key password.

The difference in 2.3.6 is that a value of null for server.ssl.key-store-password will result in the Tomcat code using changeit for the key store password instead of using null as the default. After some further team discussion, we believe this is the correct behavior for Spring Boot.

Right now, just replacing the key-password with key-store-password (in a couple hundred yaml files) solves our issue.

Another option is to implement your own TomcatConnectorCustomizer and set any keystore fields in code, if that's easier than changing yaml files.

    @Configuration
    public class TomcatConfiguration {
        @Bean
        public TomcatConnectorCustomizer customizer() {
            return connector -> {
                AbstractHttp11JsseProtocol<?> protocol = (AbstractHttp11JsseProtocol<?>) connector.getProtocolHandler();
                protocol.setKeystorePass(/* password value */);
                protocol.setKeyPass(/* password value */);
            };
        }
    }

Comment From: inad9300

It'd be worth including a mention to this breaking change in the release notes!