From 75d6a51d65a58a2476c39358e8244bc3fad25e5d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 15 Jul 2015 14:13:47 +0200 Subject: [PATCH] Allow to create SslContext from existing PrivateKey / X509Certificate Motivation: Sometimes the user already has a PrivateKey / X509Certificate which should be used to create a new SslContext. At the moment we only allow to construct it via Files. Modifications: - Add new methods to the SslContextBuilder to allow creating a SslContext from PrivateKey / X509Certificate - Mark all public constructors of *SslContext as @Deprecated, the user should use SslContextBuilder - Update tests to us SslContextBuilder. Result: Creating of SslContext is possible with PrivateKay/X509Certificate --- .../handler/ssl/JdkSslClientContext.java | 60 ++++++- .../io/netty/handler/ssl/JdkSslContext.java | 28 +++- .../handler/ssl/JdkSslServerContext.java | 59 ++++++- .../handler/ssl/OpenSslClientContext.java | 134 ++++++++++++++- .../io/netty/handler/ssl/OpenSslContext.java | 61 ++++++- .../handler/ssl/OpenSslServerContext.java | 149 +++++++++++++++-- .../java/io/netty/handler/ssl/SslContext.java | 153 +++++++++++------- .../netty/handler/ssl/SslContextBuilder.java | 125 +++++++++++--- .../ssl/util/SelfSignedCertificate.java | 25 +++ .../handler/ssl/SslContextBuilderTest.java | 110 +++++++++++++ pom.xml | 2 +- .../transport/socket/SocketSslEchoTest.java | 16 +- .../socket/SocketSslGreetingTest.java | 16 +- .../transport/socket/SocketStartTlsTest.java | 16 +- 14 files changed, 812 insertions(+), 142 deletions(-) create mode 100644 handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java index 0ec0dad63f..bba736aba7 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java @@ -24,6 +24,8 @@ import javax.net.ssl.SSLSessionContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; /** * A client-side {@link SslContext} which uses JDK's SSL/TLS implementation. @@ -34,7 +36,10 @@ public final class JdkSslClientContext extends JdkSslContext { /** * Creates a new instance. + * + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext() throws SSLException { this(null, null); } @@ -44,7 +49,9 @@ public final class JdkSslClientContext extends JdkSslContext { * * @param certChainFile an X.509 certificate chain file in PEM format. * {@code null} to use the system default + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext(File certChainFile) throws SSLException { this(certChainFile, null); } @@ -55,7 +62,9 @@ public final class JdkSslClientContext extends JdkSslContext { * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * that verifies the certificates sent from servers. * {@code null} to use the default. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { this(null, trustManagerFactory); } @@ -68,7 +77,9 @@ public final class JdkSslClientContext extends JdkSslContext { * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * that verifies the certificates sent from servers. * {@code null} to use the default. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE, JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0); @@ -93,6 +104,7 @@ public final class JdkSslClientContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public JdkSslClientContext( @@ -119,7 +131,9 @@ public final class JdkSslClientContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext( File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, @@ -144,7 +158,9 @@ public final class JdkSslClientContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext( File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, @@ -181,7 +197,9 @@ public final class JdkSslClientContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, @@ -218,21 +236,47 @@ public final class JdkSslClientContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, long sessionCacheSize, long sessionTimeout) throws SSLException { super(ciphers, cipherFilter, apn); - try { - if (trustCertChainFile != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); + ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory, + toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), + keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout); + } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; } - if (keyFile != null) { - keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory); + throw new SSLException("failed to initialize the client-side SSL context", e); + } + } + + JdkSslClientContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, + ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { + super(ciphers, cipherFilter, toNegotiator(apn, false)); + ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, + keyManagerFactory, sessionCacheSize, sessionTimeout); + } + + private static SSLContext newSSLContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, long sessionCacheSize, + long sessionTimeout) throws SSLException { + try { + if (trustCertChain != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory); } - ctx = SSLContext.getInstance(PROTOCOL); + if (keyCertChain != null) { + keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory); + } + SSLContext ctx = SSLContext.getInstance(PROTOCOL); ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(), trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), null); @@ -244,7 +288,11 @@ public final class JdkSslClientContext extends JdkSslContext { if (sessionTimeout > 0) { sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); } + return ctx; } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; + } throw new SSLException("failed to initialize the client-side SSL context", e); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 9a39d23b74..f6e6173a12 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -33,9 +33,11 @@ import java.security.KeyException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Arrays; @@ -281,7 +283,9 @@ public abstract class JdkSslContext extends SslContext { * {@code null} if it's not password-protected. * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} * @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain. + * @deprecated will be removed. */ + @Deprecated protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword, KeyManagerFactory kmf) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, @@ -294,6 +298,17 @@ public abstract class JdkSslContext extends SslContext { return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf); } + static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChain, PrivateKey key, String keyPassword, + KeyManagerFactory kmf) + throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException { + String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); + if (algorithm == null) { + algorithm = "SunX509"; + } + return buildKeyManagerFactory(certChain, algorithm, key, keyPassword, kmf); + } + /** * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, * and a certificate chain. @@ -306,14 +321,25 @@ public abstract class JdkSslContext extends SslContext { * @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, * and a certificate chain. + * @deprecated will be removed. */ + @Deprecated protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf) throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, IOException, CertificateException, KeyException, UnrecoverableKeyException { + return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm, + toPrivateKey(keyFile, keyPassword), keyPassword, kmf); + } + + static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile, + String keyAlgorithm, PrivateKey key, + String keyPassword, KeyManagerFactory kmf) + throws KeyStoreException, NoSuchAlgorithmException, IOException, + CertificateException, UnrecoverableKeyException { char[] keyPasswordChars = keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray(); - KeyStore ks = buildKeyStore(certChainFile, keyFile, keyPasswordChars); + KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars); // Set up key manager factory to use our key store if (kmf == null) { kmf = KeyManagerFactory.getInstance(keyAlgorithm); diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java index d388485afa..0b2f25ad75 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java @@ -25,6 +25,8 @@ import javax.net.ssl.SSLSessionContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; /** * A server-side {@link SslContext} which uses JDK's SSL/TLS implementation. @@ -38,7 +40,9 @@ public final class JdkSslServerContext extends JdkSslContext { * * @param certChainFile an X.509 certificate chain file in PEM format * @param keyFile a PKCS#8 private key file in PEM format + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException { this(certChainFile, keyFile, null); } @@ -50,7 +54,9 @@ public final class JdkSslServerContext extends JdkSslContext { * @param keyFile a PKCS#8 private key file in PEM format * @param keyPassword the password of the {@code keyFile}. * {@code null} if it's not password-protected. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException { this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0); @@ -71,7 +77,9 @@ public final class JdkSslServerContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext( File certChainFile, File keyFile, String keyPassword, Iterable ciphers, Iterable nextProtocols, @@ -95,7 +103,9 @@ public final class JdkSslServerContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext( File certChainFile, File keyFile, String keyPassword, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, @@ -119,7 +129,9 @@ public final class JdkSslServerContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext( File certChainFile, File keyFile, String keyPassword, Iterable ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, @@ -153,7 +165,9 @@ public final class JdkSslServerContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, @@ -187,26 +201,53 @@ public final class JdkSslServerContext extends JdkSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, long sessionCacheSize, long sessionTimeout) throws SSLException { super(ciphers, cipherFilter, apn); - if (keyFile == null && keyManagerFactory == null) { - throw new NullPointerException("keyFile, keyManagerFactory"); + try { + ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory, + toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), + keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout); + } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; + } + throw new SSLException("failed to initialize the server-side SSL context", e); + } + } + + JdkSslServerContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, + ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { + super(ciphers, cipherFilter, toNegotiator(apn, true)); + ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, + keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout); + } + + private static SSLContext newSSLContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, long sessionCacheSize, long sessionTimeout) + throws SSLException { + if (key == null && keyManagerFactory == null) { + throw new NullPointerException("key, keyManagerFactory"); } try { - if (trustCertChainFile != null) { - trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); + if (trustCertChain != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory); } - if (keyFile != null) { - keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory); + if (key != null) { + keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory); } // Initialize the SSLContext to work with our key managers. - ctx = SSLContext.getInstance(PROTOCOL); + SSLContext ctx = SSLContext.getInstance(PROTOCOL); ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), null); @@ -218,7 +259,11 @@ public final class JdkSslServerContext extends JdkSslContext { if (sessionTimeout > 0) { sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); } + return ctx; } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; + } throw new SSLException("failed to initialize the server-side SSL context", e); } } 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 6d50884a78..5e962323bd 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java @@ -26,6 +26,7 @@ import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.security.KeyStore; +import java.security.PrivateKey; import java.security.cert.X509Certificate; /** @@ -36,9 +37,11 @@ public final class OpenSslClientContext extends OpenSslContext { /** * Creates a new instance. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslClientContext() throws SSLException { - this(null, null, null, null, null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); + this((File) null, null, null, null, null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); } /** @@ -46,7 +49,9 @@ public final class OpenSslClientContext extends OpenSslContext { * * @param certChainFile an X.509 certificate chain file in PEM format. * {@code null} to use the system default + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslClientContext(File certChainFile) throws SSLException { this(certChainFile, null); } @@ -57,7 +62,9 @@ public final class OpenSslClientContext extends OpenSslContext { * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * that verifies the certificates sent from servers. * {@code null} to use the default. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { this(null, trustManagerFactory); } @@ -70,16 +77,15 @@ public final class OpenSslClientContext extends OpenSslContext { * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * that verifies the certificates sent from servers. * {@code null} to use the default. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { this(certChainFile, trustManagerFactory, null, null, null, null, null, IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); } /** - * @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, Iterable, - * CipherSuiteFilter, ApplicationProtocolConfig, long, long)} - * * Creates a new instance. * * @param certChainFile an X.509 certificate chain file in PEM format @@ -93,6 +99,7 @@ public final class OpenSslClientContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, @@ -103,9 +110,6 @@ public final class OpenSslClientContext extends OpenSslContext { } /** - * @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, File, File, String, - * KeyManagerFactory, Iterable, CipherSuiteFilter, ApplicationProtocolConfig,long, long)} - * * Creates a new instance. * * @param certChainFile an X.509 certificate chain file in PEM format @@ -120,6 +124,7 @@ public final class OpenSslClientContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable ciphers, @@ -157,7 +162,9 @@ public final class OpenSslClientContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, @@ -257,6 +264,119 @@ public final class OpenSslClientContext extends OpenSslContext { } } + @SuppressWarnings("deprecation") + OpenSslClientContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, + KeyManagerFactory keyManagerFactory, Iterable ciphers, + CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, + long sessionCacheSize, long sessionTimeout) + throws SSLException { + super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT); + boolean success = false; + try { + if (key == null && keyCertChain != null || key != null && keyCertChain == null) { + throw new IllegalArgumentException( + "Either both keyCertChain and key needs to be null or none of them"); + } + synchronized (OpenSslContext.class) { + if (trustCertChain != null) { + long trustCertChainBio = 0; + + try { + trustCertChainBio = toBIO(trustCertChain); + /* Load the certificate chain. We must NOT skip the first cert when client mode */ + if (!SSLContext.setCertificateChainBio(ctx, trustCertChainBio, false)) { + long error = SSL.getLastErrorNumber(); + if (OpenSsl.isError(error)) { + throw new SSLException( + "failed to set certificate chain: " + SSL.getErrorString(error)); + } + } + } catch (Exception e) { + throw new SSLException( + "failed to set certificate chain", e); + } finally { + if (trustCertChainBio != 0) { + SSL.freeBIO(trustCertChainBio); + } + } + } + if (keyCertChain != null && key != null) { + /* Load the certificate file and private key. */ + long keyBio = 0; + long keyCertChainBio = 0; + + try { + keyCertChainBio = toBIO(keyCertChain); + + keyBio = toBIO(key); + + if (!SSLContext.setCertificateBio( + ctx, keyCertChainBio, keyBio, keyPassword, SSL.SSL_AIDX_RSA)) { + long error = SSL.getLastErrorNumber(); + if (OpenSsl.isError(error)) { + throw new SSLException("failed to set certificate and key: " + + SSL.getErrorString(error)); + } + } + } catch (SSLException e) { + throw e; + } catch (Exception e) { + throw new SSLException("failed to set certificate and key", e); + } finally { + if (keyBio != 0) { + SSL.freeBIO(keyBio); + } + if (keyCertChainBio != 0) { + SSL.freeBIO(keyCertChainBio); + } + } + } + + SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH); + + try { + if (trustCertChain != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertChain, 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(); + } + } + } + @Override public OpenSslSessionContext sessionContext() { return sessionContext; 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 a820b94ef3..e5680180ce 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java @@ -15,7 +15,11 @@ */ package io.netty.handler.ssl; +import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.base64.Base64; +import io.netty.util.CharsetUtil; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; @@ -31,6 +35,7 @@ import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; +import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -43,6 +48,10 @@ import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBeha import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; public abstract class OpenSslContext extends SslContext { + private static final byte[] BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII); + private static final byte[] END_CERT = "\n-----END CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII); + private static final byte[] BEGIN_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n".getBytes(CharsetUtil.US_ASCII); + private static final byte[] END_PRIVATE_KEY = "\n-----END PRIVATE KEY-----\n".getBytes(CharsetUtil.US_ASCII); private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslContext.class); /** @@ -62,7 +71,7 @@ public abstract class OpenSslContext extends SslContext { /** The OpenSSL SSL_CTX object */ protected volatile long ctx; - private long aprPool; + long aprPool; @SuppressWarnings({ "unused", "FieldMayBeFinal" }) private volatile int aprPoolDestroyed; private volatile boolean rejectRemoteInitiatedRenegotiation; @@ -458,4 +467,54 @@ public abstract class OpenSslContext extends SslContext { engines.put(engine.sslPointer(), engine); } } + + /** + * Return the pointer to a in-memory BIO + * or {@code 0} if the {@code key} is {@code null}. The BIO contains the content of the {@code key}. + */ + static long toBIO(PrivateKey key) throws Exception { + if (key == null) { + return 0; + } + ByteBuf buffer = Unpooled.directBuffer(); + try { + buffer.writeBytes(BEGIN_PRIVATE_KEY); + buffer.writeBytes(Base64.encode(Unpooled.wrappedBuffer(key.getEncoded()), true)); + buffer.writeBytes(END_PRIVATE_KEY); + return newBIO(buffer); + } finally { + buffer.release(); + } + } + + /** + * Return the pointer to a in-memory BIO + * or {@code 0} if the {@code certChain} is {@code null}. The BIO contains the content of the {@code certChain}. + */ + static long toBIO(X509Certificate[] certChain) throws Exception { + if (certChain == null) { + return 0; + } + ByteBuf buffer = Unpooled.directBuffer(); + try { + for (X509Certificate cert: certChain) { + buffer.writeBytes(BEGIN_CERT); + buffer.writeBytes(Base64.encode(Unpooled.wrappedBuffer(cert.getEncoded()), true)); + buffer.writeBytes(END_CERT); + } + return newBIO(buffer); + } finally { + buffer.release(); + } + } + + private static long newBIO(ByteBuf buffer) throws Exception { + long bio = SSL.newMemBIO(); + int readable = buffer.readableBytes(); + if (SSL.writeToBIO(bio, buffer.memoryAddress(), readable) != readable) { + SSL.freeBIO(bio); + throw new IllegalStateException("Could not write data to memory BIO"); + } + return bio; + } } 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 d5ab23855c..b378bc6e48 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java @@ -15,7 +15,6 @@ */ package io.netty.handler.ssl; -import io.netty.util.internal.EmptyArrays; import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SSLContext; @@ -28,6 +27,7 @@ import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import java.io.File; import java.security.KeyStore; +import java.security.PrivateKey; import java.security.cert.X509Certificate; import static io.netty.util.internal.ObjectUtil.*; @@ -43,7 +43,9 @@ public final class OpenSslServerContext extends OpenSslContext { * * @param certChainFile an X.509 certificate chain file in PEM format * @param keyFile a PKCS#8 private key file in PEM format + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslServerContext(File certChainFile, File keyFile) throws SSLException { this(certChainFile, keyFile, null); } @@ -55,16 +57,15 @@ public final class OpenSslServerContext extends OpenSslContext { * @param keyFile a PKCS#8 private key file in PEM format * @param keyPassword the password of the {@code keyFile}. * {@code null} if it's not password-protected. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException { this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, ApplicationProtocolConfig.DISABLED, 0, 0); } /** - * @deprecated use {@link #OpenSslServerContext( - * File, File, String, Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)} - * * Creates a new instance. * * @param certChainFile an X.509 certificate chain file in PEM format @@ -78,6 +79,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslServerContext( @@ -89,9 +91,6 @@ public final class OpenSslServerContext extends OpenSslContext { } /** - * @deprecated Use the constructors that accepts {@link ApplicationProtocolConfig} or - * {@link ApplicationProtocolNegotiator} instead. - * * Creates a new instance. * * @param certChainFile an X.509 certificate chain file in PEM format @@ -106,6 +105,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslServerContext( @@ -130,8 +130,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. - * @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, - * Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)} + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslServerContext( @@ -156,8 +155,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. - * @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, - * Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)} + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslServerContext( @@ -183,7 +181,9 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslServerContext( File certChainFile, File keyFile, String keyPassword, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, @@ -218,7 +218,9 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslServerContext( File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, @@ -243,6 +245,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ @Deprecated public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword, @@ -268,8 +271,7 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. - * @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, - * Iterable, CipherSuiteFilter, OpenSslApplicationProtocolNegotiator, long, long)} + * @deprecated use {@link SslContextBuilder}} */ @Deprecated public OpenSslServerContext( @@ -307,7 +309,9 @@ public final class OpenSslServerContext extends OpenSslContext { * {@code 0} to use the default value. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * {@code 0} to use the default value. + * @deprecated use {@link SslContextBuilder} */ + @Deprecated public OpenSslServerContext( File trustCertChainFile, TrustManagerFactory trustManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, @@ -370,10 +374,123 @@ public final class OpenSslServerContext extends OpenSslContext { if (trustCertChainFile != null) { trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); } else { - char[] keyPasswordChars = - keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray(); + KeyStore ks = buildKeyStore(keyCertChainFile, keyFile, keyPassword); + trustManagerFactory.init(ks); + } - KeyStore ks = buildKeyStore(keyCertChainFile, keyFile, keyPasswordChars); + 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(); + } + } + } + + @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) throws SSLException { + super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER); + OpenSsl.ensureAvailability(); + + checkNotNull(keyCertChain, "keyCertChainFile"); + checkNotNull(key, "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); + long keyCertChainBio = 0; + try { + keyCertChainBio = toBIO(keyCertChain); + /* Load the certificate chain. We must skip the first cert when server mode */ + if (!SSLContext.setCertificateChainBio(ctx, keyCertChainBio, true)) { + long error = SSL.getLastErrorNumber(); + if (OpenSsl.isError(error)) { + String err = SSL.getErrorString(error); + throw new SSLException( + "failed to set certificate chain: " + err); + } + } + } catch (Exception e) { + throw new SSLException( + "failed to set certificate chain", e); + } finally { + if (keyCertChainBio != 0) { + SSL.freeBIO(keyCertChainBio); + } + } + + /* Load the certificate file and private key. */ + long keyBio = 0; + keyCertChainBio = 0; + try { + keyBio = toBIO(key); + keyCertChainBio = toBIO(keyCertChain); + if (!SSLContext.setCertificateBio( + ctx, keyCertChainBio, keyBio, 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 and key: " + err); + } + } + } catch (SSLException e) { + throw e; + } catch (Exception e) { + throw new SSLException("failed to set certificate and key", e); + } finally { + if (keyBio != 0) { + SSL.freeBIO(keyBio); + } + if (keyCertChainBio != 0) { + SSL.freeBIO(keyCertChainBio); + } + } + try { + if (trustManagerFactory == null) { + // Mimic the way SSLContext.getInstance(KeyManager[], null, null) works + trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + } + if (trustCertChain != null) { + trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory); + } else { + KeyStore ks = buildKeyStore(keyCertChain, key, keyPassword.toCharArray()); trustManagerFactory.init(ks); } 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 0865890f40..40964452dc 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -50,13 +50,11 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; -import java.util.ArrayList; import java.util.List; /** @@ -147,8 +145,6 @@ public abstract class SslContext { } /** - * @deprecated Use the factory methods that accept {@link ApplicationProtocolConfig} instead. - * * Creates a new server-side {@link SslContext}. * * @param certChainFile an X.509 certificate chain file in PEM format @@ -241,8 +237,6 @@ public abstract class SslContext { } /** - * @deprecated Use the factory methods that accept {@link ApplicationProtocolConfig} instead. - * * Creates a new server-side {@link SslContext}. * * @param provider the {@link SslContext} implementation to use. @@ -378,15 +372,24 @@ public abstract class SslContext { File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { - return newServerContextInternal(provider, trustCertChainFile, trustManagerFactory, keyCertChainFile, - keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, - sessionCacheSize, sessionTimeout); + try { + return newServerContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory, + toX509Certificates(keyCertChainFile), + toPrivateKey(keyFile, keyPassword), + keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, + sessionCacheSize, sessionTimeout); + } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; + } + throw new SSLException("failed to initialize the server-side SSL context", e); + } } static SslContext newServerContextInternal( SslProvider provider, - File trustCertChainFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, + X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { @@ -397,11 +400,11 @@ public abstract class SslContext { switch (provider) { case JDK: return new JdkSslServerContext( - trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, + trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); case OPENSSL: return new OpenSslServerContext( - trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, + trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); default: throw new Error(provider.toString()); @@ -466,8 +469,6 @@ public abstract class SslContext { } /** - * @deprecated Use the factory methods that accept {@link ApplicationProtocolConfig} instead. - * * Creates a new client-side {@link SslContext}. * * @param certChainFile an X.509 certificate chain file in PEM format. @@ -597,8 +598,6 @@ public abstract class SslContext { } /** - * @deprecated Use the factory methods that accept {@link ApplicationProtocolConfig}. - * * Creates a new client-side {@link SslContext}. * * @param provider the {@link SslContext} implementation to use. @@ -709,16 +708,24 @@ public abstract class SslContext { File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { - return newClientContextInternal(provider, trustCertChainFile, trustManagerFactory, - keyCertChainFile, keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, - apn, - sessionCacheSize, sessionTimeout); + try { + return newClientContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory, + toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), + keyPassword, keyManagerFactory, ciphers, cipherFilter, + apn, + sessionCacheSize, sessionTimeout); + } catch (Exception e) { + if (e instanceof SSLException) { + throw (SSLException) e; + } + throw new SSLException("failed to initialize the client-side SSL context", e); + } } static SslContext newClientContextInternal( SslProvider provider, - File trustCertChainFile, TrustManagerFactory trustManagerFactory, - File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, + X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory, + X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { if (provider == null) { @@ -727,11 +734,11 @@ public abstract class SslContext { switch (provider) { case JDK: return new JdkSslClientContext( - trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, + trustCert, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); case OPENSSL: return new OpenSslClientContext( - trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, + trustCert, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); } // Should never happen!! @@ -877,21 +884,45 @@ public abstract class SslContext { /** * Generates a new {@link KeyStore}. * - * @param certChainFile a X.509 certificate chain file in PEM format, - * @param keyFile a PKCS#8 private key file in PEM format, + * @param certChain a X.509 certificate chain + * @param key a PKCS#8 private key * @param keyPasswordChars the password of the {@code keyFile}. * {@code null} if it's not password-protected. * @return generated {@link KeyStore}. */ - static KeyStore buildKeyStore(File certChainFile, File keyFile, char[] keyPasswordChars) + static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, char[] keyPasswordChars) throws KeyStoreException, NoSuchAlgorithmException, - NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, - CertificateException, KeyException, IOException { + CertificateException, IOException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + ks.setKeyEntry("key", key, keyPasswordChars, certChain); + return ks; + } + + static KeyStore buildKeyStore(File certChainFile, File keyFile, String keyPassword) + throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, NoSuchPaddingException, InvalidKeySpecException, + InvalidAlgorithmParameterException, KeyException, IOException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + ks.setKeyEntry("key", toPrivateKey(keyFile, keyPassword), + keyPassword == null ? null : keyPassword.toCharArray(), toX509Certificates(certChainFile)); + return ks; + } + + static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException, + NoSuchPaddingException, InvalidKeySpecException, + InvalidAlgorithmParameterException, + KeyException, IOException { + if (keyFile == null) { + return null; + } ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile); byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()]; encodedKeyBuf.readBytes(encodedKey).release(); - PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey); + PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPassword == null ? null : keyPassword.toCharArray(), + encodedKey); PrivateKey key; try { @@ -907,25 +938,7 @@ public abstract class SslContext { } } } - - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - ByteBuf[] certs = PemReader.readCertificates(certChainFile); - List certChain = new ArrayList(certs.length); - - try { - for (ByteBuf buf: certs) { - certChain.add(cf.generateCertificate(new ByteBufInputStream(buf))); - } - } finally { - for (ByteBuf buf: certs) { - buf.release(); - } - } - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, null); - ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()])); - return ks; + return key; } /** @@ -934,25 +947,45 @@ public abstract class SslContext { * @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}. * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile} */ - protected static TrustManagerFactory buildTrustManagerFactory(File certChainFile, - TrustManagerFactory trustManagerFactory) + @Deprecated + protected static TrustManagerFactory buildTrustManagerFactory( + File certChainFile, TrustManagerFactory trustManagerFactory) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, null); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate[] x509Certs = toX509Certificates(certChainFile); + + return buildTrustManagerFactory(x509Certs, trustManagerFactory); + } + + static X509Certificate[] toX509Certificates(File file) throws CertificateException { + if (file == null) { + return null; + } + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + ByteBuf[] certs = PemReader.readCertificates(file); + X509Certificate[] x509Certs = new X509Certificate[certs.length]; - ByteBuf[] certs = PemReader.readCertificates(certChainFile); try { - for (ByteBuf buf: certs) { - X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(buf)); - X500Principal principal = cert.getSubjectX500Principal(); - ks.setCertificateEntry(principal.getName("RFC2253"), cert); + for (int i = 0; i < certs.length; i++) { + x509Certs[i] = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(certs[i])); } } finally { for (ByteBuf buf: certs) { buf.release(); } } + return x509Certs; + } + + static TrustManagerFactory buildTrustManagerFactory( + X509Certificate[] certChain, TrustManagerFactory trustManagerFactory) + throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + + for (X509Certificate cert: certChain) { + X500Principal principal = cert.getSubjectX500Principal(); + ks.setCertificateEntry(principal.getName("RFC2253"), cert); + } // Set up trust manager factory to use our key store. if (trustManagerFactory == null) { diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java index a0d3caf45d..a5958481cb 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java @@ -22,11 +22,14 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManagerFactory; import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; /** * Builder for configuring a new SslContext for creation. */ public final class SslContextBuilder { + /** * Creates a builder for new client-side {@link SslContext}. */ @@ -45,6 +48,17 @@ public final class SslContextBuilder { return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile); } + /** + * Creates a builder for new server-side {@link SslContext}. + * + * @param key a PKCS#8 private key + * @param keyCertChain the X.509 certificate chain + * @see #keyManager(PrivateKey, X509Certificate[]) + */ + public static SslContextBuilder forServer(PrivateKey key, X509Certificate... keyCertChain) { + return new SslContextBuilder(true).keyManager(key, keyCertChain); + } + /** * Creates a builder for new server-side {@link SslContext}. * @@ -59,6 +73,20 @@ public final class SslContextBuilder { return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile, keyPassword); } + /** + * Creates a builder for new server-side {@link SslContext}. + * + * @param key a PKCS#8 private key + * @param keyCertChain the X.509 certificate chain + * @param keyPassword the password of the {@code keyFile}, or {@code null} if it's not + * password-protected + * @see #keyManager(File, File, String) + */ + public static SslContextBuilder forServer( + PrivateKey key, String keyPassword, X509Certificate... keyCertChain) { + return new SslContextBuilder(true).keyManager(key, keyPassword, keyCertChain); + } + /** * Creates a builder for new server-side {@link SslContext}. * @@ -71,10 +99,10 @@ public final class SslContextBuilder { private final boolean forServer; private SslProvider provider; - private File trustCertChainFile; + private X509Certificate[] trustCertChain; private TrustManagerFactory trustManagerFactory; - private File keyCertChainFile; - private File keyFile; + private X509Certificate[] keyCertChain; + private PrivateKey key; private String keyPassword; private KeyManagerFactory keyManagerFactory; private Iterable ciphers; @@ -100,8 +128,19 @@ public final class SslContextBuilder { * contain an X.509 certificate chain in PEM format. {@code null} uses the system default. */ public SslContextBuilder trustManager(File trustCertChainFile) { - this.trustCertChainFile = trustCertChainFile; - this.trustManagerFactory = null; + try { + return trustManager(SslContext.toX509Certificates(trustCertChainFile)); + } catch (Exception e) { + throw new IllegalArgumentException("File does not contain valid certificates: " + trustCertChainFile, e); + } + } + + /** + * Trusted certificates for verifying the remote endpoint's certificate, {@code null} uses the system default. + */ + public SslContextBuilder trustManager(X509Certificate... trustCertChain) { + this.trustCertChain = trustCertChain != null ? trustCertChain.clone() : null; + trustManagerFactory = null; return this; } @@ -111,7 +150,7 @@ public final class SslContextBuilder { * you must use {@link #trustManager(File)}. {@code null} uses the system default. */ public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) { - this.trustCertChainFile = null; + trustCertChain = null; this.trustManagerFactory = trustManagerFactory; return this; } @@ -127,6 +166,17 @@ public final class SslContextBuilder { return keyManager(keyCertChainFile, keyFile, null); } + /** + * Identifying certificate for this host. {@code keyCertChain} and {@code key} may + * be {@code null} for client contexts, which disables mutual authentication. + * + * @param key a PKCS#8 private key + * @param keyCertChain an X.509 certificate chain + */ + public SslContextBuilder keyManager(PrivateKey key, X509Certificate... keyCertChain) { + return keyManager(key, null, keyCertChain); + } + /** * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may * be {@code null} for client contexts, which disables mutual authentication. @@ -137,14 +187,51 @@ public final class SslContextBuilder { * password-protected */ public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) { - if (forServer) { - checkNotNull(keyCertChainFile, "keyCertChainFile required for servers"); - checkNotNull(keyFile, "keyFile required for servers"); + X509Certificate[] keyCertChain; + PrivateKey key; + try { + keyCertChain = SslContext.toX509Certificates(keyCertChainFile); + } catch (Exception e) { + throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e); } - this.keyCertChainFile = keyCertChainFile; - this.keyFile = keyFile; + try { + key = SslContext.toPrivateKey(keyFile, keyPassword); + } catch (Exception e) { + throw new IllegalArgumentException("File does not contain valid private key: " + keyFile, e); + } + return keyManager(key, keyPassword, keyCertChain); + } + + /** + * Identifying certificate for this host. {@code keyCertChain} and {@code key} may + * be {@code null} for client contexts, which disables mutual authentication. + * + * @param key a PKCS#8 private key file + * @param keyPassword the password of the {@code key}, or {@code null} if it's not + * password-protected + * @param keyCertChain an X.509 certificate chain + */ + public SslContextBuilder keyManager(PrivateKey key, String keyPassword, X509Certificate... keyCertChain) { + if (forServer) { + checkNotNull(keyCertChain, "keyCertChain required for servers"); + if (keyCertChain.length == 0) { + throw new IllegalArgumentException("keyCertChain must be non-empty"); + } + checkNotNull(key, "key required for servers"); + } + if (keyCertChain == null || keyCertChain.length == 0) { + this.keyCertChain = null; + } else { + for (X509Certificate cert: keyCertChain) { + if (cert == null) { + throw new IllegalArgumentException("keyCertChain contains null entry"); + } + } + this.keyCertChain = keyCertChain.clone(); + } + this.key = key; this.keyPassword = keyPassword; - this.keyManagerFactory = null; + keyManagerFactory = null; return this; } @@ -158,9 +245,9 @@ public final class SslContextBuilder { if (forServer) { checkNotNull(keyManagerFactory, "keyManagerFactory required for servers"); } - this.keyCertChainFile = null; - this.keyFile = null; - this.keyPassword = null; + keyCertChain = null; + key = null; + keyPassword = null; this.keyManagerFactory = keyManagerFactory; return this; } @@ -216,12 +303,12 @@ public final class SslContextBuilder { */ public SslContext build() throws SSLException { if (forServer) { - return SslContext.newServerContextInternal(provider, trustCertChainFile, - trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory, + return SslContext.newServerContextInternal(provider, trustCertChain, + trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); } else { - return SslContext.newClientContextInternal(provider, trustCertChainFile, - trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory, + return SslContext.newClientContextInternal(provider, trustCertChain, + trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java index 54257a7697..d97992bacb 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java @@ -23,6 +23,7 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -33,6 +34,7 @@ import java.security.PrivateKey; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Date; @@ -63,6 +65,8 @@ public final class SelfSignedCertificate { private final File certificate; private final File privateKey; + private final X509Certificate cert; + private final PrivateKey key; /** * Creates a new instance. @@ -120,6 +124,13 @@ public final class SelfSignedCertificate { certificate = new File(paths[0]); privateKey = new File(paths[1]); + key = keypair.getPrivate(); + try { + cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate( + new FileInputStream(certificate)); + } catch (Exception e) { + throw new CertificateEncodingException(e); + } } /** @@ -136,6 +147,20 @@ public final class SelfSignedCertificate { return privateKey; } + /** + * Returns the generated X.509 certificate. + */ + public X509Certificate cert() { + return cert; + } + + /** + * Returns the generated RSA private key. + */ + public PrivateKey key() { + return key; + } + /** * Deletes the generated X.509 certificate file and RSA private key file. */ diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java new file mode 100644 index 0000000000..febf8b04db --- /dev/null +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2015 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.buffer.UnpooledByteBufAllocator; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import org.junit.Assume; +import org.junit.Test; + +import javax.net.ssl.SSLEngine; + +public class SslContextBuilderTest { + + @Test + public void testClientContextFromFileJdk() throws Exception { + testClientContextFromFile(SslProvider.JDK); + } + + @Test + public void testClientContextFromFileOpenssl() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testClientContextFromFile(SslProvider.OPENSSL); + } + + @Test + public void testClientContextJdk() throws Exception { + testClientContext(SslProvider.JDK); + } + + @Test + public void testClientContextOpenssl() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testClientContext(SslProvider.OPENSSL); + } + + @Test + public void testServerContextFromFileJdk() throws Exception { + testServerContextFromFile(SslProvider.JDK); + } + + @Test + public void testServerContextFromFileOpenssl() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testServerContextFromFile(SslProvider.OPENSSL); + } + + @Test + public void testServerContextJdk() throws Exception { + testServerContext(SslProvider.JDK); + } + + @Test + public void testServerContextOpenssl() throws Exception { + Assume.assumeTrue(OpenSsl.isAvailable()); + testServerContext(SslProvider.OPENSSL); + } + + private static void testClientContextFromFile(SslProvider provider) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(provider).keyManager( + cert.certificate(), cert.privateKey()).trustManager(cert.certificate()); + SslContext context = builder.build(); + SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); + engine.closeInbound(); + engine.closeOutbound(); + } + + private static void testClientContext(SslProvider provider) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(provider).keyManager( + cert.key(), cert.cert()).trustManager(cert.cert()); + SslContext context = builder.build(); + SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); + engine.closeInbound(); + engine.closeOutbound(); + } + + private static void testServerContextFromFile(SslProvider provider) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) + .sslProvider(provider).trustManager(cert.certificate()); + SslContext context = builder.build(); + SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); + engine.closeInbound(); + engine.closeOutbound(); + } + + private static void testServerContext(SslProvider provider) throws Exception { + SelfSignedCertificate cert = new SelfSignedCertificate(); + SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert()) + .sslProvider(provider).trustManager(cert.cert()); + SslContext context = builder.build(); + SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); + engine.closeInbound(); + engine.closeOutbound(); + } +} diff --git a/pom.xml b/pom.xml index f27d0ce91f..c093d27771 100644 --- a/pom.xml +++ b/pom.xml @@ -672,7 +672,7 @@ ${project.groupId} netty-tcnative - 1.1.33.Fork4 + 1.1.33.Fork5 ${os.detected.classifier} compile true diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java index 2fc57cfb1c..110bd1c5bc 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java @@ -26,15 +26,13 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.ssl.JdkSslClientContext; -import io.netty.handler.ssl.JdkSslServerContext; import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.OpenSslClientContext; import io.netty.handler.ssl.OpenSslContext; -import io.netty.handler.ssl.OpenSslServerContext; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandshakeCompletionEvent; +import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.testsuite.util.TestUtils; @@ -125,15 +123,17 @@ public class SocketSslEchoTest extends AbstractSocketTest { "autoRead = {5}, useChunkedWriteHandler = {6}, useCompositeByteBuf = {7}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); - serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); List clientContexts = new ArrayList(); - clientContexts.add(new JdkSslClientContext(CERT_FILE)); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build()); boolean hasOpenSsl = OpenSsl.isAvailable(); if (hasOpenSsl) { - serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); - clientContexts.add(new OpenSslClientContext(CERT_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) + .sslProvider(SslProvider.OPENSSL).build()); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL) + .trustManager(CERT_FILE).build()); } else { logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java index b0e45481fc..8ab587bdbc 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketSslGreetingTest.java @@ -26,12 +26,10 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.JdkSslClientContext; -import io.netty.handler.ssl.JdkSslServerContext; import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.OpenSslClientContext; -import io.netty.handler.ssl.OpenSslServerContext; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.logging.InternalLogger; @@ -76,15 +74,17 @@ public class SocketSslGreetingTest extends AbstractSocketTest { @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); - serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); List clientContexts = new ArrayList(); - clientContexts.add(new JdkSslClientContext(CERT_FILE)); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build()); boolean hasOpenSsl = OpenSsl.isAvailable(); if (hasOpenSsl) { - serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); - clientContexts.add(new OpenSslClientContext(CERT_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) + .sslProvider(SslProvider.OPENSSL).build()); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL) + .trustManager(CERT_FILE).build()); } else { logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); } diff --git a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java index 66f3fa2d1e..4445c7ff40 100644 --- a/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java +++ b/testsuite/src/main/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java @@ -29,13 +29,11 @@ import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.JdkSslClientContext; -import io.netty.handler.ssl.JdkSslServerContext; import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.OpenSslClientContext; -import io.netty.handler.ssl.OpenSslServerContext; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; +import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; @@ -84,15 +82,17 @@ public class SocketStartTlsTest extends AbstractSocketTest { @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") public static Collection data() throws Exception { List serverContexts = new ArrayList(); - serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build()); List clientContexts = new ArrayList(); - clientContexts.add(new JdkSslClientContext(CERT_FILE)); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build()); boolean hasOpenSsl = OpenSsl.isAvailable(); if (hasOpenSsl) { - serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); - clientContexts.add(new OpenSslClientContext(CERT_FILE)); + serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE) + .sslProvider(SslProvider.OPENSSL).build()); + clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL) + .trustManager(CERT_FILE).build()); } else { logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); }