Followed the answer - Encrypt tomcat keystore password
Extend the Http11Nio2Protocol class like this:
public class ReSetHttpProtocol extends Http11Nio2Protocol {
@Override
public void setKeystorePass(String certificateKeystorePassword) {
Decoder decoder = Base64.getDecoder();
String password = new String(decoder.decode(certificateKeystorePassword));
super.setKeystorePass(password);
}
}
I build a maven project and put the jar in tomcat/lib.
/conf/server.xml:
<Connector port="8443" protocol="com.fine.security.ReSetHttpProtocol"
maxThreads="150" scheme="https" SSLEnabled="true" relaxedQueryChars="^{}[]|"" >
<SSLHostConfig sslEnabledProtocols="TLSv1.2" ciphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" >
<Certificate certificateKeystoreFile="(absolute path of the .pfx file)"
certificateKeystoreType="JKS" certificateKeystorePassword="(encrypted password)" />
</SSLHostConfig>
</Connector>
When I use the origin password, it works normally. Then I changed certificateKeystorePassword, it failed, and the error info in catalina.out is:
** [main] org.apache.catalina.core.StandardService.initInternal Failed to initialize connector [Connector[com.fine.security.ReSetHttpProtocol-8443]]
org.apache.catalina.LifecycleException: Protocol handler initialization failed
at org.apache.catalina.connector.Connector.initInternal(Connector.java:1032)
at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
at org.apache.catalina.core.StandardService.initInternal(StandardService.java:552)
at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
at org.apache.catalina.core.StandardServer.initInternal(StandardServer.java:848)
at org.apache.catalina.startup.Catalina.load(Catalina.java:662)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:472)
Caused by: java.lang.IllegalArgumentException: keystore password was incorrect
at org.apache.catalina.startup.Catalina.load(Catalina.java:639)
at org.apache.catalina.startup.Catalina.load(Catalina.java:662)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:472)
Caused by: java.lang.IllegalArgumentException: keystore password was incorrect
at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:100)
at org.apache.tomcat.util.net.AbstractJsseEndpoint.initialiseSsl(AbstractJsseEndpoint.java:72)
at org.apache.tomcat.util.net.Nio2Endpoint.bind(Nio2Endpoint.java:158)
at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:1118)
at org.apache.tomcat.util.net.AbstractJsseEndpoint.init(AbstractJsseEndpoint.java:223)
at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:587)
at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:74)
at org.apache.catalina.connector.Connector.initInternal(Connector.java:1030)
... 13 more
Caused by: java.io.IOException: keystore password was incorrect
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2059)
at sun.security.provider.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:238)
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:206)
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:98)
... 20 more
Caused by: java.security.UnrecoverableKeyException: failed to decrypt safe contents entry: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
It seems like "keystore password was incorrect".
I searched 'setKeystorePass' in tomcat source code, but I can't find where it's called. So why can I extend the method to change the protocol configuration?
Thanks in advance!
The reason you can't find any call to
setKeystorePassis that it is performed by reflection, mostly using IntrospectionUtils#setProperty (cf. Commons Digester for details).The configuration XML is used to create Java objects:
<Connector protocol="com.fine.security.ReSetHttpProtocol"creates a connector and protocol handler,<SSLHostConfig>creates an instance ofSSLHostConfigand the closing tag</SSLHostConfig>callsConnector#addSslHostConfigon the connector,<Certificate>which creates an instance ofSSLHostConfigCertificate, configures it and at the end callsSSLHostConfig#addCertificate.<Connector>,<SSLHostConfig>and<Certificate>tags call the appropriate setters orsetPropertyon the corresponding objects. E.g.port="8443"calls the settersetPort(), butSSLEnabled="true"callssetProperty("SSLEnabled", "true")because there is no setter,So, if you want the bootstrap code to call your implementation of
setKeystorePasswordyou need to define the connector in this way:This syntax is obsolete since Tomcat 8.5 and was removed in Tomcat 10, but it is the easiest way to accomplish what you want.
If you wish to use the new syntax, you'll need to do more modifications to the standard
Http11NioProtocol:SSLHostConfigCertificatethat overrides justsetCertificateKeystorePassword,SSLHostConfigCertificatethe overridesaddCertificatewhich will call the originaladdCertificatewith the proxy from point 1,addSSLHostConfigin your protocol by calling the originaladdSSLHostConfigwith the proxy from the previous point.PS: you are not encrypting your password, you are encoding it. To decode an encoded password you just need to know the transformation (in your case Base64), to decrypt an encrypted password you need to know the encryption algorithm and a secret key. Then you'll probably need to encrypt the encryption key and so on... I find both procedures useless.