Merge branch '3.1.x'

This commit is contained in:
Scott Frederick
2023-06-09 12:01:25 -05:00
12 changed files with 355 additions and 17 deletions

View File

@@ -66,6 +66,11 @@ public class PemSslBundleProperties extends SslBundleProperties {
*/
String privateKey;
/**
* Password used to decrypt an encrypted private key.
*/
String privateKeyPassword;
public String getType() {
return this.type;
}
@@ -90,6 +95,14 @@ public class PemSslBundleProperties extends SslBundleProperties {
this.privateKey = privateKey;
}
public String getPrivateKeyPassword() {
return this.privateKeyPassword;
}
public void setPrivateKeyPassword(String privateKeyPassword) {
this.privateKeyPassword = privateKeyPassword;
}
}
}

View File

@@ -113,7 +113,8 @@ public final class PropertiesSslBundle implements SslBundle {
}
private static PemSslStoreDetails asStoreDetails(PemSslBundleProperties.Store properties) {
return new PemSslStoreDetails(properties.getType(), properties.getCertificate(), properties.getPrivateKey());
return new PemSslStoreDetails(properties.getType(), properties.getCertificate(), properties.getPrivateKey(),
properties.getPrivateKeyPassword());
}
private static SslStoreBundle asSslStoreBundle(JksSslBundleProperties properties) {

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.ssl;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.boot.ssl.SslBundle;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PropertiesSslBundle}.
*
* @author Scott Frederick
*/
class PropertiesSslBundleTests {
@Test
void pemPropertiesAreMappedToSslBundle() {
PemSslBundleProperties properties = new PemSslBundleProperties();
properties.getKey().setAlias("alias");
properties.getKey().setPassword("secret");
properties.getOptions().setCiphers(Set.of("cipher1", "cipher2", "cipher3"));
properties.getOptions().setEnabledProtocols(Set.of("protocol1", "protocol2"));
properties.getKeystore().setCertificate("cert1.pem");
properties.getKeystore().setPrivateKey("key1.pem");
properties.getKeystore().setPrivateKeyPassword("keysecret1");
properties.getKeystore().setType("PKCS12");
properties.getTruststore().setCertificate("cert2.pem");
properties.getTruststore().setPrivateKey("key2.pem");
properties.getTruststore().setPrivateKeyPassword("keysecret2");
properties.getTruststore().setType("JKS");
SslBundle sslBundle = PropertiesSslBundle.get(properties);
assertThat(sslBundle.getKey().getAlias()).isEqualTo("alias");
assertThat(sslBundle.getKey().getPassword()).isEqualTo("secret");
assertThat(sslBundle.getOptions().getCiphers()).containsExactlyInAnyOrder("cipher1", "cipher2", "cipher3");
assertThat(sslBundle.getOptions().getEnabledProtocols()).containsExactlyInAnyOrder("protocol1", "protocol2");
assertThat(sslBundle.getStores()).isNotNull();
assertThat(sslBundle.getStores()).extracting("keyStoreDetails")
.extracting("certificate", "privateKey", "privateKeyPassword", "type")
.containsExactly("cert1.pem", "key1.pem", "keysecret1", "PKCS12");
assertThat(sslBundle.getStores()).extracting("trustStoreDetails")
.extracting("certificate", "privateKey", "privateKeyPassword", "type")
.containsExactly("cert2.pem", "key2.pem", "keysecret2", "JKS");
}
@Test
void jksPropertiesAreMappedToSslBundle() {
JksSslBundleProperties properties = new JksSslBundleProperties();
properties.getKey().setAlias("alias");
properties.getKey().setPassword("secret");
properties.getOptions().setCiphers(Set.of("cipher1", "cipher2", "cipher3"));
properties.getOptions().setEnabledProtocols(Set.of("protocol1", "protocol2"));
properties.getKeystore().setLocation("cert1.p12");
properties.getKeystore().setPassword("secret1");
properties.getKeystore().setProvider("provider1");
properties.getKeystore().setType("JKS");
properties.getTruststore().setLocation("cert2.jks");
properties.getTruststore().setPassword("secret2");
properties.getTruststore().setProvider("provider2");
properties.getTruststore().setType("PKCS12");
SslBundle sslBundle = PropertiesSslBundle.get(properties);
assertThat(sslBundle.getKey().getAlias()).isEqualTo("alias");
assertThat(sslBundle.getKey().getPassword()).isEqualTo("secret");
assertThat(sslBundle.getOptions().getCiphers()).containsExactlyInAnyOrder("cipher1", "cipher2", "cipher3");
assertThat(sslBundle.getOptions().getEnabledProtocols()).containsExactlyInAnyOrder("protocol1", "protocol2");
assertThat(sslBundle.getStores()).isNotNull();
assertThat(sslBundle.getStores()).extracting("keyStoreDetails")
.extracting("location", "password", "provider", "type")
.containsExactly("cert1.p12", "secret1", "provider1", "JKS");
assertThat(sslBundle.getStores()).extracting("trustStoreDetails")
.extracting("location", "password", "provider", "type")
.containsExactly("cert2.jks", "secret2", "provider2", "PKCS12");
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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")

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -0,0 +1,7 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjZ2eXtLFXLdgICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEIlOTfeoLwq9Bs9TFtw7VSMEgZAU
RO9W+K3vvzRG4sACrcFaB7PMhv3+HUOcPFf09QHa7aSJMWsb8EXGFBK4xlFhkK0/
N0C8sRtH2N1JdYj9hiwS7I8WyybaR2W0ZALNR57iLz3WsOGQ7nVECFprElboqnSW
BtjkKTD9pz3xX/6cMkDV/2WqbS6Y/dzWJTH8yTTqjzTjbs4vguEK1Io6dlIRVqA=
-----END ENCRYPTED PRIVATE KEY-----

View File

@@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
-----END ENCRYPTED PRIVATE KEY-----