diff --git a/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java b/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java index e28578dac1..b94646cb54 100644 --- a/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java +++ b/example/src/main/java/io/netty/example/ocsp/OcspServerExample.java @@ -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 { diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java index c4cb8e5886..e75595ac17 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java @@ -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); diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java index 0fb02b8f0f..0c182d351b 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java @@ -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()); } } diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java index aaa452f2be..4b5ebfc6af 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java @@ -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. diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java index d0cfa1dae8..6c12f8ea45 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslClientContext.java @@ -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) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java index be1f94d643..ffa49d2459 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslServerContext.java @@ -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) { diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java index a8a58bc6c2..3d5505f2d4 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslX509KeyManagerFactory.java @@ -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. * diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java index 0aff7e7ca0..3301d12e91 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslClientContext.java @@ -62,13 +62,13 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted KeyManagerFactory keyManagerFactory, Iterable 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()); diff --git a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java index c0d91463f4..81334b081d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/ReferenceCountedOpenSslServerContext.java @@ -54,25 +54,25 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, Iterable 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 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( diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContext.java b/handler/src/main/java/io/netty/handler/ssl/SslContext.java index dab0ce745d..8c51d90a2d 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContext.java @@ -339,7 +339,7 @@ public abstract class SslContext { Iterable 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 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 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 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 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); } diff --git a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java index 7f2aa15e63..85818f94d2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java @@ -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); } } } diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java index 259bd6bc20..3bf4285a64 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SelfSignedCertificate.java @@ -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. *

- * 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()}. *

diff --git a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java index bc8786534e..db63b30b29 100644 --- a/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/JdkSslEngineTest.java @@ -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; diff --git a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java index 6e25e8e513..280d0acd2e 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SSLEngineTest.java @@ -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 { diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java index 50bdeac8ed..5c0f797e81 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientJava8TestUtil.java @@ -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 { diff --git a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java index d01dd65e3a..33f3092aa0 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SniClientTest.java @@ -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(); } diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java index f573855472..ee53f97ed2 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java @@ -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() diff --git a/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java b/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java index 97b90f2e90..04b6a8e08b 100644 --- a/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java +++ b/handler/src/test/java/io/netty/handler/ssl/SslContextTrustManagerTest.java @@ -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) {