diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java index 92e3cbbc6b..23f47f00af 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java @@ -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") diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java index 1dceb7885e..03840e3259 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java @@ -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); + } + } } diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java index 42ab957572..d1e9a4e112 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java @@ -317,106 +317,27 @@ public final class OpenSslServerContext extends OpenSslContext { File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable 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 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 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(); diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index fa8d96efae..ab40c9082f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -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; } /** diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java new file mode 100644 index 0000000000..22f3e05194 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/JdkSslClientContextTest.java @@ -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); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java index 9eccd91b03..73dc730b3c 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkSslServerContextTest.java @@ -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); } } diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java new file mode 100644 index 0000000000..3c08eb3008 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslClientContextTest.java @@ -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); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java b/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java new file mode 100644 index 0000000000..bcc283d6ef --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/OpenSslServerContextTest.java @@ -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); + } +} diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java new file mode 100644 index 0000000000..76526a5b25 --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextTest.java @@ -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; +} diff --git a/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem b/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem new file mode 100644 index 0000000000..d28b0478a0 --- /dev/null +++ b/handler/src/test/resources/io/netty/handler/ssl/test_encrypted_empty_pass.pem @@ -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-----