Allow to specify KeyStore type in SslContext (#9003)

Motivation:

As brought up in https://github.com/netty/netty/issues/8998, JKS can be substantially faster than pkcs12, JDK's new default. Without an option to set the KeyStore type you must change the configuration of the entire JVM which is impractical.

Modification:

- Allow to specify KeyStore type
- Add test case

Result:

Fixes https://github.com/netty/netty/issues/8998.
This commit is contained in:
SplotyCode 2019-05-10 07:29:14 +02:00 committed by Norman Maurer
parent 83210c47ca
commit d3a13a0d6a
18 changed files with 243 additions and 79 deletions

View File

@ -54,7 +54,7 @@ import io.netty.util.CharsetUtil;
/**
* ATTENTION: This is an incomplete example! In order to provide a fully functional
* end-to-end example we'd need a X.509 certificate and the matching PrivateKey.
* end-to-end example we'd need an X.509 certificate and the matching PrivateKey.
*/
@SuppressWarnings("unused")
public class OcspServerExample {

View File

@ -17,6 +17,7 @@
package io.netty.handler.ssl;
import java.io.File;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
@ -44,7 +45,7 @@ final class JdkSslClientContext extends JdkSslContext {
throws SSLException {
super(newSSLContext(provider, toX509CertificatesInternal(trustCertCollectionFile),
trustManagerFactory, null, null,
null, null, sessionCacheSize, sessionTimeout), true,
null, null, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), true,
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
}
@ -60,10 +61,11 @@ final class JdkSslClientContext extends JdkSslContext {
ApplicationProtocolConfig apn,
String[] protocols,
long sessionCacheSize,
long sessionTimeout)
long sessionTimeout,
String keyStore)
throws SSLException {
super(newSSLContext(sslContextProvider, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout),
keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore),
true, ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE, protocols, false);
}
@ -71,13 +73,14 @@ final class JdkSslClientContext extends JdkSslContext {
X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
long sessionCacheSize, long sessionTimeout) throws SSLException {
long sessionCacheSize, long sessionTimeout,
String keyStore) throws SSLException {
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
}
if (keyCertChain != null) {
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory);
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory, keyStore);
}
SSLContext ctx = sslContextProvider == null ? SSLContext.getInstance(PROTOCOL)
: SSLContext.getInstance(PROTOCOL, sslContextProvider);

View File

@ -25,6 +25,7 @@ import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
@ -433,7 +434,29 @@ public class JdkSslContext extends SslContext {
/**
* Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
* @param certChainFile a X.509 certificate chain file in PEM format
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
* @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
* @return A {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
*/
static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
KeyManagerFactory kmf, String keyStore)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
CertificateException, KeyException, IOException {
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf, keyStore);
}
/**
* Build a {@link KeyManagerFactory} based upon a key file, key file password, and a certificate chain.
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
@ -443,23 +466,43 @@ public class JdkSslContext extends SslContext {
*/
@Deprecated
protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile, File keyFile, String keyPassword,
KeyManagerFactory kmf)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
CertificateException, KeyException, IOException {
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
return buildKeyManagerFactory(certChainFile, algorithm, keyFile, keyPassword, kmf);
KeyManagerFactory kmf)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException, InvalidAlgorithmParameterException,
CertificateException, KeyException, IOException {
return buildKeyManagerFactory(certChainFile, keyFile, keyPassword, kmf, KeyStore.getDefaultType());
}
/**
* Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain.
* @param certChainFile a X.509 certificate chain file in PEM format
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
* Reference Guide for information about standard algorithm names.
* Reference Guide for information about standard algorithm names.
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param kmf The existing {@link KeyManagerFactory} that will be used if not {@code null}
* @param keyStore the {@link KeyStore} that should be used in the {@link KeyManagerFactory}
* @return A {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain.
*/
static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf,
String keyStore)
throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
CertificateException, KeyException, UnrecoverableKeyException {
return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
toPrivateKey(keyFile, keyPassword), keyPassword, kmf, keyStore);
}
/**
* Build a {@link KeyManagerFactory} based upon a key algorithm, key file, key file password,
* and a certificate chain.
* @param certChainFile an buildKeyManagerFactory X.509 certificate chain file in PEM format
* @param keyAlgorithm the standard name of the requested algorithm. See the Java Secure Socket Extension
* Reference Guide for information about standard algorithm names.
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
@ -470,11 +513,12 @@ public class JdkSslContext extends SslContext {
*/
@Deprecated
protected static KeyManagerFactory buildKeyManagerFactory(File certChainFile,
String keyAlgorithm, File keyFile, String keyPassword, KeyManagerFactory kmf)
throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
CertificateException, KeyException, UnrecoverableKeyException {
String keyAlgorithm, File keyFile,
String keyPassword, KeyManagerFactory kmf)
throws KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, InvalidAlgorithmParameterException, IOException,
CertificateException, KeyException, UnrecoverableKeyException {
return buildKeyManagerFactory(toX509Certificates(certChainFile), keyAlgorithm,
toPrivateKey(keyFile, keyPassword), keyPassword, kmf);
toPrivateKey(keyFile, keyPassword), keyPassword, kmf, KeyStore.getDefaultType());
}
}

