package sun.security.ssl;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.AlgorithmConstraints;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiFunction;
import javax.crypto.KeyGenerator;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SNIMatcher;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import sun.security.action.GetIntegerAction;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.SSLExtension.ClientExtensions;
import sun.security.ssl.SSLExtension.ServerExtensions;
final class SSLConfiguration implements Cloneable {
AlgorithmConstraints userSpecifiedAlgorithmConstraints;
List<ProtocolVersion> enabledProtocols;
List<CipherSuite> enabledCipherSuites;
ClientAuthType clientAuthType;
String identificationProtocol;
List<SNIServerName> serverNames;
Collection<SNIMatcher> sniMatchers;
String[] applicationProtocols;
boolean preferLocalCipherSuites;
boolean enableRetransmissions;
int maximumPacketSize;
List<SignatureScheme> signatureSchemes;
ProtocolVersion maximumProtocolVersion;
boolean isClientMode;
boolean enableSessionCreation;
BiFunction<SSLSocket, List<String>, String> socketAPSelector;
BiFunction<SSLEngine, List<String>, String> engineAPSelector;
HashMap<HandshakeCompletedListener, AccessControlContext>
handshakeListeners;
boolean noSniExtension;
boolean noSniMatcher;
static final boolean useExtendedMasterSecret;
static final boolean allowLegacyResumption =
Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
static final boolean allowLegacyMasterSecret =
Utilities.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
static final boolean useCompatibilityMode = Utilities.getBooleanProperty(
"jdk.tls.client.useCompatibilityMode", true);
static final boolean acknowledgeCloseNotify = Utilities.getBooleanProperty(
"jdk.tls.acknowledgeCloseNotify", false);
static final int maxHandshakeMessageSize = GetIntegerAction.privilegedGetProperty(
"jdk.tls.maxHandshakeMessageSize", 32768);
static final int maxCertificateChainLength = GetIntegerAction.privilegedGetProperty(
"jdk.tls.maxCertificateChainLength", 10);
static {
boolean supportExtendedMasterSecret = Utilities.getBooleanProperty(
"jdk.tls.useExtendedMasterSecret", true);
if (supportExtendedMasterSecret) {
try {
KeyGenerator.getInstance("SunTlsExtendedMasterSecret");
} catch (NoSuchAlgorithmException nae) {
supportExtendedMasterSecret = false;
}
}
useExtendedMasterSecret = supportExtendedMasterSecret;
}
SSLConfiguration(SSLContextImpl sslContext, boolean isClientMode) {
this.userSpecifiedAlgorithmConstraints =
SSLAlgorithmConstraints.DEFAULT;
this.enabledProtocols =
sslContext.getDefaultProtocolVersions(!isClientMode);
this.enabledCipherSuites =
sslContext.getDefaultCipherSuites(!isClientMode);
this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
this.identificationProtocol = null;
this.serverNames = Collections.emptyList();
this.sniMatchers = Collections.emptyList();
this.preferLocalCipherSuites = true;
this.applicationProtocols = new String[0];
this.enableRetransmissions = sslContext.isDTLS();
this.maximumPacketSize = 0;
this.signatureSchemes = isClientMode ?
CustomizedClientSignatureSchemes.signatureSchemes :
CustomizedServerSignatureSchemes.signatureSchemes;
this.maximumProtocolVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : enabledProtocols) {
if (pv.compareTo(maximumProtocolVersion) > 0) {
this.maximumProtocolVersion = pv;
}
}
this.isClientMode = isClientMode;
this.enableSessionCreation = true;
this.socketAPSelector = null;
this.engineAPSelector = null;
this.handshakeListeners = null;
this.noSniExtension = false;
this.noSniMatcher = false;
}
SSLParameters getSSLParameters() {
SSLParameters params = new SSLParameters();
params.setAlgorithmConstraints(this.userSpecifiedAlgorithmConstraints);
params.setProtocols(ProtocolVersion.toStringArray(enabledProtocols));
params.setCipherSuites(CipherSuite.namesOf(enabledCipherSuites));
switch (this.clientAuthType) {
case CLIENT_AUTH_REQUIRED:
params.setNeedClientAuth(true);
break;
case CLIENT_AUTH_REQUESTED:
params.setWantClientAuth(true);
break;
default:
params.setWantClientAuth(false);
}
params.setEndpointIdentificationAlgorithm(this.identificationProtocol);
if (serverNames.isEmpty() && !noSniExtension) {
params.setServerNames(null);
} else {
params.setServerNames(this.serverNames);
}
if (sniMatchers.isEmpty() && !noSniMatcher) {
params.setSNIMatchers(null);
} else {
params.setSNIMatchers(this.sniMatchers);
}
params.setApplicationProtocols(this.applicationProtocols);
params.setUseCipherSuitesOrder(this.preferLocalCipherSuites);
params.setEnableRetransmissions(this.enableRetransmissions);
params.setMaximumPacketSize(this.maximumPacketSize);
return params;
}
void setSSLParameters(SSLParameters params) {
AlgorithmConstraints ac = params.getAlgorithmConstraints();
if (ac != null) {
this.userSpecifiedAlgorithmConstraints = ac;
}
String[] sa = params.getCipherSuites();
if (sa != null) {
this.enabledCipherSuites = CipherSuite.validValuesOf(sa);
}
sa = params.getProtocols();
if (sa != null) {
this.enabledProtocols = ProtocolVersion.namesOf(sa);
this.maximumProtocolVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : enabledProtocols) {
if (pv.compareTo(maximumProtocolVersion) > 0) {
this.maximumProtocolVersion = pv;
}
}
}
if (params.getNeedClientAuth()) {
this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUIRED;
} else if (params.getWantClientAuth()) {
this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUESTED;
} else {
this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
}
String s = params.getEndpointIdentificationAlgorithm();
if (s != null) {
this.identificationProtocol = s;
}
List<SNIServerName> sniNames = params.getServerNames();
if (sniNames != null) {
this.noSniExtension = sniNames.isEmpty();
this.serverNames = sniNames;
}
Collection<SNIMatcher> matchers = params.getSNIMatchers();
if (matchers != null) {
this.noSniMatcher = matchers.isEmpty();
this.sniMatchers = matchers;
}
sa = params.getApplicationProtocols();
if (sa != null) {
this.applicationProtocols = sa;
}
this.preferLocalCipherSuites = params.getUseCipherSuitesOrder();
this.enableRetransmissions = params.getEnableRetransmissions();
this.maximumPacketSize = params.getMaximumPacketSize();
}
void addHandshakeCompletedListener(
HandshakeCompletedListener listener) {
if (handshakeListeners == null) {
handshakeListeners = new HashMap<>(4);
}
handshakeListeners.put(listener, AccessController.getContext());
}
void removeHandshakeCompletedListener(
HandshakeCompletedListener listener) {
if (handshakeListeners == null) {
throw new IllegalArgumentException("no listeners");
}
if (handshakeListeners.remove(listener) == null) {
throw new IllegalArgumentException("listener not registered");
}
if (handshakeListeners.isEmpty()) {
handshakeListeners = null;
}
}
boolean isAvailable(SSLExtension extension) {
for (ProtocolVersion protocolVersion : enabledProtocols) {
if (extension.isAvailable(protocolVersion)) {
if (isClientMode ?
ClientExtensions.defaults.contains(extension) :
ServerExtensions.defaults.contains(extension)) {
return true;
}
}
}
return false;
}
boolean isAvailable(SSLExtension extension,
ProtocolVersion protocolVersion) {
return extension.isAvailable(protocolVersion) &&
(isClientMode ? ClientExtensions.defaults.contains(extension) :
ServerExtensions.defaults.contains(extension));
}
SSLExtension[] getEnabledExtensions(SSLHandshake handshakeType) {
List<SSLExtension> extensions = new ArrayList<>();
for (SSLExtension extension : SSLExtension.values()) {
if (extension.handshakeType == handshakeType) {
if (isAvailable(extension)) {
extensions.add(extension);
}
}
}
return extensions.toArray(new SSLExtension[0]);
}
SSLExtension[] getExclusiveExtensions(SSLHandshake handshakeType,
List<SSLExtension> excluded) {
List<SSLExtension> extensions = new ArrayList<>();
for (SSLExtension extension : SSLExtension.values()) {
if (extension.handshakeType == handshakeType) {
if (isAvailable(extension) && !excluded.contains(extension)) {
extensions.add(extension);
}
}
}
return extensions.toArray(new SSLExtension[0]);
}
SSLExtension[] getEnabledExtensions(
SSLHandshake handshakeType, ProtocolVersion protocolVersion) {
return getEnabledExtensions(handshakeType, List.of(protocolVersion));
}
SSLExtension[] getEnabledExtensions(
SSLHandshake handshakeType, List<ProtocolVersion> activeProtocols) {
List<SSLExtension> extensions = new ArrayList<>();
for (SSLExtension extension : SSLExtension.values()) {
if (extension.handshakeType == handshakeType) {
if (!isAvailable(extension)) {
continue;
}
for (ProtocolVersion protocolVersion : activeProtocols) {
if (extension.isAvailable(protocolVersion)) {
extensions.add(extension);
break;
}
}
}
}
return extensions.toArray(new SSLExtension[0]);
}
void toggleClientMode() {
this.isClientMode ^= true;
this.signatureSchemes = isClientMode ?
CustomizedClientSignatureSchemes.signatureSchemes :
CustomizedServerSignatureSchemes.signatureSchemes;
}
@Override
@SuppressWarnings({"unchecked", "CloneDeclaresCloneNotSupported"})
public Object clone() {
try {
SSLConfiguration config = (SSLConfiguration)super.clone();
if (handshakeListeners != null) {
config.handshakeListeners =
(HashMap<HandshakeCompletedListener, AccessControlContext>)
handshakeListeners.clone();
}
return config;
} catch (CloneNotSupportedException cnse) {
}
return null;
}
private static final class CustomizedClientSignatureSchemes {
private static final List<SignatureScheme> signatureSchemes =
getCustomizedSignatureScheme("jdk.tls.client.SignatureSchemes");
}
private static final class CustomizedServerSignatureSchemes {
private static final List<SignatureScheme> signatureSchemes =
getCustomizedSignatureScheme("jdk.tls.server.SignatureSchemes");
}
private static List<SignatureScheme> getCustomizedSignatureScheme(
String propertyName) {
String property = GetPropertyAction.privilegedGetProperty(propertyName);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
SSLLogger.fine(
"System property " + propertyName + " is set to '" +
property + "'");
}
if (property != null && !property.isEmpty()) {
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
}
if (property != null && !property.isEmpty()) {
String[] signatureSchemeNames = property.split(",");
List<SignatureScheme> signatureSchemes =
new ArrayList<>(signatureSchemeNames.length);
for (int i = 0; i < signatureSchemeNames.length; i++) {
signatureSchemeNames[i] = signatureSchemeNames[i].trim();
if (signatureSchemeNames[i].isEmpty()) {
continue;
}
SignatureScheme scheme =
SignatureScheme.nameOf(signatureSchemeNames[i]);
if (scheme != null && scheme.isAvailable) {
signatureSchemes.add(scheme);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
SSLLogger.fine(
"The current installed providers do not " +
"support signature scheme: " +
signatureSchemeNames[i]);
}
}
}
return signatureSchemes;
}
return Collections.emptyList();
}
}