Support encrypted PKCS8 private keys in SSL bundles
Properties `ssl.bundle.pem.mybundle.keystore.private-key-password` and `ssl.bundle.pem.mybundle.truststore.private-key-password` have been added for configuring the password required to decrypt an encrypted private key. Only PKCS8 private keys with encryption are supported. PKCS1 and EC private keys with encryption are much more complex to decrypt, and are not supported. Fixes gh-35652
This commit is contained in:
@@ -18,6 +18,7 @@ package org.springframework.boot.ssl.pem;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
@@ -27,10 +28,18 @@ import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Parser for PKCS private key files in PEM format.
|
||||
*
|
||||
@@ -48,18 +57,27 @@ final class PemPrivateKeyParser {
|
||||
|
||||
private static final String PKCS8_FOOTER = "-+END\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String PKCS8_ENCRYPTED_HEADER = "-+BEGIN\\s+ENCRYPTED\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String PKCS8_ENCRYPTED_FOOTER = "-+END\\s+ENCRYPTED\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String EC_HEADER = "-+BEGIN\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+";
|
||||
|
||||
private static final String EC_FOOTER = "-+END\\s+EC\\s+PRIVATE\\s+KEY[^-]*-+";
|
||||
|
||||
private static final String BASE64_TEXT = "([a-z0-9+/=\\r\\n]+)";
|
||||
|
||||
public static final int BASE64_TEXT_GROUP = 1;
|
||||
|
||||
private static final List<PemParser> PEM_PARSERS;
|
||||
static {
|
||||
List<PemParser> parsers = new ArrayList<>();
|
||||
parsers.add(new PemParser(PKCS1_HEADER, PKCS1_FOOTER, PemPrivateKeyParser::createKeySpecForPkcs1, "RSA"));
|
||||
parsers.add(new PemParser(EC_HEADER, EC_FOOTER, PemPrivateKeyParser::createKeySpecForEc, "EC"));
|
||||
parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, PKCS8EncodedKeySpec::new, "RSA", "EC", "DSA", "Ed25519"));
|
||||
parsers.add(new PemParser(PKCS8_HEADER, PKCS8_FOOTER, PemPrivateKeyParser::createKeySpecForPkcs8, "RSA", "EC",
|
||||
"DSA", "Ed25519"));
|
||||
parsers.add(new PemParser(PKCS8_ENCRYPTED_HEADER, PKCS8_ENCRYPTED_FOOTER,
|
||||
PemPrivateKeyParser::createKeySpecForPkcs8Encrypted, "RSA", "EC", "DSA", "Ed25519"));
|
||||
PEM_PARSERS = Collections.unmodifiableList(parsers);
|
||||
}
|
||||
|
||||
@@ -81,11 +99,11 @@ final class PemPrivateKeyParser {
|
||||
private PemPrivateKeyParser() {
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1(byte[] bytes) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs1(byte[] bytes, String password) {
|
||||
return createKeySpecForAlgorithm(bytes, RSA_ALGORITHM, null);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForEc(byte[] bytes) {
|
||||
private static PKCS8EncodedKeySpec createKeySpecForEc(byte[] bytes, String password) {
|
||||
return createKeySpecForAlgorithm(bytes, EC_ALGORITHM, EC_PARAMETERS);
|
||||
}
|
||||
|
||||
@@ -106,18 +124,37 @@ final class PemPrivateKeyParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8(byte[] bytes, String password) {
|
||||
return new PKCS8EncodedKeySpec(bytes);
|
||||
}
|
||||
|
||||
private static PKCS8EncodedKeySpec createKeySpecForPkcs8Encrypted(byte[] bytes, String password) {
|
||||
return Pkcs8PrivateKeyDecryptor.decrypt(bytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a private key from the specified string.
|
||||
* @param key the private key to parse
|
||||
* @return the parsed private key
|
||||
*/
|
||||
static PrivateKey parse(String key) {
|
||||
return parse(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a private key from the specified string, using the provided password for
|
||||
* decryption if necessary.
|
||||
* @param key the private key to parse
|
||||
* @param password the password used to decrypt an encrypted private key
|
||||
* @return the parsed private key
|
||||
*/
|
||||
static PrivateKey parse(String key, String password) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
for (PemParser pemParser : PEM_PARSERS) {
|
||||
PrivateKey privateKey = pemParser.parse(key);
|
||||
PrivateKey privateKey = pemParser.parse(key, password);
|
||||
if (privateKey != null) {
|
||||
return privateKey;
|
||||
}
|
||||
@@ -136,20 +173,20 @@ final class PemPrivateKeyParser {
|
||||
|
||||
private final Pattern pattern;
|
||||
|
||||
private final Function<byte[], PKCS8EncodedKeySpec> keySpecFactory;
|
||||
private final BiFunction<byte[], String, PKCS8EncodedKeySpec> keySpecFactory;
|
||||
|
||||
private final String[] algorithms;
|
||||
|
||||
PemParser(String header, String footer, Function<byte[], PKCS8EncodedKeySpec> keySpecFactory,
|
||||
PemParser(String header, String footer, BiFunction<byte[], String, PKCS8EncodedKeySpec> keySpecFactory,
|
||||
String... algorithms) {
|
||||
this.pattern = Pattern.compile(header + BASE64_TEXT + footer, Pattern.CASE_INSENSITIVE);
|
||||
this.algorithms = algorithms;
|
||||
this.keySpecFactory = keySpecFactory;
|
||||
this.algorithms = algorithms;
|
||||
}
|
||||
|
||||
PrivateKey parse(String text) {
|
||||
PrivateKey parse(String text, String password) {
|
||||
Matcher matcher = this.pattern.matcher(text);
|
||||
return (!matcher.find()) ? null : parse(decodeBase64(matcher.group(1)));
|
||||
return (!matcher.find()) ? null : parse(decodeBase64(matcher.group(BASE64_TEXT_GROUP)), password);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase64(String content) {
|
||||
@@ -157,9 +194,9 @@ final class PemPrivateKeyParser {
|
||||
return Base64.getDecoder().decode(contentBytes);
|
||||
}
|
||||
|
||||
private PrivateKey parse(byte[] bytes) {
|
||||
private PrivateKey parse(byte[] bytes, String password) {
|
||||
try {
|
||||
PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes);
|
||||
PKCS8EncodedKeySpec keySpec = this.keySpecFactory.apply(bytes, password);
|
||||
for (String algorithm : this.algorithms) {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
|
||||
try {
|
||||
@@ -251,4 +288,34 @@ final class PemPrivateKeyParser {
|
||||
|
||||
}
|
||||
|
||||
static class Pkcs8PrivateKeyDecryptor {
|
||||
|
||||
public static final String PBES2_ALGORITHM = "PBES2";
|
||||
|
||||
static PKCS8EncodedKeySpec decrypt(byte[] bytes, String password) {
|
||||
Assert.notNull(password, "Password is required for an encrypted private key");
|
||||
try {
|
||||
EncryptedPrivateKeyInfo keyInfo = new EncryptedPrivateKeyInfo(bytes);
|
||||
AlgorithmParameters algorithmParameters = keyInfo.getAlgParameters();
|
||||
String encryptionAlgorithm = getEncryptionAlgorithm(algorithmParameters, keyInfo.getAlgName());
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptionAlgorithm);
|
||||
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(password.toCharArray()));
|
||||
Cipher cipher = Cipher.getInstance(encryptionAlgorithm);
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
|
||||
return keyInfo.getKeySpec(cipher);
|
||||
}
|
||||
catch (IOException | GeneralSecurityException ex) {
|
||||
throw new IllegalArgumentException("Error decrypting private key", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getEncryptionAlgorithm(AlgorithmParameters algParameters, String algName) {
|
||||
if (algParameters != null && PBES2_ALGORITHM.equals(algName)) {
|
||||
return algParameters.toString();
|
||||
}
|
||||
return algName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,14 +84,14 @@ public class PemSslStoreBundle implements SslStoreBundle {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Assert.notNull(details.certificate(), "CertificateContent must not be null");
|
||||
Assert.notNull(details.certificate(), "Certificate content must not be null");
|
||||
String type = (!StringUtils.hasText(details.type())) ? KeyStore.getDefaultType() : details.type();
|
||||
KeyStore store = KeyStore.getInstance(type);
|
||||
store.load(null);
|
||||
String certificateContent = PemContent.load(details.certificate());
|
||||
String privateKeyContent = PemContent.load(details.privateKey());
|
||||
X509Certificate[] certificates = PemCertificateParser.parse(certificateContent);
|
||||
PrivateKey privateKey = PemPrivateKeyParser.parse(privateKeyContent);
|
||||
PrivateKey privateKey = PemPrivateKeyParser.parse(privateKeyContent, details.privateKeyPassword());
|
||||
addCertificates(store, certificates, privateKey);
|
||||
return store;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,16 @@ import org.springframework.util.StringUtils;
|
||||
* that can be loaded by {@link ResourceUtils#getURL})
|
||||
* @param privateKey the private key content (either the PEM content itself or something
|
||||
* that can be loaded by {@link ResourceUtils#getURL})
|
||||
* @param privateKeyPassword a password used to decrypt an encrypted private key
|
||||
* @author Scott Frederick
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public record PemSslStoreDetails(String type, String certificate, String privateKey) {
|
||||
public record PemSslStoreDetails(String type, String certificate, String privateKey, String privateKeyPassword) {
|
||||
|
||||
public PemSslStoreDetails(String type, String certificate, String privateKey) {
|
||||
this(type, certificate, privateKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link PemSslStoreDetails} instance with a new private key.
|
||||
@@ -42,7 +47,16 @@ public record PemSslStoreDetails(String type, String certificate, String private
|
||||
* @return a new {@link PemSslStoreDetails} instance
|
||||
*/
|
||||
public PemSslStoreDetails withPrivateKey(String privateKey) {
|
||||
return new PemSslStoreDetails(this.type, this.certificate, privateKey);
|
||||
return new PemSslStoreDetails(this.type, this.certificate, privateKey, this.privateKeyPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link PemSslStoreDetails} instance with a new private key password.
|
||||
* @param password the new private key password
|
||||
* @return a new {@link PemSslStoreDetails} instance
|
||||
*/
|
||||
public PemSslStoreDetails withPrivateKeyPassword(String password) {
|
||||
return new PemSslStoreDetails(this.type, this.certificate, this.privateKey, password);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
|
||||
@@ -84,6 +84,47 @@ class PemPrivateKeyParserTests {
|
||||
assertThatIllegalStateException().isThrownBy(() -> PemPrivateKeyParser.parse(read("test-banner.txt")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsePkcs8EncryptedRsaKeyFile() throws Exception {
|
||||
// created with:
|
||||
// openssl genpkey -aes-256-cbc -algorithm RSA \
|
||||
// -pkeyopt rsa_keygen_bits:4096 -out key-rsa-encrypted.key
|
||||
PrivateKey privateKey = PemPrivateKeyParser.parse(read("ssl/pkcs8/key-rsa-encrypted.pem"), "test");
|
||||
assertThat(privateKey).isNotNull();
|
||||
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
|
||||
assertThat(privateKey.getAlgorithm()).isEqualTo("RSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsePkcs8EncryptedEcKeyFile() throws Exception {
|
||||
// created with:
|
||||
// openssl genpkey -aes-256-cbc -algorithm EC \
|
||||
// -pkeyopt ec_paramgen_curve:prime256v1 -out key-ec-encrypted.key
|
||||
PrivateKey privateKey = PemPrivateKeyParser.parse(read("ssl/pkcs8/key-ec-encrypted.pem"), "test");
|
||||
assertThat(privateKey).isNotNull();
|
||||
assertThat(privateKey.getFormat()).isEqualTo("PKCS#8");
|
||||
assertThat(privateKey.getAlgorithm()).isEqualTo("EC");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failParsingPkcs1EncryptedKeyFile() throws Exception {
|
||||
// created with:
|
||||
// openssl genrsa -aes-256-cbc -out key-rsa-encrypted.pem
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> PemPrivateKeyParser.parse(read("ssl/pkcs1/key-rsa-encrypted.pem"), "test"))
|
||||
.withMessageContaining("Unrecognized private key format");
|
||||
}
|
||||
|
||||
@Test
|
||||
void failParsingEcEncryptedKeyFile() throws Exception {
|
||||
// created with:
|
||||
// openssl ecparam -genkey -name prime256v1 | openssl ec -aes-128-cbc -out
|
||||
// key-ec-prime256v1-encrypted.pem
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> PemPrivateKeyParser.parse(read("ssl/ec/key-ec-prime256v1-encrypted.pem"), "test"))
|
||||
.withMessageContaining("Unrecognized private key format");
|
||||
}
|
||||
|
||||
private String read(String path) throws IOException {
|
||||
return new ClassPathResource(path).getContentAsString(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,17 @@ class PemSslStoreBundleTests {
|
||||
assertThat(bundle.getTrustStore()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasKeyStoreDetailsCertAndEncryptedKey() {
|
||||
PemSslStoreDetails keyStoreDetails = PemSslStoreDetails.forCertificate("classpath:test-cert.pem")
|
||||
.withPrivateKey("classpath:ssl/pkcs8/key-rsa-encrypted.pem")
|
||||
.withPrivateKeyPassword("test");
|
||||
PemSslStoreDetails trustStoreDetails = null;
|
||||
PemSslStoreBundle bundle = new PemSslStoreBundle(keyStoreDetails, trustStoreDetails);
|
||||
assertThat(bundle.getKeyStore()).satisfies(storeContainingCertAndKey("ssl"));
|
||||
assertThat(bundle.getTrustStore()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenHasKeyStoreDetailsAndTrustStoreDetailsWithoutKey() {
|
||||
PemSslStoreDetails keyStoreDetails = PemSslStoreDetails.forCertificate("classpath:test-cert.pem")
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,3F26BBC4C7A6F3B5B3A5C03C7CC59B33
|
||||
|
||||
rp4qC+n+qIG+WKJp4BHQHk5z0oraaaLZvoVK5glESGx5IcR3mCsN7tdg2aZ7yEk+
|
||||
HnT4nQuM3R5pv248cmK0xDUje8N7FLe8lixVnEyQx3JdZfGdauowt9yaxL3AJypX
|
||||
idWxNrxz1xff5RSMI6+PFv2SQpG0l794EpOjZxOUABM=
|
||||
-----END EC PRIVATE KEY-----
|
||||
@@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,2036BA8802B364C6BE415A48E1AC9966
|
||||
|
||||
0euP/daGIbl798XGc2Tc6u6NmnCl8C6hE0a2grO+wbvFEa13IT4hNVNTzqahfLb8
|
||||
STF9LXdvPbz9HybyWnG8KNm5sqsSY1omsHQI38khhwuhpHuWfjb62TqWobwrAmAU
|
||||
qh2+kjc7upzW48axJLRQ69vfE4zKczZ4Sqv8qImPz9SiXPeaXinHf/wKfgXZZOvr
|
||||
6A8nCykLglVrFbrZWydqKrTM9w/LTXsn0gNpDmrfZERDgWnabKnjYidHkXdgAoFy
|
||||
ulsBjKvj2hJFtnJHTkCPXj6XAj18cwUWsN7inFJR5wqjIWXuLqfv/fNbK9lPzjAk
|
||||
vMTEVWwynekvXFsSvnHAyEJfmRCNF6DOE+5VwjEROCFq8xceK4fz4T3BHC7XSgis
|
||||
2ofBZwjCAJ0pP6SdRpbbZ0wlXH09wqDDTZ2vWD6bEG7hO9VuP/aO76MIWydLa78z
|
||||
bDb2R6MnYKmYEInPzwfU4H66Z3/cz8orfYLlnF55DRd4rO+fJm6Y/XY14ac3x0S+
|
||||
P67s5e0WbdImagUsOxGNBjgOr84lwmtk/LoVd5nKho8JyQw3V7tiyrsKsM1lAXdS
|
||||
TaJXKRnN6LbgC8d+Tgfjc/qMUdVqLlN8zATIa6E4sc861DDiJsneXuFm43pHc/ja
|
||||
0sxSifBKgLGenj8xY5ANEfrYnXKrzaQemUnMzd+zan6OS02I+e8WTQevESt1j0D8
|
||||
7mgLJ9W2SfNNfiEd5OKar21lLNOc3POaFn7M73zL/gyhTROipPR3fpV+08k27UKl
|
||||
nzGhFtRwQieXl34QjM0JrHokKKfv8FsAJsbrGnz2/wcm6jJGmrj5VVsogMKc90v4
|
||||
ZTM44NAKymM0UTuIp/rKb/UUVYDpl9VWDegWh2+XKX8io31ENrDMcvQTJ/mzNCuZ
|
||||
SINrAeMbVD6W5L0i7THEt32YsDmbFsBaEJBlNXlUNBa/NCK0pjwAn5AYOFUvEqBC
|
||||
oZUEleUMU6Q3TKI287o37euw3No6jo5VdPULlrwHsZAjmq9EUvRCgMoIEPdxv5XG
|
||||
a4PljE7DQLlk6G9d+gjRzClLFfkadSTbtH9o2011RtWUKP4dcRuSOcBm64xVIO6J
|
||||
rhe38sE3yzHvLUM/mvLsEM8B0AHl85nrEstlbBzftXMo2CAJ2Gs9c61PYfft+xiT
|
||||
pRdHx4HB1P+kfHa96ayvAytOexHih2iVKVG5CchOr5tbWmkhXVE5cZAKzvczDYFd
|
||||
YvniHNiqt6LO8EbJOzz+Yxessmd0zBXj/rjxTMdGwaRjiFI0BIguYpvmGoMS8+bp
|
||||
spRj2DMtqjNZz68BEEfgKQwDHPCTblYSR+3Uw4sja608sflqQ8rTmOwbfta3SNS8
|
||||
Kq/zzWfYzarQ8hAk5n/H1Jm6AQdvcptyMjuF8FAiMnvt8xDCBRMD3xY2BHb/tcs9
|
||||
dBWBxhb76CKIrV3pzm9gGhhZ32Ndq9KmmE+bWCYyvrLxvPJxODfM7X5XamPmG5SH
|
||||
wEKSbp0wPF0b6vyxt5M72OYnU8UnxYWu6PlbVvczfWEu9fIw/oIN/kDT/QUojTyt
|
||||
wBgzVSTSNFaDtVXJw10IWQgsgWdNY5XueHKH060P5g+14woxU8i3TboNd+tC3hRl
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,7 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjZ2eXtLFXLdgICCAAw
|
||||
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIlOTfeoLwq9Bs9TFtw7VSMEgZAU
|
||||
RO9W+K3vvzRG4sACrcFaB7PMhv3+HUOcPFf09QHa7aSJMWsb8EXGFBK4xlFhkK0/
|
||||
N0C8sRtH2N1JdYj9hiwS7I8WyybaR2W0ZALNR57iLz3WsOGQ7nVECFprElboqnSW
|
||||
BtjkKTD9pz3xX/6cMkDV/2WqbS6Y/dzWJTH8yTTqjzTjbs4vguEK1Io6dlIRVqA=
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
@@ -0,0 +1,54 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
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
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user