Support private key encrypted with empty password

Motivation:

A user may use a private key which is encrypted with an empty password. Because of this we should only handle a null password in a special way.

Modifications:

- Correctly handle private key that is encrypted with empty password.
- Make OpenSsl*Context implementions consistent in terms of initialization in the constructor.

Result:

Correctly support private key that is encrypted with empty password.
This commit is contained in:
Norman Maurer 2016-03-16 14:51:33 +01:00
parent 89da788fd2
commit 0a87c98993
10 changed files with 254 additions and 222 deletions

View File

@ -171,97 +171,9 @@ public final class OpenSslClientContext extends OpenSslContext {
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout)
throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, null,
ClientAuth.NONE);
boolean success = false;
try {
if (trustCertChainFile != null && !trustCertChainFile.isFile()) {
throw new IllegalArgumentException("trustCertChainFile is not a file: " + trustCertChainFile);
}
if (keyCertChainFile != null && !keyCertChainFile.isFile()) {
throw new IllegalArgumentException("keyCertChainFile is not a file: " + keyCertChainFile);
}
if (keyFile != null && !keyFile.isFile()) {
throw new IllegalArgumentException("keyFile is not a file: " + keyFile);
}
if (keyFile == null && keyCertChainFile != null || keyFile != null && keyCertChainFile == null) {
throw new IllegalArgumentException(
"Either both keyCertChainFile and keyFile needs to be null or none of them");
}
synchronized (OpenSslContext.class) {
if (keyCertChainFile != null && keyFile != null) {
/* Load the certificate file and private key. */
try {
if (!SSLContext.setCertificate(
ctx, keyCertChainFile.getPath(), keyFile.getPath(), keyPassword, SSL.SSL_AIDX_RSA)) {
long error = SSL.getLastErrorNumber();
if (OpenSsl.isError(error)) {
throw new SSLException("failed to set certificate: " +
keyCertChainFile + " and " + keyFile +
" (" + SSL.getErrorString(error) + ')');
}
}
// We may have more then one cert in the chain so add all of them now. We must NOT skip the
// first cert when client mode.
if (!SSLContext.setCertificateChainFile(ctx, keyCertChainFile.getPath(), false)) {
long error = SSL.getLastErrorNumber();
if (OpenSsl.isError(error)) {
throw new SSLException(
"failed to set certificate chain: "
+ keyCertChainFile + " (" + SSL.getErrorString(error) + ')');
}
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set certificate: " + keyCertChainFile + " and " + keyFile, e);
}
}
SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
try {
if (trustCertChainFile != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
} else if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
throws Exception {
extendedManager.checkServerTrusted(peerCerts, auth, engine);
}
});
} else {
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
throws Exception {
manager.checkServerTrusted(peerCerts, auth);
}
});
}
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
}
sessionContext = new OpenSslClientSessionContext(ctx);
success = true;
} finally {
if (!success) {
destroy();
}
}
this(toX509CertificatesInternal(trustCertChainFile), trustManagerFactory,
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}
@SuppressWarnings("deprecation")

View File

@ -35,8 +35,10 @@ import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateRevokedException;
@ -566,4 +568,20 @@ public abstract class OpenSslContext extends SslContext {
}
return bio;
}
static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException {
try {
return SslContext.toPrivateKey(keyFile, keyPassword);
} catch (Exception e) {
throw new SSLException(e);
}
}
static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException {
try {
return SslContext.toX509Certificates(file);
} catch (CertificateException e) {
throw new SSLException(e);
}
}
}

View File

