Add support for KeyManagerFactory when using SslProvider.OpenSsl.
Motivation: To be able to use SslProvider.OpenSsl with existing java apps that use the JDK SSL API we need to also provide a way to use it with an existing KeyManagerFactory. Modification: Make use of new tcnative apis and so hook in KeyManagerFactory. Result: SslProvider.OpenSsl can be used with KeyManagerFactory as well.
This commit is contained in:
parent
3a69adfefb
commit
5e64985089
@ -341,17 +341,6 @@ public 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.
|
||||
@ -375,20 +364,4 @@ public class JdkSslContext extends SslContext {
|
||||
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, key, keyPasswordChars);
|
||||
// Set up key manager factory to use our key store
|
||||
if (kmf == null) {
|
||||
kmf = KeyManagerFactory.getInstance(keyAlgorithm);
|
||||
}
|
||||
kmf.init(ks, keyPasswordChars);
|
||||
|
||||
return kmf;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.util.internal.NativeLibraryLoader;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
@ -48,6 +49,7 @@ public final class OpenSsl {
|
||||
static final Set<String> AVAILABLE_CIPHER_SUITES;
|
||||
private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES;
|
||||
private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES;
|
||||
private static final boolean SUPPORTS_KEYMANAGER_FACTORY;
|
||||
|
||||
// Protocols
|
||||
static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
|
||||
@ -117,9 +119,12 @@ public final class OpenSsl {
|
||||
|
||||
if (cause == null) {
|
||||
final Set<String> availableOpenSslCipherSuites = new LinkedHashSet<String>(128);
|
||||
boolean supportsKeyManagerFactory = false;
|
||||
final long aprPool = Pool.create(0);
|
||||
try {
|
||||
final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
|
||||
long privateKeyBio = 0;
|
||||
long certBio = 0;
|
||||
try {
|
||||
SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
|
||||
SSLContext.setCipherSuite(sslCtx, "ALL");
|
||||
@ -132,8 +137,22 @@ public final class OpenSsl {
|
||||
}
|
||||
availableOpenSslCipherSuites.add(c);
|
||||
}
|
||||
try {
|
||||
SelfSignedCertificate cert = new SelfSignedCertificate();
|
||||
certBio = OpenSslContext.toBIO(cert.cert());
|
||||
SSL.setCertificateChainBio(ssl, certBio, false);
|
||||
supportsKeyManagerFactory = true;
|
||||
} catch (Throwable ignore) {
|
||||
logger.debug("KeyManagerFactory not supported.");
|
||||
}
|
||||
} finally {
|
||||
SSL.freeSSL(ssl);
|
||||
if (privateKeyBio != 0) {
|
||||
SSL.freeBIO(privateKeyBio);
|
||||
}
|
||||
if (certBio != 0) {
|
||||
SSL.freeBIO(certBio);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
SSLContext.free(sslCtx);
|
||||
@ -163,10 +182,12 @@ public final class OpenSsl {
|
||||
availableCipherSuites.add(cipher);
|
||||
}
|
||||
AVAILABLE_CIPHER_SUITES = availableCipherSuites;
|
||||
SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory;
|
||||
} else {
|
||||
AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
|
||||
AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
|
||||
AVAILABLE_CIPHER_SUITES = Collections.emptySet();
|
||||
SUPPORTS_KEYMANAGER_FACTORY = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,6 +289,13 @@ public final class OpenSsl {
|
||||
return AVAILABLE_OPENSSL_CIPHER_SUITES.contains(cipherSuite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if {@link javax.net.ssl.KeyManagerFactory} is supported when using OpenSSL.
|
||||
*/
|
||||
public static boolean supportsKeyManagerFactory() {
|
||||
return SUPPORTS_KEYMANAGER_FACTORY;
|
||||
}
|
||||
|
||||
static boolean isError(long errorCode) {
|
||||
return errorCode != SSL.SSL_ERROR_NONE;
|
||||
}
|
||||
|
@ -15,24 +15,34 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
import org.apache.tomcat.jni.CertificateRequestedCallback;
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
import org.apache.tomcat.jni.SSLContext;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
||||
*/
|
||||
public final class OpenSslClientContext extends OpenSslContext {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslClientContext.class);
|
||||
private final OpenSslSessionContext sessionContext;
|
||||
|
||||
/**
|
||||
@ -188,51 +198,37 @@ public final class OpenSslClientContext extends OpenSslContext {
|
||||
ClientAuth.NONE);
|
||||
boolean success = false;
|
||||
try {
|
||||
checkKeyManagerFactory(keyManagerFactory);
|
||||
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 (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));
|
||||
}
|
||||
try {
|
||||
if (!OpenSsl.supportsKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory not supported");
|
||||
}
|
||||
// We may have more then one cert in the chain so add all of them now. We must NOT skip the
|
||||
// first cert when client mode.
|
||||
if (!SSLContext.setCertificateChainBio(ctx, keyCertChainBio, false)) {
|
||||
long error = SSL.getLastErrorNumber();
|
||||
if (OpenSsl.isError(error)) {
|
||||
throw new SSLException(
|
||||
"failed to set certificate chain: " + SSL.getErrorString(error));
|
||||
}
|
||||
if (keyCertChain != null && key != null) {
|
||||
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
|
||||
}
|
||||
} 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);
|
||||
} else {
|
||||
if (keyCertChain != null) {
|
||||
keyManagerFactory = buildKeyManagerFactory(
|
||||
keyCertChain, key, keyPassword, keyManagerFactory);
|
||||
}
|
||||
if (keyCertChainBio != 0) {
|
||||
SSL.freeBIO(keyCertChainBio);
|
||||
if (keyManagerFactory != null) {
|
||||
X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers());
|
||||
OpenSslKeyMaterialManager materialManager = useExtendedKeyManager(keyManager) ?
|
||||
new OpenSslExtendedKeyMaterialManager(
|
||||
(X509ExtendedKeyManager) keyManager, keyPassword) :
|
||||
new OpenSslKeyMaterialManager(keyManager, keyPassword);
|
||||
SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
|
||||
engineMap, materialManager));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SSLException("failed to set certificate and key", e);
|
||||
}
|
||||
|
||||
SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
|
||||
@ -278,6 +274,11 @@ public final class OpenSslClientContext extends OpenSslContext {
|
||||
return sessionContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
OpenSslKeyMaterialManager keyMaterialManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No cache is currently supported for client side mode.
|
||||
private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
|
||||
private OpenSslClientSessionContext(OpenSslContext context) {
|
||||
@ -348,4 +349,77 @@ public final class OpenSslClientContext extends OpenSslContext {
|
||||
manager.checkServerTrusted(peerCerts, auth, engine);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class OpenSslCertificateRequestedCallback implements CertificateRequestedCallback {
|
||||
private final OpenSslEngineMap engineMap;
|
||||
private final OpenSslKeyMaterialManager keyManagerHolder;
|
||||
|
||||
OpenSslCertificateRequestedCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
|
||||
this.engineMap = engineMap;
|
||||
this.keyManagerHolder = keyManagerHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requested(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
|
||||
final OpenSslEngine engine = engineMap.get(ssl);
|
||||
try {
|
||||
final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
|
||||
final String[] keyTypes = keyTypesSet.toArray(new String[keyTypesSet.size()]);
|
||||
final X500Principal[] issuers;
|
||||
if (asn1DerEncodedPrincipals == null) {
|
||||
issuers = null;
|
||||
} else {
|
||||
issuers = new X500Principal[asn1DerEncodedPrincipals.length];
|
||||
for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
|
||||
issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
|
||||
}
|
||||
}
|
||||
keyManagerHolder.setKeyMaterial(engine, keyTypes, issuers);
|
||||
} catch (Throwable cause) {
|
||||
logger.debug("request of key failed", cause);
|
||||
SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
|
||||
e.initCause(cause);
|
||||
engine.handshakeException = e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the supported key types for client certificates.
|
||||
*
|
||||
* @param clientCertificateTypes {@code ClientCertificateType} values provided by the server.
|
||||
* See https://www.ietf.org/assignments/tls-parameters/tls-parameters.xml.
|
||||
* @return supported key types that can be used in {@code X509KeyManager.chooseClientAlias} and
|
||||
* {@code X509ExtendedKeyManager.chooseEngineClientAlias}.
|
||||
*/
|
||||
private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
|
||||
Set<String> result = new HashSet<String>(clientCertificateTypes.length);
|
||||
for (byte keyTypeCode : clientCertificateTypes) {
|
||||
String keyType = clientKeyType(keyTypeCode);
|
||||
if (keyType == null) {
|
||||
// Unsupported client key type -- ignore
|
||||
continue;
|
||||
}
|
||||
result.add(keyType);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String clientKeyType(byte clientCertificateType) {
|
||||
// See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
|
||||
switch (clientCertificateType) {
|
||||
case CertificateRequestedCallback.TLS_CT_RSA_SIGN:
|
||||
return OpenSslKeyMaterialManager.KEY_TYPE_RSA; // RFC rsa_sign
|
||||
case CertificateRequestedCallback.TLS_CT_RSA_FIXED_DH:
|
||||
return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA; // RFC rsa_fixed_dh
|
||||
case CertificateRequestedCallback.TLS_CT_ECDSA_SIGN:
|
||||
return OpenSslKeyMaterialManager.KEY_TYPE_EC; // RFC ecdsa_sign
|
||||
case CertificateRequestedCallback.TLS_CT_RSA_FIXED_ECDH:
|
||||
return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA; // RFC rsa_fixed_ecdh
|
||||
case CertificateRequestedCallback.TLS_CT_ECDSA_FIXED_ECDH:
|
||||
return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC; // RFC ecdsa_fixed_ecdh
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package io.netty.handler.ssl;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
@ -26,12 +27,14 @@ import org.apache.tomcat.jni.Pool;
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
import org.apache.tomcat.jni.SSLContext;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivateKey;
|
||||
@ -57,7 +60,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
* To make it easier for users to replace JDK implemention with OpenSsl version we also use
|
||||
* {@code jdk.tls.rejectClientInitiatedRenegotiation} to allow disabling client initiated renegotiation.
|
||||
* Java8+ uses this system property as well.
|
||||
*
|
||||
* <p>
|
||||
* See also <a href="http://blog.ivanristic.com/2014/03/ssl-tls-improvements-in-java-8.html">
|
||||
* Significant SSL/TLS improvements in Java 8</a>
|
||||
*/
|
||||
@ -69,20 +72,23 @@ public abstract class OpenSslContext extends SslContext {
|
||||
// TODO: Maybe make configurable ?
|
||||
protected static final int VERIFY_DEPTH = 10;
|
||||
|
||||
/** The OpenSSL SSL_CTX object */
|
||||
/**
|
||||
* The OpenSSL SSL_CTX object
|
||||
*/
|
||||
protected volatile long ctx;
|
||||
final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
|
||||
long aprPool;
|
||||
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
|
||||
private volatile int aprPoolDestroyed;
|
||||
private volatile boolean rejectRemoteInitiatedRenegotiation;
|
||||
private final List<String> unmodifiableCiphers;
|
||||
private final long sessionCacheSize;
|
||||
private final long sessionTimeout;
|
||||
private final OpenSslApplicationProtocolNegotiator apn;
|
||||
private final int mode;
|
||||
private final Certificate[] keyCertChain;
|
||||
private final ClientAuth clientAuth;
|
||||
|
||||
final Certificate[] keyCertChain;
|
||||
final ClientAuth clientAuth;
|
||||
final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
|
||||
volatile boolean rejectRemoteInitiatedRenegotiation;
|
||||
|
||||
static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
|
||||
new OpenSslApplicationProtocolNegotiator() {
|
||||
@ -178,7 +184,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
convertedCiphers = null;
|
||||
} else {
|
||||
convertedCiphers = new ArrayList<String>();
|
||||
for (String c: ciphers) {
|
||||
for (String c : ciphers) {
|
||||
if (c == null) {
|
||||
break;
|
||||
}
|
||||
@ -247,18 +253,18 @@ public abstract class OpenSslContext extends SslContext {
|
||||
int selectorBehavior = opensslSelectorFailureBehavior(apn.selectorFailureBehavior());
|
||||
|
||||
switch (apn.protocol()) {
|
||||
case NPN:
|
||||
SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
case ALPN:
|
||||
SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
case NPN_AND_ALPN:
|
||||
SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
||||
SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
case NPN:
|
||||
SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
case ALPN:
|
||||
SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
case NPN_AND_ALPN:
|
||||
SSLContext.setNpnProtos(ctx, protocols, selectorBehavior);
|
||||
SSLContext.setAlpnProtos(ctx, protocols, selectorBehavior);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,12 +300,12 @@ public abstract class OpenSslContext extends SslContext {
|
||||
|
||||
private static int opensslSelectorFailureBehavior(SelectorFailureBehavior behavior) {
|
||||
switch (behavior) {
|
||||
case NO_ADVERTISE:
|
||||
return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
|
||||
default:
|
||||
throw new Error();
|
||||
case NO_ADVERTISE:
|
||||
return SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE;
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
return SSL.SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,10 +336,11 @@ public abstract class OpenSslContext extends SslContext {
|
||||
|
||||
@Override
|
||||
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
||||
return new OpenSslEngine(ctx, alloc, isClient(), sessionContext(), apn, engineMap,
|
||||
rejectRemoteInitiatedRenegotiation, peerHost, peerPort, keyCertChain, clientAuth);
|
||||
return new OpenSslEngine(this, alloc, peerHost, peerPort);
|
||||
}
|
||||
|
||||
abstract OpenSslKeyMaterialManager keyMaterialManager();
|
||||
|
||||
/**
|
||||
* Returns a new server-side {@link SSLEngine} with the current configuration.
|
||||
*/
|
||||
@ -356,6 +363,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
|
||||
/**
|
||||
* Returns the stats of this context.
|
||||
*
|
||||
* @deprecated use {@link #sessionContext#stats()}
|
||||
*/
|
||||
@Deprecated
|
||||
@ -380,6 +388,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
|
||||
/**
|
||||
* Sets the SSL session ticket keys of this context.
|
||||
*
|
||||
* @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
|
||||
*/
|
||||
@Deprecated
|
||||
@ -434,9 +443,19 @@ public abstract class OpenSslContext extends SslContext {
|
||||
throw new IllegalStateException("no X509TrustManager found");
|
||||
}
|
||||
|
||||
protected static X509KeyManager chooseX509KeyManager(KeyManager[] kms) {
|
||||
for (KeyManager km : kms) {
|
||||
if (km instanceof X509KeyManager) {
|
||||
return (X509KeyManager) km;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("no X509KeyManager found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a {@link ApplicationProtocolConfig} object to a
|
||||
* {@link OpenSslApplicationProtocolNegotiator} object.
|
||||
*
|
||||
* @param config The configuration which defines the translation
|
||||
* @return The results of the translation
|
||||
*/
|
||||
@ -446,38 +465,42 @@ public abstract class OpenSslContext extends SslContext {
|
||||
}
|
||||
|
||||
switch (config.protocol()) {
|
||||
case NONE:
|
||||
return NONE_PROTOCOL_NEGOTIATOR;
|
||||
case ALPN:
|
||||
case NPN:
|
||||
case NPN_AND_ALPN:
|
||||
switch (config.selectedListenerFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
case ACCEPT:
|
||||
switch (config.selectorFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
case NO_ADVERTISE:
|
||||
return new OpenSslDefaultApplicationProtocolNegotiator(
|
||||
config);
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectorFailureBehavior())
|
||||
.append(" behavior").toString());
|
||||
case NONE:
|
||||
return NONE_PROTOCOL_NEGOTIATOR;
|
||||
case ALPN:
|
||||
case NPN:
|
||||
case NPN_AND_ALPN:
|
||||
switch (config.selectedListenerFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
case ACCEPT:
|
||||
switch (config.selectorFailureBehavior()) {
|
||||
case CHOOSE_MY_LAST_PROTOCOL:
|
||||
case NO_ADVERTISE:
|
||||
return new OpenSslDefaultApplicationProtocolNegotiator(
|
||||
config);
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectorFailureBehavior())
|
||||
.append(" behavior").toString());
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior())
|
||||
.append(" behavior").toString());
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
new StringBuilder("OpenSSL provider does not support ")
|
||||
.append(config.selectedListenerFailureBehavior())
|
||||
.append(" behavior").toString());
|
||||
}
|
||||
default:
|
||||
throw new Error();
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean useExtendedTrustManager(X509TrustManager trustManager) {
|
||||
return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
|
||||
return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
|
||||
}
|
||||
|
||||
static boolean useExtendedKeyManager(X509KeyManager keyManager) {
|
||||
return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager;
|
||||
}
|
||||
|
||||
abstract static class AbstractCertificateVerifier implements CertificateVerifier {
|
||||
@ -490,7 +513,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
@Override
|
||||
public final int verify(long ssl, byte[][] chain, String auth) {
|
||||
X509Certificate[] peerCerts = certificates(chain);
|
||||
final OpenSslEngine engine = engineMap.remove(ssl);
|
||||
final OpenSslEngine engine = engineMap.get(ssl);
|
||||
try {
|
||||
verify(engine, peerCerts, auth);
|
||||
return CertificateVerifier.X509_V_OK;
|
||||
@ -521,6 +544,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
|
||||
private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
|
||||
private final Map<Long, OpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
|
||||
|
||||
@Override
|
||||
public OpenSslEngine remove(long ssl) {
|
||||
return engines.remove(ssl);
|
||||
@ -530,8 +554,41 @@ public abstract class OpenSslContext extends SslContext {
|
||||
public void add(OpenSslEngine engine) {
|
||||
engines.put(engine.sslPointer(), engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenSslEngine get(long ssl) {
|
||||
return engines.get(ssl);
|
||||
}
|
||||
}
|
||||
|
||||
static void setKeyMaterial(long ctx, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
|
||||
throws SSLException {
|
||||
/* Load the certificate file and private key. */
|
||||
long keyBio = 0;
|
||||
long keyCertChainBio = 0;
|
||||
|
||||
try {
|
||||
keyCertChainBio = toBIO(keyCertChain);
|
||||
keyBio = toBIO(key);
|
||||
|
||||
SSLContext.setCertificateBio(
|
||||
ctx, keyCertChainBio, keyBio,
|
||||
keyPassword == null ? StringUtil.EMPTY_STRING : keyPassword, SSL.SSL_AIDX_RSA);
|
||||
// We may have more then one cert in the chain so add all of them now.
|
||||
SSLContext.setCertificateChainBio(ctx, keyCertChainBio, false);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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}.
|
||||
@ -554,7 +611,7 @@ public abstract class OpenSslContext extends SslContext {
|
||||
* 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 {
|
||||
static long toBIO(X509Certificate... certChain) throws Exception {
|
||||
if (certChain == null) {
|
||||
return 0;
|
||||
}
|
||||
@ -615,11 +672,4 @@ public abstract class OpenSslContext extends SslContext {
|
||||
buffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
static void checkKeyManagerFactory(KeyManagerFactory keyManagerFactory) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory is currently not supported with OpenSslContext");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,7 @@ public final class OpenSslEngine extends SSLEngine {
|
||||
// OpenSSL state
|
||||
private long ssl;
|
||||
private long networkBIO;
|
||||
private boolean certificateSet;
|
||||
|
||||
private enum HandshakeState {
|
||||
/**
|
||||
@ -221,64 +222,35 @@ public final class OpenSslEngine extends SSLEngine {
|
||||
private final Certificate[] localCerts;
|
||||
private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
|
||||
private final ByteBuffer[] singleDstBuffer = new ByteBuffer[1];
|
||||
private final OpenSslKeyMaterialManager keyMaterialManager;
|
||||
|
||||
// This is package-private as we set it from OpenSslContext if an exception is thrown during
|
||||
// the verification step.
|
||||
SSLHandshakeException handshakeException;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param sslCtx an OpenSSL {@code SSL_CTX} object
|
||||
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
|
||||
*/
|
||||
@Deprecated
|
||||
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
|
||||
@SuppressWarnings("unused") String fallbackApplicationProtocol) {
|
||||
this(sslCtx, alloc, false, null, OpenSslContext.NONE_PROTOCOL_NEGOTIATOR, OpenSslEngineMap.EMPTY, false,
|
||||
ClientAuth.NONE);
|
||||
}
|
||||
|
||||
OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
|
||||
boolean clientMode, OpenSslSessionContext sessionContext,
|
||||
OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap,
|
||||
boolean rejectRemoteInitiatedRenegation,
|
||||
ClientAuth clientAuth) {
|
||||
this(sslCtx, alloc, clientMode, sessionContext, apn, engineMap, rejectRemoteInitiatedRenegation, null, -1,
|
||||
null, clientAuth);
|
||||
}
|
||||
|
||||
OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
|
||||
boolean clientMode, OpenSslSessionContext sessionContext,
|
||||
OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap,
|
||||
boolean rejectRemoteInitiatedRenegation, String peerHost, int peerPort,
|
||||
Certificate[] localCerts,
|
||||
ClientAuth clientAuth) {
|
||||
OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort) {
|
||||
super(peerHost, peerPort);
|
||||
OpenSsl.ensureAvailability();
|
||||
if (sslCtx == 0) {
|
||||
throw new NullPointerException("sslCtx");
|
||||
}
|
||||
|
||||
this.alloc = checkNotNull(alloc, "alloc");
|
||||
this.apn = checkNotNull(apn, "apn");
|
||||
ssl = SSL.newSSL(sslCtx, !clientMode);
|
||||
session = new OpenSslSession(sessionContext);
|
||||
apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator();
|
||||
ssl = SSL.newSSL(context.ctx, !context.isClient());
|
||||
session = new OpenSslSession(context.sessionContext());
|
||||
networkBIO = SSL.makeNetworkBIO(ssl);
|
||||
this.clientMode = clientMode;
|
||||
this.engineMap = engineMap;
|
||||
this.rejectRemoteInitiatedRenegation = rejectRemoteInitiatedRenegation;
|
||||
this.localCerts = localCerts;
|
||||
clientMode = context.isClient();
|
||||
engineMap = context.engineMap;
|
||||
rejectRemoteInitiatedRenegation = context.rejectRemoteInitiatedRenegotiation;
|
||||
localCerts = context.keyCertChain;
|
||||
|
||||
// Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the
|
||||
// needed JNI methods.
|
||||
setClientAuth(clientMode ? ClientAuth.NONE : checkNotNull(clientAuth, "clientAuth"));
|
||||
setClientAuth(clientMode ? ClientAuth.NONE : context.clientAuth);
|
||||
|
||||
// Use SNI if peerHost was specified
|
||||
// See https://github.com/netty/netty/issues/4746
|
||||
if (clientMode && peerHost != null) {
|
||||
SSL.setTlsExtHostName(ssl, peerHost);
|
||||
}
|
||||
keyMaterialManager = context.keyMaterialManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1287,6 +1259,11 @@ public final class OpenSslEngine extends SSLEngine {
|
||||
lastAccessed = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
if (!certificateSet && keyMaterialManager != null) {
|
||||
certificateSet = true;
|
||||
keyMaterialManager.setKeyMaterial(this);
|
||||
}
|
||||
|
||||
int code = SSL.doHandshake(ssl);
|
||||
if (code <= 0) {
|
||||
// Check if we have a pending exception that was created during the handshake and if so throw it after
|
||||
@ -1311,6 +1288,7 @@ public final class OpenSslEngine extends SSLEngine {
|
||||
}
|
||||
// if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished.
|
||||
session.handshakeFinished();
|
||||
engineMap.remove(ssl);
|
||||
return FINISHED;
|
||||
}
|
||||
|
||||
|
@ -17,18 +17,6 @@ package io.netty.handler.ssl;
|
||||
|
||||
interface OpenSslEngineMap {
|
||||
|
||||
OpenSslEngineMap EMPTY = new OpenSslEngineMap() {
|
||||
@Override
|
||||
public OpenSslEngine remove(long ssl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(OpenSslEngine engine) {
|
||||
// NOOP
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the {@link OpenSslEngine} with the given {@code ssl} address and
|
||||
* return it.
|
||||
@ -39,4 +27,9 @@ interface OpenSslEngineMap {
|
||||
* Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}.
|
||||
*/
|
||||
void add(OpenSslEngine engine);
|
||||
|
||||
/**
|
||||
* Get the {@link OpenSslEngine} for the given {@code ssl} address.
|
||||
*/
|
||||
OpenSslEngine get(long ssl);
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2016 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
final class OpenSslExtendedKeyMaterialManager extends OpenSslKeyMaterialManager {
|
||||
|
||||
private final X509ExtendedKeyManager keyManager;
|
||||
|
||||
OpenSslExtendedKeyMaterialManager(X509ExtendedKeyManager keyManager, String password) {
|
||||
super(keyManager, password);
|
||||
this.keyManager = keyManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String chooseClientAlias(OpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) {
|
||||
return keyManager.chooseEngineClientAlias(keyTypes, issuer, engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String chooseServerAlias(OpenSslEngine engine, String type) {
|
||||
return keyManager.chooseEngineServerAlias(type, null, engine);
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2016 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import org.apache.tomcat.jni.SSL;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and
|
||||
* {@link X509Certificate}s.
|
||||
*/
|
||||
class OpenSslKeyMaterialManager {
|
||||
|
||||
// Code in this class is inspired by code of conscrypts:
|
||||
// - https://android.googlesource.com/platform/external/
|
||||
// conscrypt/+/master/src/main/java/org/conscrypt/OpenSSLEngineImpl.java
|
||||
// - https://android.googlesource.com/platform/external/
|
||||
// conscrypt/+/master/src/main/java/org/conscrypt/SSLParametersImpl.java
|
||||
//
|
||||
static final String KEY_TYPE_RSA = "RSA";
|
||||
static final String KEY_TYPE_DH_RSA = "DH_RSA";
|
||||
static final String KEY_TYPE_EC = "EC";
|
||||
static final String KEY_TYPE_EC_EC = "EC_EC";
|
||||
static final String KEY_TYPE_EC_RSA = "EC_RSA";
|
||||
|
||||
// key type mappings for types.
|
||||
private static final Map<String, String> KEY_TYPES = new HashMap<String, String>();
|
||||
static {
|
||||
KEY_TYPES.put("RSA", KEY_TYPE_RSA);
|
||||
KEY_TYPES.put("DHE_RSA", KEY_TYPE_RSA);
|
||||
KEY_TYPES.put("ECDHE_RSA", KEY_TYPE_RSA);
|
||||
KEY_TYPES.put("ECDHE_ECDSA", KEY_TYPE_EC);
|
||||
KEY_TYPES.put("ECDH_RSA", KEY_TYPE_EC_RSA);
|
||||
KEY_TYPES.put("ECDH_ECDSA", KEY_TYPE_EC_EC);
|
||||
KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA);
|
||||
}
|
||||
|
||||
private final X509KeyManager keyManager;
|
||||
private final String password;
|
||||
|
||||
OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) {
|
||||
this.keyManager = keyManager;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
void setKeyMaterial(OpenSslEngine engine) throws SSLException {
|
||||
long ssl = engine.sslPointer();
|
||||
String[] authMethods = SSL.authenticationMethods(ssl);
|
||||
for (String authMethod : authMethods) {
|
||||
String type = KEY_TYPES.get(authMethod);
|
||||
if (type != null) {
|
||||
setKeyMaterial(ssl, chooseServerAlias(engine, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setKeyMaterial(OpenSslEngine engine, String[] keyTypes, X500Principal[] issuer) throws SSLException {
|
||||
setKeyMaterial(engine.sslPointer(), chooseClientAlias(engine, keyTypes, issuer));
|
||||
}
|
||||
|
||||
private void setKeyMaterial(long ssl, String alias) throws SSLException {
|
||||
long keyBio = 0;
|
||||
long keyCertChainBio = 0;
|
||||
try {
|
||||
// TODO: Should we cache these and so not need to do a memory copy all the time ?
|
||||
PrivateKey key = keyManager.getPrivateKey(alias);
|
||||
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
|
||||
|
||||
if (certificates != null && certificates.length != 0) {
|
||||
keyCertChainBio = OpenSslContext.toBIO(keyManager.getCertificateChain(alias));
|
||||
if (key != null) {
|
||||
keyBio = OpenSslContext.toBIO(key);
|
||||
}
|
||||
SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password);
|
||||
|
||||
// We may have more then one cert in the chain so add all of them now.
|
||||
SSL.setCertificateChainBio(ssl, keyCertChainBio, false);
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new SSLException(e);
|
||||
} finally {
|
||||
if (keyBio != 0) {
|
||||
SSL.freeBIO(keyBio);
|
||||
}
|
||||
if (keyCertChainBio != 0) {
|
||||
SSL.freeBIO(keyCertChainBio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String chooseClientAlias(@SuppressWarnings("unused") OpenSslEngine engine,
|
||||
String[] keyTypes, X500Principal[] issuer) {
|
||||
return keyManager.chooseClientAlias(keyTypes, issuer, null);
|
||||
}
|
||||
|
||||
protected String chooseServerAlias(@SuppressWarnings("unused") OpenSslEngine engine, String type) {
|
||||
return keyManager.chooseServerAlias(type, null, null);
|
||||
}
|
||||
}
|
@ -23,7 +23,9 @@ import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.File;
|
||||
import java.security.KeyStore;
|
||||
@ -38,6 +40,7 @@ import static io.netty.util.internal.ObjectUtil.*;
|
||||
public final class OpenSslServerContext extends OpenSslContext {
|
||||
private static final byte[] ID = new byte[] {'n', 'e', 't', 't', 'y'};
|
||||
private final OpenSslServerSessionContext sessionContext;
|
||||
private final OpenSslKeyMaterialManager keyMaterialManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
@ -346,63 +349,43 @@ public final class OpenSslServerContext extends OpenSslContext {
|
||||
// Create a new SSL_CTX and configure it.
|
||||
boolean success = false;
|
||||
try {
|
||||
checkKeyManagerFactory(keyManagerFactory);
|
||||
checkNotNull(keyCertChain, "keyCertChainFile");
|
||||
checkNotNull(key, "keyFile");
|
||||
|
||||
if (keyPassword == null) {
|
||||
keyPassword = "";
|
||||
}
|
||||
|
||||
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);
|
||||
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
||||
if (!OpenSsl.supportsKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory not supported");
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
/* Set certificate verification policy. */
|
||||
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
||||
|
||||
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
|
||||
keyMaterialManager = null;
|
||||
} else {
|
||||
if (keyCertChain != null) {
|
||||
keyManagerFactory = buildKeyManagerFactory(
|
||||
keyCertChain, key, keyPassword, keyManagerFactory);
|
||||
}
|
||||
|
||||
if (keyManagerFactory != null) {
|
||||
X509KeyManager keyManager = chooseX509KeyManager(
|
||||
buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory)
|
||||
.getKeyManagers());
|
||||
keyMaterialManager = useExtendedKeyManager(keyManager) ?
|
||||
new OpenSslExtendedKeyMaterialManager(
|
||||
(X509ExtendedKeyManager) keyManager, keyPassword) :
|
||||
new OpenSslKeyMaterialManager(keyManager, keyPassword);
|
||||
} else {
|
||||
keyMaterialManager = null;
|
||||
}
|
||||
}
|
||||
} 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 (trustCertCollection != null) {
|
||||
@ -448,6 +431,11 @@ public final class OpenSslServerContext extends OpenSslContext {
|
||||
return sessionContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
OpenSslKeyMaterialManager keyMaterialManager() {
|
||||
return keyMaterialManager;
|
||||
}
|
||||
|
||||
private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {
|
||||
private final X509TrustManager manager;
|
||||
|
||||
|
@ -24,6 +24,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
|
||||
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
@ -51,6 +52,8 @@ 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.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
@ -1034,4 +1037,38 @@ public abstract class SslContext {
|
||||
throw new SSLException(e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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, key, keyPasswordChars);
|
||||
// Set up key manager factory to use our key store
|
||||
if (kmf == null) {
|
||||
kmf = KeyManagerFactory.getInstance(keyAlgorithm);
|
||||
}
|
||||
kmf.init(ks, keyPasswordChars);
|
||||
|
||||
return kmf;
|
||||
}
|
||||
|
||||
static KeyManagerFactory buildDefaultKeyManagerFactory()
|
||||
throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(null, null);
|
||||
return keyManagerFactory;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user