View File

@ -17,6 +17,7 @@
package io.netty.handler.ssl;
import java.io.File;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
@ -45,7 +46,7 @@ final class JdkSslServerContext extends JdkSslContext {
throws SSLException {
super(newSSLContext(provider, null, null,
toX509CertificatesInternal(certChainFile), toPrivateKeyInternal(keyFile, keyPassword),
keyPassword, null, sessionCacheSize, sessionTimeout), false,
keyPassword, null, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), false,
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
}
@ -63,17 +64,18 @@ final class JdkSslServerContext extends JdkSslContext {
long sessionTimeout,
ClientAuth clientAuth,
String[] protocols,
boolean startTls)
boolean startTls,
String keyStore)
throws SSLException {
super(newSSLContext(provider, trustCertCollection, trustManagerFactory, keyCertChain, key,
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout), false,
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore), false,
ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, protocols, startTls);
}
private static SSLContext newSSLContext(Provider sslContextProvider, X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
long sessionCacheSize, long sessionTimeout)
long sessionCacheSize, long sessionTimeout, String keyStore)
throws SSLException {
if (key == null && keyManagerFactory == null) {
throw new NullPointerException("key, keyManagerFactory");
@ -81,10 +83,10 @@ final class JdkSslServerContext extends JdkSslContext {
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
}
if (key != null) {
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory);
keyManagerFactory = buildKeyManagerFactory(keyCertChain, key, keyPassword, keyManagerFactory, null);
}
// Initialize the SSLContext to work with our key managers.

View File

@ -46,7 +46,8 @@ final class OpenSslClientContext extends OpenSslContext {
String[] protocols,
long sessionCacheSize,
long sessionTimeout,
boolean enableOcsp)
boolean enableOcsp,
String keyStore)
throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain,
ClientAuth.NONE, protocols, false, enableOcsp);
@ -54,7 +55,7 @@ final class OpenSslClientContext extends OpenSslContext {
try {
OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword);
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
keyCertChain, key, keyPassword, keyManagerFactory, keyStore);
success = true;
} finally {
if (!success) {

View File

@ -48,7 +48,8 @@ final class OpenSslServerContext extends OpenSslContext {
ClientAuth clientAuth,
String[] protocols,
boolean startTls,
boolean enableOcsp)
boolean enableOcsp,
String keyStore)
throws SSLException {
super(ciphers, cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER,
keyCertChain, clientAuth, protocols, startTls, enableOcsp);
@ -57,7 +58,7 @@ final class OpenSslServerContext extends OpenSslContext {
try {
OpenSslKeyMaterialProvider.validateKeyMaterialSupported(keyCertChain, key, keyPassword);
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
keyCertChain, key, keyPassword, keyManagerFactory, keyStore);
success = true;
} finally {
if (!success) {

View File

@ -53,6 +53,8 @@ import java.util.Map;
* Special {@link KeyManagerFactory} that pre-compute the keymaterial used when {@link SslProvider#OPENSSL} or
* {@link SslProvider#OPENSSL_REFCNT} is used and so will improve handshake times and its performance.
*
*
*
* Because the keymaterial is pre-computed any modification to the {@link KeyStore} is ignored after
* {@link #init(KeyStore, char[])} is called.
*

View File

@ -62,13 +62,13 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
String[] protocols, long sessionCacheSize, long sessionTimeout,
boolean enableOcsp) throws SSLException {
boolean enableOcsp, String keyStore) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain,
ClientAuth.NONE, protocols, false, enableOcsp, true);
boolean success = false;
try {
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
keyCertChain, key, keyPassword, keyManagerFactory, keyStore);
success = true;
} finally {
if (!success) {
@ -86,8 +86,9 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
OpenSslEngineMap engineMap,
X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory) throws SSLException {
X509Certificate[] keyCertChain, PrivateKey key,
String keyPassword, KeyManagerFactory keyManagerFactory,
String keyStore) throws SSLException {
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");
@ -107,7 +108,7 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
// javadocs state that keyManagerFactory has precedent over keyCertChain
if (keyManagerFactory == null && keyCertChain != null) {
char[] keyPasswordChars = keyStorePassword(keyPassword);
KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars);
KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore);
if (ks.aliases().hasMoreElements()) {
keyManagerFactory = new OpenSslX509KeyManagerFactory();
} else {
@ -140,7 +141,7 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
} else if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());

View File

@ -54,25 +54,25 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
boolean enableOcsp) throws SSLException {
boolean enableOcsp, String keyStore) throws SSLException {
this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
cipherFilter, toNegotiator(apn), sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
enableOcsp);
enableOcsp, keyStore);
}
private ReferenceCountedOpenSslServerContext(
ReferenceCountedOpenSslServerContext(
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
boolean enableOcsp) throws SSLException {
boolean enableOcsp, String keyStore) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain,
clientAuth, protocols, startTls, enableOcsp, true);
// Create a new SSL_CTX and configure it.
boolean success = false;
try {
sessionContext = newSessionContext(this, ctx, engineMap, trustCertCollection, trustManagerFactory,
keyCertChain, key, keyPassword, keyManagerFactory);
keyCertChain, key, keyPassword, keyManagerFactory, keyStore);
success = true;
} finally {
if (!success) {
@ -91,7 +91,8 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
X509Certificate[] trustCertCollection,
TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key,
String keyPassword, KeyManagerFactory keyManagerFactory)
String keyPassword, KeyManagerFactory keyManagerFactory,
String keyStore)
throws SSLException {
OpenSslKeyMaterialProvider keyMaterialProvider = null;
try {
@ -110,7 +111,7 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
// keyManagerFactory for the server so build one if it is not specified.
if (keyManagerFactory == null) {
char[] keyPasswordChars = keyStorePassword(keyPassword);
KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars);
KeyStore ks = buildKeyStore(keyCertChain, key, keyPasswordChars, keyStore);
if (ks.aliases().hasMoreElements()) {
keyManagerFactory = new OpenSslX509KeyManagerFactory();
} else {
@ -129,7 +130,7 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
}
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
} else if (trustManagerFactory == null) {
// Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
trustManagerFactory = TrustManagerFactory.getInstance(

View File

@ -339,7 +339,7 @@ public abstract class SslContext {
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(provider, null, null, certChainFile, keyFile, keyPassword, null,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
}
/**
@ -382,12 +382,57 @@ public abstract class SslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(provider, trustCertCollectionFile, trustManagerFactory, keyCertChainFile,
keyFile, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
sessionCacheSize, sessionTimeout, KeyStore.getDefaultType());
}
/**
* Creates a new server-side {@link SslContext}.
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param trustCertCollectionFile an X.509 certificate collection file in PEM format.
* This provides the certificate collection used for mutual authentication.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from clients.
* {@code null} to use the default or the results of parsing
* {@code trustCertCollectionFile}.
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
* @param keyCertChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
* that is used to encrypt data being sent to clients.
* {@code null} to use the default or the results of parsing
* {@code keyCertChainFile} and {@code keyFile}.
* This parameter is ignored if {@code provider} is not {@link SslProvider#JDK}.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param cipherFilter a filter to apply over the supplied list of ciphers
* Only required if {@code provider} is {@link SslProvider#JDK}
* @param apn Provides a means to configure parameters related to application protocol negotiation.
* @param sessionCacheSize the size of the cache used for storing SSL session objects.
* {@code 0} to use the default value.
* @param sessionTimeout the timeout for the cached SSL session objects, in seconds.
* {@code 0} to use the default value.
* @param keyStore the keystore type that should be used
* @return a new server-side {@link SslContext}
*/
static SslContext newServerContext(
SslProvider provider,
File trustCertCollectionFile, TrustManagerFactory trustManagerFactory,
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout, String keyStore) throws SSLException {
try {
return newServerContextInternal(provider, null, toX509Certificates(trustCertCollectionFile),
trustManagerFactory, toX509Certificates(keyCertChainFile),
toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
sessionCacheSize, sessionTimeout, ClientAuth.NONE, null, false, false);
sessionCacheSize, sessionTimeout, ClientAuth.NONE, null,
false, false, keyStore);
} catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
@ -403,7 +448,7 @@ public abstract class SslContext {
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
boolean enableOcsp) throws SSLException {
boolean enableOcsp, String keyStoreType) throws SSLException {
if (provider == null) {
provider = defaultServerProvider();
@ -417,19 +462,19 @@ public abstract class SslContext {
return new JdkSslServerContext(sslContextProvider,
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls);
clientAuth, protocols, startTls, keyStoreType);
case OPENSSL:
verifyNullSslContextProvider(provider, sslContextProvider);
return new OpenSslServerContext(
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls, enableOcsp);
clientAuth, protocols, startTls, enableOcsp, keyStoreType);
case OPENSSL_REFCNT:
verifyNullSslContextProvider(provider, sslContextProvider);
return new ReferenceCountedOpenSslServerContext(
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls, enableOcsp);
clientAuth, protocols, startTls, enableOcsp, keyStoreType);
default:
throw new Error(provider.toString());
}
@ -745,7 +790,8 @@ public abstract class SslContext {
toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter,
apn, null, sessionCacheSize, sessionTimeout, false);
apn, null, sessionCacheSize, sessionTimeout, false,
KeyStore.getDefaultType());
} catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
@ -760,7 +806,7 @@ public abstract class SslContext {
X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
long sessionCacheSize, long sessionTimeout, boolean enableOcsp) throws SSLException {
long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStoreType) throws SSLException {
if (provider == null) {
provider = defaultClientProvider();
}
@ -771,19 +817,20 @@ public abstract class SslContext {
}
return new JdkSslClientContext(sslContextProvider,
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout);
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize,
sessionTimeout, keyStoreType);
case OPENSSL:
verifyNullSslContextProvider(provider, sslContextProvider);
return new OpenSslClientContext(
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
enableOcsp);
enableOcsp, keyStoreType);
case OPENSSL_REFCNT:
verifyNullSslContextProvider(provider, sslContextProvider);
return new ReferenceCountedOpenSslClientContext(
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
enableOcsp);
enableOcsp, keyStoreType);
default:
throw new Error(provider.toString());
}
@ -1026,16 +1073,21 @@ public abstract class SslContext {
/**
* Generates a new {@link KeyStore}.
*
* @param certChain a X.509 certificate chain
* @param certChain an X.509 certificate chain
* @param key a PKCS#8 private key
* @param keyPasswordChars the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* @param keyStoreType The KeyStore Type you want to use
* @return generated {@link KeyStore}.
*/
static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, char[] keyPasswordChars)
static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key,
char[] keyPasswordChars, String keyStoreType)
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
if (keyStoreType == null) {
keyStoreType = KeyStore.getDefaultType();
}
KeyStore ks = KeyStore.getInstance(keyStoreType);
ks.load(null, null);
ks.setKeyEntry(ALIAS, key, keyPasswordChars, certChain);
return ks;
@ -1095,9 +1147,22 @@ public abstract class SslContext {
protected static TrustManagerFactory buildTrustManagerFactory(
File certChainFile, TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
return buildTrustManagerFactory(certChainFile, trustManagerFactory, KeyStore.getDefaultType());
}
/**
* Build a {@link TrustManagerFactory} from a certificate chain file.
* @param certChainFile The certificate file to build from.
* @param trustManagerFactory The existing {@link TrustManagerFactory} that will be used if not {@code null}.
* @param keyType The KeyStore Type you want to use
* @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
*/
static TrustManagerFactory buildTrustManagerFactory(
File certChainFile, TrustManagerFactory trustManagerFactory, String keyType)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
X509Certificate[] x509Certs = toX509Certificates(certChainFile);
return buildTrustManagerFactory(x509Certs, trustManagerFactory);
return buildTrustManagerFactory(x509Certs, trustManagerFactory, keyType);
}
static X509Certificate[] toX509Certificates(File file) throws CertificateException {
@ -1141,9 +1206,12 @@ public abstract class SslContext {
}
static TrustManagerFactory buildTrustManagerFactory(
X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory)
X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory, String keyStoreType)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
if (keyStoreType == null) {
keyStoreType = KeyStore.getDefaultType();
}
final KeyStore ks = KeyStore.getInstance(keyStoreType);
ks.load(null, null);
int i = 1;
@ -1179,10 +1247,22 @@ public abstract class SslContext {
}
static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChain, PrivateKey key, String keyPassword,
KeyManagerFactory kmf)
KeyManagerFactory kmf, String keyStoreType)
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
return buildKeyManagerFactory(certChain, KeyManagerFactory.getDefaultAlgorithm(), key, keyPassword, kmf);
return buildKeyManagerFactory(certChain, KeyManagerFactory.getDefaultAlgorithm(), key,
keyPassword, kmf, keyStoreType);
}
static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile,
String keyAlgorithm, PrivateKey key,
String keyPassword, KeyManagerFactory kmf,
String keyStore)
throws KeyStoreException, NoSuchAlgorithmException, IOException,
CertificateException, UnrecoverableKeyException {
char[] keyPasswordChars = keyStorePassword(keyPassword);
KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars, keyStore);
return buildKeyManagerFactory(ks, keyAlgorithm, keyPasswordChars, kmf);
}
static KeyManagerFactory buildKeyManagerFactory(X509Certificate[] certChainFile,
@ -1191,7 +1271,7 @@ public abstract class SslContext {
throws KeyStoreException, NoSuchAlgorithmException, IOException,
CertificateException, UnrecoverableKeyException {
char[] keyPasswordChars = keyStorePassword(keyPassword);
KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars);
KeyStore ks = buildKeyStore(certChainFile, key, keyPasswordChars, KeyStore.getDefaultType());
return buildKeyManagerFactory(ks, keyAlgorithm, keyPasswordChars, kmf);
}

View File

@ -20,6 +20,7 @@ import static java.util.Objects.requireNonNull;
import io.netty.util.internal.UnstableApi;
import java.security.KeyStore;
import java.security.Provider;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
@ -149,6 +150,7 @@ public final class SslContextBuilder {
private String[] protocols;
private boolean startTls;
private boolean enableOcsp;
private String keyStoreType = KeyStore.getDefaultType();
private SslContextBuilder(boolean forServer) {
this.forServer = forServer;
@ -162,6 +164,14 @@ public final class SslContextBuilder {
return this;
}
/**
* Sets the {@link KeyStore} type that should be used. {@code null} uses the default one.
*/
public SslContextBuilder keyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
return this;
}
/**
* The SSLContext {@link Provider} to use. {@code null} uses the default one. This is only
* used with {@link SslProvider#JDK}.
@ -447,11 +457,11 @@ public final class SslContextBuilder {
return SslContext.newServerContextInternal(provider, sslContextProvider, trustCertCollection,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
enableOcsp);
enableOcsp, keyStoreType);
} else {
return SslContext.newClientContextInternal(provider, sslContextProvider, trustCertCollection,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp);
ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp, keyStoreType);
}
}
}

View File

@ -48,7 +48,7 @@ import java.util.Date;
* It is purely for testing purposes, and thus it is very insecure.
* It even uses an insecure pseudo-random generator for faster generation internally.
* </p><p>
* A X.509 certificate file and a RSA private key file are generated in a system's temporary directory using
* An X.509 certificate file and a RSA private key file are generated in a system's temporary directory using
* {@link java.io.File#createTempFile(String, String)}, and they are deleted when the JVM exits using
* {@link java.io.File#deleteOnExit()}.
* </p><p>

View File

@ -19,7 +19,6 @@ import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectorFactory;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import java.security.Provider;
@ -34,7 +33,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;

View File

@ -2915,7 +2915,7 @@ public abstract class SSLEngineTest {
SelfSignedCertificate ssc = new SelfSignedCertificate();
KeyManagerFactory kmf = useKeyManagerFactory ?
SslContext.buildKeyManagerFactory(
new java.security.cert.X509Certificate[] { ssc.cert()}, ssc.key(), null, null) : null;
new java.security.cert.X509Certificate[] { ssc.cert()}, ssc.key(), null, null, null) : null;
SslContextBuilder clientContextBuilder = SslContextBuilder.forClient();
if (mutualAuth) {
@ -3261,7 +3261,7 @@ public abstract class SSLEngineTest {
throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
return SslContext.buildKeyManagerFactory(
new java.security.cert.X509Certificate[] { ssc.cert() }, ssc.key(), null, null);
new java.security.cert.X509Certificate[] { ssc.cert() }, ssc.key(), null, null, null);
}
private final class TestTrustManagerFactory extends X509ExtendedTrustManager {

View File

@ -264,7 +264,7 @@ final class SniClientJava8TestUtil {
IOException, CertificateException {
return new SniX509KeyManagerFactory(
new SNIHostName(hostname), SslContext.buildKeyManagerFactory(
new X509Certificate[] { cert.cert() }, cert.key(), null, null));
new X509Certificate[] { cert.cert() }, cert.key(), null, null, null));
}
private static final class SniX509KeyManagerFactory extends KeyManagerFactory {

View File

@ -107,8 +107,7 @@ public class SniClientTest {
} else {
// The used OpenSSL version does support a KeyManagerFactory, so use it.
KeyManagerFactory kmf = SniClientJava8TestUtil.newSniX509KeyManagerFactory(cert, sniHostName);
sslServerContext = SslContextBuilder.forServer(kmf)
sslServerContext = SslContextBuilder.forServer(kmf)
.sslProvider(sslServerProvider)
.build();
}

View File

@ -52,6 +52,17 @@ public class SslContextBuilderTest {
testClientContext(SslProvider.OPENSSL);
}
@Test
public void testKeyStoreTypeJdk() throws Exception {
testKeyStoreType(SslProvider.JDK);
}
@Test
public void testKeyStoreTypeOpenssl() throws Exception {
Assume.assumeTrue(OpenSsl.isAvailable());
testKeyStoreType(SslProvider.OPENSSL);
}
@Test
public void testServerContextFromFileJdk() throws Exception {
testServerContextFromFile(SslProvider.JDK);
@ -141,6 +152,17 @@ public class SslContextBuilderTest {
}
}
private static void testKeyStoreType(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey())
.sslProvider(provider)
.keyStoreType("PKCS12");
SslContext context = builder.build();
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
engine.closeInbound();
engine.closeOutbound();
}
private static void testInvalidCipher(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forClient()

View File

@ -110,7 +110,7 @@ public class SslContextTrustManagerTest {
throws Exception {
X509Certificate[] certCollection = loadCertCollection(resourceNames);
TrustManagerFactory tmf = SslContext.buildTrustManagerFactory(
certCollection, null);
certCollection, null, null);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {