package sun.security.ssl;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.AlgorithmConstraints;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiFunction;
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.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;
int maximumPacketSize = 0;
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 {
boolean supportExtendedMasterSecret = Utilities.getBooleanProperty(
"jdk.tls.useExtendedMasterSecret", true);
if (supportExtendedMasterSecret) {
try {
JsseJce.getKeyGenerator("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.<SNIServerName>emptyList();
this.sniMatchers = Collections.<SNIMatcher>emptyList();
this.preferLocalCipherSuites = false;
this.applicationProtocols = new String[0];
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);
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();
}
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, Arrays.asList(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]);
}
@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;
}
}