|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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; |
|
|
|
// The configured signature schemes for "signature_algorithms" and |
|
|
|
List<SignatureScheme> signatureSchemes; |
|
|
|
|
|
ProtocolVersion maximumProtocolVersion; |
|
|
|
|
|
boolean isClientMode; |
|
boolean enableSessionCreation; |
|
|
|
|
|
BiFunction<SSLSocket, List<String>, String> socketAPSelector; |
|
BiFunction<SSLEngine, List<String>, String> engineAPSelector; |
|
|
|
@SuppressWarnings("removal") |
|
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; |
|
} // otherwise, use the default value |
|
|
|
String[] sa = params.getCipherSuites(); |
|
if (sa != null) { |
|
this.enabledCipherSuites = CipherSuite.validValuesOf(sa); |
|
} // otherwise, use the default values |
|
|
|
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; |
|
} |
|
} |
|
} // otherwise, use the default values |
|
|
|
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; |
|
} // otherwise, use the default value |
|
|
|
List<SNIServerName> sniNames = params.getServerNames(); |
|
if (sniNames != null) { |
|
this.noSniExtension = sniNames.isEmpty(); |
|
this.serverNames = sniNames; |
|
} // null if none has been set |
|
|
|
Collection<SNIMatcher> matchers = params.getSNIMatchers(); |
|
if (matchers != null) { |
|
this.noSniMatcher = matchers.isEmpty(); |
|
this.sniMatchers = matchers; |
|
} // null if none has been set |
|
|
|
sa = params.getApplicationProtocols(); |
|
if (sa != null) { |
|
this.applicationProtocols = sa; |
|
} // otherwise, use the default values |
|
|
|
this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); |
|
this.enableRetransmissions = params.getEnableRetransmissions(); |
|
this.maximumPacketSize = params.getMaximumPacketSize(); |
|
} |
|
|
|
|
|
@SuppressWarnings("removal") |
|
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({"removal","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) { |
|
// unlikely |
|
} |
|
|
|
return null; |
|
} |
|
|
|
|
|
// lazy initialization holder class idiom for static default parameters |
|
// |
|
|
|
private static final class CustomizedClientSignatureSchemes { |
|
private static final List<SignatureScheme> signatureSchemes = |
|
getCustomizedSignatureScheme("jdk.tls.client.SignatureSchemes"); |
|
} |
|
|
|
// lazy initialization holder class idiom for static default parameters |
|
// |
|
|
|
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(); |
|
} |
|
} |