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
This commit is contained in:
Norman Maurer 2015-07-15 14:13:47 +02:00
parent 48a01f17e2
commit 36d682b6cf
14 changed files with 810 additions and 134 deletions

View File

@ -16,8 +16,6 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import java.io.File;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@ -25,6 +23,9 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; 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. * A client-side {@link SslContext} which uses JDK's SSL/TLS implementation.
@ -35,7 +36,10 @@ public final class JdkSslClientContext extends JdkSslContext {
/** /**
* Creates a new instance. * Creates a new instance.
*
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext() throws SSLException { public JdkSslClientContext() throws SSLException {
this(null, null); this(null, null);
} }
@ -45,7 +49,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* *
* @param certChainFile an X.509 certificate chain file in PEM format. * @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default * {@code null} to use the system default
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext(File certChainFile) throws SSLException { public JdkSslClientContext(File certChainFile) throws SSLException {
this(certChainFile, null); this(certChainFile, null);
} }
@ -56,7 +62,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers. * that verifies the certificates sent from servers.
* {@code null} to use the default. * {@code null} to use the default.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { public JdkSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
this(null, trustManagerFactory); this(null, trustManagerFactory);
} }
@ -69,7 +77,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers. * that verifies the certificates sent from servers.
* {@code null} to use the default. * {@code null} to use the default.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { public JdkSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE, this(certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0); JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
@ -91,7 +101,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext( public JdkSslClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -116,7 +128,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext( public JdkSslClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory, File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
@ -153,7 +167,9 @@ public final class JdkSslClientContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -190,21 +206,47 @@ public final class JdkSslClientContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, public JdkSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn); super(ciphers, cipherFilter, apn);
try { try {
if (trustCertChainFile != null) { ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory,
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout);
} catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
} }
if (keyFile != null) { throw new SSLException("failed to initialize the client-side SSL context", e);
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory); }
}
JdkSslClientContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> 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(), ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(),
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
null); null);
@ -216,7 +258,11 @@ public final class JdkSslClientContext extends JdkSslContext {
if (sessionTimeout > 0) { if (sessionTimeout > 0) {
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
} }
return ctx;
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
}
throw new SSLException("failed to initialize the client-side SSL context", e); throw new SSLException("failed to initialize the client-side SSL context", e);
} }
} }

View File

@ -33,9 +33,11 @@ import java.security.KeyException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security; import java.security.Security;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -281,7 +283,9 @@ public abstract class JdkSslContext extends SslContext {
* {@code null} if it's not password-protected. * {@code null} if it's not password-protected.
* @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null} * @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. * @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, protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
KeyManagerFactory kmf) KeyManagerFactory kmf)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
@ -294,6 +298,17 @@ public abstract class JdkSslContext extends SslContext {
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf); 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, * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain. * 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} * @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, * @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain. * and a certificate chain.
* @deprecated will be removed.
*/ */
@Deprecated
protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf) String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf)
throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, InvalidAlgorithmParameterException, IOException, InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
CertificateException, KeyException, UnrecoverableKeyException { 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(); 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 // Set up key manager factory to use our key store
if (kmf == null) { if (kmf == null) {
kmf = KeyManagerFactory.getInstance(keyAlgorithm); kmf = KeyManagerFactory.getInstance(keyAlgorithm);

View File

@ -16,8 +16,6 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import java.io.File;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
@ -26,6 +24,9 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; 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. * A server-side {@link SslContext} which uses JDK's SSL/TLS implementation.
@ -39,7 +40,9 @@ public final class JdkSslServerContext extends JdkSslContext {
* *
* @param certChainFile an X.509 certificate chain file in PEM format * @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key 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 { public JdkSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null); this(certChainFile, keyFile, null);
} }
@ -51,7 +54,9 @@ public final class JdkSslServerContext extends JdkSslContext {
* @param keyFile a PKCS#8 private key file in PEM format * @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}. * @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected. * {@code null} if it's not password-protected.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException { public JdkSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0); JdkDefaultApplicationProtocolNegotiator.INSTANCE, 0, 0);
@ -72,7 +77,9 @@ public final class JdkSslServerContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslServerContext( public JdkSslServerContext(
File certChainFile, File keyFile, String keyPassword, File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -96,7 +103,9 @@ public final class JdkSslServerContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslServerContext( public JdkSslServerContext(
File certChainFile, File keyFile, String keyPassword, File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
@ -130,7 +139,9 @@ public final class JdkSslServerContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -164,26 +175,53 @@ public final class JdkSslServerContext extends JdkSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, public JdkSslServerContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn); super(ciphers, cipherFilter, apn);
if (keyFile == null && keyManagerFactory == null) { try {
throw new NullPointerException("keyFile, keyManagerFactory"); 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<String> 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 { try {
if (trustCertChainFile != null) { if (trustCertChain != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); trustManagerFactory = buildTrustManagerFactory(trustCertChain, trustManagerFactory);
} }
if (keyFile != null) { if (key != null) {
keyManagerFactory = buildKeyManagerFactory(keyCertChainFile, keyFile, keyPassword, keyManagerFactory); keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory);
} }
// Initialize the SSLContext to work with our key managers. // Initialize the SSLContext to work with our key managers.
ctx = SSLContext.getInstance(PROTOCOL); SSLContext ctx = SSLContext.getInstance(PROTOCOL);
ctx.init(keyManagerFactory.getKeyManagers(), ctx.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(), trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
null); null);
@ -195,7 +233,11 @@ public final class JdkSslServerContext extends JdkSslContext {
if (sessionTimeout > 0) { if (sessionTimeout > 0) {
sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE)); sessCtx.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
} }
return ctx;
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
}
throw new SSLException("failed to initialize the server-side SSL context", e); throw new SSLException("failed to initialize the server-side SSL context", e);
} }
} }

View File

@ -26,6 +26,7 @@ import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.io.File; import java.io.File;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
/** /**
@ -36,9 +37,11 @@ public final class OpenSslClientContext extends OpenSslContext {
/** /**
* Creates a new instance. * Creates a new instance.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslClientContext() throws SSLException { 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. * @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default * {@code null} to use the system default
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslClientContext(File certChainFile) throws SSLException { public OpenSslClientContext(File certChainFile) throws SSLException {
this(certChainFile, null); this(certChainFile, null);
} }
@ -57,7 +62,9 @@ public final class OpenSslClientContext extends OpenSslContext {
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers. * that verifies the certificates sent from servers.
* {@code null} to use the default. * {@code null} to use the default.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException { public OpenSslClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
this(null, trustManagerFactory); this(null, trustManagerFactory);
} }
@ -70,16 +77,15 @@ public final class OpenSslClientContext extends OpenSslContext {
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s * @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers. * that verifies the certificates sent from servers.
* {@code null} to use the default. * {@code null} to use the default.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException { public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
this(certChainFile, trustManagerFactory, null, null, null, null, null, this(certChainFile, trustManagerFactory, null, null, null, null, null,
IdentityCipherSuiteFilter.INSTANCE, null, 0, 0); IdentityCipherSuiteFilter.INSTANCE, null, 0, 0);
} }
/** /**
* @deprecated use {@link #OpenSslClientContext(File, TrustManagerFactory, Iterable,
* CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
*
* Creates a new instance. * Creates a new instance.
* *
* @param certChainFile an X.509 certificate chain file in PEM format * @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. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated @Deprecated
public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable<String> ciphers, public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable<String> 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. * Creates a new instance.
* *
* @param certChainFile an X.509 certificate chain file in PEM format * @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. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated @Deprecated
public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable<String> ciphers, public OpenSslClientContext(File certChainFile, TrustManagerFactory trustManagerFactory, Iterable<String> ciphers,
@ -157,7 +162,9 @@ public final class OpenSslClientContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory, public OpenSslClientContext(File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, File keyCertChainFile, File keyFile, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, KeyManagerFactory keyManagerFactory, Iterable<String> 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<String> 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 @Override
public OpenSslSessionContext sessionContext() { public OpenSslSessionContext sessionContext() {
return sessionContext; return sessionContext;

View File

@ -15,7 +15,11 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; 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.PlatformDependent;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; 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.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -43,6 +48,10 @@ import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBeha
import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; import static io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
public abstract class OpenSslContext extends SslContext { 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); private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslContext.class);
/** /**
@ -62,7 +71,7 @@ public abstract class OpenSslContext extends SslContext {
/** The OpenSSL SSL_CTX object */ /** The OpenSSL SSL_CTX object */
protected volatile long ctx; protected volatile long ctx;
private long aprPool; long aprPool;
@SuppressWarnings({ "unused", "FieldMayBeFinal" }) @SuppressWarnings({ "unused", "FieldMayBeFinal" })
private volatile int aprPoolDestroyed; private volatile int aprPoolDestroyed;
private volatile boolean rejectRemoteInitiatedRenegotiation; private volatile boolean rejectRemoteInitiatedRenegotiation;
@ -458,4 +467,54 @@ public abstract class OpenSslContext extends SslContext {
engines.put(engine.sslPointer(), engine); engines.put(engine.sslPointer(), engine);
} }
} }
/**
* Return the pointer to a <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
* 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 <a href="https://www.openssl.org/docs/crypto/BIO_get_mem_ptr.html">in-memory BIO</a>
* 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;
}
} }

View File

@ -15,7 +15,6 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.util.internal.EmptyArrays;
import org.apache.tomcat.jni.SSL; import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLContext;
@ -28,6 +27,7 @@ import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.io.File; import java.io.File;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import static io.netty.util.internal.ObjectUtil.*; 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 certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key 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 { public OpenSslServerContext(File certChainFile, File keyFile) throws SSLException {
this(certChainFile, keyFile, null); 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 keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}. * @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected. * {@code null} if it's not password-protected.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException { public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword) throws SSLException {
this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE, this(certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
ApplicationProtocolConfig.DISABLED, 0, 0); ApplicationProtocolConfig.DISABLED, 0, 0);
} }
/** /**
* @deprecated use {@link #OpenSslServerContext(
* File, File, String, Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
*
* Creates a new instance. * Creates a new instance.
* *
* @param certChainFile an X.509 certificate chain file in PEM format * @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. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated @Deprecated
public OpenSslServerContext( public OpenSslServerContext(
@ -102,8 +104,7 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, * @deprecated use {@link SslContextBuilder}
* Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
*/ */
@Deprecated @Deprecated
public OpenSslServerContext( public OpenSslServerContext(
@ -128,8 +129,7 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, * @deprecated use {@link SslContextBuilder}
* Iterable, CipherSuiteFilter, ApplicationProtocolConfig, long, long)}
*/ */
@Deprecated @Deprecated
public OpenSslServerContext( public OpenSslServerContext(
@ -155,7 +155,9 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslServerContext( public OpenSslServerContext(
File certChainFile, File keyFile, String keyPassword, File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
@ -190,7 +192,9 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslServerContext( public OpenSslServerContext(
File trustCertChainFile, TrustManagerFactory trustManagerFactory, File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
@ -215,6 +219,7 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated @Deprecated
public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword, public OpenSslServerContext(File certChainFile, File keyFile, String keyPassword,
@ -240,8 +245,7 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link #OpenSslServerContext(File, TrustManagerFactory, File, File, String, KeyManagerFactory, * @deprecated use {@link SslContextBuilder}}
* Iterable, CipherSuiteFilter, OpenSslApplicationProtocolNegotiator, long, long)}
*/ */
@Deprecated @Deprecated
public OpenSslServerContext( public OpenSslServerContext(
@ -279,7 +283,9 @@ public final class OpenSslServerContext extends OpenSslContext {
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds. * @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value. * {@code 0} to use the default value.
* @deprecated use {@link SslContextBuilder}
*/ */
@Deprecated
public OpenSslServerContext( public OpenSslServerContext(
File trustCertChainFile, TrustManagerFactory trustManagerFactory, File trustCertChainFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
@ -342,10 +348,123 @@ public final class OpenSslServerContext extends OpenSslContext {
if (trustCertChainFile != null) { if (trustCertChainFile != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory); trustManagerFactory = buildTrustManagerFactory(trustCertChainFile, trustManagerFactory);
} else { } else {
char[] keyPasswordChars = KeyStore ks = buildKeyStore(keyCertChainFile, keyFile, keyPassword);
keyPassword == null ? EmptyArrays.EMPTY_CHARS : keyPassword.toCharArray(); 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<String> 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); trustManagerFactory.init(ks);
} }

View File

@ -47,13 +47,11 @@ import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -275,15 +273,24 @@ public abstract class SslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContextInternal(provider, trustCertChainFile, trustManagerFactory, keyCertChainFile, try {
keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, return newServerContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory,
sessionCacheSize, sessionTimeout); 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( static SslContext newServerContextInternal(
SslProvider provider, SslProvider provider,
File trustCertChainFile, TrustManagerFactory trustManagerFactory, X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
@ -294,11 +301,11 @@ public abstract class SslContext {
switch (provider) { switch (provider) {
case JDK: case JDK:
return new JdkSslServerContext( return new JdkSslServerContext(
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
case OPENSSL: case OPENSSL:
return new OpenSslServerContext( return new OpenSslServerContext(
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
default: default:
throw new Error(provider.toString()); throw new Error(provider.toString());
@ -536,15 +543,24 @@ public abstract class SslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContextInternal(provider, trustCertChainFile, trustManagerFactory, try {
keyCertChainFile, keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, return newClientContextInternal(provider, toX509Certificates(trustCertChainFile), trustManagerFactory,
sessionCacheSize, sessionTimeout); 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( static SslContext newClientContextInternal(
SslProvider provider, SslProvider provider,
File trustCertChainFile, TrustManagerFactory trustManagerFactory, X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
if (provider == null) { if (provider == null) {
@ -553,11 +569,11 @@ public abstract class SslContext {
switch (provider) { switch (provider) {
case JDK: case JDK:
return new JdkSslClientContext( return new JdkSslClientContext(
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
case OPENSSL: case OPENSSL:
return new OpenSslClientContext( return new OpenSslClientContext(
trustCertChainFile, trustManagerFactory, keyCertChainFile, keyFile, keyPassword, trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
} }
// Should never happen!! // Should never happen!!
@ -683,21 +699,45 @@ public abstract class SslContext {
/** /**
* Generates a new {@link KeyStore}. * Generates a new {@link KeyStore}.
* *
* @param certChainFile a X.509 certificate chain file in PEM format, * @param certChain a X.509 certificate chain
* @param keyFile a PKCS#8 private key file in PEM format, * @param key a PKCS#8 private key
* @param keyPasswordChars the password of the {@code keyFile}. * @param keyPasswordChars the password of the {@code keyFile}.
* {@code null} if it's not password-protected. * {@code null} if it's not password-protected.
* @return generated {@link KeyStore}. * @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, throws KeyStoreException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException, CertificateException, IOException {
CertificateException, KeyException, 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); ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile);
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()]; byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
encodedKeyBuf.readBytes(encodedKey).release(); encodedKeyBuf.readBytes(encodedKey).release();
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey); PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPassword == null ? null : keyPassword.toCharArray(),
encodedKey);
PrivateKey key; PrivateKey key;
try { try {
@ -713,25 +753,7 @@ public abstract class SslContext {
} }
} }
} }
return key;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
List<Certificate> certChain = new ArrayList<Certificate>(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;
} }
/** /**
@ -740,25 +762,45 @@ public abstract class SslContext {
* @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}. * @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} * @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
*/ */
protected static TrustManagerFactory buildTrustManagerFactory(File certChainFile, @Deprecated
TrustManagerFactory trustManagerFactory) protected static TrustManagerFactory buildTrustManagerFactory(
File certChainFile, TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException { throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
KeyStore ks = KeyStore.getInstance("JKS"); X509Certificate[] x509Certs = toX509Certificates(certChainFile);
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509"); 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 { try {
for (ByteBuf buf: certs) { for (int i = 0; i < certs.length; i++) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(buf)); x509Certs[i] = (X509Certificate) cf.generateCertificate(new ByteBufInputStream(certs[i]));
X500Principal principal = cert.getSubjectX500Principal();
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
} }
} finally { } finally {
for (ByteBuf buf: certs) { for (ByteBuf buf: certs) {
buf.release(); 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. // Set up trust manager factory to use our key store.
if (trustManagerFactory == null) { if (trustManagerFactory == null) {

View File

@ -22,11 +22,14 @@ import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.io.File; import java.io.File;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
/** /**
* Builder for configuring a new SslContext for creation. * Builder for configuring a new SslContext for creation.
*/ */
public final class SslContextBuilder { public final class SslContextBuilder {
/** /**
* Creates a builder for new client-side {@link SslContext}. * Creates a builder for new client-side {@link SslContext}.
*/ */
@ -45,6 +48,17 @@ public final class SslContextBuilder {
return new SslContextBuilder(true).keyManager(keyCertChainFile, keyFile); 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}. * 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); 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}. * Creates a builder for new server-side {@link SslContext}.
* *
@ -71,10 +99,10 @@ public final class SslContextBuilder {
private final boolean forServer; private final boolean forServer;
private SslProvider provider; private SslProvider provider;
private File trustCertChainFile; private X509Certificate[] trustCertChain;
private TrustManagerFactory trustManagerFactory; private TrustManagerFactory trustManagerFactory;
private File keyCertChainFile; private X509Certificate[] keyCertChain;
private File keyFile; private PrivateKey key;
private String keyPassword; private String keyPassword;
private KeyManagerFactory keyManagerFactory; private KeyManagerFactory keyManagerFactory;
private Iterable<String> ciphers; private Iterable<String> ciphers;
@ -100,8 +128,19 @@ public final class SslContextBuilder {
* contain an X.509 certificate chain in PEM format. {@code null} uses the system default. * contain an X.509 certificate chain in PEM format. {@code null} uses the system default.
*/ */
public SslContextBuilder trustManager(File trustCertChainFile) { public SslContextBuilder trustManager(File trustCertChainFile) {
this.trustCertChainFile = trustCertChainFile; try {
this.trustManagerFactory = null; 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; return this;
} }
@ -111,7 +150,7 @@ public final class SslContextBuilder {
* you must use {@link #trustManager(File)}. {@code null} uses the system default. * you must use {@link #trustManager(File)}. {@code null} uses the system default.
*/ */
public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) { public SslContextBuilder trustManager(TrustManagerFactory trustManagerFactory) {
this.trustCertChainFile = null; trustCertChain = null;
this.trustManagerFactory = trustManagerFactory; this.trustManagerFactory = trustManagerFactory;
return this; return this;
} }
@ -127,6 +166,17 @@ public final class SslContextBuilder {
return keyManager(keyCertChainFile, keyFile, null); 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 * Identifying certificate for this host. {@code keyCertChainFile} and {@code keyFile} may
* be {@code null} for client contexts, which disables mutual authentication. * be {@code null} for client contexts, which disables mutual authentication.
@ -137,14 +187,51 @@ public final class SslContextBuilder {
* password-protected * password-protected
*/ */
public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) { public SslContextBuilder keyManager(File keyCertChainFile, File keyFile, String keyPassword) {
if (forServer) { X509Certificate[] keyCertChain;
checkNotNull(keyCertChainFile, "keyCertChainFile required for servers"); PrivateKey key;
checkNotNull(keyFile, "keyFile required for servers"); try {
keyCertChain = SslContext.toX509Certificates(keyCertChainFile);
} catch (Exception e) {
throw new IllegalArgumentException("File does not contain valid certificates: " + keyCertChainFile, e);
} }
this.keyCertChainFile = keyCertChainFile; try {
this.keyFile = keyFile; 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.keyPassword = keyPassword;
this.keyManagerFactory = null; keyManagerFactory = null;
return this; return this;
} }
@ -158,9 +245,9 @@ public final class SslContextBuilder {
if (forServer) { if (forServer) {
checkNotNull(keyManagerFactory, "keyManagerFactory required for servers"); checkNotNull(keyManagerFactory, "keyManagerFactory required for servers");
} }
this.keyCertChainFile = null; keyCertChain = null;
this.keyFile = null; key = null;
this.keyPassword = null; keyPassword = null;
this.keyManagerFactory = keyManagerFactory; this.keyManagerFactory = keyManagerFactory;
return this; return this;
} }
@ -216,12 +303,12 @@ public final class SslContextBuilder {
*/ */
public SslContext build() throws SSLException { public SslContext build() throws SSLException {
if (forServer) { if (forServer) {
return SslContext.newServerContextInternal(provider, trustCertChainFile, return SslContext.newServerContextInternal(provider, trustCertChain,
trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
} else { } else {
return SslContext.newClientContextInternal(provider, trustCertChainFile, return SslContext.newClientContextInternal(provider, trustCertChain,
trustManagerFactory, keyCertChainFile, keyFile, keyPassword, keyManagerFactory, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
} }
} }

View File

@ -23,6 +23,7 @@ import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@ -33,6 +34,7 @@ import java.security.PrivateKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date; import java.util.Date;
@ -63,6 +65,8 @@ public final class SelfSignedCertificate {
private final File certificate; private final File certificate;
private final File privateKey; private final File privateKey;
private final X509Certificate cert;
private final PrivateKey key;
/** /**
* Creates a new instance. * Creates a new instance.
@ -120,6 +124,13 @@ public final class SelfSignedCertificate {
certificate = new File(paths[0]); certificate = new File(paths[0]);
privateKey = new File(paths[1]); 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; 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. * Deletes the generated X.509 certificate file and RSA private key file.
*/ */

View File

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

View File

@ -681,7 +681,7 @@
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>netty-tcnative</artifactId> <artifactId>netty-tcnative</artifactId>
<version>1.1.33.Fork3</version> <version>1.1.33.Fork5</version>
<classifier>${os.detected.classifier}</classifier> <classifier>${os.detected.classifier}</classifier>
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>

View File

@ -26,15 +26,13 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler; 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.OpenSsl;
import io.netty.handler.ssl.OpenSslClientContext;
import io.netty.handler.ssl.OpenSslContext; import io.netty.handler.ssl.OpenSslContext;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslHandshakeCompletionEvent; import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.testsuite.util.TestUtils; import io.netty.testsuite.util.TestUtils;
@ -121,15 +119,17 @@ public class SocketSslEchoTest extends AbstractSocketTest {
"renegotiation = {2}, autoRead = {3}, useChunkedWriteHandler = {4}, useCompositeByteBuf = {5}") "renegotiation = {2}, autoRead = {3}, useChunkedWriteHandler = {4}, useCompositeByteBuf = {5}")
public static Collection<Object[]> data() throws Exception { public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>(); List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build());
List<SslContext> clientContexts = new ArrayList<SslContext>(); List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE)); clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build());
boolean hasOpenSsl = OpenSsl.isAvailable(); boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) { if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
clientContexts.add(new OpenSslClientContext(CERT_FILE)); .sslProvider(SslProvider.OPENSSL).build());
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL)
.trustManager(CERT_FILE).build());
} else { } else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
} }

View File

@ -26,12 +26,10 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; 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.OpenSsl;
import io.netty.handler.ssl.OpenSslClientContext;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext; 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.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -76,15 +74,17 @@ public class SocketSslGreetingTest extends AbstractSocketTest {
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<Object[]> data() throws Exception { public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>(); List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build());
List<SslContext> clientContexts = new ArrayList<SslContext>(); List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE)); clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build());
boolean hasOpenSsl = OpenSsl.isAvailable(); boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) { if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
clientContexts.add(new OpenSslClientContext(CERT_FILE)); .sslProvider(SslProvider.OPENSSL).build());
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL)
.trustManager(CERT_FILE).build());
} else { } else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
} }

View File

@ -29,13 +29,11 @@ import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; 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.OpenSsl;
import io.netty.handler.ssl.OpenSslClientContext;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup;
@ -84,15 +82,17 @@ public class SocketStartTlsTest extends AbstractSocketTest {
@Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}") @Parameters(name = "{index}: serverEngine = {0}, clientEngine = {1}")
public static Collection<Object[]> data() throws Exception { public static Collection<Object[]> data() throws Exception {
List<SslContext> serverContexts = new ArrayList<SslContext>(); List<SslContext> serverContexts = new ArrayList<SslContext>();
serverContexts.add(new JdkSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE).sslProvider(SslProvider.JDK).build());
List<SslContext> clientContexts = new ArrayList<SslContext>(); List<SslContext> clientContexts = new ArrayList<SslContext>();
clientContexts.add(new JdkSslClientContext(CERT_FILE)); clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.JDK).trustManager(CERT_FILE).build());
boolean hasOpenSsl = OpenSsl.isAvailable(); boolean hasOpenSsl = OpenSsl.isAvailable();
if (hasOpenSsl) { if (hasOpenSsl) {
serverContexts.add(new OpenSslServerContext(CERT_FILE, KEY_FILE)); serverContexts.add(SslContextBuilder.forServer(CERT_FILE, KEY_FILE)
clientContexts.add(new OpenSslClientContext(CERT_FILE)); .sslProvider(SslProvider.OPENSSL).build());
clientContexts.add(SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL)
.trustManager(CERT_FILE).build());
} else { } else {
logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause()); logger.warn("OpenSSL is unavailable and thus will not be tested.", OpenSsl.unavailabilityCause());
} }