blob: 44aa635b30a9a8d2bb796ed4f01c645941036bab [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.conscrypt;
import java.security.AlgorithmConstraints;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import javax.crypto.SecretKey;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIMatcher;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
/**
* The instances of this class encapsulate all the info
* about enabled cipher suites and protocols,
* as well as the information about client/server mode of
* ssl socket, whether it require/want client authentication or not,
* and controls whether new SSL sessions may be established by this
* socket or not.
*/
final class SSLParametersImpl implements Cloneable {
// default source of X.509 certificate based authentication keys
private static volatile X509KeyManager defaultX509KeyManager;
// default source of X.509 certificate based authentication trust decisions
private static volatile X509TrustManager defaultX509TrustManager;
// default SSL parameters
private static volatile SSLParametersImpl defaultParameters;
// client session context contains the set of reusable
// client-side SSL sessions
private final ClientSessionContext clientSessionContext;
// server session context contains the set of reusable
// server-side SSL sessions
private final ServerSessionContext serverSessionContext;
// source of X.509 certificate based authentication keys or null if not provided
private final X509KeyManager x509KeyManager;
// source of Pre-Shared Key (PSK) authentication keys or null if not provided.
@SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
private final PSKKeyManager pskKeyManager;
// source of X.509 certificate based authentication trust decisions or null if not provided
private final X509TrustManager x509TrustManager;
// protocols enabled for SSL connection
String[] enabledProtocols;
// set to indicate when obsolete protocols are filtered
boolean isEnabledProtocolsFiltered;
// The TLS 1.0-1.2 cipher suites enabled for the SSL connection. TLS 1.3 cipher suites
// cannot be customized, so for simplicity this field never contains any TLS 1.3 suites.
String[] enabledCipherSuites;
// if the peer with this parameters tuned to work in client mode
private boolean client_mode = true;
// if the peer with this parameters tuned to require client authentication
private boolean need_client_auth = false;
// if the peer with this parameters tuned to request client authentication
private boolean want_client_auth = false;
// if the peer with this parameters allowed to cteate new SSL session
private boolean enable_session_creation = true;
// Endpoint identification algorithm (e.g., HTTPS)
private String endpointIdentificationAlgorithm;
// Whether to use the local cipher suites order
private boolean useCipherSuitesOrder;
private Collection<SNIMatcher> sniMatchers;
private AlgorithmConstraints algorithmConstraints;
// client-side only, bypasses the property based configuration, used for tests
private boolean ctVerificationEnabled;
// server-side only. SCT and OCSP data to send to clients which request it
byte[] sctExtension;
byte[] ocspResponse;
byte[] applicationProtocols = EmptyArray.BYTE;
ApplicationProtocolSelectorAdapter applicationProtocolSelector;
boolean useSessionTickets;
private Boolean useSni;
/**
* Whether the TLS Channel ID extension is enabled. This field is
* server-side only.
*/
boolean channelIdEnabled;
/**
* Initializes the parameters. Naturally this constructor is used
* in SSLContextImpl.engineInit method which directly passes its
* parameters. In other words this constructor holds all
* the functionality provided by SSLContext.init method.
* See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
* SecureRandom)} for more information
*/
SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
SecureRandom sr, ClientSessionContext clientSessionContext,
ServerSessionContext serverSessionContext, String[] protocols)
throws KeyManagementException {
this.serverSessionContext = serverSessionContext;
this.clientSessionContext = clientSessionContext;
// initialize key managers
if (kms == null) {
x509KeyManager = getDefaultX509KeyManager();
// There's no default PSK key manager
pskKeyManager = null;
} else {
x509KeyManager = findFirstX509KeyManager(kms);
pskKeyManager = findFirstPSKKeyManager(kms);
}
// initialize x509TrustManager
if (tms == null) {
x509TrustManager = getDefaultX509TrustManager();
} else {
x509TrustManager = findFirstX509TrustManager(tms);
}
// initialize the list of cipher suites and protocols enabled by default
enabledProtocols = NativeCrypto.checkEnabledProtocols(
protocols == null ? NativeCrypto.DEFAULT_PROTOCOLS : protocols).clone();
boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
boolean pskCipherSuitesNeeded = pskKeyManager != null;
enabledCipherSuites = getDefaultCipherSuites(
x509CipherSuitesNeeded, pskCipherSuitesNeeded);
// We ignore the SecureRandom passed in by the caller. The native code below
// directly accesses /dev/urandom, which makes it irrelevant.
}
// Copy constructor for the purposes of changing the final fields
@SuppressWarnings("deprecation") // for PSKKeyManager
private SSLParametersImpl(ClientSessionContext clientSessionContext,
ServerSessionContext serverSessionContext,
X509KeyManager x509KeyManager,
PSKKeyManager pskKeyManager,
X509TrustManager x509TrustManager,
SSLParametersImpl sslParams) {
this.clientSessionContext = clientSessionContext;
this.serverSessionContext = serverSessionContext;
this.x509KeyManager = x509KeyManager;
this.pskKeyManager = pskKeyManager;
this.x509TrustManager = x509TrustManager;
this.enabledProtocols =
(sslParams.enabledProtocols == null) ? null : sslParams.enabledProtocols.clone();
this.isEnabledProtocolsFiltered = sslParams.isEnabledProtocolsFiltered;
this.enabledCipherSuites =
(sslParams.enabledCipherSuites == null) ? null : sslParams.enabledCipherSuites.clone();
this.client_mode = sslParams.client_mode;
this.need_client_auth = sslParams.need_client_auth;
this.want_client_auth = sslParams.want_client_auth;
this.enable_session_creation = sslParams.enable_session_creation;
this.endpointIdentificationAlgorithm = sslParams.endpointIdentificationAlgorithm;
this.useCipherSuitesOrder = sslParams.useCipherSuitesOrder;
this.ctVerificationEnabled = sslParams.ctVerificationEnabled;
this.sctExtension =
(sslParams.sctExtension == null) ? null : sslParams.sctExtension.clone();
this.ocspResponse =
(sslParams.ocspResponse == null) ? null : sslParams.ocspResponse.clone();
this.applicationProtocols =
(sslParams.applicationProtocols == null) ? null : sslParams.applicationProtocols.clone();
this.applicationProtocolSelector = sslParams.applicationProtocolSelector;
this.useSessionTickets = sslParams.useSessionTickets;
this.useSni = sslParams.useSni;
this.channelIdEnabled = sslParams.channelIdEnabled;
}
static SSLParametersImpl getDefault() throws KeyManagementException {
SSLParametersImpl result = defaultParameters;
if (result == null) {
// single-check idiom
defaultParameters = result = new SSLParametersImpl(null,
null,
null,
new ClientSessionContext(),
new ServerSessionContext(),
null);
}
return (SSLParametersImpl) result.clone();
}
/**
* Returns the appropriate session context.
*/
AbstractSessionContext getSessionContext() {
return client_mode ? clientSessionContext : serverSessionContext;
}
/**
* @return client session context
*/
ClientSessionContext getClientSessionContext() {
return clientSessionContext;
}
/**
* @return X.509 key manager or {@code null} for none.
*/
X509KeyManager getX509KeyManager() {
return x509KeyManager;
}
/**
* @return Pre-Shared Key (PSK) key manager or {@code null} for none.
*/
@SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
PSKKeyManager getPSKKeyManager() {
return pskKeyManager;
}
/**
* @return X.509 trust manager or {@code null} for none.
*/
X509TrustManager getX509TrustManager() {
return x509TrustManager;
}
/**
* @return the names of enabled cipher suites
*/
String[] getEnabledCipherSuites() {
if (Arrays.asList(enabledProtocols).contains(NativeCrypto.SUPPORTED_PROTOCOL_TLSV1_3)) {
return SSLUtils.concat(
NativeCrypto.SUPPORTED_TLS_1_3_CIPHER_SUITES, enabledCipherSuites);
}
return enabledCipherSuites.clone();
}
/**
* Sets the enabled cipher suites after filtering through OpenSSL.
*/
void setEnabledCipherSuites(String[] cipherSuites) {
// Filter out any TLS 1.3 cipher suites the user may have passed. Our TLS 1.3 suites
// are always enabled, no matter what the user requests, so we only store the 1.0-1.2
// suites in enabledCipherSuites.
enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(
filterFromCipherSuites(cipherSuites,
NativeCrypto.SUPPORTED_TLS_1_3_CIPHER_SUITES_SET));
}
/**
* @return the set of enabled protocols
*/
String[] getEnabledProtocols() {
return enabledProtocols.clone();
}
/**
* Sets the list of available protocols for use in SSL connection.
* @throws IllegalArgumentException if {@code protocols == null}
*/
void setEnabledProtocols(String[] protocols) {
if (protocols == null) {
throw new IllegalArgumentException("protocols == null");
}
String[] filteredProtocols =
filterFromProtocols(protocols, NativeCrypto.OBSOLETE_PROTOCOL_SSLV3);
isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length;
enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone();
}
/**
* Sets the list of ALPN protocols.
*
* @param protocols the list of ALPN protocols
*/
void setApplicationProtocols(String[] protocols) {
this.applicationProtocols = SSLUtils.encodeProtocols(protocols);
}
String[] getApplicationProtocols() {
return SSLUtils.decodeProtocols(applicationProtocols);
}
/**
* Used for server-mode only. Sets or clears the application-provided ALPN protocol selector.
* If set, will override the protocol list provided by {@link #setApplicationProtocols(String[])}.
*/
void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter applicationProtocolSelector) {
this.applicationProtocolSelector = applicationProtocolSelector;
}
/**
* Returns the application protocol (ALPN) selector for this socket.
*/
ApplicationProtocolSelectorAdapter getApplicationProtocolSelector() {
return applicationProtocolSelector;
}
/**
* Tunes the peer holding this parameters to work in client mode.
* @param mode if the peer is configured to work in client mode
*/
void setUseClientMode(boolean mode) {
client_mode = mode;
}
/**
* Returns the value indicating if the parameters configured to work
* in client mode.
*/
boolean getUseClientMode() {
return client_mode;
}
/**
* Tunes the peer holding this parameters to require client authentication
*/
void setNeedClientAuth(boolean need) {
need_client_auth = need;
// reset the want_client_auth setting
want_client_auth = false;
}
/**
* Returns the value indicating if the peer with this parameters tuned
* to require client authentication
*/
boolean getNeedClientAuth() {
return need_client_auth;
}
/**
* Tunes the peer holding this parameters to request client authentication
*/
void setWantClientAuth(boolean want) {
want_client_auth = want;
// reset the need_client_auth setting
need_client_auth = false;
}
/**
* Returns the value indicating if the peer with this parameters
* tuned to request client authentication
*/
boolean getWantClientAuth() {
return want_client_auth;
}
/**
* Allows/disallows the peer holding this parameters to
* create new SSL session
*/
void setEnableSessionCreation(boolean flag) {
enable_session_creation = flag;
}
/**
* Returns the value indicating if the peer with this parameters
* allowed to cteate new SSL session
*/
boolean getEnableSessionCreation() {
return enable_session_creation;
}
void setUseSessionTickets(boolean useSessionTickets) {
this.useSessionTickets = useSessionTickets;
}
/**
* Whether connections using this SSL connection should use the TLS
* extension Server Name Indication (SNI).
*/
void setUseSni(boolean flag) {
useSni = flag;
}
/**
* Returns whether connections using this SSL connection should use the TLS
* extension Server Name Indication (SNI).
*/
boolean getUseSni() {
return useSni != null ? useSni : isSniEnabledByDefault();
}
/**
* For testing only.
*/
void setCTVerificationEnabled(boolean enabled) {
ctVerificationEnabled = enabled;
}
/**
* For testing only.
*/
void setSCTExtension(byte[] extension) {
sctExtension = extension;
}
/**
* For testing only.
*/
void setOCSPResponse(byte[] response) {
ocspResponse = response;
}
byte[] getOCSPResponse() {
return ocspResponse;
}
/**
* This filters {@code obsoleteProtocol} from the list of {@code protocols}
* down to help with app compatibility.
*/
private static String[] filterFromProtocols(String[] protocols, String obsoleteProtocol) {
if (protocols.length == 1 && obsoleteProtocol.equals(protocols[0])) {
return EMPTY_STRING_ARRAY;
}
ArrayList<String> newProtocols = new ArrayList<String>();
for (String protocol : protocols) {
if (!obsoleteProtocol.equals(protocol)) {
newProtocols.add(protocol);
}
}
return newProtocols.toArray(EMPTY_STRING_ARRAY);
}
private static String[] filterFromCipherSuites(String[] cipherSuites, Set<String> toRemove) {
if (cipherSuites == null || cipherSuites.length == 0) {
return cipherSuites;
}
ArrayList<String> newCipherSuites = new ArrayList<String>(cipherSuites.length);
for (String cipherSuite : cipherSuites) {
if (!toRemove.contains(cipherSuite)) {
newCipherSuites.add(cipherSuite);
}
}
return newCipherSuites.toArray(EMPTY_STRING_ARRAY);
}
private static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
* Returns whether Server Name Indication (SNI) is enabled by default for
* sockets. For more information on SNI, see RFC 6066 section 3.
*/
private boolean isSniEnabledByDefault() {
try {
String enableSNI = System.getProperty("jsse.enableSNIExtension", "true");
if ("true".equalsIgnoreCase(enableSNI)) {
return true;
} else if ("false".equalsIgnoreCase(enableSNI)) {
return false;
} else {
throw new RuntimeException(
"Can only set \"jsse.enableSNIExtension\" to \"true\" or \"false\"");
}
} catch (SecurityException e) {
return true;
}
}
/**
* For abstracting the X509KeyManager calls between
* {@link X509KeyManager#chooseClientAlias(String[], java.security.Principal[], java.net.Socket)}
* and
* {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)}
*/
interface AliasChooser {
String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
String[] keyTypes);
String chooseServerAlias(X509KeyManager keyManager, String keyType);
}
/**
* For abstracting the {@code PSKKeyManager} calls between those taking an {@code SSLSocket} and
* those taking an {@code SSLEngine}.
*/
@SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
interface PSKCallbacks {
String chooseServerPSKIdentityHint(PSKKeyManager keyManager);
String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint);
SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity);
}
/**
* Returns the clone of this object.
* @return the clone.
*/
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
SSLParametersImpl cloneWithTrustManager(X509TrustManager newTrustManager) {
return new SSLParametersImpl(clientSessionContext, serverSessionContext,
x509KeyManager, pskKeyManager, newTrustManager, this);
}
private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException {
X509KeyManager result = defaultX509KeyManager;
if (result == null) {
// single-check idiom
defaultX509KeyManager = result = createDefaultX509KeyManager();
}
return result;
}
private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException {
try {
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(null, null);
KeyManager[] kms = kmf.getKeyManagers();
X509KeyManager result = findFirstX509KeyManager(kms);
if (result == null) {
throw new KeyManagementException("No X509KeyManager among default KeyManagers: "
+ Arrays.toString(kms));
}
return result;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
} catch (KeyStoreException e) {
throw new KeyManagementException(e);
} catch (UnrecoverableKeyException e) {
throw new KeyManagementException(e);
}
}
/**
* Finds the first {@link X509KeyManager} element in the provided array.
*
* @return the first {@code X509KeyManager} or {@code null} if not found.
*/
private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) {
for (KeyManager km : kms) {
if (km instanceof X509KeyManager) {
return (X509KeyManager)km;
}
}
return null;
}
/**
* Finds the first {@link PSKKeyManager} element in the provided array.
*
* @return the first {@code PSKKeyManager} or {@code null} if not found.
*/
@SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) {
for (KeyManager km : kms) {
if (km instanceof PSKKeyManager) {
return (PSKKeyManager)km;
} else if (km != null) {
try {
return DuckTypedPSKKeyManager.getInstance(km);
} catch (NoSuchMethodException ignored) {}
}
}
return null;
}
/**
* Gets the default X.509 trust manager.
*/
static X509TrustManager getDefaultX509TrustManager()
throws KeyManagementException {
X509TrustManager result = defaultX509TrustManager;
if (result == null) {
// single-check idiom
defaultX509TrustManager = result = createDefaultX509TrustManager();
}
return result;
}
private static X509TrustManager createDefaultX509TrustManager()
throws KeyManagementException {
try {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init((KeyStore) null);
TrustManager[] tms = tmf.getTrustManagers();
X509TrustManager trustManager = findFirstX509TrustManager(tms);
if (trustManager == null) {
throw new KeyManagementException(
"No X509TrustManager in among default TrustManagers: "
+ Arrays.toString(tms));
}
return trustManager;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
} catch (KeyStoreException e) {
throw new KeyManagementException(e);
}
}
/**
* Finds the first {@link X509TrustManager} element in the provided array.
*
* @return the first {@code X509ExtendedTrustManager} or
* {@code X509TrustManager} or {@code null} if not found.
*/
private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
return null;
}
String getEndpointIdentificationAlgorithm() {
return endpointIdentificationAlgorithm;
}
void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
}
boolean getUseCipherSuitesOrder() {
return useCipherSuitesOrder;
}
Collection<SNIMatcher> getSNIMatchers() {
if (sniMatchers == null) {
return null;
}
return new ArrayList<>(sniMatchers);
}
void setSNIMatchers(Collection<SNIMatcher> sniMatchers) {
this.sniMatchers = sniMatchers != null ? new ArrayList<>(sniMatchers) : null;
}
AlgorithmConstraints getAlgorithmConstraints() {
return algorithmConstraints;
}
void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) {
this.algorithmConstraints = algorithmConstraints;
}
void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
this.useCipherSuitesOrder = useCipherSuitesOrder;
}
private static String[] getDefaultCipherSuites(
boolean x509CipherSuitesNeeded,
boolean pskCipherSuitesNeeded) {
if (x509CipherSuitesNeeded) {
// X.509 based cipher suites need to be listed.
if (pskCipherSuitesNeeded) {
// Both X.509 and PSK based cipher suites need to be listed. Because TLS-PSK is not
// normally used, we assume that when PSK cipher suites are requested here they
// should be preferred over other cipher suites. Thus, we give PSK cipher suites
// higher priority than X.509 cipher suites.
// NOTE: There are cipher suites that use both X.509 and PSK (e.g., those based on
// RSA_PSK key exchange). However, these cipher suites are not currently supported.
return SSLUtils.concat(
NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
} else {
// Only X.509 cipher suites need to be listed.
return SSLUtils.concat(
NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
}
} else if (pskCipherSuitesNeeded) {
// Only PSK cipher suites need to be listed.
return SSLUtils.concat(
NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
} else {
// Neither X.509 nor PSK cipher suites need to be listed.
return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV};
}
}
/**
* Check if SCT verification is enforced for a given hostname.
*/
boolean isCTVerificationEnabled(String hostname) {
if (hostname == null) {
return false;
}
// Bypass the check. This is used for testing only
if (ctVerificationEnabled) {
return true;
}
return Platform.isCTVerificationRequired(hostname);
}
}