Allow PEM certificates to be used without a key store password
Closes gh-31253
This commit is contained in:
parent
dfb8979456
commit
7abc7df7b8
|
@ -205,7 +205,6 @@ The following example shows setting SSL properties using PEM-encoded certificate
|
||||||
certificate: "classpath:my-cert.crt"
|
certificate: "classpath:my-cert.crt"
|
||||||
certificate-private-key: "classpath:my-cert.key"
|
certificate-private-key: "classpath:my-cert.key"
|
||||||
trust-certificate: "classpath:ca-cert.crt"
|
trust-certificate: "classpath:ca-cert.crt"
|
||||||
key-store-password: "secret"
|
|
||||||
----
|
----
|
||||||
|
|
||||||
See {spring-boot-module-code}/web/server/Ssl.java[`Ssl`] for details of all of the supported properties.
|
See {spring-boot-module-code}/web/server/Ssl.java[`Ssl`] for details of all of the supported properties.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2021 the original author or authors.
|
* Copyright 2012-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -183,6 +183,10 @@ class SslServerCustomizer implements JettyServerCustomizer {
|
||||||
}
|
}
|
||||||
if (sslStoreProvider != null) {
|
if (sslStoreProvider != null) {
|
||||||
try {
|
try {
|
||||||
|
String keyPassword = sslStoreProvider.getKeyPassword();
|
||||||
|
if (keyPassword != null) {
|
||||||
|
factory.setKeyManagerPassword(keyPassword);
|
||||||
|
}
|
||||||
factory.setKeyStore(sslStoreProvider.getKeyStore());
|
factory.setKeyStore(sslStoreProvider.getKeyStore());
|
||||||
factory.setTrustStore(sslStoreProvider.getTrustStore());
|
factory.setTrustStore(sslStoreProvider.getTrustStore());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2021 the original author or authors.
|
* Copyright 2012-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -115,11 +115,11 @@ public class SslServerCustomizer implements NettyServerCustomizer {
|
||||||
? KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
? KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
|
||||||
: new ConfigurableAliasKeyManagerFactory(ssl.getKeyAlias(),
|
: new ConfigurableAliasKeyManagerFactory(ssl.getKeyAlias(),
|
||||||
KeyManagerFactory.getDefaultAlgorithm());
|
KeyManagerFactory.getDefaultAlgorithm());
|
||||||
char[] keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword().toCharArray() : null;
|
String keyPassword = (sslStoreProvider != null) ? sslStoreProvider.getKeyPassword() : null;
|
||||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
if (keyPassword == null) {
|
||||||
keyPassword = ssl.getKeyStorePassword().toCharArray();
|
keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword() : ssl.getKeyStorePassword();
|
||||||
}
|
}
|
||||||
keyManagerFactory.init(keyStore, keyPassword);
|
keyManagerFactory.init(keyStore, (keyPassword != null) ? keyPassword.toCharArray() : null);
|
||||||
return keyManagerFactory;
|
return keyManagerFactory;
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
|
|
@ -93,6 +93,10 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
|
||||||
configureEnabledProtocols(protocol, ssl);
|
configureEnabledProtocols(protocol, ssl);
|
||||||
if (sslStoreProvider != null) {
|
if (sslStoreProvider != null) {
|
||||||
configureSslStoreProvider(protocol, sslHostConfig, certificate, sslStoreProvider);
|
configureSslStoreProvider(protocol, sslHostConfig, certificate, sslStoreProvider);
|
||||||
|
String keyPassword = sslStoreProvider.getKeyPassword();
|
||||||
|
if (keyPassword != null) {
|
||||||
|
certificate.setCertificateKeyPassword(ciphers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
configureSslKeyStore(certificate, ssl);
|
configureSslKeyStore(certificate, ssl);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2021 the original author or authors.
|
* Copyright 2012-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -112,11 +112,11 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
|
||||||
SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias());
|
SslConfigurationValidator.validateKeyAlias(keyStore, ssl.getKeyAlias());
|
||||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
KeyManagerFactory keyManagerFactory = KeyManagerFactory
|
||||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
char[] keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword().toCharArray() : null;
|
String keyPassword = (sslStoreProvider != null) ? sslStoreProvider.getKeyPassword() : null;
|
||||||
if (keyPassword == null && ssl.getKeyStorePassword() != null) {
|
if (keyPassword == null) {
|
||||||
keyPassword = ssl.getKeyStorePassword().toCharArray();
|
keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword() : ssl.getKeyStorePassword();
|
||||||
}
|
}
|
||||||
keyManagerFactory.init(keyStore, keyPassword);
|
keyManagerFactory.init(keyStore, (keyPassword != null) ? keyPassword.toCharArray() : null);
|
||||||
if (ssl.getKeyAlias() != null) {
|
if (ssl.getKeyAlias() != null) {
|
||||||
return getConfigurableAliasKeyManagers(ssl, keyManagerFactory.getKeyManagers());
|
return getConfigurableAliasKeyManagers(ssl, keyManagerFactory.getKeyManagers());
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,11 @@ import java.security.cert.X509Certificate;
|
||||||
*/
|
*/
|
||||||
public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
|
|
||||||
private static final char[] NO_PASSWORD = {};
|
/**
|
||||||
|
* The password of the private key entry in the {@link #getKeyStore provided
|
||||||
|
* KeyStore}.
|
||||||
|
*/
|
||||||
|
private static final String KEY_PASSWORD = "";
|
||||||
|
|
||||||
private static final String DEFAULT_KEY_ALIAS = "spring-boot-web";
|
private static final String DEFAULT_KEY_ALIAS = "spring-boot-web";
|
||||||
|
|
||||||
|
@ -45,7 +49,7 @@ public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
@Override
|
@Override
|
||||||
public KeyStore getKeyStore() throws Exception {
|
public KeyStore getKeyStore() throws Exception {
|
||||||
return createKeyStore(this.ssl.getCertificate(), this.ssl.getCertificatePrivateKey(),
|
return createKeyStore(this.ssl.getCertificate(), this.ssl.getCertificatePrivateKey(),
|
||||||
this.ssl.getKeyStorePassword(), this.ssl.getKeyStoreType(), this.ssl.getKeyAlias());
|
this.ssl.getKeyStoreType(), this.ssl.getKeyAlias());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,7 +58,12 @@ public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return createKeyStore(this.ssl.getTrustCertificate(), this.ssl.getTrustCertificatePrivateKey(),
|
return createKeyStore(this.ssl.getTrustCertificate(), this.ssl.getTrustCertificatePrivateKey(),
|
||||||
this.ssl.getTrustStorePassword(), this.ssl.getTrustStoreType(), this.ssl.getKeyAlias());
|
this.ssl.getTrustStoreType(), this.ssl.getKeyAlias());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeyPassword() {
|
||||||
|
return KEY_PASSWORD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,20 +71,18 @@ public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
* specified file path and an optional private key.
|
* specified file path and an optional private key.
|
||||||
* @param certPath the path to the certificate authority file
|
* @param certPath the path to the certificate authority file
|
||||||
* @param keyPath the path to the private file
|
* @param keyPath the path to the private file
|
||||||
* @param password the key store password
|
|
||||||
* @param storeType the {@code KeyStore} type to create
|
* @param storeType the {@code KeyStore} type to create
|
||||||
* @param keyAlias the alias to use when adding keys to the {@code KeyStore}
|
* @param keyAlias the alias to use when adding keys to the {@code KeyStore}
|
||||||
* @return the {@code KeyStore}
|
* @return the {@code KeyStore}
|
||||||
*/
|
*/
|
||||||
private KeyStore createKeyStore(String certPath, String keyPath, String password, String storeType,
|
private KeyStore createKeyStore(String certPath, String keyPath, String storeType, String keyAlias) {
|
||||||
String keyAlias) {
|
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance((storeType != null) ? storeType : KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance((storeType != null) ? storeType : KeyStore.getDefaultType());
|
||||||
keyStore.load(null);
|
keyStore.load(null);
|
||||||
X509Certificate[] certificates = CertificateParser.parse(certPath);
|
X509Certificate[] certificates = CertificateParser.parse(certPath);
|
||||||
PrivateKey privateKey = (keyPath != null) ? PrivateKeyParser.parse(keyPath) : null;
|
PrivateKey privateKey = (keyPath != null) ? PrivateKeyParser.parse(keyPath) : null;
|
||||||
try {
|
try {
|
||||||
addCertificates(keyStore, certificates, privateKey, password, keyAlias);
|
addCertificates(keyStore, certificates, privateKey, keyAlias);
|
||||||
}
|
}
|
||||||
catch (KeyStoreException ex) {
|
catch (KeyStoreException ex) {
|
||||||
throw new IllegalStateException("Error adding certificates to KeyStore: " + ex.getMessage(), ex);
|
throw new IllegalStateException("Error adding certificates to KeyStore: " + ex.getMessage(), ex);
|
||||||
|
@ -88,11 +95,10 @@ public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCertificates(KeyStore keyStore, X509Certificate[] certificates, PrivateKey privateKey,
|
private void addCertificates(KeyStore keyStore, X509Certificate[] certificates, PrivateKey privateKey,
|
||||||
String password, String keyAlias) throws KeyStoreException {
|
String keyAlias) throws KeyStoreException {
|
||||||
String alias = (keyAlias != null) ? keyAlias : DEFAULT_KEY_ALIAS;
|
String alias = (keyAlias != null) ? keyAlias : DEFAULT_KEY_ALIAS;
|
||||||
if (privateKey != null) {
|
if (privateKey != null) {
|
||||||
keyStore.setKeyEntry(alias, privateKey, ((password != null) ? password.toCharArray() : NO_PASSWORD),
|
keyStore.setKeyEntry(alias, privateKey, KEY_PASSWORD.toCharArray(), certificates);
|
||||||
certificates);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int index = 0; index < certificates.length; index++) {
|
for (int index = 0; index < certificates.length; index++) {
|
||||||
|
@ -102,9 +108,10 @@ public final class CertificateFileSslStoreProvider implements SslStoreProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SslStoreProvider} if the appropriate SSL properties are configured.
|
* Create an {@link SslStoreProvider} if the appropriate SSL properties are
|
||||||
|
* configured.
|
||||||
* @param ssl the SSL properties
|
* @param ssl the SSL properties
|
||||||
* @return a {@code SslStoreProvider} or {@code null}
|
* @return an {@code SslStoreProvider} or {@code null}
|
||||||
*/
|
*/
|
||||||
public static SslStoreProvider from(Ssl ssl) {
|
public static SslStoreProvider from(Ssl ssl) {
|
||||||
if (ssl != null && ssl.isEnabled()) {
|
if (ssl != null && ssl.isEnabled()) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2019 the original author or authors.
|
* Copyright 2012-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -41,4 +41,13 @@ public interface SslStoreProvider {
|
||||||
*/
|
*/
|
||||||
KeyStore getTrustStore() throws Exception;
|
KeyStore getTrustStore() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the password of the private key in the key store.
|
||||||
|
* @return the key password
|
||||||
|
* @since 2.7.1
|
||||||
|
*/
|
||||||
|
default String getKeyPassword() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,7 +285,6 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
||||||
ssl.setCertificate("classpath:test-cert.pem");
|
ssl.setCertificate("classpath:test-cert.pem");
|
||||||
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
ssl.setCertificatePrivateKey("classpath:test-key.pem");
|
||||||
ssl.setTrustCertificate("classpath:test-cert.pem");
|
ssl.setTrustCertificate("classpath:test-cert.pem");
|
||||||
ssl.setKeyStorePassword("secret");
|
|
||||||
testClientAuthSuccess(ssl, buildTrustAllSslWithClientKeyConnector("test.p12", "secret"));
|
testClientAuthSuccess(ssl, buildTrustAllSslWithClientKeyConnector("test.p12", "secret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -732,7 +732,6 @@ public abstract class AbstractServletWebServerFactoryTests {
|
||||||
ssl.setCertificate(cert);
|
ssl.setCertificate(cert);
|
||||||
ssl.setCertificatePrivateKey(privateKey);
|
ssl.setCertificatePrivateKey(privateKey);
|
||||||
ssl.setTrustCertificate(cert);
|
ssl.setTrustCertificate(cert);
|
||||||
ssl.setKeyStorePassword("secret");
|
|
||||||
return ssl;
|
return ssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue