How to replace the static keystore with a dynamic one for a spring-boot-application

I want to add/replace SSL certificates dynamically to my spring boot (tomcat) application without the need to restart it. I have a long way to go still, but currently I am stuck with a javax.crypto.BadPaddingException and don’t know why.

So here is what I am trying to do.

First, I am defining my own TomcatServletWebServerFactoryin order to set a SslStoreProvider.

public class PathWatchingTomcatFactory extends TomcatServletWebServerFactory {
  public PathWatchingTomcatFactory(PathWatchingSslStoreProvider pathWatchingSslStoreProvider) {

My PathWatchingSslStoreProvider provides a PathMatchingKeyStore.

public class PathWatchingSslStoreProvider implements SslStoreProvider {
  private final PathWatchingKeyStore pathWatchingKeyStore;

  public PathWatchingSslStoreProvider(PathWatchingKeyStore pathWatchingKeyStore) {
    this.pathWatchingKeyStore = pathWatchingKeyStore;

  public KeyStore getKeyStore() throws Exception {
    return pathWatchingKeyStore;

The PathWatchingKeyStore seems only necessary in order to provide a service provider interface to it.

public class PathWatchingKeyStore extends KeyStore {
  protected PathWatchingKeyStore(
    PathWatchingKeyStoreSpi pathWatchingKeyStoreSpi,
    DynamicProvider provider)
    super(pathWatchingKeyStoreSpi, provider, KeyStore.getDefaultType());


  private void initialize() {
    // Loading a keystore marks it internally as initialized and only
    // initialized keystores work properly. Unfortunately
    // nobody initializes this keystore. So we have to do it
    // ourselves.
    // Internally the keystore will delegate loading to the
    // KeyStoreSpi, which, in our case is the PathWatchingKeyStoreSpi.
    try {
      load(null, null);
    catch (Exception e) {

Now, on startup, the keystore will be loaded. And because I provide a SslStoreProvider my keystore will be loaded by the SslStoreProviderUrlStreamHandlerFactory by requesting my PathWatchingKeyStoreSpi to store its keystore into a ByteArrayOutputStream whose content is finally copied into the InputStream that is used to load an internally used keystore.

In the following code snippet you can see how I try to write the contents of an already existing keystore. No dynamic at all right now. I only want to see if the spring boot application starts with all these custom classes in place. But it doesn’t.

public class PathWatchingKeyStoreSpi extends KeyStoreSpi {
  private static final Logger LOGGER = LoggerFactory.getLogger(PathWatchingKeyStoreSpi.class);

  private final Path keyStoreLocation;

  public PathWatchingKeyStoreSpi(@Value("${server.ssl.key-store}") Path keyStoreLocation) {

    this.keyStoreLocation = keyStoreLocation;

  public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
    try {
      final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      keyStore.load(new FileInputStream(keyStoreLocation.toString()), "secret".toCharArray());

      // Password must be empty because the SslConnectorCustomizer sets the keystore
      // password used by the tomcat to the empty string if the SslStoreProvider
      // returns a keystore. And because that is what we wanted to do in the first place,
      // providing a dynamic keystore, this is what we have to do., "".toCharArray());
    catch (Exception e) {

I can see that the keystore is loaded but as soon as the SSLUtilBase tries to read the key from that store, it throws a BadPaddingException:

Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
    at java.base/com.sun.crypto.provider.CipherCore.unpad( ~[na:na]
    at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer( ~[na:na]
    at java.base/com.sun.crypto.provider.CipherCore.doFinal( ~[na:na]
    at java.base/com.sun.crypto.provider.PKCS12PBECipherCore.implDoFinal( ~[na:na]
    at java.base/com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede.engineDoFinal( ~[na:na]
    at java.base/javax.crypto.Cipher.doFinal( ~[na:na]
    at java.base/$engineGetKey$0( ~[na:na]
    at java.base/$ ~[na:na]
    at java.base/ ~[na:na]
    ... 25 common frames omitted

I created the static keystore I am using here as follows:

keytool -genkey -alias tomcat -keyalg RSA

First of all, is the direction I am going to solve my problem promising? Or am I totally wrong? I first tried to only inject my own X509ExtendedKeyManager. I could see in the debugger that it is the key manager that is asked for a certificate for an incoming request but nonetheless the tomcat endpoint seems to be initialized with a keystore without the manager being involved.

Has anybody ever tried to implement and use a dynamic keystore/trustore for a spring boot application using tomcat as servelt container?

Any help is welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *