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:
Norman Maurer 2016-06-20 14:07:53 +02:00
parent 3a69adfefb
commit 5e64985089
10 changed files with 504 additions and 223 deletions

View File

@ -341,17 +341,6 @@ public class JdkSslContext extends SslContext {
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf); return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf);
} }
static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChain, PrivateKey key, String keyPassword,
KeyManagerFactory kmf)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
return buildKeyManagerFactory(certChain, algorithm, key, keyPassword, kmf);
}
/** /**
* Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password, * Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain. * and a certificate chain.
@ -375,20 +364,4 @@ public class JdkSslContext extends SslContext {
return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm, return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
toPrivateKey(keyFile, keyPassword), keyPassword, kmf); 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;
}
} }

View File

@ -17,6 +17,7 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.internal.NativeLibraryLoader; import io.netty.util.internal.NativeLibraryLoader;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -48,6 +49,7 @@ public final class OpenSsl {
static final Set<String> AVAILABLE_CIPHER_SUITES; static final Set<String> AVAILABLE_CIPHER_SUITES;
private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES; private static final Set<String> AVAILABLE_OPENSSL_CIPHER_SUITES;
private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES; private static final Set<String> AVAILABLE_JAVA_CIPHER_SUITES;
private static final boolean SUPPORTS_KEYMANAGER_FACTORY;
// Protocols // Protocols
static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello"; static final String PROTOCOL_SSL_V2_HELLO = "SSLv2Hello";
@ -117,9 +119,12 @@ public final class OpenSsl {
if (cause == null) { if (cause == null) {
final Set<String> availableOpenSslCipherSuites = new LinkedHashSet<String>(128); final Set<String> availableOpenSslCipherSuites = new LinkedHashSet<String>(128);
boolean supportsKeyManagerFactory = false;
final long aprPool = Pool.create(0); final long aprPool = Pool.create(0);
try { try {
final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER); final long sslCtx = SSLContext.make(aprPool, SSL.SSL_PROTOCOL_ALL, SSL.SSL_MODE_SERVER);
long privateKeyBio = 0;
long certBio = 0;
try { try {
SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL); SSLContext.setOptions(sslCtx, SSL.SSL_OP_ALL);
SSLContext.setCipherSuite(sslCtx, "ALL"); SSLContext.setCipherSuite(sslCtx, "ALL");
@ -132,8 +137,22 @@ public final class OpenSsl {
} }
availableOpenSslCipherSuites.add(c); 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 { } finally {
SSL.freeSSL(ssl); SSL.freeSSL(ssl);
if (privateKeyBio != 0) {
SSL.freeBIO(privateKeyBio);
}
if (certBio != 0) {
SSL.freeBIO(certBio);
}
} }
} finally { } finally {
SSLContext.free(sslCtx); SSLContext.free(sslCtx);
@ -163,10 +182,12 @@ public final class OpenSsl {
availableCipherSuites.add(cipher); availableCipherSuites.add(cipher);
} }
AVAILABLE_CIPHER_SUITES = availableCipherSuites; AVAILABLE_CIPHER_SUITES = availableCipherSuites;
SUPPORTS_KEYMANAGER_FACTORY = supportsKeyManagerFactory;
} else { } else {
AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet(); AVAILABLE_OPENSSL_CIPHER_SUITES = Collections.emptySet();
AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet(); AVAILABLE_JAVA_CIPHER_SUITES = Collections.emptySet();
AVAILABLE_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); 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) { static boolean isError(long errorCode) {
return errorCode != SSL.SSL_ERROR_NONE; return errorCode != SSL.SSL_ERROR_NONE;
} }

View File

@ -15,24 +15,34 @@
*/ */
package io.netty.handler.ssl; 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.SSL;
import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
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.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.X509Certificate; 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. * 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 OpenSslSessionContext sessionContext; private final OpenSslSessionContext sessionContext;
/** /**
@ -188,51 +198,37 @@ public final class OpenSslClientContext extends OpenSslContext {
ClientAuth.NONE); ClientAuth.NONE);
boolean success = false; boolean success = false;
try { try {
checkKeyManagerFactory(keyManagerFactory);
if (key == null && keyCertChain != null || key != null && keyCertChain == null) { if (key == null && keyCertChain != null || key != null && keyCertChain == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Either both keyCertChain and key needs to be null or none of them"); "Either both keyCertChain and key needs to be null or none of them");
} }
synchronized (OpenSslContext.class) { synchronized (OpenSslContext.class) {
if (keyCertChain != null && key != null) {
/* Load the certificate file and private key. */
long keyBio = 0;
long keyCertChainBio = 0;
try { try {
keyCertChainBio = toBIO(keyCertChain); if (!OpenSsl.supportsKeyManagerFactory()) {
if (keyManagerFactory != null) {
keyBio = toBIO(key); throw new IllegalArgumentException(
"KeyManagerFactory not supported");
if (!SSLContext.setCertificateBio( }
ctx, keyCertChainBio, keyBio, keyPassword, SSL.SSL_AIDX_RSA)) { if (keyCertChain != null && key != null) {
long error = SSL.getLastErrorNumber(); setKeyMaterial(ctx, keyCertChain, key, keyPassword);
if (OpenSsl.isError(error)) { }
throw new SSLException("failed to set certificate and key: " } else {
+ SSL.getErrorString(error)); if (keyCertChain != null) {
keyManagerFactory = buildKeyManagerFactory(
keyCertChain, key, keyPassword, keyManagerFactory);
}
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));
} }
} }
// 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));
}
}
} catch (SSLException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new SSLException("failed to set certificate and key", 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); SSLContext.setVerify(ctx, SSL.SSL_VERIFY_NONE, VERIFY_DEPTH);
@ -278,6 +274,11 @@ public final class OpenSslClientContext extends OpenSslContext {
return sessionContext; return sessionContext;
} }
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return null;
}
// No cache is currently supported for client side mode. // No cache is currently supported for client side mode.
private static final class OpenSslClientSessionContext extends OpenSslSessionContext { private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
private OpenSslClientSessionContext(OpenSslContext context) { private OpenSslClientSessionContext(OpenSslContext context) {
@ -348,4 +349,77 @@ public final class OpenSslClientContext extends OpenSslContext {
manager.checkServerTrusted(peerCerts, auth, engine); 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;
}
}
}
} }

