Use TrustManager for certificate verification
Motivation: To make OpenSsl*Context a drop in replacement for JdkSsl*Context we need to use TrustManager. Modifications: Correctly hook in the TrustManager Result: Better compatibility
This commit is contained in:
parent
473c23aec9
commit
393e3ea383
@ -23,12 +23,7 @@ import io.netty.util.internal.EmptyArrays;
|
|||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.EncryptedPrivateKeyInfo;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.SecretKeyFactory;
|
|
||||||
import javax.crypto.spec.PBEKeySpec;
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
@ -38,7 +33,6 @@ import javax.security.auth.x500.X500Principal;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyException;
|
import java.security.KeyException;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@ -401,39 +395,4 @@ public abstract class JdkSslContext extends SslContext {
|
|||||||
|
|
||||||
return trustManagerFactory;
|
return trustManagerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a key specification for an (encrypted) private key.
|
|
||||||
*
|
|
||||||
* @param password characters, if {@code null} or empty an unencrypted key is assumed
|
|
||||||
* @param key bytes of the DER encoded private key
|
|
||||||
*
|
|
||||||
* @return a key specification
|
|
||||||
*
|
|
||||||
* @throws IOException if parsing {@code key} fails
|
|
||||||
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
|
|
||||||
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
|
|
||||||
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
|
|
||||||
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
|
|
||||||
* {@code key}
|
|
||||||
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
|
|
||||||
*/
|
|
||||||
private static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
|
|
||||||
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
|
||||||
InvalidKeyException, InvalidAlgorithmParameterException {
|
|
||||||
|
|
||||||
if (password == null || password.length == 0) {
|
|
||||||
return new PKCS8EncodedKeySpec(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
|
||||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
|
||||||
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
|
||||||
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
|
||||||
|
|
||||||
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
@ -15,7 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
import org.apache.tomcat.jni.CertificateVerifier;
|
||||||
import org.apache.tomcat.jni.SSL;
|
import org.apache.tomcat.jni.SSL;
|
||||||
import org.apache.tomcat.jni.SSLContext;
|
import org.apache.tomcat.jni.SSLContext;
|
||||||
|
|
||||||
@ -24,17 +29,21 @@ import javax.net.ssl.SSLException;
|
|||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import javax.security.auth.x500.X500Principal;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.util.ArrayList;
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
* A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
||||||
*/
|
*/
|
||||||
public final class OpenSslClientContext extends OpenSslContext {
|
public final class OpenSslClientContext extends OpenSslContext {
|
||||||
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslClientContext.class);
|
||||||
private final X509TrustManager[] managers;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -116,32 +125,31 @@ public final class OpenSslClientContext extends OpenSslContext {
|
|||||||
}
|
}
|
||||||
SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
|
SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
|
||||||
|
|
||||||
// check if verification should take place or not.
|
try {
|
||||||
if (trustManagerFactory != null) {
|
// Set up trust manager factory to use our key store.
|
||||||
try {
|
if (trustManagerFactory == null) {
|
||||||
if (certChainFile == null) {
|
trustManagerFactory = TrustManagerFactory.getInstance(
|
||||||
trustManagerFactory.init((KeyStore) null);
|
TrustManagerFactory.getDefaultAlgorithm());
|
||||||
} else {
|
|
||||||
initTrustManagerFactory(certChainFile, trustManagerFactory);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SSLException("failed to initialize the client-side SSL context", e);
|
|
||||||
}
|
}
|
||||||
TrustManager[] tms = trustManagerFactory.getTrustManagers();
|
initTrustManagerFactory(certChainFile, trustManagerFactory);
|
||||||
if (tms == null || tms.length == 0) {
|
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
|
||||||
managers = null;
|
|
||||||
} else {
|
SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
|
||||||
List<X509TrustManager> managerList = new ArrayList<X509TrustManager>(tms.length);
|
@Override
|
||||||
for (TrustManager tm: tms) {
|
public boolean verify(long ssl, byte[][] chain, String auth) {
|
||||||
if (tm instanceof X509TrustManager) {
|
X509Certificate[] peerCerts = certificates(chain);
|
||||||
managerList.add((X509TrustManager) tm);
|
try {
|
||||||
|
manager.checkServerTrusted(peerCerts, auth);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debug("verification of certificate failed", e);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
managers = managerList.toArray(new X509TrustManager[managerList.size()]);
|
});
|
||||||
}
|
} catch (Exception e) {
|
||||||
} else {
|
throw new SSLException("unable to setup trustmanager", e);
|
||||||
managers = null;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
} finally {
|
} finally {
|
||||||
@ -151,13 +159,35 @@ public final class OpenSslClientContext extends OpenSslContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void initTrustManagerFactory(File certChainFile, TrustManagerFactory trustManagerFactory)
|
||||||
|
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
|
KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
|
ks.load(null, null);
|
||||||
|
if (certChainFile != null) {
|
||||||
|
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||||
|
try {
|
||||||
|
for (ByteBuf buf: certs) {
|
||||||
|
X509Certificate cert = (X509Certificate) X509_CERT_FACTORY.generateCertificate(
|
||||||
|
new ByteBufInputStream(buf));
|
||||||
|
X500Principal principal = cert.getSubjectX500Principal();
|
||||||
|
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
for (ByteBuf buf: certs) {
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trustManagerFactory.init(ks);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||||
List<String> protos = applicationProtocolNegotiator().protocols();
|
List<String> protos = applicationProtocolNegotiator().protocols();
|
||||||
if (protos.isEmpty()) {
|
if (protos.isEmpty()) {
|
||||||
return new OpenSslEngine(ctx, alloc, null, isClient(), managers);
|
return new OpenSslEngine(ctx, alloc, null, isClient());
|
||||||
} else {
|
} else {
|
||||||
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(), managers);
|
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,9 @@ import org.apache.tomcat.jni.SSLContext;
|
|||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -221,6 +224,7 @@ public abstract class OpenSslContext extends SslContext {
|
|||||||
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@code SSL_CTX} object of this context.
|
* Returns the {@code SSL_CTX} object of this context.
|
||||||
*/
|
*/
|
||||||
@ -265,6 +269,23 @@ public abstract class OpenSslContext extends SslContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static X509Certificate[] certificates(byte[][] chain) {
|
||||||
|
X509Certificate[] peerCerts = new X509Certificate[chain.length];
|
||||||
|
for (int i = 0; i < peerCerts.length; i++) {
|
||||||
|
peerCerts[i] = new OpenSslX509Certificate(chain[i]);
|
||||||
|
}
|
||||||
|
return peerCerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
|
||||||
|
for (TrustManager m: managers) {
|
||||||
|
if (m instanceof X509TrustManager) {
|
||||||
|
return (X509TrustManager) m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("no X509TrustManager found");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate a {@link ApplicationProtocolConfig} object to a
|
* Translate a {@link ApplicationProtocolConfig} object to a
|
||||||
* {@link OpenSslApplicationProtocolNegotiator} object.
|
* {@link OpenSslApplicationProtocolNegotiator} object.
|
||||||
@ -273,7 +294,7 @@ public abstract class OpenSslContext extends SslContext {
|
|||||||
* @return The results of the translation
|
* @return The results of the translation
|
||||||
*/
|
*/
|
||||||
static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config,
|
static OpenSslApplicationProtocolNegotiator toNegotiator(ApplicationProtocolConfig config,
|
||||||
boolean isServer) {
|
boolean isServer) {
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
return OpenSslDefaultApplicationProtocolNegotiator.INSTANCE;
|
||||||
}
|
}
|
||||||
@ -289,7 +310,8 @@ public abstract class OpenSslContext extends SslContext {
|
|||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
new StringBuilder("OpenSSL provider does not support ")
|
new StringBuilder("OpenSSL provider does not support ")
|
||||||
.append(config.selectedListenerFailureBehavior()).append(" behavior").toString());
|
.append(config.selectedListenerFailureBehavior())
|
||||||
|
.append(" behavior").toString());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("OpenSSL provider does not support client mode");
|
throw new UnsupportedOperationException("OpenSSL provider does not support client mode");
|
||||||
|
@ -32,17 +32,13 @@ import javax.net.ssl.SSLSession;
|
|||||||
import javax.net.ssl.SSLSessionBindingEvent;
|
import javax.net.ssl.SSLSessionBindingEvent;
|
||||||
import javax.net.ssl.SSLSessionBindingListener;
|
import javax.net.ssl.SSLSessionBindingListener;
|
||||||
import javax.net.ssl.SSLSessionContext;
|
import javax.net.ssl.SSLSessionContext;
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
import javax.security.cert.X509Certificate;
|
import javax.security.cert.X509Certificate;
|
||||||
import java.io.ByteArrayInputStream;
|
import javax.security.cert.CertificateException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ReadOnlyBufferException;
|
import java.nio.ReadOnlyBufferException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
@ -130,7 +126,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
private int lastPrimingReadResult;
|
private int lastPrimingReadResult;
|
||||||
|
|
||||||
private final X509TrustManager[] managers;
|
|
||||||
private final boolean clientMode;
|
private final boolean clientMode;
|
||||||
private final ByteBufAllocator alloc;
|
private final ByteBufAllocator alloc;
|
||||||
private final String fallbackApplicationProtocol;
|
private final String fallbackApplicationProtocol;
|
||||||
@ -146,7 +141,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
|
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
|
||||||
this(sslCtx, alloc, fallbackApplicationProtocol, false, null);
|
this(sslCtx, alloc, fallbackApplicationProtocol, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,7 +152,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
* @param clientMode {@code true} if this is used for clients, {@code false} otherwise
|
* @param clientMode {@code true} if this is used for clients, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
|
OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
|
||||||
boolean clientMode, X509TrustManager[] managers) {
|
boolean clientMode) {
|
||||||
OpenSsl.ensureAvailability();
|
OpenSsl.ensureAvailability();
|
||||||
if (sslCtx == 0) {
|
if (sslCtx == 0) {
|
||||||
throw new NullPointerException("sslContext");
|
throw new NullPointerException("sslContext");
|
||||||
@ -171,11 +166,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
networkBIO = SSL.makeNetworkBIO(ssl);
|
networkBIO = SSL.makeNetworkBIO(ssl);
|
||||||
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
|
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
|
||||||
this.clientMode = clientMode;
|
this.clientMode = clientMode;
|
||||||
if (managers == null || managers.length == 0) {
|
|
||||||
this.managers = null;
|
|
||||||
} else {
|
|
||||||
this.managers = managers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -375,7 +365,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
// In handshake or close_notify stages, check if call to wrap was made
|
// In handshake or close_notify stages, check if call to wrap was made
|
||||||
// without regard to the handshake status.
|
// without regard to the handshake status.
|
||||||
SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus(true);
|
SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
|
||||||
|
|
||||||
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
|
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
|
||||||
return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
|
return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
|
||||||
@ -407,7 +397,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSLEngineResult(getEngineStatus(), handshakeStatus(true), 0, bytesProduced);
|
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There was no pending data in the network BIO -- encrypt any application data
|
// There was no pending data in the network BIO -- encrypt any application data
|
||||||
@ -430,7 +420,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
int capacity = dst.remaining();
|
int capacity = dst.remaining();
|
||||||
if (capacity < pendingNet) {
|
if (capacity < pendingNet) {
|
||||||
return new SSLEngineResult(
|
return new SSLEngineResult(
|
||||||
BUFFER_OVERFLOW, handshakeStatus(true), bytesConsumed, bytesProduced);
|
BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the pending data from the network BIO into the dst buffer
|
// Write the pending data from the network BIO into the dst buffer
|
||||||
@ -440,12 +430,12 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
throw new SSLException(e);
|
throw new SSLException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSLEngineResult(getEngineStatus(), handshakeStatus(true), bytesConsumed, bytesProduced);
|
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSLEngineResult(getEngineStatus(), handshakeStatus(true), bytesConsumed, bytesProduced);
|
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -490,7 +480,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
// In handshake or close_notify stages, check if call to unwrap was made
|
// In handshake or close_notify stages, check if call to unwrap was made
|
||||||
// without regard to the handshake status.
|
// without regard to the handshake status.
|
||||||
SSLEngineResult.HandshakeStatus handshakeStatus = handshakeStatus(true);
|
SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
|
||||||
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
|
if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
|
||||||
return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
|
return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
|
||||||
}
|
}
|
||||||
@ -532,7 +522,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
// Do we have enough room in dsts to write decrypted data?
|
// Do we have enough room in dsts to write decrypted data?
|
||||||
if (capacity < pendingApp) {
|
if (capacity < pendingApp) {
|
||||||
return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus(true), bytesConsumed, 0);
|
return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write decrypted data to dsts buffers
|
// Write decrypted data to dsts buffers
|
||||||
@ -575,7 +565,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
closeInbound();
|
closeInbound();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSLEngineResult(getEngineStatus(), handshakeStatus(true), bytesConsumed, bytesProduced);
|
return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -679,33 +669,27 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
if (chain == null && clientCert == null) {
|
if (chain == null && clientCert == null) {
|
||||||
throw new SSLPeerUnverifiedException("peer not verified");
|
throw new SSLPeerUnverifiedException("peer not verified");
|
||||||
}
|
}
|
||||||
try {
|
int len = 0;
|
||||||
int len = 0;
|
if (chain != null) {
|
||||||
if (chain != null) {
|
len += chain.length;
|
||||||
len += chain.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
Certificate[] peerCerts;
|
|
||||||
if (clientCert != null) {
|
|
||||||
len++;
|
|
||||||
peerCerts = new Certificate[len];
|
|
||||||
peerCerts[i++] = SslContext.X509_CERT_FACTORY.generateCertificate(
|
|
||||||
new ByteArrayInputStream(clientCert));
|
|
||||||
} else {
|
|
||||||
peerCerts = new Certificate[len];
|
|
||||||
}
|
|
||||||
if (chain != null) {
|
|
||||||
int a = 0;
|
|
||||||
for (; i < peerCerts.length; i++) {
|
|
||||||
peerCerts[i] = SslContext.X509_CERT_FACTORY.generateCertificate(
|
|
||||||
new ByteArrayInputStream(chain[a++]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return peerCerts;
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
Certificate[] peerCerts;
|
||||||
|
if (clientCert != null) {
|
||||||
|
len++;
|
||||||
|
peerCerts = new Certificate[len];
|
||||||
|
peerCerts[i++] = new OpenSslX509Certificate(clientCert);
|
||||||
|
} else {
|
||||||
|
peerCerts = new Certificate[len];
|
||||||
|
}
|
||||||
|
if (chain != null) {
|
||||||
|
int a = 0;
|
||||||
|
for (; i < peerCerts.length; i++) {
|
||||||
|
peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peerCerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -851,7 +835,7 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
for (int i = 0; i < peerCerts.length; i++) {
|
for (int i = 0; i < peerCerts.length; i++) {
|
||||||
try {
|
try {
|
||||||
peerCerts[i] = X509Certificate.getInstance(chain[i]);
|
peerCerts[i] = X509Certificate.getInstance(chain[i]);
|
||||||
} catch (javax.security.cert.CertificateException e) {
|
} catch (CertificateException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -997,16 +981,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
|
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
|
||||||
try {
|
|
||||||
return handshakeStatus(false);
|
|
||||||
} catch (SSLException e) {
|
|
||||||
// Can not happen!
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No need to synchronize access as it is only called from within synchronized methods
|
|
||||||
private SSLEngineResult.HandshakeStatus handshakeStatus(boolean verify) throws SSLException {
|
|
||||||
if (accepted == 0 || destroyed != 0) {
|
if (accepted == 0 || destroyed != 0) {
|
||||||
return NOT_HANDSHAKING;
|
return NOT_HANDSHAKING;
|
||||||
}
|
}
|
||||||
@ -1037,10 +1011,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
} else {
|
} else {
|
||||||
this.applicationProtocol = null;
|
this.applicationProtocol = null;
|
||||||
}
|
}
|
||||||
if (verify && managers != null) {
|
|
||||||
peerCerts = initPeerCertChain();
|
|
||||||
verifyCertificates(managers, peerCerts, cipher);
|
|
||||||
}
|
|
||||||
return FINISHED;
|
return FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1131,31 +1101,6 @@ public final class OpenSslEngine extends SSLEngine {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyCertificates(X509TrustManager[] managers, Certificate[] peerCerts, String cipher)
|
|
||||||
throws SSLException {
|
|
||||||
|
|
||||||
List<java.security.cert.X509Certificate> certs =
|
|
||||||
new ArrayList<java.security.cert.X509Certificate>(peerCerts.length);
|
|
||||||
for (Certificate c: peerCerts) {
|
|
||||||
if (c instanceof java.security.cert.X509Certificate) {
|
|
||||||
certs.add((java.security.cert.X509Certificate) c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
java.security.cert.X509Certificate[] certArray =
|
|
||||||
certs.toArray(new java.security.cert.X509Certificate[certs.size()]);
|
|
||||||
for (X509TrustManager tm: managers) {
|
|
||||||
try {
|
|
||||||
if (clientMode) {
|
|
||||||
tm.checkServerTrusted(certArray, cipher);
|
|
||||||
} else {
|
|
||||||
tm.checkClientTrusted(certArray, cipher);
|
|
||||||
}
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
throw new SSLException("peer certificate chain not trusted", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
super.finalize();
|
super.finalize();
|
||||||
|
@ -15,13 +15,29 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.ssl;
|
package io.netty.handler.ssl;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
import org.apache.tomcat.jni.CertificateVerifier;
|
||||||
import org.apache.tomcat.jni.SSL;
|
import org.apache.tomcat.jni.SSL;
|
||||||
import org.apache.tomcat.jni.SSLContext;
|
import org.apache.tomcat.jni.SSLContext;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
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;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.*;
|
import static io.netty.util.internal.ObjectUtil.*;
|
||||||
@ -30,6 +46,7 @@ import static io.netty.util.internal.ObjectUtil.*;
|
|||||||
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
|
||||||
*/
|
*/
|
||||||
public final class OpenSslServerContext extends OpenSslContext {
|
public final class OpenSslServerContext extends OpenSslContext {
|
||||||
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -166,6 +183,62 @@ public final class OpenSslServerContext extends OpenSslContext {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SSLException("failed to set certificate: " + certChainFile + " and " + keyFile, e);
|
throw new SSLException("failed to set certificate: " + certChainFile + " and " + keyFile, e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
|
ks.load(null, null);
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
KeyFactory rsaKF = KeyFactory.getInstance("RSA");
|
||||||
|
KeyFactory dsaKF = KeyFactory.getInstance("DSA");
|
||||||
|
|
||||||
|
ByteBuf encodedKeyBuf = PemReader.readPrivateKey(keyFile);
|
||||||
|
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
|
||||||
|
encodedKeyBuf.readBytes(encodedKey).release();
|
||||||
|
|
||||||
|
char[] keyPasswordChars = keyPassword.toCharArray();
|
||||||
|
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
|
||||||
|
|
||||||
|
PrivateKey key;
|
||||||
|
try {
|
||||||
|
key = rsaKF.generatePrivate(encodedKeySpec);
|
||||||
|
} catch (InvalidKeySpecException ignore) {
|
||||||
|
key = dsaKF.generatePrivate(encodedKeySpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Certificate> certChain = new ArrayList<Certificate>();
|
||||||
|
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
||||||
|
try {
|
||||||
|
for (ByteBuf buf: certs) {
|
||||||
|
certChain.add(cf.generateCertificate(new ByteBufInputStream(buf)));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
for (ByteBuf buf: certs) {
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()]));
|
||||||
|
|
||||||
|
// This mimics the behavior of using SSLContext.init(...);
|
||||||
|
TrustManagerFactory factory = TrustManagerFactory.getInstance(
|
||||||
|
TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
factory.init((KeyStore) null);
|
||||||
|
final X509TrustManager manager = chooseTrustManager(factory.getTrustManagers());
|
||||||
|
SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
|
||||||
|
@Override
|
||||||
|
public boolean verify(long ssl, byte[][] chain, String auth) {
|
||||||
|
X509Certificate[] peerCerts = certificates(chain);
|
||||||
|
try {
|
||||||
|
manager.checkClientTrusted(peerCerts, auth);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debug("verification of certificate failed", e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SSLException("unable to setup trustmanager", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
} finally {
|
} finally {
|
||||||
@ -175,16 +248,13 @@ public final class OpenSslServerContext extends OpenSslContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new server-side {@link javax.net.ssl.SSLEngine} with the current configuration.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
public SSLEngine newEngine(ByteBufAllocator alloc) {
|
||||||
List<String> protos = applicationProtocolNegotiator().protocols();
|
List<String> protos = applicationProtocolNegotiator().protocols();
|
||||||
if (protos.isEmpty()) {
|
if (protos.isEmpty()) {
|
||||||
return new OpenSslEngine(ctx, alloc, null, isClient(), null);
|
return new OpenSslEngine(ctx, alloc, null, isClient());
|
||||||
} else {
|
} else {
|
||||||
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(), null);
|
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 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 java.io.ByteArrayInputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateExpiredException;
|
||||||
|
import java.security.cert.CertificateNotYetValidException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class OpenSslX509Certificate extends X509Certificate {
|
||||||
|
|
||||||
|
private final byte[] bytes;
|
||||||
|
private X509Certificate wrapped;
|
||||||
|
|
||||||
|
public OpenSslX509Certificate(byte[] bytes) {
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
|
||||||
|
unwrap().checkValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
|
||||||
|
unwrap().checkValidity(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return unwrap().getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigInteger getSerialNumber() {
|
||||||
|
return unwrap().getSerialNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getIssuerDN() {
|
||||||
|
return unwrap().getIssuerDN();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Principal getSubjectDN() {
|
||||||
|
return unwrap().getSubjectDN();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getNotBefore() {
|
||||||
|
return unwrap().getNotBefore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getNotAfter() {
|
||||||
|
return unwrap().getNotAfter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getTBSCertificate() throws CertificateEncodingException {
|
||||||
|
return unwrap().getTBSCertificate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return unwrap().getSignature();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSigAlgName() {
|
||||||
|
return unwrap().getSigAlgName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSigAlgOID() {
|
||||||
|
return unwrap().getSigAlgOID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getSigAlgParams() {
|
||||||
|
return unwrap().getSigAlgParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean[] getIssuerUniqueID() {
|
||||||
|
return unwrap().getIssuerUniqueID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean[] getSubjectUniqueID() {
|
||||||
|
return unwrap().getSubjectUniqueID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean[] getKeyUsage() {
|
||||||
|
return unwrap().getKeyUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBasicConstraints() {
|
||||||
|
return unwrap().getBasicConstraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getEncoded() {
|
||||||
|
return bytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(PublicKey key)
|
||||||
|
throws CertificateException, NoSuchAlgorithmException,
|
||||||
|
InvalidKeyException, NoSuchProviderException, SignatureException {
|
||||||
|
unwrap().verify(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(PublicKey key, String sigProvider)
|
||||||
|
throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
|
||||||
|
NoSuchProviderException, SignatureException {
|
||||||
|
unwrap().verify(key, sigProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return unwrap().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
return unwrap().getPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasUnsupportedCriticalExtension() {
|
||||||
|
return unwrap().hasUnsupportedCriticalExtension();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getCriticalExtensionOIDs() {
|
||||||
|
return unwrap().getCriticalExtensionOIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getNonCriticalExtensionOIDs() {
|
||||||
|
return unwrap().getNonCriticalExtensionOIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getExtensionValue(String oid) {
|
||||||
|
return unwrap().getExtensionValue(oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private X509Certificate unwrap() {
|
||||||
|
X509Certificate wrapped = this.wrapped;
|
||||||
|
if (wrapped == null) {
|
||||||
|
try {
|
||||||
|
wrapped = this.wrapped = (X509Certificate) SslContext.X509_CERT_FACTORY.generateCertificate(
|
||||||
|
new ByteArrayInputStream(bytes));
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,12 @@ import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
|
|||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
import javax.net.ssl.KeyManager;
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.EncryptedPrivateKeyInfo;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.SecretKeyFactory;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -35,12 +41,16 @@ import javax.net.ssl.TrustManagerFactory;
|
|||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -723,23 +733,38 @@ public abstract class SslContext {
|
|||||||
return new SslHandler(engine);
|
return new SslHandler(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initTrustManagerFactory(File certChainFile, TrustManagerFactory trustManagerFactory)
|
/**
|
||||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
* Generates a key specification for an (encrypted) private key.
|
||||||
KeyStore ks = KeyStore.getInstance("JKS");
|
*
|
||||||
ks.load(null, null);
|
* @param password characters, if {@code null} or empty an unencrypted key is assumed
|
||||||
ByteBuf[] certs = PemReader.readCertificates(certChainFile);
|
* @param key bytes of the DER encoded private key
|
||||||
try {
|
*
|
||||||
for (ByteBuf buf: certs) {
|
* @return a key specification
|
||||||
X509Certificate cert = (X509Certificate) X509_CERT_FACTORY.generateCertificate(
|
*
|
||||||
new ByteBufInputStream(buf));
|
* @throws IOException if parsing {@code key} fails
|
||||||
X500Principal principal = cert.getSubjectX500Principal();
|
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
|
||||||
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
|
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
|
||||||
}
|
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
|
||||||
} finally {
|
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
|
||||||
for (ByteBuf buf: certs) {
|
* {@code key}
|
||||||
buf.release();
|
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
|
||||||
}
|
*/
|
||||||
|
protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
|
||||||
|
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
|
||||||
|
InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
|
|
||||||
|
if (password == null || password.length == 0) {
|
||||||
|
return new PKCS8EncodedKeySpec(key);
|
||||||
}
|
}
|
||||||
trustManagerFactory.init(ks);
|
|
||||||
|
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
|
||||||
|
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||||
|
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
|
||||||
|
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
|
||||||
|
|
||||||
|
return encryptedPrivateKeyInfo.getKeySpec(cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user