
META-INF.modules.java.base.classes.sun.security.ssl.HandshakeContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java.base Show documentation
Show all versions of java.base Show documentation
Bytecoder java.base Module
/*
* Copyright (c) 2018, 2019, 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.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import javax.crypto.SecretKey;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLHandshakeException;
import javax.security.auth.x500.X500Principal;
import sun.security.ssl.NamedGroup.NamedGroupSpec;
import static sun.security.ssl.NamedGroup.NamedGroupSpec.*;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
abstract class HandshakeContext implements ConnectionContext {
// System properties
// By default, disable the unsafe legacy session renegotiation.
static final boolean allowUnsafeRenegotiation =
Utilities.getBooleanProperty(
"sun.security.ssl.allowUnsafeRenegotiation", false);
// For maximum interoperability and backward compatibility, RFC 5746
// allows server (or client) to accept ClientHello (or ServerHello)
// message without the secure renegotiation_info extension or SCSV.
//
// For maximum security, RFC 5746 also allows server (or client) to
// reject such message with a fatal "handshake_failure" alert.
//
// By default, allow such legacy hello messages.
static final boolean allowLegacyHelloMessages =
Utilities.getBooleanProperty(
"sun.security.ssl.allowLegacyHelloMessages", true);
// registered handshake message actors
LinkedHashMap handshakeConsumers;
final HashMap handshakeProducers;
// context
final SSLContextImpl sslContext;
final TransportContext conContext;
final SSLConfiguration sslConfig;
// consolidated parameters
final List activeProtocols;
final List activeCipherSuites;
final AlgorithmConstraints algorithmConstraints;
final ProtocolVersion maximumActiveProtocol;
// output stream
final HandshakeOutStream handshakeOutput;
// handshake transcript hash
final HandshakeHash handshakeHash;
// negotiated security parameters
SSLSessionImpl handshakeSession;
boolean handshakeFinished;
// boolean isInvalidated;
boolean kickstartMessageDelivered;
// Resumption
boolean isResumption;
SSLSessionImpl resumingSession;
// Session is using stateless resumption
boolean statelessResumption = false;
final Queue> delegatedActions;
volatile boolean taskDelegated = false;
volatile Exception delegatedThrown = null;
ProtocolVersion negotiatedProtocol;
CipherSuite negotiatedCipherSuite;
final List handshakePossessions;
final List handshakeCredentials;
SSLKeyDerivation handshakeKeyDerivation;
SSLKeyExchange handshakeKeyExchange;
SecretKey baseReadSecret;
SecretKey baseWriteSecret;
// protocol version being established
int clientHelloVersion;
String applicationProtocol;
RandomCookie clientHelloRandom;
RandomCookie serverHelloRandom;
byte[] certRequestContext;
////////////////////
// Extensions
// the extensions used in the handshake
final Map
handshakeExtensions;
// MaxFragmentLength
int maxFragmentLength;
// SignatureScheme
List localSupportedSignAlgs;
List peerRequestedSignatureSchemes;
List peerRequestedCertSignSchemes;
// Known authorities
X500Principal[] peerSupportedAuthorities = null;
// SupportedGroups
List clientRequestedNamedGroups;
// HelloRetryRequest
NamedGroup serverSelectedNamedGroup;
// if server name indicator is negotiated
//
// May need a public API for the indication in the future.
List requestedServerNames;
SNIServerName negotiatedServerName;
// OCSP Stapling info
boolean staplingActive = false;
protected HandshakeContext(SSLContextImpl sslContext,
TransportContext conContext) throws IOException {
this.sslContext = sslContext;
this.conContext = conContext;
this.sslConfig = (SSLConfiguration)conContext.sslConfig.clone();
this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
if (activeProtocols.isEmpty()) {
throw new SSLHandshakeException(
"No appropriate protocol (protocol is disabled or " +
"cipher suites are inappropriate)");
}
ProtocolVersion maximumVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : this.activeProtocols) {
if (maximumVersion == ProtocolVersion.NONE ||
pv.compare(maximumVersion) > 0) {
maximumVersion = pv;
}
}
this.maximumActiveProtocol = maximumVersion;
this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
if (activeCipherSuites.isEmpty()) {
throw new SSLHandshakeException("No appropriate cipher suite");
}
this.algorithmConstraints =
new SSLAlgorithmConstraints(sslConfig.algorithmConstraints);
this.handshakeConsumers = new LinkedHashMap<>();
this.handshakeProducers = new HashMap<>();
this.handshakeHash = conContext.inputRecord.handshakeHash;
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
this.handshakeFinished = false;
this.kickstartMessageDelivered = false;
this.delegatedActions = new LinkedList<>();
this.handshakeExtensions = new HashMap<>();
this.handshakePossessions = new LinkedList<>();
this.handshakeCredentials = new LinkedList<>();
this.requestedServerNames = null;
this.negotiatedServerName = null;
this.negotiatedCipherSuite = conContext.cipherSuite;
initialize();
}
/**
* Constructor for PostHandshakeContext
*/
HandshakeContext(TransportContext conContext) {
this.sslContext = conContext.sslContext;
this.conContext = conContext;
this.sslConfig = conContext.sslConfig;
this.negotiatedProtocol = conContext.protocolVersion;
this.negotiatedCipherSuite = conContext.cipherSuite;
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
this.delegatedActions = new LinkedList<>();
this.handshakeProducers = null;
this.handshakeHash = null;
this.activeProtocols = null;
this.activeCipherSuites = null;
this.algorithmConstraints = null;
this.maximumActiveProtocol = null;
this.handshakeExtensions = Collections.emptyMap(); // Not in TLS13
this.handshakePossessions = null;
this.handshakeCredentials = null;
}
// Initialize the non-final class variables.
private void initialize() {
ProtocolVersion inputHelloVersion;
ProtocolVersion outputHelloVersion;
if (conContext.isNegotiated) {
inputHelloVersion = conContext.protocolVersion;
outputHelloVersion = conContext.protocolVersion;
} else {
if (activeProtocols.contains(ProtocolVersion.SSL20Hello)) {
inputHelloVersion = ProtocolVersion.SSL20Hello;
// Per TLS 1.3 protocol, implementation MUST NOT send an SSL
// version 2.0 compatible CLIENT-HELLO.
if (maximumActiveProtocol.useTLS13PlusSpec()) {
outputHelloVersion = maximumActiveProtocol;
} else {
outputHelloVersion = ProtocolVersion.SSL20Hello;
}
} else {
inputHelloVersion = maximumActiveProtocol;
outputHelloVersion = maximumActiveProtocol;
}
}
conContext.inputRecord.setHelloVersion(inputHelloVersion);
conContext.outputRecord.setHelloVersion(outputHelloVersion);
if (!conContext.isNegotiated) {
conContext.protocolVersion = maximumActiveProtocol;
}
conContext.outputRecord.setVersion(conContext.protocolVersion);
}
private static List getActiveProtocols(
List enabledProtocols,
List enabledCipherSuites,
AlgorithmConstraints algorithmConstraints) {
boolean enabledSSL20Hello = false;
ArrayList protocols = new ArrayList<>(4);
for (ProtocolVersion protocol : enabledProtocols) {
if (!enabledSSL20Hello && protocol == ProtocolVersion.SSL20Hello) {
enabledSSL20Hello = true;
continue;
}
if (!algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
protocol.name, null)) {
// Ignore disabled protocol.
continue;
}
boolean found = false;
Map cachedStatus =
new EnumMap<>(NamedGroupSpec.class);
for (CipherSuite suite : enabledCipherSuites) {
if (suite.isAvailable() && suite.supports(protocol)) {
if (isActivatable(suite,
algorithmConstraints, cachedStatus)) {
protocols.add(protocol);
found = true;
break;
}
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Ignore unsupported cipher suite: " + suite +
" for " + protocol);
}
}
if (!found && (SSLLogger.isOn) && SSLLogger.isOn("handshake")) {
SSLLogger.fine(
"No available cipher suite for " + protocol);
}
}
if (!protocols.isEmpty()) {
if (enabledSSL20Hello) {
protocols.add(ProtocolVersion.SSL20Hello);
}
Collections.sort(protocols);
}
return Collections.unmodifiableList(protocols);
}
private static List getActiveCipherSuites(
List enabledProtocols,
List enabledCipherSuites,
AlgorithmConstraints algorithmConstraints) {
List suites = new LinkedList<>();
if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
Map cachedStatus =
new EnumMap<>(NamedGroupSpec.class);
for (CipherSuite suite : enabledCipherSuites) {
if (!suite.isAvailable()) {
continue;
}
boolean isSupported = false;
for (ProtocolVersion protocol : enabledProtocols) {
if (!suite.supports(protocol)) {
continue;
}
if (isActivatable(suite,
algorithmConstraints, cachedStatus)) {
suites.add(suite);
isSupported = true;
break;
}
}
if (!isSupported &&
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.finest(
"Ignore unsupported cipher suite: " + suite);
}
}
}
return Collections.unmodifiableList(suites);
}
/**
* Parse the handshake record and return the contentType
*/
static byte getHandshakeType(TransportContext conContext,
Plaintext plaintext) throws IOException {
// struct {
// HandshakeType msg_type; /* handshake type */
// uint24 length; /* bytes in message */
// select (HandshakeType) {
// ...
// } body;
// } Handshake;
if (plaintext.contentType != ContentType.HANDSHAKE.id) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Unexpected operation for record: " + plaintext.contentType);
}
if (plaintext.fragment == null || plaintext.fragment.remaining() < 4) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid handshake message: insufficient data");
}
byte handshakeType = (byte)Record.getInt8(plaintext.fragment);
int handshakeLen = Record.getInt24(plaintext.fragment);
if (handshakeLen != plaintext.fragment.remaining()) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid handshake message: insufficient handshake body");
}
return handshakeType;
}
void dispatch(byte handshakeType, Plaintext plaintext) throws IOException {
if (conContext.transport.useDelegatedTask()) {
boolean hasDelegated = !delegatedActions.isEmpty();
if (hasDelegated ||
(handshakeType != SSLHandshake.FINISHED.id &&
handshakeType != SSLHandshake.KEY_UPDATE.id &&
handshakeType != SSLHandshake.NEW_SESSION_TICKET.id)) {
if (!hasDelegated) {
taskDelegated = false;
delegatedThrown = null;
}
// Clone the fragment for delegated actions.
//
// The plaintext may share the application buffers. It is
// fine to use shared buffers if no delegated actions.
// However, for delegated actions, the shared buffers may be
// polluted in application layer before the delegated actions
// executed.
ByteBuffer fragment = ByteBuffer.wrap(
new byte[plaintext.fragment.remaining()]);
fragment.put(plaintext.fragment);
fragment = fragment.rewind();
delegatedActions.add(new SimpleImmutableEntry<>(
handshakeType,
fragment
));
} else {
dispatch(handshakeType, plaintext.fragment);
}
} else {
dispatch(handshakeType, plaintext.fragment);
}
}
void dispatch(byte handshakeType,
ByteBuffer fragment) throws IOException {
SSLConsumer consumer;
if (handshakeType == SSLHandshake.HELLO_REQUEST.id) {
// For TLS 1.2 and prior versions, the HelloRequest message MAY
// be sent by the server at any time.
consumer = SSLHandshake.HELLO_REQUEST;
} else {
consumer = handshakeConsumers.get(handshakeType);
}
if (consumer == null) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected handshake message: " +
SSLHandshake.nameOf(handshakeType));
}
try {
consumer.consume(this, fragment);
} catch (UnsupportedOperationException unsoe) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported handshake message: " +
SSLHandshake.nameOf(handshakeType), unsoe);
} catch (BufferUnderflowException | BufferOverflowException be) {
throw conContext.fatal(Alert.DECODE_ERROR,
"Illegal handshake message: " +
SSLHandshake.nameOf(handshakeType), be);
}
// update handshake hash after handshake message consumption.
handshakeHash.consume();
}
abstract void kickstart() throws IOException;
/**
* Check if the given cipher suite is enabled and available within
* the current active cipher suites.
*
* Does not check if the required server certificates are available.
*/
boolean isNegotiable(CipherSuite cs) {
return isNegotiable(activeCipherSuites, cs);
}
/**
* Check if the given cipher suite is enabled and available within
* the proposed cipher suite list.
*
* Does not check if the required server certificates are available.
*/
static final boolean isNegotiable(
List proposed, CipherSuite cs) {
return proposed.contains(cs) && cs.isNegotiable();
}
/**
* Check if the given cipher suite is enabled and available within
* the proposed cipher suite list and specific protocol version.
*
* Does not check if the required server certificates are available.
*/
static final boolean isNegotiable(List proposed,
ProtocolVersion protocolVersion, CipherSuite cs) {
return proposed.contains(cs) &&
cs.isNegotiable() && cs.supports(protocolVersion);
}
/**
* Check if the given protocol version is enabled and available.
*/
boolean isNegotiable(ProtocolVersion protocolVersion) {
return activeProtocols.contains(protocolVersion);
}
/**
* Set the active protocol version and propagate it to the SSLSocket
* and our handshake streams. Called from ClientHandshaker
* and ServerHandshaker with the negotiated protocol version.
*/
void setVersion(ProtocolVersion protocolVersion) {
this.conContext.protocolVersion = protocolVersion;
}
private static boolean isActivatable(CipherSuite suite,
AlgorithmConstraints algorithmConstraints,
Map cachedStatus) {
if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
if (suite.keyExchange == null) {
// TLS 1.3, no definition of key exchange in cipher suite.
return true;
}
// Is at least one of the group types available?
boolean groupAvailable, retval = false;
NamedGroupSpec[] groupTypes = suite.keyExchange.groupTypes;
for (NamedGroupSpec groupType : groupTypes) {
if (groupType != NAMED_GROUP_NONE) {
Boolean checkedStatus = cachedStatus.get(groupType);
if (checkedStatus == null) {
groupAvailable = SupportedGroups.isActivatable(
algorithmConstraints, groupType);
cachedStatus.put(groupType, groupAvailable);
if (!groupAvailable &&
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"No activated named group in " + groupType);
}
} else {
groupAvailable = checkedStatus;
}
retval |= groupAvailable;
} else {
retval |= true;
}
}
if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("No active named group(s), ignore " + suite);
}
return retval;
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Ignore disabled cipher suite: " + suite);
}
return false;
}
List getRequestedServerNames() {
if (requestedServerNames == null) {
return Collections.emptyList();
}
return requestedServerNames;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy