mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
497 lines
19 KiB
Java
497 lines
19 KiB
Java
/*
|
|
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
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;
|
|
|
|
/**
|
|
* SSL/(D)TLS configuration.
|
|
*/
|
|
final class SSLConfiguration implements Cloneable {
|
|
// configurations with SSLParameters
|
|
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
|
|
// "signature_algorithms_cert" extensions
|
|
List<SignatureScheme> signatureSchemes;
|
|
|
|
// the maximum protocol version of enabled protocols
|
|
ProtocolVersion maximumProtocolVersion;
|
|
|
|
// Configurations per SSLSocket or SSLEngine instance.
|
|
boolean isClientMode;
|
|
boolean enableSessionCreation;
|
|
|
|
// the application layer protocol negotiation configuration
|
|
BiFunction<SSLSocket, List<String>, String> socketAPSelector;
|
|
BiFunction<SSLEngine, List<String>, String> engineAPSelector;
|
|
|
|
HashMap<HandshakeCompletedListener, AccessControlContext>
|
|
handshakeListeners;
|
|
|
|
boolean noSniExtension;
|
|
boolean noSniMatcher;
|
|
|
|
// To switch off the extended_master_secret extension.
|
|
static final boolean useExtendedMasterSecret;
|
|
|
|
// Allow session resumption without Extended Master Secret extension.
|
|
static final boolean allowLegacyResumption =
|
|
Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
|
|
|
|
// Allow full handshake without Extended Master Secret extension.
|
|
static final boolean allowLegacyMasterSecret =
|
|
Utilities.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
|
|
|
|
// Allow full handshake without Extended Master Secret extension.
|
|
static final boolean useCompatibilityMode = Utilities.getBooleanProperty(
|
|
"jdk.tls.client.useCompatibilityMode", true);
|
|
|
|
// Respond a close_notify alert if receiving close_notify alert.
|
|
static final boolean acknowledgeCloseNotify = Utilities.getBooleanProperty(
|
|
"jdk.tls.acknowledgeCloseNotify", false);
|
|
|
|
// Set the max size limit for Handshake Message to 2^15
|
|
static final int maxHandshakeMessageSize = GetIntegerAction.privilegedGetProperty(
|
|
"jdk.tls.maxHandshakeMessageSize", 32768);
|
|
|
|
// Set the max certificate chain length to 10
|
|
static final int maxCertificateChainLength = GetIntegerAction.privilegedGetProperty(
|
|
"jdk.tls.maxCertificateChainLength", 10);
|
|
|
|
// Is the extended_master_secret extension supported?
|
|
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) {
|
|
|
|
// Configurations with SSLParameters, default values.
|
|
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; // please reset it explicitly later
|
|
|
|
this.signatureSchemes = isClientMode ?
|
|
CustomizedClientSignatureSchemes.signatureSchemes :
|
|
CustomizedServerSignatureSchemes.signatureSchemes;
|
|
this.maximumProtocolVersion = ProtocolVersion.NONE;
|
|
for (ProtocolVersion pv : enabledProtocols) {
|
|
if (pv.compareTo(maximumProtocolVersion) > 0) {
|
|
this.maximumProtocolVersion = pv;
|
|
}
|
|
}
|
|
|
|
// Configurations per SSLSocket or SSLEngine instance.
|
|
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) {
|
|
// 'null' indicates none has been set
|
|
params.setServerNames(null);
|
|
} else {
|
|
params.setServerNames(this.serverNames);
|
|
}
|
|
|
|
if (sniMatchers.isEmpty() && !noSniMatcher) {
|
|
// 'null' indicates none has been set
|
|
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();
|
|
}
|
|
|
|
// SSLSocket only
|
|
void addHandshakeCompletedListener(
|
|
HandshakeCompletedListener listener) {
|
|
|
|
if (handshakeListeners == null) {
|
|
handshakeListeners = new HashMap<>(4);
|
|
}
|
|
|
|
handshakeListeners.put(listener, AccessController.getContext());
|
|
}
|
|
|
|
// SSLSocket only
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return true if the extension is available.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Return true if the extension is available for the specific protocol.
|
|
*/
|
|
boolean isAvailable(SSLExtension extension,
|
|
ProtocolVersion protocolVersion) {
|
|
return extension.isAvailable(protocolVersion) &&
|
|
(isClientMode ? ClientExtensions.defaults.contains(extension) :
|
|
ServerExtensions.defaults.contains(extension));
|
|
}
|
|
|
|
/**
|
|
* Get the enabled extensions for the specific handshake message.
|
|
*
|
|
* Used to consume handshake extensions.
|
|
*/
|
|
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]);
|
|
}
|
|
|
|
/**
|
|
* Get the enabled extensions for the specific handshake message, excluding
|
|
* the specified extensions.
|
|
*
|
|
* Used to consume handshake extensions.
|
|
*/
|
|
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]);
|
|
}
|
|
|
|
/**
|
|
* Get the enabled extensions for the specific handshake message
|
|
* and the specific protocol version.
|
|
*
|
|
* Used to produce handshake extensions after handshake protocol
|
|
* version negotiation.
|
|
*/
|
|
SSLExtension[] getEnabledExtensions(
|
|
SSLHandshake handshakeType, ProtocolVersion protocolVersion) {
|
|
return getEnabledExtensions(handshakeType, List.of(protocolVersion));
|
|
}
|
|
|
|
/**
|
|
* Get the enabled extensions for the specific handshake message
|
|
* and the specific protocol versions.
|
|
*
|
|
* Used to produce ClientHello extensions before handshake protocol
|
|
* version negotiation.
|
|
*/
|
|
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;
|
|
|
|
// reset the signature schemes
|
|
this.signatureSchemes = isClientMode ?
|
|
CustomizedClientSignatureSchemes.signatureSchemes :
|
|
CustomizedServerSignatureSchemes.signatureSchemes;
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings({"unchecked", "CloneDeclaresCloneNotSupported"})
|
|
public Object clone() {
|
|
// Note that only references to the configurations are copied.
|
|
try {
|
|
SSLConfiguration config = (SSLConfiguration)super.clone();
|
|
if (handshakeListeners != null) {
|
|
config.handshakeListeners =
|
|
(HashMap<HandshakeCompletedListener, AccessControlContext>)
|
|
handshakeListeners.clone();
|
|
}
|
|
|
|
return config;
|
|
} catch (CloneNotSupportedException cnse) {
|
|
// unlikely
|
|
}
|
|
|
|
return null; // unlikely
|
|
}
|
|
|
|
|
|
// lazy initialization holder class idiom for static default parameters
|
|
//
|
|
// See Effective Java Second Edition: Item 71.
|
|
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
|
|
//
|
|
// See Effective Java Second Edition: Item 71.
|
|
private static final class CustomizedServerSignatureSchemes {
|
|
private static final List<SignatureScheme> signatureSchemes =
|
|
getCustomizedSignatureScheme("jdk.tls.server.SignatureSchemes");
|
|
}
|
|
|
|
/*
|
|
* Get the customized signature schemes specified by the given
|
|
* system property.
|
|
*/
|
|
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()) {
|
|
// remove double quote marks from beginning/end of the property
|
|
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();
|
|
}
|
|
}
|