@ -317,106 +317,27 @@ public final class OpenSslServerContext extends OpenSslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, null,
ClientAuth.NONE);
OpenSsl.ensureAvailability();
checkKeyManagerFactory(keyManagerFactory);
checkNotNull(keyCertChainFile, "keyCertChainFile");
if (!keyCertChainFile.isFile()) {
throw new IllegalArgumentException("keyCertChainFile is not a file: " + keyCertChainFile);
}
checkNotNull(keyFile, "keyFile");
if (!keyFile.isFile()) {
throw new IllegalArgumentException("keyFile is not a file: " + keyFile);
}
if (keyPassword == null) {
keyPassword = "";
}
// Create a new SSL_CTX and configure it.
boolean success = false;
try {
synchronized (OpenSslContext.class) {
/* Set certificate verification policy. */
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
/* Load the certificate chain. We must skip the first cert when server mode */
if (!SSLContext.setCertificateChainFile(ctx, keyCertChainFile.getPath(), true)) {
long error = SSL.getLastErrorNumber();
if (OpenSsl.isError(error)) {
String err = SSL.getErrorString(error);
throw new SSLException(
"failed to set certificate chain: " + keyCertChainFile + " (" + err + ')');
}
}
/* Load the certificate file and private key. */
try {
if (!SSLContext.setCertificate(
ctx, keyCertChainFile.getPath(), keyFile.getPath(), keyPassword, SSL.SSL_AIDX_RSA)) {
long error = SSL.getLastErrorNumber();
if (OpenSsl.isError(error)) {
String err = SSL.getErrorString(error);
throw new SSLException("failed to set certificate: " +
keyCertChainFile + " and " + keyFile + " (" + err + ')');
}
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("failed to set certificate: " + keyCertChainFile + " and " + keyFile, e);
}
try {
if (trustCertChainFile != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
} else if (trustManagerFactory == null) {
// Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
throws Exception {
extendedManager.checkClientTrusted(peerCerts, auth, engine);
}
});
} else {
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth)
throws Exception {
manager.checkClientTrusted(peerCerts, auth);
}
});
}
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
}
sessionContext = new OpenSslServerSessionContext(ctx);
success = true;
} finally {
if (!success) {
destroy();
}
}
this(toX509CertificatesInternal(trustCertChainFile), trustManagerFactory,
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter,
apn, sessionCacheSize, sessionTimeout, ClientAuth.NONE);
}
@SuppressWarnings("deprecation")
OpenSslServerContext(
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth) throws SSLException {
this(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth);
}
@SuppressWarnings("deprecation")
private OpenSslServerContext(
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain,
clientAuth);
OpenSsl.ensureAvailability();

View File