View File

@ -18,6 +18,7 @@ package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; 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.SSL;
import org.apache.tomcat.jni.SSLContext; 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.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivateKey; 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 * 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. * {@code jdk.tls.rejectClientInitiatedRenegotiation} to allow disabling client initiated renegotiation.
* Java8+ uses this system property as well. * 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"> * 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> * Significant SSL/TLS improvements in Java 8</a>
*/ */
@ -69,20 +72,23 @@ public abstract class OpenSslContext extends SslContext {
// TODO: Maybe make configurable ? // TODO: Maybe make configurable ?
protected static final int VERIFY_DEPTH = 10; protected static final int VERIFY_DEPTH = 10;
/** The OpenSSL SSL_CTX object */ /**
* The OpenSSL SSL_CTX object
*/
protected volatile long ctx; protected volatile long ctx;
final OpenSslEngineMap engineMap = new DefaultOpenSslEngineMap();
long aprPool; long aprPool;
@SuppressWarnings({ "unused", "FieldMayBeFinal" }) @SuppressWarnings({ "unused", "FieldMayBeFinal" })
private volatile int aprPoolDestroyed; private volatile int aprPoolDestroyed;
private volatile boolean rejectRemoteInitiatedRenegotiation;
private final List<String> unmodifiableCiphers; private final List<String> unmodifiableCiphers;
private final long sessionCacheSize; private final long sessionCacheSize;
private final long sessionTimeout; private final long sessionTimeout;
private final OpenSslApplicationProtocolNegotiator apn; private final OpenSslApplicationProtocolNegotiator apn;
private final int mode; 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 = static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
new OpenSslApplicationProtocolNegotiator() { new OpenSslApplicationProtocolNegotiator() {
@ -178,7 +184,7 @@ public abstract class OpenSslContext extends SslContext {
convertedCiphers = null; convertedCiphers = null;
} else { } else {
convertedCiphers = new ArrayList<String>(); convertedCiphers = new ArrayList<String>();
for (String c: ciphers) { for (String c : ciphers) {
if (c == null) { if (c == null) {
break; break;
} }
@ -330,10 +336,11 @@ public abstract class OpenSslContext extends SslContext {
@Override @Override
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
return new OpenSslEngine(ctx, alloc, isClient(), sessionContext(), apn, engineMap, return new OpenSslEngine(this, alloc, peerHost, peerPort);
rejectRemoteInitiatedRenegotiation, peerHost, peerPort, keyCertChain, clientAuth);
} }
abstract OpenSslKeyMaterialManager keyMaterialManager();
/** /**
* Returns a new server-side {@link SSLEngine} with the current configuration. * 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. * Returns the stats of this context.
*
* @deprecated use {@link #sessionContext#stats()} * @deprecated use {@link #sessionContext#stats()}
*/ */
@Deprecated @Deprecated
@ -380,6 +388,7 @@ public abstract class OpenSslContext extends SslContext {
/** /**
* Sets the SSL session ticket keys of this context. * Sets the SSL session ticket keys of this context.
*
* @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])} * @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
*/ */
@Deprecated @Deprecated
@ -434,9 +443,19 @@ public abstract class OpenSslContext extends SslContext {
throw new IllegalStateException("no X509TrustManager found"); 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 * Translate a {@link ApplicationProtocolConfig} object to a
* {@link OpenSslApplicationProtocolNegotiator} object. * {@link OpenSslApplicationProtocolNegotiator} object.
*
* @param config The configuration which defines the translation * @param config The configuration which defines the translation
* @return The results of the translation * @return The results of the translation
*/ */
@ -480,6 +499,10 @@ public abstract class OpenSslContext extends SslContext {
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 { abstract static class AbstractCertificateVerifier implements CertificateVerifier {
private final OpenSslEngineMap engineMap; private final OpenSslEngineMap engineMap;
@ -490,7 +513,7 @@ public abstract class OpenSslContext extends SslContext {
@Override @Override
public final int verify(long ssl, byte[][] chain, String auth) { public final int verify(long ssl, byte[][] chain, String auth) {
X509Certificate[] peerCerts = certificates(chain); X509Certificate[] peerCerts = certificates(chain);
final OpenSslEngine engine = engineMap.remove(ssl); final OpenSslEngine engine = engineMap.get(ssl);
try { try {
verify(engine, peerCerts, auth); verify(engine, peerCerts, auth);
return CertificateVerifier.X509_V_OK; return CertificateVerifier.X509_V_OK;
@ -521,6 +544,7 @@ public abstract class OpenSslContext extends SslContext {
private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap { private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
private final Map<Long, OpenSslEngine> engines = PlatformDependent.newConcurrentHashMap(); private final Map<Long, OpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
@Override @Override
public OpenSslEngine remove(long ssl) { public OpenSslEngine remove(long ssl) {
return engines.remove(ssl); return engines.remove(ssl);
@ -530,8 +554,41 @@ public abstract class OpenSslContext extends SslContext {
public void add(OpenSslEngine engine) { public void add(OpenSslEngine engine) {
engines.put(engine.sslPointer(), 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> * 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}. * 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> * 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}. * 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) { if (certChain == null) {
return 0; return 0;
} }
@ -615,11 +672,4 @@ public abstract class OpenSslContext extends SslContext {
buffer.release(); buffer.release();
} }
} }
static void checkKeyManagerFactory(KeyManagerFactory keyManagerFactory) {
if (keyManagerFactory != null) {
throw new IllegalArgumentException(
"KeyManagerFactory is currently not supported with OpenSslContext");
}
}
} }

View File

@ -172,6 +172,7 @@ public final class OpenSslEngine extends SSLEngine {
// OpenSSL state // OpenSSL state
private long ssl; private long ssl;
private long networkBIO; private long networkBIO;
private boolean certificateSet;
private enum HandshakeState { private enum HandshakeState {
/** /**
@ -221,64 +222,35 @@ public final class OpenSslEngine extends SSLEngine {
private final Certificate[] localCerts; private final Certificate[] localCerts;
private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1]; private final ByteBuffer[] singleSrcBuffer = new ByteBuffer[1];
private final ByteBuffer[] singleDstBuffer = 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 // This is package-private as we set it from OpenSslContext if an exception is thrown during
// the verification step. // the verification step.
SSLHandshakeException handshakeException; SSLHandshakeException handshakeException;
/** OpenSslEngine(OpenSslContext context, ByteBufAllocator alloc, String peerHost, int peerPort) {
* 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) {
super(peerHost, peerPort); super(peerHost, peerPort);
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
if (sslCtx == 0) {
throw new NullPointerException("sslCtx");
}
this.alloc = checkNotNull(alloc, "alloc"); this.alloc = checkNotNull(alloc, "alloc");
this.apn = checkNotNull(apn, "apn"); apn = (OpenSslApplicationProtocolNegotiator) context.applicationProtocolNegotiator();
ssl = SSL.newSSL(sslCtx, !clientMode); ssl = SSL.newSSL(context.ctx, !context.isClient());
session = new OpenSslSession(sessionContext); session = new OpenSslSession(context.sessionContext());
networkBIO = SSL.makeNetworkBIO(ssl); networkBIO = SSL.makeNetworkBIO(ssl);
this.clientMode = clientMode; clientMode = context.isClient();
this.engineMap = engineMap; engineMap = context.engineMap;
this.rejectRemoteInitiatedRenegation = rejectRemoteInitiatedRenegation; rejectRemoteInitiatedRenegation = context.rejectRemoteInitiatedRenegotiation;
this.localCerts = localCerts; localCerts = context.keyCertChain;
// Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the // Set the client auth mode, this needs to be done via setClientAuth(...) method so we actually call the
// needed JNI methods. // needed JNI methods.
setClientAuth(clientMode ? ClientAuth.NONE : checkNotNull(clientAuth, "clientAuth")); setClientAuth(clientMode ? ClientAuth.NONE : context.clientAuth);
// Use SNI if peerHost was specified // Use SNI if peerHost was specified
// See https://github.com/netty/netty/issues/4746 // See https://github.com/netty/netty/issues/4746
if (clientMode && peerHost != null) { if (clientMode && peerHost != null) {
SSL.setTlsExtHostName(ssl, peerHost); SSL.setTlsExtHostName(ssl, peerHost);
} }
keyMaterialManager = context.keyMaterialManager();
} }
@Override @Override
@ -1287,6 +1259,11 @@ public final class OpenSslEngine extends SSLEngine {
lastAccessed = System.currentTimeMillis(); lastAccessed = System.currentTimeMillis();
} }
if (!certificateSet && keyMaterialManager != null) {
certificateSet = true;
keyMaterialManager.setKeyMaterial(this);
}
int code = SSL.doHandshake(ssl); int code = SSL.doHandshake(ssl);
if (code <= 0) { if (code <= 0) {
// Check if we have a pending exception that was created during the handshake and if so throw it after // 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. // if SSL_do_handshake returns > 0 or sslError == SSL.SSL_ERROR_NAME it means the handshake was finished.
session.handshakeFinished(); session.handshakeFinished();
engineMap.remove(ssl);
return FINISHED; return FINISHED;
} }

View File

@ -17,18 +17,6 @@ package io.netty.handler.ssl;
interface OpenSslEngineMap { 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 * Remove the {@link OpenSslEngine} with the given {@code ssl} address and
* return it. * return it.
@ -39,4 +27,9 @@ interface OpenSslEngineMap {
* Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}. * Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}.
*/ */
void add(OpenSslEngine engine); void add(OpenSslEngine engine);
/**
* Get the {@link OpenSslEngine} for the given {@code ssl} address.
*/
OpenSslEngine get(long ssl);
} }

View File

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

View File

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

View File

@ -23,7 +23,9 @@ import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException; 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.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.io.File; import java.io.File;
import java.security.KeyStore; import java.security.KeyStore;
@ -38,6 +40,7 @@ import static io.netty.util.internal.ObjectUtil.*;
public final class OpenSslServerContext extends OpenSslContext { public final class OpenSslServerContext extends OpenSslContext {
private static final byte[] ID = new byte[] {'n', 'e', 't', 't', 'y'}; private static final byte[] ID = new byte[] {'n', 'e', 't', 't', 'y'};
private final OpenSslServerSessionContext sessionContext; private final OpenSslServerSessionContext sessionContext;
private final OpenSslKeyMaterialManager keyMaterialManager;
/** /**
* Creates a new instance. * Creates a new instance.
@ -346,63 +349,43 @@ public final class OpenSslServerContext extends OpenSslContext {
// Create a new SSL_CTX and configure it. // Create a new SSL_CTX and configure it.
boolean success = false; boolean success = false;
try { try {
checkKeyManagerFactory(keyManagerFactory);
checkNotNull(keyCertChain, "keyCertChainFile"); checkNotNull(keyCertChain, "keyCertChainFile");
checkNotNull(key, "keyFile"); checkNotNull(key, "keyFile");
if (keyPassword == null) { synchronized (OpenSslContext.class) {
keyPassword = ""; try {
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
if (!OpenSsl.supportsKeyManagerFactory()) {
if (keyManagerFactory != null) {
throw new IllegalArgumentException(
"KeyManagerFactory not supported");
} }
synchronized (OpenSslContext.class) {
/* Set certificate verification policy. */ /* Set certificate verification policy. */
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
long keyCertChainBio = 0;
try { setKeyMaterial(ctx, keyCertChain, key, keyPassword);
keyCertChainBio = toBIO(keyCertChain); keyMaterialManager = null;
/* Load the certificate chain. We must skip the first cert when server mode */ } else {
if (!SSLContext.setCertificateChainBio(ctx, keyCertChainBio, true)) { if (keyCertChain != null) {
long error = SSL.getLastErrorNumber(); keyManagerFactory = buildKeyManagerFactory(
if (OpenSsl.isError(error)) { keyCertChain, key, keyPassword, keyManagerFactory);
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. */ if (keyManagerFactory != null) {
long keyBio = 0; X509KeyManager keyManager = chooseX509KeyManager(
keyCertChainBio = 0; buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory)
try { .getKeyManagers());
keyBio = toBIO(key); keyMaterialManager = useExtendedKeyManager(keyManager) ?
keyCertChainBio = toBIO(keyCertChain); new OpenSslExtendedKeyMaterialManager(
if (!SSLContext.setCertificateBio( (X509ExtendedKeyManager) keyManager, keyPassword) :
ctx, keyCertChainBio, keyBio, keyPassword, SSL.SSL_AIDX_RSA)) { new OpenSslKeyMaterialManager(keyManager, keyPassword);
long error = SSL.getLastErrorNumber(); } else {
if (OpenSsl.isError(error)) { keyMaterialManager = null;
String err = SSL.getErrorString(error);
throw new SSLException("failed to set certificate and key: " + err);
} }
} }
} catch (SSLException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new SSLException("failed to set certificate and key", 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 { try {
if (trustCertCollection != null) { if (trustCertCollection != null) {
@ -448,6 +431,11 @@ public final class OpenSslServerContext extends OpenSslContext {
return sessionContext; return sessionContext;
} }
@Override
OpenSslKeyMaterialManager keyMaterialManager() {
return keyMaterialManager;
}
private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {
private final X509TrustManager manager; private final X509TrustManager manager;

View File

@ -24,6 +24,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol; import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior; import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.util.internal.EmptyArrays;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
@ -51,6 +52,8 @@ import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
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;
@ -1034,4 +1037,38 @@ public abstract class SslContext {
throw new SSLException(e); 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;
}
} }