diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/WebServerSslBundle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/WebServerSslBundle.java index 115dba9afa..f659031492 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/WebServerSslBundle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/WebServerSslBundle.java @@ -16,6 +16,8 @@ package org.springframework.boot.web.server; +import java.security.KeyStore; + import org.springframework.boot.ssl.NoSuchSslBundleException; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundleKey; @@ -57,21 +59,30 @@ public final class WebServerSslBundle implements SslBundle { this.managers = SslManagerBundle.from(this.stores, this.key); } - private static SslStoreBundle createPemStoreBundle(Ssl ssl) { + private static SslStoreBundle createPemKeyStoreBundle(Ssl ssl) { PemSslStoreDetails keyStoreDetails = new PemSslStoreDetails(ssl.getKeyStoreType(), ssl.getCertificate(), ssl.getCertificatePrivateKey()) .withAlias(ssl.getKeyAlias()); - PemSslStoreDetails trustStoreDetails = new PemSslStoreDetails(ssl.getTrustStoreType(), - ssl.getTrustCertificate(), ssl.getTrustCertificatePrivateKey()); - return new PemSslStoreBundle(keyStoreDetails, trustStoreDetails); + return new PemSslStoreBundle(keyStoreDetails, null); } - private static SslStoreBundle createJksStoreBundle(Ssl ssl) { + private static SslStoreBundle createPemTrustStoreBundle(Ssl ssl) { + PemSslStoreDetails trustStoreDetails = new PemSslStoreDetails(ssl.getTrustStoreType(), + ssl.getTrustCertificate(), ssl.getTrustCertificatePrivateKey()) + .withAlias(ssl.getKeyAlias()); + return new PemSslStoreBundle(null, trustStoreDetails); + } + + private static SslStoreBundle createJksKeyStoreBundle(Ssl ssl) { JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails(ssl.getKeyStoreType(), ssl.getKeyStoreProvider(), ssl.getKeyStore(), ssl.getKeyStorePassword()); + return new JksSslStoreBundle(keyStoreDetails, null); + } + + private static SslStoreBundle createJksTrustStoreBundle(Ssl ssl) { JksSslStoreDetails trustStoreDetails = new JksSslStoreDetails(ssl.getTrustStoreType(), ssl.getTrustStoreProvider(), ssl.getTrustStore(), ssl.getTrustStorePassword()); - return new JksSslStoreBundle(keyStoreDetails, trustStoreDetails); + return new JksSslStoreBundle(null, trustStoreDetails); } @Override @@ -132,22 +143,79 @@ public final class WebServerSslBundle implements SslBundle { } private static SslStoreBundle createStoreBundle(Ssl ssl) { - if (hasCertificateProperties(ssl)) { - return createPemStoreBundle(ssl); - } - if (hasJavaKeyStoreProperties(ssl)) { - return createJksStoreBundle(ssl); - } - throw new IllegalStateException("SSL is enabled but no trust material is configured"); + KeyStore keyStore = createKeyStore(ssl); + KeyStore trustStore = createTrustStore(ssl); + return new WebServerSslStoreBundle(keyStore, trustStore, ssl.getKeyStorePassword()); } - private static boolean hasCertificateProperties(Ssl ssl) { + private static KeyStore createKeyStore(Ssl ssl) { + if (hasPemKeyStoreProperties(ssl)) { + return createPemKeyStoreBundle(ssl).getKeyStore(); + } + else if (hasJksKeyStoreProperties(ssl)) { + return createJksKeyStoreBundle(ssl).getKeyStore(); + } + return null; + } + + private static KeyStore createTrustStore(Ssl ssl) { + if (hasPemTrustStoreProperties(ssl)) { + return createPemTrustStoreBundle(ssl).getTrustStore(); + } + else if (hasJksTrustStoreProperties(ssl)) { + return createJksTrustStoreBundle(ssl).getTrustStore(); + } + return null; + } + + private static boolean hasPemKeyStoreProperties(Ssl ssl) { return Ssl.isEnabled(ssl) && ssl.getCertificate() != null && ssl.getCertificatePrivateKey() != null; } - private static boolean hasJavaKeyStoreProperties(Ssl ssl) { - return Ssl.isEnabled(ssl) && ssl.getKeyStore() != null - || (ssl.getKeyStoreType() != null && ssl.getKeyStoreType().equals("PKCS11")); + private static boolean hasPemTrustStoreProperties(Ssl ssl) { + return Ssl.isEnabled(ssl) && ssl.getTrustCertificate() != null; + } + + private static boolean hasJksKeyStoreProperties(Ssl ssl) { + return Ssl.isEnabled(ssl) && (ssl.getKeyStore() != null + || (ssl.getKeyStoreType() != null && ssl.getKeyStoreType().equals("PKCS11"))); + } + + private static boolean hasJksTrustStoreProperties(Ssl ssl) { + return Ssl.isEnabled(ssl) && (ssl.getTrustStore() != null + || (ssl.getTrustStoreType() != null && ssl.getTrustStoreType().equals("PKCS11"))); + } + + private static final class WebServerSslStoreBundle implements SslStoreBundle { + + private final KeyStore keyStore; + + private final KeyStore trustStore; + + private final String keyStorePassword; + + private WebServerSslStoreBundle(KeyStore keyStore, KeyStore trustStore, String keyStorePassword) { + Assert.state(keyStore != null || trustStore != null, "SSL is enabled but no trust material is configured"); + this.keyStore = keyStore; + this.trustStore = trustStore; + this.keyStorePassword = keyStorePassword; + } + + @Override + public KeyStore getKeyStore() { + return this.keyStore; + } + + @Override + public KeyStore getTrustStore() { + return this.trustStore; + } + + @Override + public String getKeyStorePassword() { + return this.keyStorePassword; + } + } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/WebServerSslBundleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/WebServerSslBundleTests.java index 66ce3353d3..41a58ce434 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/WebServerSslBundleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/WebServerSslBundleTests.java @@ -88,6 +88,25 @@ class WebServerSslBundleTests { .withMessageContaining("must be empty or null for PKCS11 hardware key stores"); } + @Test + void whenFromPkcs11Properties() { + Ssl ssl = new Ssl(); + ssl.setKeyStoreType("PKCS11"); + ssl.setKeyStoreProvider(MockPkcs11SecurityProvider.NAME); + ssl.setTrustStoreType("PKCS11"); + ssl.setTrustStoreProvider(MockPkcs11SecurityProvider.NAME); + ssl.setKeyPassword("password"); + ssl.setClientAuth(Ssl.ClientAuth.NONE); + SslBundle bundle = WebServerSslBundle.get(ssl); + assertThat(bundle).isNotNull(); + assertThat(bundle.getProtocol()).isEqualTo("TLS"); + SslBundleKey key = bundle.getKey(); + assertThat(key.getPassword()).isEqualTo("password"); + SslStoreBundle stores = bundle.getStores(); + assertThat(stores.getKeyStore()).isNotNull(); + assertThat(stores.getTrustStore()).isNotNull(); + } + @Test void whenFromPemProperties() { Ssl ssl = new Ssl(); @@ -115,6 +134,61 @@ class WebServerSslBundleTests { assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2"); } + @Test + void whenPemKeyStoreAndJksTrustStoreProperties() { + Ssl ssl = new Ssl(); + ssl.setCertificate("classpath:test-cert.pem"); + ssl.setCertificatePrivateKey("classpath:test-key.pem"); + ssl.setKeyStoreType("PKCS12"); + ssl.setKeyPassword("password"); + ssl.setTrustStore("classpath:test.p12"); + ssl.setTrustStorePassword("secret"); + ssl.setTrustStoreType("PKCS12"); + ssl.setClientAuth(Ssl.ClientAuth.NONE); + ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" }); + ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" }); + ssl.setProtocol("TLSv1.1"); + SslBundle bundle = WebServerSslBundle.get(ssl); + assertThat(bundle).isNotNull(); + SslBundleKey key = bundle.getKey(); + assertThat(key.getAlias()).isNull(); + assertThat(key.getPassword()).isEqualTo("password"); + SslStoreBundle stores = bundle.getStores(); + assertThat(stores.getKeyStorePassword()).isNull(); + assertThat(stores.getKeyStore()).isNotNull(); + assertThat(stores.getTrustStore()).isNotNull(); + SslOptions options = bundle.getOptions(); + assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE"); + assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2"); + } + + @Test + void whenJksKeyStoreAndPemTrustStoreProperties() { + Ssl ssl = new Ssl(); + ssl.setKeyStore("classpath:test.p12"); + ssl.setKeyStoreType("PKCS12"); + ssl.setKeyPassword("password"); + ssl.setTrustCertificate("classpath:test-cert-chain.pem"); + ssl.setTrustStorePassword("secret"); + ssl.setTrustStoreType("PKCS12"); + ssl.setClientAuth(Ssl.ClientAuth.NONE); + ssl.setCiphers(new String[] { "ONE", "TWO", "THREE" }); + ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" }); + ssl.setProtocol("TLSv1.1"); + SslBundle bundle = WebServerSslBundle.get(ssl); + assertThat(bundle).isNotNull(); + SslBundleKey key = bundle.getKey(); + assertThat(key.getAlias()).isNull(); + assertThat(key.getPassword()).isEqualTo("password"); + SslStoreBundle stores = bundle.getStores(); + assertThat(stores.getKeyStorePassword()).isNull(); + assertThat(stores.getKeyStore()).isNotNull(); + assertThat(stores.getTrustStore()).isNotNull(); + SslOptions options = bundle.getOptions(); + assertThat(options.getCiphers()).containsExactly("ONE", "TWO", "THREE"); + assertThat(options.getEnabledProtocols()).containsExactly("TLSv1.1", "TLSv1.2"); + } + @Test void whenMissingPropertiesThrowsException() { Ssl ssl = new Ssl();