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:
parent
9d417c1626
commit
5ac84760c4
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
2
pom.xml
2
pom.xml
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user