title: Configure Spring Boot's with custom SSLContext programmatically (for mTLS)
body: <h2>Problem</h2>
  <p>Programmatically configure Spring Boot's to use my custom <code>SSLContext</code>. And use for mTLS.</p>
  <h2>Context</h2>
  <p>Spring's <a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-configure-webserver" rel="nofollow noreferrer">documentation</a> provides only one, clear way how to configure SSL (via <code>application.properties</code>):</p>
  <pre><code>server.port=8443
  server.ssl.key-store=classpath:keystore.jks
  server.ssl.key-store-password=secret
  server.ssl.trust-store=classpath:truststore.jks
  ...
  </code></pre>
  <p>However, this solution lacks <em>depth</em> as there are scenarios, where I'd like to advantage of customizing my own <code>SSLContext</code>. For example, configuring mTLS to trust not only single certificate generated via <code>keytool</code>, but to trust both my self-signed certificate and ones placed within Java's default TrustStore (<code>lib/security/cacerts</code>).</p>
  <p>Of course, I could combine them using already mentioned <code>keytool</code>, but I'm looking for more flexible approach, hence providing my own <code>SSLContext</code>.</p>
  <p>Spring provides a section on <a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-configure-webserver" rel="nofollow noreferrer">Configure the Web Server</a>, which says to use either something like <code>TomcatServletWebServerFactory</code> or <code>ConfigurableServletWebServerFactory</code>, but they do not really go into depth.</p>
  <p>I've tried creating a <code>Component</code>:</p>
  <pre><code>@Component
  public class CustomServletCustomizer implements WebServerFactoryCustomizer&lt;ConfigurableServletWebServerFactory&gt; {
  
      private static final String SERVER_CERTIFICATE_PATH = &quot;identity.jks&quot;;
      private static final char[] PASSWORD = &quot;secret&quot;.toCharArray();
  
      private static final String CLIENT_CERTIFICATE_PATH = &quot;certs/client.cer&quot;;
  
      @Override
      public void customize(ConfigurableServletWebServerFactory factory) {
          factory.setSslStoreProvider(new SslStoreProvider() {
              @Override
              public KeyStore getKeyStore() throws Exception {
                  var certificateAsInputStream = this.getClass().getClassLoader().getResourceAsStream(SERVER_CERTIFICATE_PATH);
                  var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                  keyStore.load(certificateAsInputStream, PASSWORD);
  
                  return keyStore;
              }
  
              @Override
              public KeyStore getTrustStore() throws Exception {
                  var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                  keyStore.load(null);
                  var organisationRootCertBytes = this.getClass().getClassLoader().getResourceAsStream(CLIENT_CERTIFICATE_PATH).readAllBytes();
                  var certificateFactory = CertificateFactory.getInstance(&quot;X.509&quot;);
                  var certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(organisationRootCertBytes));
                  keyStore.setCertificateEntry(&quot;server&quot;, certificate);
  
                  return keyStore;
              }
          });
      }
  }
  </code></pre>
  <p>But to no avail.</p>