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 9d417c1626
commit 5ac84760c4
14 changed files with 812 additions and 142 deletions

View File

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

View File

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

View File

@ -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<String> ciphers, Iterable<String> 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<String> 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<String> 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<String> 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<String> 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<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 {
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);
}
}

View File

@ -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<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.
*
* @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<String> 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<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
public OpenSslSessionContext sessionContext() {
return sessionContext;

View File

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

View File

@ -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<String> 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<String> 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<String> 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<String> 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<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;
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) {

View File

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

View File

@ -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.
*/

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

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

View File

@ -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<Object[]> data() throws Exception {
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>();
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());
}

View File

@ -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<Object[]> data() throws Exception {
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>();
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());
}

View File

@ -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<Object[]> data() throws Exception {
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>();
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());
}