@ -853,7 +853,7 @@ public abstract class SslContext {
/**
* Generates a key specification for an (encrypted) private key.
*
* @param password characters, if {@code null} or empty an unencrypted key is assumed
* @param password characters, if {@code null} an unencrypted key is assumed
* @param key bytes of the DER encoded private key
*
* @return a key specification
@ -870,7 +870,7 @@ public abstract class SslContext {
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidKeyException, InvalidAlgorithmParameterException {
if (password == null || password.length == 0) {
if (password == null) {
return new PKCS8EncodedKeySpec(key);
}
@ -930,24 +930,21 @@ public abstract class SslContext {
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
encodedKeyBuf.readBytes(encodedKey).release();
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPassword == null ? null : keyPassword.toCharArray(),
encodedKey);
PrivateKey key;
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(
keyPassword == null ? null : keyPassword.toCharArray(), encodedKey);
try {
key = KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore) {
try {
key = KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore2) {
try {
key = KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException e) {
throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
}
}
}
return key;
}
/**

View File

@ -0,0 +1,29 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you 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:
*
* http://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 io.netty.handler.ssl;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.SSLException;
import java.io.File;
public class JdkSslClientContextTest extends SslContextTest {
@Override
protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
return new JdkSslClientContext(crtFile, InsecureTrustManagerFactory.INSTANCE, crtFile, keyFile, pass,
null, null, IdentityCipherSuiteFilter.INSTANCE, ApplicationProtocolConfig.DISABLED, 0, 0);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 The Netty Project
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
@ -13,38 +13,15 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import org.junit.Test;
import javax.net.ssl.SSLException;
import java.io.File;
public class JdkSslServerContextTest {
public class JdkSslServerContextTest extends SslContextTest {
@Test
public void testJdkSslServerWithEncryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_encrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
new JdkSslServerContext(crtFile, keyFile, "12345");
}
@Test
public void testJdkSslServerWithEncryptedPrivateKey2() throws SSLException {
File keyFile = new File(getClass().getResource("test2_encrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test2.crt").getFile());
new JdkSslServerContext(crtFile, keyFile, "12345");
}
@Test
public void testJdkSslServerWithUnencryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
new JdkSslServerContext(crtFile, keyFile, "");
new JdkSslServerContext(crtFile, keyFile, null);
@Override
protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
return new JdkSslServerContext(crtFile, keyFile, pass);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you 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:
*
* http://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 io.netty.handler.ssl;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import javax.net.ssl.SSLException;
import java.io.File;
public class OpenSslClientContextTest extends SslContextTest {
@Override
protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
return new OpenSslClientContext(crtFile, InsecureTrustManagerFactory.INSTANCE, crtFile, keyFile, pass,
null, null, IdentityCipherSuiteFilter.INSTANCE, ApplicationProtocolConfig.DISABLED, 0, 0);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you 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:
*
* http://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 io.netty.handler.ssl;
import org.junit.Assume;
import javax.net.ssl.SSLException;
import java.io.File;
public class OpenSslServerContextTest extends SslContextTest {
@Override
protected SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException {
Assume.assumeTrue(OpenSsl.isAvailable());
return new OpenSslServerContext(crtFile, keyFile, pass);
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you 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:
*
* http://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 io.netty.handler.ssl;
import org.junit.Assert;
import org.junit.Test;
import javax.net.ssl.SSLException;
import java.io.File;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
public abstract class SslContextTest {
@Test(expected = IOException.class)
public void testUnencryptedEmptyPassword() throws Exception {
PrivateKey key = SslContext.toPrivateKey(
new File(getClass().getResource("test2_unencrypted.pem").getFile()), "");
Assert.assertNotNull(key);
}
@Test
public void testUnEncryptedNullPassword() throws Exception {
PrivateKey key = SslContext.toPrivateKey(
new File(getClass().getResource("test2_unencrypted.pem").getFile()), null);
Assert.assertNotNull(key);
}
@Test
public void testEncryptedEmptyPassword() throws Exception {
PrivateKey key = SslContext.toPrivateKey(
new File(getClass().getResource("test_encrypted_empty_pass.pem").getFile()), "");
Assert.assertNotNull(key);
}
@Test(expected = InvalidKeySpecException.class)
public void testEncryptedNullPassword() throws Exception {
SslContext.toPrivateKey(
new File(getClass().getResource("test_encrypted_empty_pass.pem").getFile()), null);
}
@Test
public void testSslServerWithEncryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_encrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
newServerContext(crtFile, keyFile, "12345");
}
@Test
public void testSslServerWithEncryptedPrivateKey2() throws SSLException {
File keyFile = new File(getClass().getResource("test2_encrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test2.crt").getFile());
newServerContext(crtFile, keyFile, "12345");
}
@Test
public void testSslServerWithUnencryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
newServerContext(crtFile, keyFile, null);
}
@Test(expected = SSLException.class)
public void testSslServerWithUnencryptedPrivateKeyEmptyPass() throws SSLException {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
newServerContext(crtFile, keyFile, "");
}
protected abstract SslContext newServerContext(File crtFile, File keyFile, String pass) throws SSLException;
}

View File

@ -0,0 +1,29 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6TAbBgkqhkiG9w0BBQMwDgQIr2JTEf0lU/cCAggABIIEyKHtsAnPt1FmHeAF
AWZFF4KddWUW13iaaxXs+cHENCN1dpVxDPiCWNIkstQxC16bwKlfb0qFz42jQg4O
DM4kmtUyJoAt76wewteZvFPjzSuIepvJc8+SVaQuZPafSbGUcSPyATZbA2LmRWcx
HIvuya7gJX40aZm1VT1NM439UybJgmlrByoRGBjsrvYXGMZnMWLEYBgccN1wZ6WI
Hpuzv8LlIqFXL1DZZFFeFI60INlvfdwZWsz5SGf1zP68ZoGlS3R7LuJWMTEXfDUR
CLSturHFKI0jRAsloSYuHV/dJ6Io8hYWhAu84wc++dMTT+iSnaMWv7McF2nEFWx9
UVKZiar509z/Rm/C+yN7TkT8TMaPRoYC6jxDW3IsE2wpBdLSCeF8Nh0vCNeay13I
2OcOFz1UrZcopEqXwl5SBUfhU3VjNm5N+h2p9WW9HTa3TXZDbZ04kyJEPYj3e8OE
viVDMRunJGWRkJ2//oCp2E9+NBYMMj9gN11mCUTyFmfpY/Kec/0nhQMCcZRlMzUD
i9AAzCVsm1HYnSzRq4JwnTbt6KBnPyS/rW+IfKQ+2aI/7Or6JfzrpX/v7/ECe98S
zaVhO9rJjsfML0ceR8UBK+dkl1R6hYV2+xXO8UZEo+X9IR1c03gDXMW0nZ4/Jgq4
d3PmzuAuObCGnyr2k3PJqOoNZk3f44qfkNPQgbIROgX5zA8GNpL4XxDFFOW4YN2i
opod3doJ5r00WVmJowrKjWYMs0Ljik9FFla3986oZ+s/+WEWoFOssXvJMYiYQUIT
mrvXsyqNI+Nhs1rl8zzuZQ0BKGfwHyrUEcAgngH7itKR+IoqNXx1+pcIqJe/oIQr
oIEr5bYxAtDSi6DUGrvsuVoe5l5ByE+YvW68S7gIore4UXl77EVWQ5yfuyFteQE6
Bm8vcnjGeoo0YDPwwziN1MUsYJPX6cnFIV20phXm4w5YAYfrI2yKt3AIKFkDU9n0
5yLogR4NlkKgHd2DjELJXqvFBSivW6wZXL0wusDI5imopET/SD3vUexYML480Ulk
CxhbYc9FAgcKgYr4K6vyPAYj4K/w0qKC7ceAdJarPxS2qeXBHeHAepr1wHBu7G9S
7J8yHht0xB06Ar3nJ6J6KcCikv6ZMpQImYeSbE/5jrsh8hnqdDmKz5dAvwq4fwy9
r0zUev0HB4+ON4XH8XAcwt5p/3zJISRpQZb4G7vm7VaA5cIbPOneuMxO2pw3CTI3
qXnvWWkGdoc4BYacHJxA/awCUk6SrglCM5X479BG+ZnMzkCuXbJHn+zh2BgSQ7B3
Z3JsNtnCaPGp8QEqFYk9tLdFcZ6/PlUsTawcIjG/6VMvpLcNBahHqwqDg1rax4Mx
xJDnGfVXFsbBQIYku8yvrXi7QnANgD/N+AAAjo6CDNJZmhsc2nhE7fsVWxz1hbl1
vK1N13kT8CbfOFCUs8Od/O369vbv5C7yvK3JeG11sNfOZ/MqYBAVPDFLPdtJwFu6
0M/c8+TDgLOp+qlRkIgDwXkjed4ncjDHSl9g41Zp4n1uR94VsKtneCNLuj4+qr4h
uhBnUap1s7uBZ0PW9TA5DXXNvhCrHu2vz3YIMBmlYv/i5qu+bPtPki7eTfm8UWAB
p9pJJHa/9J0gfM4a4w==
-----END ENCRYPTED PRIVATE KEY-----