/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
import io.netty.handler.ssl.ApplicationProtocolConfig.SelectorFailureBehavior;
import io.netty.util.internal.EmptyArrays;
import java.security.Provider;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
/**
* A secure socket protocol implementation which acts as a factory for {@link SSLEngine} and {@link SslHandler}.
* Internally, it is implemented via JDK's {@link SSLContext} or OpenSSL's {@code SSL_CTX}.
*
* <h3>Making your server support SSL/TLS</h3>
* <pre>
* // In your {@link ChannelInitializer}:
* {@link ChannelPipeline} p = channel.pipeline();
* {@link SslContext} sslCtx = {@link SslContextBuilder#forServer(File, File) SslContextBuilder.forServer(...)}.build();
* p.addLast("ssl", {@link #newHandler(ByteBufAllocator) sslCtx.newHandler(channel.alloc())});
* ...
* </pre>
*
* <h3>Making your client support SSL/TLS</h3>
* <pre>
* // In your {@link ChannelInitializer}:
* {@link ChannelPipeline} p = channel.pipeline();
* {@link SslContext} sslCtx = {@link SslContextBuilder#forClient() SslContextBuilder.forClient()}.build();
* p.addLast("ssl", {@link #newHandler(ByteBufAllocator, String, int) sslCtx.newHandler(channel.alloc(), host, port)});
* ...
* </pre>
*/
public abstract class SslContext {
static final CertificateFactory X509_CERT_FACTORY;
static {
try {
X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new IllegalStateException("unable to instance X.509 CertificateFactory", e);
}
}
private final boolean startTls;
/**
* Returns the default server-side implementation provider currently in use.
*
* @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
*/
public static SslProvider defaultServerProvider() {
return defaultProvider();
}
/**
* Returns the default client-side implementation provider currently in use.
*
* @return {@link SslProvider#OPENSSL} if OpenSSL is available. {@link SslProvider#JDK} otherwise.
*/
public static SslProvider defaultClientProvider() {
return defaultProvider();
}
private static SslProvider defaultProvider() {
if (OpenSsl.isAvailable()) {
return SslProvider.OPENSSL;
} else {
return SslProvider.JDK;
}
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(File certChainFile, File keyFile) throws SSLException {
return newServerContext(certChainFile, keyFile, null);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(null, certChainFile, keyFile, keyPassword);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @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 ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(
null, certChainFile, keyFile, keyPassword,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @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 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
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(
null, certChainFile, keyFile, keyPassword,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format
* @param keyFile a PKCS#8 private key file in PEM format
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile) throws SSLException {
return newServerContext(provider, certChainFile, keyFile, null);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
SslProvider provider, File certChainFile, File keyFile, String keyPassword) throws SSLException {
return newServerContext(provider, certChainFile, keyFile, keyPassword, null, IdentityCipherSuiteFilter.INSTANCE,
null, 0, 0);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @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 ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
SslProvider provider,
File certChainFile, File keyFile, String keyPassword,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(provider, certChainFile, keyFile, keyPassword,
ciphers, IdentityCipherSuiteFilter.INSTANCE,
toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @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 trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(
SslProvider provider,
File certChainFile, File keyFile, String keyPassword, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newServerContext(
provider, null, trustManagerFactory, certChainFile, keyFile, keyPassword,
null, ciphers, IdentityCipherSuiteFilter.INSTANCE,
toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
}
/**
* Creates a new server-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @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 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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newServerContext(SslProvider provider,
File certChainFile, File keyFile, String keyPassword,
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);
}
/**
* 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.
* @return a new server-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public 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) 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);
} catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
}
throw new SSLException("failed to initialize the server-side SSL context", e);
}
}
static SslContext newServerContextInternal(
SslProvider provider,
Provider sslContextProvider,
X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
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 {
if (provider == null) {
provider = defaultServerProvider();
}
switch (provider) {
case JDK:
if (enableOcsp) {
throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
}
return new JdkSslServerContext(sslContextProvider,
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls);
case OPENSSL:
verifyNullSslContextProvider(provider, sslContextProvider);
return new OpenSslServerContext(
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls, enableOcsp);
case OPENSSL_REFCNT:
verifyNullSslContextProvider(provider, sslContextProvider);
return new ReferenceCountedOpenSslServerContext(
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth, protocols, startTls, enableOcsp);
default:
throw new Error(provider.toString());
}
}
private static void verifyNullSslContextProvider(SslProvider provider, Provider sslContextProvider) {
if (sslContextProvider != null) {
throw new IllegalArgumentException("Java Security Provider unsupported for SslProvider: " + provider);
}
}
/**
* Creates a new client-side {@link SslContext}.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext() throws SSLException {
return newClientContext(null, null, null);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(File certChainFile) throws SSLException {
return newClientContext(null, certChainFile);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, null, trustManagerFactory);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(null, certChainFile, trustManagerFactory);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @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.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
null, certChainFile, trustManagerFactory,
ciphers, nextProtocols, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @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
* @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.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
null, certChainFile, trustManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(SslProvider provider) throws SSLException {
return newClientContext(provider, null, null);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(SslProvider provider, File certChainFile) throws SSLException {
return newClientContext(provider, certChainFile, null);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
SslProvider provider, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, null, trustManagerFactory);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
SslProvider provider, File certChainFile, TrustManagerFactory trustManagerFactory) throws SSLException {
return newClientContext(provider, certChainFile, trustManagerFactory, null, IdentityCipherSuiteFilter.INSTANCE,
null, 0, 0);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @param ciphers the cipher suites to enable, in the order of preference.
* {@code null} to use the default cipher suites.
* @param nextProtocols the application layer protocols to accept, in the order of preference.
* {@code null} to disable TLS NPN/ALPN extension.
* @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.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
SslProvider provider,
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, Iterable<String> nextProtocols,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
provider, certChainFile, trustManagerFactory, null, null, null, null,
ciphers, IdentityCipherSuiteFilter.INSTANCE,
toApplicationProtocolConfig(nextProtocols), sessionCacheSize, sessionTimeout);
}
/**
* Creates a new client-side {@link SslContext}.
*
* @param provider the {@link SslContext} implementation to use.
* {@code null} to use the current default one.
* @param certChainFile an X.509 certificate chain file in PEM format.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@code null} to use the default.
* @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
* @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.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
SslProvider provider,
File certChainFile, TrustManagerFactory trustManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException {
return newClientContext(
provider, certChainFile, trustManagerFactory, null, null, null, null,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout);
}
/**
* Creates a new client-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.
* {@code null} to use the system default
* @param trustManagerFactory the {@link TrustManagerFactory} that provides the {@link TrustManager}s
* that verifies the certificates sent from servers.
* {@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.
* This provides the public key for mutual authentication.
* {@code null} to use the system default
* @param keyFile a PKCS#8 private key file in PEM format.
* This provides the private key for mutual authentication.
* {@code null} for no mutual authentication.
* @param keyPassword the password of the {@code keyFile}.
* {@code null} if it's not password-protected.
* Ignored if {@code keyFile} is {@code null}.
* @param keyManagerFactory the {@link KeyManagerFactory} that provides the {@link KeyManager}s
* that is used to encrypt data being sent to servers.
* {@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
* @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.
*
* @return a new client-side {@link SslContext}
* @deprecated Replaced by {@link SslContextBuilder}
*/
@Deprecated
public static SslContext newClientContext(
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) throws SSLException {
try {
return newClientContextInternal(provider, null,
toX509Certificates(trustCertCollectionFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter,
apn, null, sessionCacheSize, sessionTimeout, false);
} catch (Exception e) {
if (e instanceof SSLException) {
throw (SSLException) e;
}
throw new SSLException("failed to initialize the client-side SSL context", e);
}
}
static SslContext newClientContextInternal(
SslProvider provider,
Provider sslContextProvider,
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 {
if (provider == null) {
provider = defaultClientProvider();
}
switch (provider) {
case JDK:
if (enableOcsp) {
throw new IllegalArgumentException("OCSP is not supported with this SslProvider: " + provider);
}
return new JdkSslClientContext(sslContextProvider,
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout);
case OPENSSL:
verifyNullSslContextProvider(provider, sslContextProvider);
return new OpenSslClientContext(
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
enableOcsp);
case OPENSSL_REFCNT:
verifyNullSslContextProvider(provider, sslContextProvider);
return new ReferenceCountedOpenSslClientContext(
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout,
enableOcsp);
default:
throw new Error(provider.toString());
}
}
static ApplicationProtocolConfig toApplicationProtocolConfig(Iterable<String> nextProtocols) {
ApplicationProtocolConfig apn;
if (nextProtocols == null) {
apn = ApplicationProtocolConfig.DISABLED;
} else {
apn = new ApplicationProtocolConfig(
Protocol.NPN_AND_ALPN, SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL,
SelectedListenerFailureBehavior.ACCEPT, nextProtocols);
}
return apn;
}
/**
* Creates a new instance (startTls set to {@code false}).
*/
protected SslContext() {
this(false);
}
/**
* Creates a new instance.
*/
protected SslContext(boolean startTls) {
this.startTls = startTls;
}
/**
* Returns {@code true} if and only if this context is for server-side.
*/
public final boolean isServer() {
return !isClient();
}
/**
* Returns the {@code true} if and only if this context is for client-side.
*/
public abstract boolean isClient();
/**
* Returns the list of enabled cipher suites, in the order of preference.
*/
public abstract List<String> cipherSuites();
/**
* Returns the size of the cache used for storing SSL session objects.
*/
public abstract long sessionCacheSize();
/**
* Returns the timeout for the cached SSL session objects, in seconds.
*/
public abstract long sessionTimeout();
/**
* @deprecated Use {@link #applicationProtocolNegotiator()} instead.
*/
@Deprecated
public final List<String> nextProtocols() {
return applicationProtocolNegotiator().protocols();
}
/**
* Returns the object responsible for negotiating application layer protocols for the TLS NPN/ALPN extensions.
*/
public abstract ApplicationProtocolNegotiator applicationProtocolNegotiator();
/**
* Creates a new {@link SSLEngine}.
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
* wrap in a {@link SslHandler} and insert it into a pipeline. See {@link #newHandler(ByteBufAllocator)}.
* @return a new {@link SSLEngine}
*/
public abstract SSLEngine newEngine(ByteBufAllocator alloc);
/**
* Creates a new {@link SSLEngine} using advisory peer information.
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the object must be released. One way to do this is to
* wrap in a {@link SslHandler} and insert it into a pipeline.
* See {@link #newHandler(ByteBufAllocator, String, int)}.
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
*
* @return a new {@link SSLEngine}
*/
public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort);
/**
* Returns the {@link SSLSessionContext} object held by this context.
*/
public abstract SSLSessionContext sessionContext();
/**
* Creates a new {@link SslHandler}.
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
* memory!</p>
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):</p>
* <pre>
* SSLEngine sslEngine = sslHandler.engine();
* SSLParameters sslParameters = sslEngine.getSSLParameters();
* // only available since Java 7
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
* sslEngine.setSSLParameters(sslParameters);
* </pre>
*
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
*
* @return a new {@link SslHandler}
*/
public final SslHandler newHandler(ByteBufAllocator alloc) {
return new SslHandler(newEngine(alloc), startTls);
}
/**
* Creates a new {@link SslHandler} with advisory peer information.
* <p>If {@link SslProvider#OPENSSL_REFCNT} is used then the returned {@link SslHandler} will release the engine
* that is wrapped. If the returned {@link SslHandler} is not inserted into a pipeline then you may leak native
* memory!</p>
* <p><b>Beware</b>: the underlying generated {@link SSLEngine} won't have
* <a href="https://wiki.openssl.org/index.php/Hostname_validation">hostname verification</a> enabled by default.
* If you create {@link SslHandler} for the client side and want proper security, we advice that you configure
* the {@link SSLEngine} (see {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}):</p>
* <pre>
* SSLEngine sslEngine = sslHandler.engine();
* SSLParameters sslParameters = sslEngine.getSSLParameters();
* // only available since Java 7
* sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
* sslEngine.setSSLParameters(sslParameters);
* </pre>
*
* @param alloc If supported by the SSLEngine then the SSLEngine will use this to allocate ByteBuf objects.
* @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port
*
* @return a new {@link SslHandler}
*/
public final SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort) {
return new SslHandler(newEngine(alloc, peerHost, peerPort), startTls);
}
/**
* Generates a key specification for an (encrypted) private key.
*
* @param password characters, if {@code null} an unencrypted key is assumed
* @param key bytes of the DER encoded private key
*
* @return a key specification
*
* @throws IOException if parsing {@code key} fails
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unknown
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unknown
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt
* {@code key}
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
*/
protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidKeyException, InvalidAlgorithmParameterException {
if (password == null) {
return new PKCS8EncodedKeySpec(key);
}
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
return encryptedPrivateKeyInfo.getKeySpec(cipher);
}
/**
* Generates a new {@link KeyStore}.
*
* @param certChain a 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.
* @return generated {@link KeyStore}.
*/
static KeyStore buildKeyStore(X509Certificate[] certChain, PrivateKey key, char[] keyPasswordChars)
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
ks.setKeyEntry("key", key, keyPasswordChars, certChain);
return ks;
}
static PrivateKey toPrivateKey(File keyFile, String keyPassword) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException,
InvalidAlgorithmParameterException,
KeyException, IOException {
if (keyFile == null) {
return null;
}
return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyFile), keyPassword);
}
static PrivateKey toPrivateKey(InputStream keyInputStream, String keyPassword) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeySpecException,
InvalidAlgorithmParameterException,
KeyException, IOException {
if (keyInputStream == null) {
return null;
}
return getPrivateKeyFromByteBuffer(PemReader.readPrivateKey(keyInputStream), keyPassword);
}
private static PrivateKey getPrivateKeyFromByteBuffer(ByteBuf encodedKeyBuf, String keyPassword)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidAlgorithmParameterException, KeyException, IOException {
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
encodedKeyBuf.readBytes(encodedKey).release();
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(
keyPassword == null ? null : keyPassword.toCharArray(), encodedKey);
try {
return KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore) {
try {
return KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException ignore2) {
try {
return KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
} catch (InvalidKeySpecException e) {
throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
}
}
}
}
/**
* 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}.
* @return A {@link TrustManagerFactory} which contains the certificates in {@code certChainFile}
*/
@Deprecated
protected static TrustManagerFactory buildTrustManagerFactory(
File certChainFile, TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
X509Certificate[] x509Certs = toX509Certificates(certChainFile);
return buildTrustManagerFactory(x509Certs, trustManagerFactory);
}
static X509Certificate[] toX509Certificates(File file) throws CertificateException {
if (file == null) {
return null;
}
return getCertificatesFromBuffers(PemReader.readCertificates(file));
}
static X509Certificate[] toX509Certificates(InputStream in) throws CertificateException {
if (in == null) {
return null;
}
return getCertificatesFromBuffers(PemReader.readCertificates(in));
}
private static X509Certificate[] getCertificatesFromBuffers(ByteBuf[] certs) throws CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate[] x509Certs = new X509Certificate[certs.length];
int i = 0;
try {
for (; i < certs.length; i++) {
InputStream is = new ByteBufInputStream(certs[i], true);
try {
x509Certs[i] = (X509Certificate) cf.generateCertificate(is);
} finally {
try {
is.close();
} catch (IOException e) {
// This is not expected to happen, but re-throw in case it does.
throw new RuntimeException(e);
}
}
}
} finally {
for (; i < certs.length; i++) {
certs[i].release();
}
}
return x509Certs;
}
static TrustManagerFactory buildTrustManagerFactory(
X509Certificate[] certCollection, TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
int i = 1;
for (X509Certificate cert: certCollection) {
String alias = Integer.toString(i);
ks.setCertificateEntry(alias, cert);
i++;
}
// Set up trust manager factory to use our key store.
if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
}
trustManagerFactory.init(ks);
return trustManagerFactory;
}
static PrivateKey toPrivateKeyInternal(File keyFile, String keyPassword) throws SSLException {
try {
return toPrivateKey(keyFile, keyPassword);
} catch (Exception e) {
throw new SSLException(e);
}
}
static X509Certificate[] toX509CertificatesInternal(File file) throws SSLException {
try {
return toX509Certificates(file);
} catch (CertificateException 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;
}
}