feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,297 @@
/*
* Copyright (c) 2003, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.Locale;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
/**
* SSL/TLS Alter description
*/
enum Alert {
// Please refer to TLS Alert Registry for the latest TLS Alert values:
// https://www.iana.org/assignments/tls-parameters/
CLOSE_NOTIFY ((byte)0, "close_notify", false),
UNEXPECTED_MESSAGE ((byte)10, "unexpected_message", false),
BAD_RECORD_MAC ((byte)20, "bad_record_mac", false),
DECRYPTION_FAILED ((byte)21, "decryption_failed", false),
RECORD_OVERFLOW ((byte)22, "record_overflow", false),
DECOMPRESSION_FAILURE ((byte)30, "decompression_failure", false),
HANDSHAKE_FAILURE ((byte)40, "handshake_failure", true),
NO_CERTIFICATE ((byte)41, "no_certificate", true),
BAD_CERTIFICATE ((byte)42, "bad_certificate", true),
UNSUPPORTED_CERTIFICATE ((byte)43, "unsupported_certificate", true),
CERTIFICATE_REVOKED ((byte)44, "certificate_revoked", true),
CERTIFICATE_EXPIRED ((byte)45, "certificate_expired", true),
CERTIFICATE_UNKNOWN ((byte)46, "certificate_unknown", true),
ILLEGAL_PARAMETER ((byte)47, "illegal_parameter", true),
UNKNOWN_CA ((byte)48, "unknown_ca", true),
ACCESS_DENIED ((byte)49, "access_denied", true),
DECODE_ERROR ((byte)50, "decode_error", true),
DECRYPT_ERROR ((byte)51, "decrypt_error", true),
EXPORT_RESTRICTION ((byte)60, "export_restriction", true),
PROTOCOL_VERSION ((byte)70, "protocol_version", true),
INSUFFICIENT_SECURITY ((byte)71, "insufficient_security", true),
INTERNAL_ERROR ((byte)80, "internal_error", false),
INAPPROPRIATE_FALLBACK ((byte)86, "inappropriate_fallback", false),
USER_CANCELED ((byte)90, "user_canceled", false),
NO_RENEGOTIATION ((byte)100, "no_renegotiation", true),
MISSING_EXTENSION ((byte)109, "missing_extension", true),
UNSUPPORTED_EXTENSION ((byte)110, "unsupported_extension", true),
CERT_UNOBTAINABLE ((byte)111, "certificate_unobtainable", true),
UNRECOGNIZED_NAME ((byte)112, "unrecognized_name", true),
BAD_CERT_STATUS_RESPONSE((byte)113,
"bad_certificate_status_response", true),
BAD_CERT_HASH_VALUE ((byte)114, "bad_certificate_hash_value", true),
UNKNOWN_PSK_IDENTITY ((byte)115, "unknown_psk_identity", true),
CERTIFICATE_REQUIRED ((byte)116, "certificate_required", true),
NO_APPLICATION_PROTOCOL ((byte)120, "no_application_protocol", true);
// ordinal value of the Alert
final byte id;
// description of the Alert
final String description;
// Does tha alert happen during handshake only?
final boolean handshakeOnly;
// Alert message consumer
static final SSLConsumer alertConsumer = new AlertConsumer();
private Alert(byte id, String description, boolean handshakeOnly) {
this.id = id;
this.description = description;
this.handshakeOnly = handshakeOnly;
}
static Alert valueOf(byte id) {
for (Alert al : Alert.values()) {
if (al.id == id) {
return al;
}
}
return null;
}
static String nameOf(byte id) {
for (Alert al : Alert.values()) {
if (al.id == id) {
return al.description;
}
}
return "UNKNOWN ALERT (" + (id & 0x0FF) + ")";
}
SSLException createSSLException(String reason) {
return createSSLException(reason, null);
}
SSLException createSSLException(String reason, Throwable cause) {
if (reason == null) {
reason = (cause != null) ? cause.getMessage() : "";
}
SSLException ssle;
if ((cause != null) && (cause instanceof IOException)) {
ssle = new SSLException(reason);
} else if ((this == UNEXPECTED_MESSAGE)) {
ssle = new SSLProtocolException(reason);
} else if (handshakeOnly) {
ssle = new SSLHandshakeException(reason);
} else {
ssle = new SSLException(reason);
}
if (cause != null) {
ssle.initCause(cause);
}
return ssle;
}
/**
* SSL/TLS Alert level.
*/
enum Level {
WARNING ((byte)1, "warning"),
FATAL ((byte)2, "fatal");
// ordinal value of the Alert level
final byte level;
// description of the Alert level
final String description;
private Level(byte level, String description) {
this.level = level;
this.description = description;
}
static Level valueOf(byte level) {
for (Level lv : Level.values()) {
if (lv.level == level) {
return lv;
}
}
return null;
}
static String nameOf(byte level) {
for (Level lv : Level.values()) {
if (lv.level == level) {
return lv.description;
}
}
return "UNKNOWN ALERT LEVEL (" + (level & 0x0FF) + ")";
}
}
/**
* The Alert message.
*/
private static final class AlertMessage {
private final byte level; // level
private final byte id; // description
AlertMessage(TransportContext context,
ByteBuffer m) throws IOException {
// struct {
// AlertLevel level;
// AlertDescription description;
// } Alert;
if (m.remaining() != 2) {
throw context.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid Alert message: no sufficient data");
}
this.level = m.get(); // level
this.id = m.get(); // description
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"Alert\": '{'\n" +
" \"level\" : \"{0}\",\n" +
" \"description\": \"{1}\"\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
Level.nameOf(level),
Alert.nameOf(id)
};
return messageFormat.format(messageFields);
}
}
/**
* Consumer of alert messages
*/
private static final class AlertConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private AlertConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer m) throws IOException {
TransportContext tc = (TransportContext)context;
AlertMessage am = new AlertMessage(tc, m);
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("Received alert message", am);
}
Level level = Level.valueOf(am.level);
Alert alert = Alert.valueOf(am.id);
if (alert == Alert.CLOSE_NOTIFY) {
tc.isInputCloseNotified = true;
tc.closeInbound();
if (tc.peerUserCanceled) {
tc.closeOutbound();
} else if (tc.handshakeContext != null) {
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
"Received close_notify during handshake");
}
} else if (alert == Alert.USER_CANCELED) {
if (level == Level.WARNING) {
tc.peerUserCanceled = true;
} else {
throw tc.fatal(alert,
"Received fatal close_notify alert", true, null);
}
} else if ((level == Level.WARNING) && (alert != null)) {
// Terminate the connection if an alert with a level of warning
// is received during handshaking, except the no_certificate
// warning.
if (alert.handshakeOnly && (tc.handshakeContext != null)) {
// It's OK to get a no_certificate alert from a client of
// which we requested client authentication. However,
// if we required it, then this is not acceptable.
if (tc.sslConfig.isClientMode ||
alert != Alert.NO_CERTIFICATE ||
(tc.sslConfig.clientAuthType !=
ClientAuthType.CLIENT_AUTH_REQUESTED)) {
throw tc.fatal(Alert.HANDSHAKE_FAILURE,
"received handshake warning: " + alert.description);
} else {
// Otherwise ignore the warning but remove the
// Certificate and CertificateVerify handshake
// consumer so the state machine doesn't expect it.
tc.handshakeContext.handshakeConsumers.remove(
SSLHandshake.CERTIFICATE.id);
tc.handshakeContext.handshakeConsumers.remove(
SSLHandshake.CERTIFICATE_VERIFY.id);
}
} // Otherwise, ignore the warning
} else { // fatal or unknown
String diagnostic;
if (alert == null) {
alert = Alert.UNEXPECTED_MESSAGE;
diagnostic = "Unknown alert description (" + am.id + ")";
} else {
diagnostic = "Received fatal alert: " + alert.description;
}
throw tc.fatal(alert, diagnostic, true, null);
}
}
}
}

View File

@@ -0,0 +1,536 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Security;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSocket;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].
*/
final class AlpnExtension {
static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();
static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();
static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();
static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();
// Note: we reuse ServerHello operations for EncryptedExtensions for now.
// Please be careful about any code or specification changes in the future.
static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();
static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();
static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
static final SSLStringizer alpnStringizer = new AlpnStringizer();
// Encoding Charset to convert between String and byte[]
static final Charset alpnCharset;
static {
String alpnCharsetString = AccessController.doPrivileged(
(PrivilegedAction<String>) ()
-> Security.getProperty("jdk.tls.alpnCharset"));
if ((alpnCharsetString == null)
|| (alpnCharsetString.length() == 0)) {
alpnCharsetString = "ISO_8859_1";
}
alpnCharset = Charset.forName(alpnCharsetString);
}
/**
* The "application_layer_protocol_negotiation" extension.
*
* See RFC 7301 for the specification of this extension.
*/
static final class AlpnSpec implements SSLExtensionSpec {
final List<String> applicationProtocols;
private AlpnSpec(String[] applicationProtocols) {
this.applicationProtocols = Collections.unmodifiableList(
Arrays.asList(applicationProtocols));
}
private AlpnSpec(ByteBuffer buffer) throws IOException {
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
if (buffer.remaining() < 2) {
throw new SSLProtocolException(
"Invalid application_layer_protocol_negotiation: " +
"insufficient data (length=" + buffer.remaining() + ")");
}
int listLen = Record.getInt16(buffer);
if (listLen < 2 || listLen != buffer.remaining()) {
throw new SSLProtocolException(
"Invalid application_layer_protocol_negotiation: " +
"incorrect list length (length=" + listLen + ")");
}
List<String> protocolNames = new LinkedList<>();
while (buffer.hasRemaining()) {
// opaque ProtocolName<1..2^8-1>, RFC 7301.
byte[] bytes = Record.getBytes8(buffer);
if (bytes.length == 0) {
throw new SSLProtocolException(
"Invalid application_layer_protocol_negotiation " +
"extension: empty application protocol name");
}
String appProtocol = new String(bytes, alpnCharset);
protocolNames.add(appProtocol);
}
this.applicationProtocols =
Collections.unmodifiableList(protocolNames);
}
@Override
public String toString() {
return applicationProtocols.toString();
}
}
private static final class AlpnStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new AlpnSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of the extension in a ClientHello
* handshake message.
*/
private static final class CHAlpnProducer implements HandshakeProducer {
static final int MAX_AP_LENGTH = 255;
static final int MAX_AP_LIST_LENGTH = 65535;
// Prevent instantiation of this class.
private CHAlpnProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.info(
"Ignore client unavailable extension: " +
SSLExtension.CH_ALPN.name);
}
chc.applicationProtocol = "";
chc.conContext.applicationProtocol = "";
return null;
}
String[] laps = chc.sslConfig.applicationProtocols;
if ((laps == null) || (laps.length == 0)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.info(
"No available application protocols");
}
return null;
}
// Produce the extension: first find the overall length
int listLength = 0; // ProtocolNameList length
for (String ap : laps) {
int length = ap.getBytes(alpnCharset).length;
if (length == 0) {
// log the configuration problem
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.severe(
"Application protocol name cannot be empty");
}
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Application protocol name cannot be empty");
}
if (length <= MAX_AP_LENGTH) {
// opaque ProtocolName<1..2^8-1>, RFC 7301.
listLength += (length + 1);
} else {
// log the configuration problem
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.severe(
"Application protocol name (" + ap +
") exceeds the size limit (" +
MAX_AP_LENGTH + " bytes)");
}
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Application protocol name (" + ap +
") exceeds the size limit (" +
MAX_AP_LENGTH + " bytes)");
}
if (listLength > MAX_AP_LIST_LENGTH) {
// log the configuration problem
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.severe(
"The configured application protocols (" +
Arrays.toString(laps) +
") exceed the size limit (" +
MAX_AP_LIST_LENGTH + " bytes)");
}
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The configured application protocols (" +
Arrays.toString(laps) +
") exceed the size limit (" +
MAX_AP_LIST_LENGTH + " bytes)");
}
}
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
byte[] extData = new byte[listLength + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, listLength);
// opaque ProtocolName<1..2^8-1>;
for (String ap : laps) {
Record.putBytes8(m, ap.getBytes(alpnCharset));
}
// Update the context.
chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
new AlpnSpec(chc.sslConfig.applicationProtocols));
return extData;
}
}
/**
* Network data consumer of the extension in a ClientHello
* handshake message.
*/
private static final class CHAlpnConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHAlpnConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
shc.applicationProtocol = "";
shc.conContext.applicationProtocol = "";
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.info(
"Ignore server unavailable extension: " +
SSLExtension.CH_ALPN.name);
}
return; // ignore the extension
}
// Is the extension enabled?
boolean noAPSelector;
if (shc.conContext.transport instanceof SSLEngine) {
noAPSelector = (shc.sslConfig.engineAPSelector == null);
} else {
noAPSelector = (shc.sslConfig.socketAPSelector == null);
}
boolean noAlpnProtocols =
shc.sslConfig.applicationProtocols == null ||
shc.sslConfig.applicationProtocols.length == 0;
if (noAPSelector && noAlpnProtocols) {
shc.applicationProtocol = "";
shc.conContext.applicationProtocol = "";
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore server unenabled extension: " +
SSLExtension.CH_ALPN.name);
}
return; // ignore the extension
}
// Parse the extension.
AlpnSpec spec;
try {
spec = new AlpnSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// Update the context.
if (noAPSelector) { // noAlpnProtocols is false
List<String> protocolNames = spec.applicationProtocols;
boolean matched = false;
// Use server application protocol preference order.
for (String ap : shc.sslConfig.applicationProtocols) {
if (protocolNames.contains(ap)) {
shc.applicationProtocol = ap;
shc.conContext.applicationProtocol = ap;
matched = true;
break;
}
}
if (!matched) {
throw shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
"No matching application layer protocol values");
}
} // Otherwise, applicationProtocol will be set by the
// application selector callback later.
shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
// No impact on session resumption.
//
// [RFC 7301] Unlike many other TLS extensions, this extension
// does not establish properties of the session, only of the
// connection. When session resumption or session tickets are
// used, the previous contents of this extension are irrelevant,
// and only the values in the new handshake messages are
// considered.
}
}
/**
* The absence processing if the extension is not present in
* a ClientHello handshake message.
*/
private static final class CHAlpnAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Please don't use the previous negotiated application protocol.
shc.applicationProtocol = "";
shc.conContext.applicationProtocol = "";
}
}
/**
* Network data producer of the extension in the ServerHello
* handshake message.
*/
private static final class SHAlpnProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHAlpnProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// In response to ALPN request only
AlpnSpec requestedAlps =
(AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
if (requestedAlps == null) {
// Ignore, this extension was not requested and accepted.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable extension: " +
SSLExtension.SH_ALPN.name);
}
shc.applicationProtocol = "";
shc.conContext.applicationProtocol = "";
return null;
}
List<String> alps = requestedAlps.applicationProtocols;
if (shc.conContext.transport instanceof SSLEngine) {
if (shc.sslConfig.engineAPSelector != null) {
SSLEngine engine = (SSLEngine)shc.conContext.transport;
shc.applicationProtocol =
shc.sslConfig.engineAPSelector.apply(engine, alps);
if ((shc.applicationProtocol == null) ||
(!shc.applicationProtocol.isEmpty() &&
!alps.contains(shc.applicationProtocol))) {
throw shc.conContext.fatal(
Alert.NO_APPLICATION_PROTOCOL,
"No matching application layer protocol values");
}
}
} else {
if (shc.sslConfig.socketAPSelector != null) {
SSLSocket socket = (SSLSocket)shc.conContext.transport;
shc.applicationProtocol =
shc.sslConfig.socketAPSelector.apply(socket, alps);
if ((shc.applicationProtocol == null) ||
(!shc.applicationProtocol.isEmpty() &&
!alps.contains(shc.applicationProtocol))) {
throw shc.conContext.fatal(
Alert.NO_APPLICATION_PROTOCOL,
"No matching application layer protocol values");
}
}
}
if ((shc.applicationProtocol == null) ||
(shc.applicationProtocol.isEmpty())) {
// Ignore, no negotiated application layer protocol.
shc.applicationProtocol = "";
shc.conContext.applicationProtocol = "";
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore, no negotiated application layer protocol");
}
return null;
}
// opaque ProtocolName<1..2^8-1>, RFC 7301.
byte[] bytes = shc.applicationProtocol.getBytes(alpnCharset);
int listLen = bytes.length + 1; // 1: length byte
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
byte[] extData = new byte[listLen + 2]; // 2: list length
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, listLen);
Record.putBytes8(m, bytes);
// Update the context.
shc.conContext.applicationProtocol = shc.applicationProtocol;
// Clean or register the extension
//
// No further use of the request and respond extension any more.
shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
return extData;
}
}
/**
* Network data consumer of the extension in the ServerHello
* handshake message.
*/
private static final class SHAlpnConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHAlpnConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to ALPN request only
AlpnSpec requestedAlps =
(AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
if (requestedAlps == null ||
requestedAlps.applicationProtocols == null ||
requestedAlps.applicationProtocols.isEmpty()) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected " + SSLExtension.CH_ALPN.name + " extension");
}
// Parse the extension.
AlpnSpec spec;
try {
spec = new AlpnSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// Only one application protocol is allowed.
if (spec.applicationProtocols.size() != 1) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
"Only one application protocol name " +
"is allowed in ServerHello message");
}
// The respond application protocol must be one of the requested.
if (!requestedAlps.applicationProtocols.containsAll(
spec.applicationProtocols)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
"Only client specified application protocol " +
"is allowed in ServerHello message");
}
// Update the context.
chc.applicationProtocol = spec.applicationProtocols.get(0);
chc.conContext.applicationProtocol = chc.applicationProtocol;
// Clean or register the extension
//
// No further use of the request and respond extension any more.
chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
}
}
/**
* The absence processing if the extension is not present in
* the ServerHello handshake message.
*/
private static final class SHAlpnAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Please don't use the previous negotiated application protocol.
chc.applicationProtocol = "";
chc.conContext.applicationProtocol = "";
}
}
}

View File

@@ -0,0 +1,460 @@
/*
* Copyright (c) 2012, 2018, 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.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import sun.security.ssl.CipherSuite.MacAlg;
/**
* This class represents an SSL/TLS message authentication token,
* which encapsulates a sequence number and ensures that attempts to
* delete or reorder messages can be detected.
*/
abstract class Authenticator {
// byte array containing the additional authentication information for
// each record
protected final byte[] block; // at least 8 bytes for sequence number
private Authenticator(byte[] block) {
this.block = block;
}
/**
* Constructs the message authentication token for the specified
* SSL/TLS protocol.
*/
static Authenticator valueOf(ProtocolVersion protocolVersion) {
if (protocolVersion.useTLS13PlusSpec()) {
return new TLS13Authenticator(protocolVersion);
} else if (protocolVersion.useTLS10PlusSpec()) {
return new TLS10Authenticator(protocolVersion);
} else {
return new SSL30Authenticator();
}
}
@SuppressWarnings({"unchecked"})
static <T extends Authenticator & MAC> T
valueOf(ProtocolVersion protocolVersion, MacAlg macAlg,
SecretKey key) throws NoSuchAlgorithmException,
InvalidKeyException {
if (protocolVersion.useTLS13PlusSpec()) {
throw new RuntimeException("No MacAlg used in TLS 1.3");
} else if (protocolVersion.useTLS10PlusSpec()) {
return (T)(new TLS10Mac(protocolVersion, macAlg, key));
} else {
return (T)(new SSL30Mac(protocolVersion, macAlg, key));
}
}
static Authenticator nullTlsMac() {
return new SSLNullMac();
}
/**
* Checks whether the sequence number is close to wrap.
*
* Sequence numbers are of type uint64 and may not exceed 2^64-1.
* Sequence numbers do not wrap. When the sequence number is near
* to wrap, we need to close the connection immediately.
*
* @return true if the sequence number is close to wrap
*/
abstract boolean seqNumOverflow();
/**
* Checks whether the sequence number close to renew.
*
* Sequence numbers are of type uint64 and may not exceed 2^64-1.
* Sequence numbers do not wrap. If a TLS
* implementation would need to wrap a sequence number, it must
* renegotiate instead.
*
* @return true if the sequence number is huge enough to renew
*/
abstract boolean seqNumIsHuge();
/**
* Gets the current sequence number.
*
* @return the byte array of the current sequence number
*/
final byte[] sequenceNumber() {
return Arrays.copyOf(block, 8);
}
/**
* Increase the sequence number.
*/
final void increaseSequenceNumber() {
/*
* The sequence number in the block array is a 64-bit
* number stored in big-endian format.
*/
int k = 7;
while ((k >= 0) && (++block[k] == 0)) {
k--;
}
}
/**
* Acquires the current message authentication information with the
* specified record type and fragment length, and then increases the
* sequence number if using implicit sequence number.
*
* @param type the record type
* @param length the fragment of the record
* @param sequence the explicit sequence number of the record
*
* @return the byte array of the current message authentication information
*/
byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
throw new UnsupportedOperationException("Used by AEAD algorithms only");
}
private static class SSLAuthenticator extends Authenticator {
private SSLAuthenticator(byte[] block) {
super(block);
}
@Override
boolean seqNumOverflow() {
/*
* Conservatively, we don't allow more records to be generated
* when there are only 2^8 sequence numbers left.
*/
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF);
}
@Override
boolean seqNumIsHuge() {
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
}
}
// For null MAC only.
private static class SSLNullAuthenticator extends SSLAuthenticator {
private SSLNullAuthenticator() {
super(new byte[8]);
}
}
// For SSL 3.0
private static class SSL30Authenticator extends SSLAuthenticator {
// Block size of SSL v3.0:
// sequence number + record type + + record length
private static final int BLOCK_SIZE = 11; // 8 + 1 + 2
private SSL30Authenticator() {
super(new byte[BLOCK_SIZE]);
}
@Override
byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
byte[] ad = block.clone();
// Increase the implicit sequence number in the block array.
increaseSequenceNumber();
ad[8] = type;
ad[9] = (byte)(length >> 8);
ad[10] = (byte)(length);
return ad;
}
}
// For TLS 1.0 - 1.2
private static class TLS10Authenticator extends SSLAuthenticator {
// Block size of TLS v1.0/1.1/1.2.
// sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE = 13; // 8 + 1 + 2 + 2
private TLS10Authenticator(ProtocolVersion protocolVersion) {
super(new byte[BLOCK_SIZE]);
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
}
@Override
byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
byte[] ad = block.clone();
if (sequence != null) {
if (sequence.length != 8) {
throw new RuntimeException(
"Insufficient explicit sequence number bytes");
}
System.arraycopy(sequence, 0, ad, 0, sequence.length);
} else { // Otherwise, use the implicit sequence number.
// Increase the implicit sequence number in the block array.
increaseSequenceNumber();
}
ad[8] = type;
ad[11] = (byte)(length >> 8);
ad[12] = (byte)(length);
return ad;
}
}
// For TLS 1.3
private static final class TLS13Authenticator extends SSLAuthenticator {
// Block size of TLS v1.3:
// record type + protocol version + record length + sequence number
private static final int BLOCK_SIZE = 13; // 1 + 2 + 2 + 8
private TLS13Authenticator(ProtocolVersion protocolVersion) {
super(new byte[BLOCK_SIZE]);
block[9] = ProtocolVersion.TLS12.major;
block[10] = ProtocolVersion.TLS12.minor;
}
@Override
byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
byte[] ad = Arrays.copyOfRange(block, 8, 13);
// Increase the implicit sequence number in the block array.
increaseSequenceNumber();
ad[0] = type;
ad[3] = (byte)(length >> 8);
ad[4] = (byte)(length & 0xFF);
return ad;
}
}
interface MAC {
MacAlg macAlg();
/**
* Compute and returns the MAC for the remaining data
* in this ByteBuffer.
*
* On return, the bb position == limit, and limit will
* have not changed.
*
* @param type record type
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the MAC computation
* @param sequence the explicit sequence number, or null if using
* the implicit sequence number for the computation
*
* @return the MAC result
*/
byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated);
/**
* Compute and returns the MAC for the remaining data
* in this ByteBuffer.
*
* On return, the bb position == limit, and limit will
* have not changed.
*
* @param type record type
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the MAC computation
*
* @return the MAC result
*/
default byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
return compute(type, bb, null, isSimulated);
}
}
private class MacImpl implements MAC {
// internal identifier for the MAC algorithm
private final MacAlg macAlg;
// JCE Mac object
private final Mac mac;
private MacImpl() {
macAlg = MacAlg.M_NULL;
mac = null;
}
private MacImpl(ProtocolVersion protocolVersion, MacAlg macAlg,
SecretKey key) throws NoSuchAlgorithmException,
InvalidKeyException {
if (macAlg == null) {
throw new RuntimeException("Null MacAlg");
}
// using SSL MAC computation?
boolean useSSLMac = (protocolVersion.id < ProtocolVersion.TLS10.id);
String algorithm;
switch (macAlg) {
case M_MD5:
algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
break;
case M_SHA:
algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
break;
case M_SHA256:
algorithm = "HmacSHA256"; // TLS 1.2+
break;
case M_SHA384:
algorithm = "HmacSHA384"; // TLS 1.2+
break;
default:
throw new RuntimeException("Unknown MacAlg " + macAlg);
}
Mac m = JsseJce.getMac(algorithm);
m.init(key);
this.macAlg = macAlg;
this.mac = m;
}
@Override
public MacAlg macAlg() {
return macAlg;
}
@Override
public byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
if (macAlg.size == 0) {
return new byte[0];
}
if (!isSimulated) {
// Uses the explicit sequence number for the computation.
byte[] additional =
acquireAuthenticationBytes(type, bb.remaining(), sequence);
mac.update(additional);
}
mac.update(bb);
return mac.doFinal();
}
}
// NULL SSL MAC
private static final
class SSLNullMac extends SSLNullAuthenticator implements MAC {
private final MacImpl macImpl;
public SSLNullMac() {
super();
this.macImpl = new MacImpl();
}
@Override
public MacAlg macAlg() {
return macImpl.macAlg;
}
@Override
public byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
return macImpl.compute(type, bb, sequence, isSimulated);
}
}
// For SSL 3.0
private static final
class SSL30Mac extends SSL30Authenticator implements MAC {
private final MacImpl macImpl;
public SSL30Mac(ProtocolVersion protocolVersion,
MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
InvalidKeyException {
super();
this.macImpl = new MacImpl(protocolVersion, macAlg, key);
}
@Override
public MacAlg macAlg() {
return macImpl.macAlg;
}
@Override
public byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
return macImpl.compute(type, bb, sequence, isSimulated);
}
}
// For TLS 1.0 - 1.2
private static final
class TLS10Mac extends TLS10Authenticator implements MAC {
private final MacImpl macImpl;
public TLS10Mac(ProtocolVersion protocolVersion,
MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
InvalidKeyException {
super(protocolVersion);
this.macImpl = new MacImpl(protocolVersion, macAlg, key);
}
@Override
public MacAlg macAlg() {
return macImpl.macAlg;
}
@Override
public byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
return macImpl.compute(type, bb, sequence, isSimulated);
}
}
static final long toLong(byte[] recordEnS) {
if (recordEnS != null && recordEnS.length == 8) {
return ((recordEnS[0] & 0xFFL) << 56) |
((recordEnS[1] & 0xFFL) << 48) |
((recordEnS[2] & 0xFFL) << 40) |
((recordEnS[3] & 0xFFL) << 32) |
((recordEnS[4] & 0xFFL) << 24) |
((recordEnS[5] & 0xFFL) << 16) |
((recordEnS[6] & 0xFFL) << 8) |
(recordEnS[7] & 0xFFL);
}
return -1L;
}
}

View File

@@ -0,0 +1,646 @@
/*
* Copyright (c) 2002, 2018, 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.*;
import java.net.*;
import java.nio.channels.SocketChannel;
import java.util.Set;
import javax.net.ssl.*;
/**
* Abstract base class for SSLSocketImpl.
*
* Its purpose is to house code with no SSL related logic (or no logic at all).
* This makes SSLSocketImpl shorter and easier to read. It contains a few
* constants and static methods plus overridden java.net.Socket methods.
*
* Methods are defined final to ensure that they are not accidentally
* overridden in SSLSocketImpl.
*
* @see javax.net.ssl.SSLSocket
* @see SSLSocketImpl
*/
abstract class BaseSSLSocketImpl extends SSLSocket {
/*
* Normally "self" is "this" ... but not when this connection is
* layered over a preexisting socket. If we're using an existing
* socket, we delegate some actions to it. Else, we delegate
* instead to "super". This is important to ensure that we don't
* recurse infinitely ... e.g. close() calling itself, or doing
* I/O in terms of our own streams.
*/
private final Socket self;
private final InputStream consumedInput;
BaseSSLSocketImpl() {
super();
this.self = this;
this.consumedInput = null;
}
BaseSSLSocketImpl(Socket socket) {
super();
this.self = socket;
this.consumedInput = null;
}
BaseSSLSocketImpl(Socket socket, InputStream consumed) {
super();
this.self = socket;
this.consumedInput = consumed;
}
//
// CONSTANTS AND STATIC METHODS
//
/**
* TLS requires that a close_notify warning alert is sent before the
* connection is closed in order to avoid truncation attacks. Some
* implementations (MS IIS and others) don't do that. The property
* below controls whether we accept that or treat it as an error.
*
* The default is "false", i.e. tolerate the broken behavior.
*/
private static final String PROP_NAME =
"com.sun.net.ssl.requireCloseNotify";
static final boolean requireCloseNotify =
Utilities.getBooleanProperty(PROP_NAME, false);
//
// MISC SOCKET METHODS
//
/**
* Returns the unique {@link java.nio.SocketChannel SocketChannel} object
* associated with this socket, if any.
* @see java.net.Socket#getChannel
*/
@Override
public final SocketChannel getChannel() {
if (self == this) {
return super.getChannel();
} else {
return self.getChannel();
}
}
/**
* Binds the address to the socket.
* @see java.net.Socket#bind
*/
@Override
public void bind(SocketAddress bindpoint) throws IOException {
/*
* Bind to this socket
*/
if (self == this) {
super.bind(bindpoint);
} else {
// If we're binding on a layered socket...
throw new IOException(
"Underlying socket should already be connected");
}
}
/**
* Returns the address of the endpoint this socket is connected to
* @see java.net.Socket#getLocalSocketAddress
*/
@Override
public SocketAddress getLocalSocketAddress() {
if (self == this) {
return super.getLocalSocketAddress();
} else {
return self.getLocalSocketAddress();
}
}
/**
* Returns the address of the endpoint this socket is connected to
* @see java.net.Socket#getRemoteSocketAddress
*/
@Override
public SocketAddress getRemoteSocketAddress() {
if (self == this) {
return super.getRemoteSocketAddress();
} else {
return self.getRemoteSocketAddress();
}
}
/**
* Connects this socket to the server.
*
* This method is either called on an unconnected SSLSocketImpl by the
* application, or it is called in the constructor of a regular
* SSLSocketImpl. If we are layering on top on another socket, then
* this method should not be called, because we assume that the
* underlying socket is already connected by the time it is passed to
* us.
*
* @param endpoint the <code>SocketAddress</code>
* @throws IOException if an error occurs during the connection
*/
@Override
public final void connect(SocketAddress endpoint) throws IOException {
connect(endpoint, 0);
}
/**
* Returns the connection state of the socket.
* @see java.net.Socket#isConnected
*/
@Override
public final boolean isConnected() {
if (self == this) {
return super.isConnected();
} else {
return self.isConnected();
}
}
/**
* Returns the binding state of the socket.
* @see java.net.Socket#isBound
*/
@Override
public final boolean isBound() {
if (self == this) {
return super.isBound();
} else {
return self.isBound();
}
}
//
// CLOSE RELATED METHODS
//
/**
* Places the input stream for this socket at "end of stream". Any data
* sent to the input stream side of the socket is acknowledged and then
* silently discarded.
*
* @see java.net.Socket#shutdownInput
*/
@Override
public void shutdownInput() throws IOException {
if (self == this) {
super.shutdownInput();
} else {
self.shutdownInput();
}
}
/**
* Disables the output stream for this socket. For a TCP socket, any
* previously written data will be sent followed by TCP's normal
* connection termination sequence.
*
* @see java.net.Socket#shutdownOutput
*/
@Override
public void shutdownOutput() throws IOException {
if (self == this) {
super.shutdownOutput();
} else {
self.shutdownOutput();
}
}
/**
* Returns the input state of the socket
* @see java.net.Socket#isInputShutdown
*/
@Override
public boolean isInputShutdown() {
if (self == this) {
return super.isInputShutdown();
} else {
return self.isInputShutdown();
}
}
/**
* Returns the output state of the socket
* @see java.net.Socket#isOutputShutdown
*/
@Override
public boolean isOutputShutdown() {
if (self == this) {
return super.isOutputShutdown();
} else {
return self.isOutputShutdown();
}
}
/**
* Ensures that the SSL connection is closed down as cleanly
* as possible, in case the application forgets to do so.
* This allows SSL connections to be implicitly reclaimed,
* rather than forcing them to be explicitly reclaimed at
* the penalty of prematurly killing SSL sessions.
*/
@Override
@SuppressWarnings("deprecation")
protected final void finalize() throws Throwable {
try {
close();
} catch (IOException e1) {
try {
if (self == this) {
super.close();
}
} catch (IOException e2) {
// ignore
}
} finally {
// We called close on the underlying socket above to
// make doubly sure all resources got released. We
// don't finalize self in the case of overlain sockets,
// that's a different object which the GC will finalize
// separately.
super.finalize();
}
}
//
// GET ADDRESS METHODS
//
/**
* Returns the address of the remote peer for this connection.
*/
@Override
public final InetAddress getInetAddress() {
if (self == this) {
return super.getInetAddress();
} else {
return self.getInetAddress();
}
}
/**
* Gets the local address to which the socket is bound.
*
* @return the local address to which the socket is bound.
* @since 1.1
*/
@Override
public final InetAddress getLocalAddress() {
if (self == this) {
return super.getLocalAddress();
} else {
return self.getLocalAddress();
}
}
/**
* Returns the number of the remote port that this connection uses.
*/
@Override
public final int getPort() {
if (self == this) {
return super.getPort();
} else {
return self.getPort();
}
}
/**
* Returns the number of the local port that this connection uses.
*/
@Override
public final int getLocalPort() {
if (self == this) {
return super.getLocalPort();
} else {
return self.getLocalPort();
}
}
//
// SOCKET OPTION METHODS
//
/**
* Enables or disables the Nagle optimization.
* @see java.net.Socket#setTcpNoDelay
*/
@Override
public final void setTcpNoDelay(boolean value) throws SocketException {
if (self == this) {
super.setTcpNoDelay(value);
} else {
self.setTcpNoDelay(value);
}
}
/**
* Returns true if the Nagle optimization is disabled. This
* relates to low-level buffering of TCP traffic, delaying the
* traffic to promote better throughput.
*
* @see java.net.Socket#getTcpNoDelay
*/
@Override
public final boolean getTcpNoDelay() throws SocketException {
if (self == this) {
return super.getTcpNoDelay();
} else {
return self.getTcpNoDelay();
}
}
/**
* Assigns the socket's linger timeout.
* @see java.net.Socket#setSoLinger
*/
@Override
public final void setSoLinger(boolean flag, int linger)
throws SocketException {
if (self == this) {
super.setSoLinger(flag, linger);
} else {
self.setSoLinger(flag, linger);
}
}
/**
* Returns the socket's linger timeout.
* @see java.net.Socket#getSoLinger
*/
@Override
public final int getSoLinger() throws SocketException {
if (self == this) {
return super.getSoLinger();
} else {
return self.getSoLinger();
}
}
/**
* Send one byte of urgent data on the socket.
* @see java.net.Socket#sendUrgentData
* At this point, there seems to be no specific requirement to support
* this for an SSLSocket. An implementation can be provided if a need
* arises in future.
*/
@Override
public final void sendUrgentData(int data) throws SocketException {
throw new SocketException("This method is not supported "
+ "by SSLSockets");
}
/**
* Enable/disable OOBINLINE (receipt of TCP urgent data) By default, this
* option is disabled and TCP urgent data received on a socket is silently
* discarded.
* @see java.net.Socket#setOOBInline
* Setting OOBInline does not have any effect on SSLSocket,
* since currently we don't support sending urgent data.
*/
@Override
public final void setOOBInline(boolean on) throws SocketException {
throw new SocketException("This method is ineffective, since"
+ " sending urgent data is not supported by SSLSockets");
}
/**
* Tests if OOBINLINE is enabled.
* @see java.net.Socket#getOOBInline
*/
@Override
public final boolean getOOBInline() throws SocketException {
throw new SocketException("This method is ineffective, since"
+ " sending urgent data is not supported by SSLSockets");
}
/**
* Returns the socket timeout.
* @see java.net.Socket#getSoTimeout
*/
@Override
public final int getSoTimeout() throws SocketException {
if (self == this) {
return super.getSoTimeout();
} else {
return self.getSoTimeout();
}
}
@Override
public final void setSendBufferSize(int size) throws SocketException {
if (self == this) {
super.setSendBufferSize(size);
} else {
self.setSendBufferSize(size);
}
}
@Override
public final int getSendBufferSize() throws SocketException {
if (self == this) {
return super.getSendBufferSize();
} else {
return self.getSendBufferSize();
}
}
@Override
public final void setReceiveBufferSize(int size) throws SocketException {
if (self == this) {
super.setReceiveBufferSize(size);
} else {
self.setReceiveBufferSize(size);
}
}
@Override
public final int getReceiveBufferSize() throws SocketException {
if (self == this) {
return super.getReceiveBufferSize();
} else {
return self.getReceiveBufferSize();
}
}
/**
* Enable/disable SO_KEEPALIVE.
* @see java.net.Socket#setKeepAlive
*/
@Override
public final void setKeepAlive(boolean on) throws SocketException {
if (self == this) {
super.setKeepAlive(on);
} else {
self.setKeepAlive(on);
}
}
/**
* Tests if SO_KEEPALIVE is enabled.
* @see java.net.Socket#getKeepAlive
*/
@Override
public final boolean getKeepAlive() throws SocketException {
if (self == this) {
return super.getKeepAlive();
} else {
return self.getKeepAlive();
}
}
/**
* Sets traffic class or type-of-service octet in the IP header for
* packets sent from this Socket.
* @see java.net.Socket#setTrafficClass
*/
@Override
public final void setTrafficClass(int tc) throws SocketException {
if (self == this) {
super.setTrafficClass(tc);
} else {
self.setTrafficClass(tc);
}
}
/**
* Gets traffic class or type-of-service in the IP header for packets
* sent from this Socket.
* @see java.net.Socket#getTrafficClass
*/
@Override
public final int getTrafficClass() throws SocketException {
if (self == this) {
return super.getTrafficClass();
} else {
return self.getTrafficClass();
}
}
/**
* Enable/disable SO_REUSEADDR.
* @see java.net.Socket#setReuseAddress
*/
@Override
public final void setReuseAddress(boolean on) throws SocketException {
if (self == this) {
super.setReuseAddress(on);
} else {
self.setReuseAddress(on);
}
}
/**
* Tests if SO_REUSEADDR is enabled.
* @see java.net.Socket#getReuseAddress
*/
@Override
public final boolean getReuseAddress() throws SocketException {
if (self == this) {
return super.getReuseAddress();
} else {
return self.getReuseAddress();
}
}
/**
* Sets performance preferences for this socket.
*
* @see java.net.Socket#setPerformancePreferences(int, int, int)
*/
@Override
public void setPerformancePreferences(int connectionTime,
int latency, int bandwidth) {
if (self == this) {
super.setPerformancePreferences(
connectionTime, latency, bandwidth);
} else {
self.setPerformancePreferences(
connectionTime, latency, bandwidth);
}
}
@Override
public String toString() {
if (self == this) {
return super.toString();
}
return self.toString();
}
@Override
public InputStream getInputStream() throws IOException {
if (self == this) {
return super.getInputStream();
}
if (consumedInput != null) {
return new SequenceInputStream(consumedInput,
self.getInputStream());
}
return self.getInputStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
if (self == this) {
return super.getOutputStream();
}
return self.getOutputStream();
}
@Override
public void close() throws IOException {
if (self == this) {
super.close();
} else {
self.close();
}
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
if (self == this) {
super.setSoTimeout(timeout);
} else {
self.setSoTimeout(timeout);
}
}
boolean isLayered() {
return (self != this);
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SignatureAlgorithmsExtension.SignatureSchemesSpec;
/**
* Pack of the "signature_algorithms_cert" extensions.
*/
final class CertSignAlgsExtension {
static final HandshakeProducer chNetworkProducer =
new CHCertSignatureSchemesProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHCertSignatureSchemesConsumer();
static final HandshakeConsumer chOnTradeConsumer =
new CHCertSignatureSchemesUpdate();
static final HandshakeProducer crNetworkProducer =
new CRCertSignatureSchemesProducer();
static final ExtensionConsumer crOnLoadConsumer =
new CRCertSignatureSchemesConsumer();
static final HandshakeConsumer crOnTradeConsumer =
new CRCertSignatureSchemesUpdate();
static final SSLStringizer ssStringizer =
new CertSignatureSchemesStringizer();
private static final
class CertSignatureSchemesStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new SignatureSchemesSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of a "signature_algorithms_cert" extension in
* the ClientHello handshake message.
*/
private static final
class CHCertSignatureSchemesProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHCertSignatureSchemesProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"signature_algorithms_cert extension");
}
return null; // ignore the extension
}
// Produce the extension.
if (chc.localSupportedSignAlgs == null) {
chc.localSupportedSignAlgs =
SignatureScheme.getSupportedAlgorithms(
chc.sslConfig,
chc.algorithmConstraints, chc.activeProtocols);
}
int vectorLen = SignatureScheme.sizeInRecord() *
chc.localSupportedSignAlgs.size();
byte[] extData = new byte[vectorLen + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, vectorLen);
for (SignatureScheme ss : chc.localSupportedSignAlgs) {
Record.putInt16(m, ss.id);
}
// Update the context.
chc.handshakeExtensions.put(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT,
new SignatureSchemesSpec(chc.localSupportedSignAlgs));
return extData;
}
}
/**
* Network data consumer of a "signature_algorithms_cert" extension in
* the ClientHello handshake message.
*/
private static final
class CHCertSignatureSchemesConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHCertSignatureSchemesConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"signature_algorithms_cert extension");
}
return; // ignore the extension
}
// Parse the extension.
SignatureSchemesSpec spec;
try {
spec = new SignatureSchemesSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// Update the context.
shc.handshakeExtensions.put(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT, spec);
// No impact on session resumption.
}
}
/**
* After session creation consuming of a "signature_algorithms_cert"
* extension in the ClientHello handshake message.
*/
private static final class CHCertSignatureSchemesUpdate
implements HandshakeConsumer {
// Prevent instantiation of this class.
private CHCertSignatureSchemesUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
SignatureSchemesSpec spec = (SignatureSchemesSpec)
shc.handshakeExtensions.get(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
if (spec == null) {
// Ignore, no signature_algorithms_cert extension requested.
return;
}
// update the context
List<SignatureScheme> schemes =
SignatureScheme.getSupportedAlgorithms(
shc.sslConfig,
shc.algorithmConstraints, shc.negotiatedProtocol,
spec.signatureSchemes);
shc.peerRequestedCertSignSchemes = schemes;
shc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
if (shc.sslConfig.clientAuthType !=
ClientAuthType.CLIENT_AUTH_NONE) {
shc.handshakeProducers.putIfAbsent(
SSLHandshake.CERTIFICATE_REQUEST.id,
SSLHandshake.CERTIFICATE_REQUEST);
}
shc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
shc.handshakeProducers.putIfAbsent(
SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
}
}
}
/**
* Network data producer of a "signature_algorithms_cert" extension in
* the CertificateRequest handshake message.
*/
private static final
class CRCertSignatureSchemesProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CRCertSignatureSchemesProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"signature_algorithms_cert extension");
}
return null; // ignore the extension
}
// Produce the extension.
List<ProtocolVersion> protocols = Arrays.asList(shc.negotiatedProtocol);
protocols = Collections.unmodifiableList(protocols);
List<SignatureScheme> sigAlgs =
SignatureScheme.getSupportedAlgorithms(
shc.sslConfig,
shc.algorithmConstraints,
protocols);
int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size();
byte[] extData = new byte[vectorLen + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, vectorLen);
for (SignatureScheme ss : sigAlgs) {
Record.putInt16(m, ss.id);
}
// Update the context.
shc.handshakeExtensions.put(
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT,
new SignatureSchemesSpec(shc.localSupportedSignAlgs));
return extData;
}
}
/**
* Network data consumer of a "signature_algorithms_cert" extension in
* the CertificateRequest handshake message.
*/
private static final
class CRCertSignatureSchemesConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CRCertSignatureSchemesConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"signature_algorithms_cert extension");
}
return; // ignore the extension
}
// Parse the extension.
SignatureSchemesSpec spec;
try {
spec = new SignatureSchemesSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// Update the context.
chc.handshakeExtensions.put(
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT, spec);
// No impact on session resumption.
}
}
/**
* After session creation consuming of a "signature_algorithms_cert"
* extension in the CertificateRequest handshake message.
*/
private static final class CRCertSignatureSchemesUpdate
implements HandshakeConsumer {
// Prevent instantiation of this class.
private CRCertSignatureSchemesUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
SignatureSchemesSpec spec = (SignatureSchemesSpec)
chc.handshakeExtensions.get(
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT);
if (spec == null) {
// Ignore, no "signature_algorithms_cert" extension requested.
return;
}
// update the context
List<SignatureScheme> schemes =
SignatureScheme.getSupportedAlgorithms(
chc.sslConfig,
chc.algorithmConstraints, chc.negotiatedProtocol,
spec.signatureSchemes);
chc.peerRequestedCertSignSchemes = schemes;
chc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,406 @@
/*
* Copyright (c) 2020, 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.ByteBuffer;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.*;
import javax.net.ssl.SSLProtocolException;
import javax.security.auth.x500.X500Principal;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "certificate_authorities" extensions.
*/
final class CertificateAuthoritiesExtension {
static final HandshakeProducer chNetworkProducer =
new CHCertificateAuthoritiesProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHCertificateAuthoritiesConsumer();
static final HandshakeProducer crNetworkProducer =
new CRCertificateAuthoritiesProducer();
static final ExtensionConsumer crOnLoadConsumer =
new CRCertificateAuthoritiesConsumer();
static final SSLStringizer ssStringizer =
new CertificateAuthoritiesStringizer();
/**
* The "certificate_authorities" extension.
*/
static final class CertificateAuthoritiesSpec implements SSLExtensionSpec {
final List<byte[]> authorities; // certificate authorities
private CertificateAuthoritiesSpec(List<byte[]> authorities) {
this.authorities = authorities;
}
private CertificateAuthoritiesSpec(ByteBuffer m) throws IOException {
if (m.remaining() < 3) { // 2: the length of the list
// 1: at least one byte authorities
throw new SSLProtocolException(
"Invalid certificate_authorities extension: " +
"insufficient data");
}
int listLen = Record.getInt16(m);
if (listLen == 0) {
throw new SSLProtocolException(
"Invalid certificate_authorities extension: " +
"no certificate authorities");
}
if (listLen > m.remaining()) {
throw new SSLProtocolException(
"Invalid certificate_authorities extension: " +
"insufficient data");
}
this.authorities = new LinkedList<>();
while (listLen > 0) {
// opaque DistinguishedName<1..2^16-1>;
byte[] encoded = Record.getBytes16(m);
listLen -= (2 + encoded.length);
authorities.add(encoded);
}
}
private static List<byte[]> getEncodedAuthorities(
X509Certificate[] trustedCerts) {
List<byte[]> authorities = new ArrayList<>(trustedCerts.length);
int sizeAccount = 0;
for (X509Certificate cert : trustedCerts) {
X500Principal x500Principal = cert.getSubjectX500Principal();
byte[] encodedPrincipal = x500Principal.getEncoded();
sizeAccount += encodedPrincipal.length;
if (sizeAccount > 0xFFFF) { // the size limit of this extension
// If there too many trusts CAs such that they exceed the
// size limit of the extension, enabling this extension
// does not really make sense as there is no way to
// indicate the peer certificate selection accurately.
// In such cases, the extension is just ignored, rather
// than fatal close, for better compatibility and
// interoperability.
return Collections.emptyList();
}
if (encodedPrincipal.length != 0) {
authorities.add(encodedPrincipal);
}
}
return authorities;
}
X500Principal[] getAuthorities() {
X500Principal[] principals = new X500Principal[authorities.size()];
int i = 0;
for (byte[] encoded : authorities) {
principals[i++] = new X500Principal(encoded);
}
return principals;
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"certificate authorities\": '['\n{0}']'", Locale.ENGLISH);
StringBuilder builder = new StringBuilder(512);
for (byte[] encoded : authorities) {
X500Principal principal = new X500Principal(encoded);
builder.append(principal.toString());
builder.append("\n");
}
Object[] messageFields = {
Utilities.indent(builder.toString())
};
return messageFormat.format(messageFields);
}
}
private static final
class CertificateAuthoritiesStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new CertificateAuthoritiesSpec(buffer))
.toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of a "certificate_authorities" extension in
* the ClientHello handshake message.
*/
private static final
class CHCertificateAuthoritiesProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHCertificateAuthoritiesProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(
SSLExtension.CH_CERTIFICATE_AUTHORITIES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"certificate_authorities extension");
}
return null; // ignore the extension
}
// Produce the extension.
X509Certificate[] caCerts =
chc.sslContext.getX509TrustManager().getAcceptedIssuers();
if (caCerts.length == 0) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No available certificate authorities");
}
return null; // ignore the extension
}
List<byte[]> encodedCAs =
CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts);
if (encodedCAs.isEmpty()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"The number of CAs exceeds the maximum size" +
"of the certificate_authorities extension");
}
return null; // ignore the extension
}
CertificateAuthoritiesSpec spec =
new CertificateAuthoritiesSpec(encodedCAs);
int vectorLen = 0;
for (byte[] encoded : spec.authorities) {
vectorLen += encoded.length + 2;
}
byte[] extData = new byte[vectorLen + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, vectorLen);
for (byte[] encoded : spec.authorities) {
Record.putBytes16(m, encoded);
}
// Update the context.
chc.handshakeExtensions.put(
SSLExtension.CH_CERTIFICATE_AUTHORITIES, spec);
return extData;
}
}
/**
* Network data consumer of a "certificate_authorities" extension in
* the ClientHello handshake message.
*/
private static final
class CHCertificateAuthoritiesConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHCertificateAuthoritiesConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(
SSLExtension.CH_CERTIFICATE_AUTHORITIES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"certificate_authorities extension");
}
return; // ignore the extension
}
// Parse the extension.
CertificateAuthoritiesSpec spec =
new CertificateAuthoritiesSpec(buffer);
// Update the context.
shc.peerSupportedAuthorities = spec.getAuthorities();
shc.handshakeExtensions.put(
SSLExtension.CH_CERTIFICATE_AUTHORITIES, spec);
// No impact on session resumption.
}
}
/**
* Network data producer of a "certificate_authorities" extension in
* the CertificateRequest handshake message.
*/
private static final
class CRCertificateAuthoritiesProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CRCertificateAuthoritiesProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(
SSLExtension.CR_CERTIFICATE_AUTHORITIES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"certificate_authorities extension");
}
return null; // ignore the extension
}
// Produce the extension.
X509Certificate[] caCerts =
shc.sslContext.getX509TrustManager().getAcceptedIssuers();
if (caCerts.length == 0) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No available certificate authorities");
}
return null; // ignore the extension
}
List<byte[]> encodedCAs =
CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts);
if (encodedCAs.isEmpty()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Too many certificate authorities to use " +
"the certificate_authorities extension");
}
return null; // ignore the extension
}
CertificateAuthoritiesSpec spec =
new CertificateAuthoritiesSpec(encodedCAs);
int vectorLen = 0;
for (byte[] encoded : spec.authorities) {
vectorLen += encoded.length + 2;
}
byte[] extData = new byte[vectorLen + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, vectorLen);
for (byte[] encoded : spec.authorities) {
Record.putBytes16(m, encoded);
}
// Update the context.
shc.handshakeExtensions.put(
SSLExtension.CR_CERTIFICATE_AUTHORITIES, spec);
return extData;
}
}
/**
* Network data consumer of a "certificate_authorities" extension in
* the CertificateRequest handshake message.
*/
private static final
class CRCertificateAuthoritiesConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CRCertificateAuthoritiesConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(
SSLExtension.CR_CERTIFICATE_AUTHORITIES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable " +
"certificate_authorities extension");
}
return; // ignore the extension
}
// Parse the extension.
CertificateAuthoritiesSpec spec =
new CertificateAuthoritiesSpec(buffer);
// Update the context.
chc.peerSupportedAuthorities = spec.getAuthorities();
chc.handshakeExtensions.put(
SSLExtension.CR_CERTIFICATE_AUTHORITIES, spec);
// No impact on session resumption.
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,369 @@
/*
* Copyright (c) 2015, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import javax.net.ssl.SSLHandshakeException;
import java.security.cert.X509Certificate;
import sun.security.provider.certpath.OCSPResponse;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import static sun.security.ssl.CertStatusExtension.*;
import static sun.security.ssl.CertificateMessage.*;
/**
* Consumers and producers for the CertificateStatus handshake message.
* This message takes one of two related but slightly different forms,
* depending on the type of stapling selected by the server. The message
* data will be of the form(s):
*
* [status_request, RFC 6066]
*
* struct {
* CertificateStatusType status_type;
* select (status_type) {
* case ocsp: OCSPResponse;
* } response;
* } CertificateStatus;
*
* opaque OCSPResponse<1..2^24-1>;
*
* [status_request_v2, RFC 6961]
*
* struct {
* CertificateStatusType status_type;
* select (status_type) {
* case ocsp: OCSPResponse;
* case ocsp_multi: OCSPResponseList;
* } response;
* } CertificateStatus;
*
* opaque OCSPResponse<0..2^24-1>;
*
* struct {
* OCSPResponse ocsp_response_list<1..2^24-1>;
* } OCSPResponseList;
*/
final class CertificateStatus {
static final SSLConsumer handshakeConsumer =
new CertificateStatusConsumer();
static final HandshakeProducer handshakeProducer =
new CertificateStatusProducer();
static final HandshakeAbsence handshakeAbsence =
new CertificateStatusAbsence();
/**
* The CertificateStatus handshake message.
*/
static final class CertificateStatusMessage extends HandshakeMessage {
final CertStatusRequestType statusType;
int encodedResponsesLen = 0;
int messageLength = -1;
final List<byte[]> encodedResponses = new ArrayList<>();
CertificateStatusMessage(HandshakeContext handshakeContext) {
super(handshakeContext);
ServerHandshakeContext shc =
(ServerHandshakeContext)handshakeContext;
// Get the Certificates from the SSLContextImpl amd the Stapling
// parameters
StatusResponseManager.StaplingParameters stapleParams =
shc.stapleParams;
if (stapleParams == null) {
throw new IllegalArgumentException(
"Unexpected null stapling parameters");
}
X509Certificate[] certChain =
(X509Certificate[])shc.handshakeSession.getLocalCertificates();
if (certChain == null) {
throw new IllegalArgumentException(
"Unexpected null certificate chain");
}
// Walk the certificate list and add the correct encoded responses
// to the encoded responses list
statusType = stapleParams.statReqType;
if (statusType == CertStatusRequestType.OCSP) {
// Just worry about the first cert in the chain
byte[] resp = stapleParams.responseMap.get(certChain[0]);
if (resp == null) {
// A not-found return status means we should include
// a zero-length response in CertificateStatus.
// This is highly unlikely to happen in practice.
resp = new byte[0];
}
encodedResponses.add(resp);
encodedResponsesLen += resp.length + 3;
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
for (X509Certificate cert : certChain) {
byte[] resp = stapleParams.responseMap.get(cert);
if (resp == null) {
resp = new byte[0];
}
encodedResponses.add(resp);
encodedResponsesLen += resp.length + 3;
}
} else {
throw new IllegalArgumentException(
"Unsupported StatusResponseType: " + statusType);
}
messageLength = messageLength();
}
CertificateStatusMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
statusType = CertStatusRequestType.valueOf((byte)Record.getInt8(m));
if (statusType == CertStatusRequestType.OCSP) {
byte[] respDER = Record.getBytes24(m);
// Convert the incoming bytes to a OCSPResponse strucutre
if (respDER.length > 0) {
encodedResponses.add(respDER);
encodedResponsesLen = 3 + respDER.length;
} else {
throw handshakeContext.conContext.fatal(
Alert.HANDSHAKE_FAILURE,
"Zero-length OCSP Response");
}
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
int respListLen = Record.getInt24(m);
encodedResponsesLen = respListLen;
// Add each OCSP reponse into the array list in the order
// we receive them off the wire. A zero-length array is
// allowed for ocsp_multi, and means that a response for
// a given certificate is not available.
while (respListLen > 0) {
byte[] respDER = Record.getBytes24(m);
encodedResponses.add(respDER);
respListLen -= (respDER.length + 3);
}
if (respListLen != 0) {
throw handshakeContext.conContext.fatal(
Alert.INTERNAL_ERROR,
"Bad OCSP response list length");
}
} else {
throw handshakeContext.conContext.fatal(
Alert.HANDSHAKE_FAILURE,
"Unsupported StatusResponseType: " + statusType);
}
messageLength = messageLength();
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE_STATUS;
}
@Override
public int messageLength() {
int len = 1;
if (messageLength == -1) {
if (statusType == CertStatusRequestType.OCSP) {
len += encodedResponsesLen;
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
len += 3 + encodedResponsesLen;
}
messageLength = len;
}
return messageLength;
}
@Override
public void send(HandshakeOutStream s) throws IOException {
s.putInt8(statusType.id);
if (statusType == CertStatusRequestType.OCSP) {
s.putBytes24(encodedResponses.get(0));
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
s.putInt24(encodedResponsesLen);
for (byte[] respBytes : encodedResponses) {
if (respBytes != null) {
s.putBytes24(respBytes);
} else {
s.putBytes24(null);
}
}
} else {
// It is highly unlikely that we will fall into this section
// of the code.
throw new SSLHandshakeException("Unsupported status_type: " +
statusType.id);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
// Stringify the encoded OCSP response list
for (byte[] respDER : encodedResponses) {
if (respDER.length > 0) {
try {
OCSPResponse oResp = new OCSPResponse(respDER);
sb.append(oResp.toString()).append("\n");
} catch (IOException ioe) {
sb.append("OCSP Response Exception: ").append(ioe)
.append("\n");
}
} else {
sb.append("<Zero-length entry>\n");
}
}
MessageFormat messageFormat = new MessageFormat(
"\"CertificateStatus\": '{'\n" +
" \"type\" : \"{0}\",\n" +
" \"responses \" : [\n" + "{1}\n" + " ]\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
statusType.name,
Utilities.indent(Utilities.indent(sb.toString()))
};
return messageFormat.format(messageFields);
}
}
/**
* The CertificateStatus handshake message consumer.
*/
private static final class CertificateStatusConsumer
implements SSLConsumer {
// Prevent instantiation of this class.
private CertificateStatusConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
CertificateStatusMessage cst =
new CertificateStatusMessage(chc, message);
// Log the message
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming server CertificateStatus handshake message",
cst);
}
// Pin the received responses to the SSLSessionImpl. It will
// be retrieved by the X509TrustManagerImpl during the certificate
// checking phase.
chc.handshakeSession.setStatusResponses(cst.encodedResponses);
// Now perform the check
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
// Update the handshake consumers to remove this message, indicating
// that it has been processed.
chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_STATUS.id);
}
}
/**
* The CertificateStatus handshake message consumer.
*/
private static final class CertificateStatusProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private CertificateStatusProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// Only the server-side should be a producer of this message
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// If stapling is not active, immediately return without producing
// a message or any further processing.
if (!shc.staplingActive) {
return null;
}
// Create the CertificateStatus message from info in the
CertificateStatusMessage csm = new CertificateStatusMessage(shc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced server CertificateStatus handshake message", csm);
}
// Output the handshake message.
csm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The handshake message has been delivered.
return null;
}
}
private static final class CertificateStatusAbsence
implements HandshakeAbsence {
// Prevent instantiation of this class
private CertificateStatusAbsence() {
// blank
}
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Processing should only continue if stapling is active
if (chc.staplingActive) {
// Because OCSP stapling is active, it means two things
// if we're here: 1) The server hello asserted the
// status_request[_v2] extension. 2) The CertificateStatus
// message was not sent. This means that cert path checking
// was deferred, but must happen immediately.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Server did not send CertificateStatus, " +
"checking cert chain without status info.");
}
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,252 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.net.ssl.SSLException;
import sun.security.ssl.SSLCipher.SSLReadCipher;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SSLTrafficKeyDerivation.LegacyTrafficKeyDerivation;
/**
* Pack of the ChangeCipherSpec message.
*/
final class ChangeCipherSpec {
static final SSLConsumer t10Consumer =
new T10ChangeCipherSpecConsumer();
static final HandshakeProducer t10Producer =
new T10ChangeCipherSpecProducer();
static final SSLConsumer t13Consumer =
new T13ChangeCipherSpecConsumer();
/**
* The "ChangeCipherSpec" message producer.
*/
private static final
class T10ChangeCipherSpecProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private T10ChangeCipherSpecProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
SSLKeyDerivation kd = hc.handshakeKeyDerivation;
if (!(kd instanceof LegacyTrafficKeyDerivation)) {
throw new UnsupportedOperationException("Not supported.");
}
LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
CipherSuite ncs = hc.negotiatedCipherSuite;
Authenticator writeAuthenticator;
if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
writeAuthenticator =
Authenticator.valueOf(hc.negotiatedProtocol);
} else {
try {
writeAuthenticator = Authenticator.valueOf(
hc.negotiatedProtocol, ncs.macAlg,
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"clientMacKey" : "serverMacKey"));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
// unlikely
throw new SSLException("Algorithm missing: ", e);
}
}
SecretKey writeKey =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"clientWriteKey" : "serverWriteKey");
SecretKey writeIv =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"clientWriteIv" : "serverWriteIv");
IvParameterSpec iv = (writeIv == null) ? null :
new IvParameterSpec(writeIv.getEncoded());
SSLWriteCipher writeCipher;
try {
writeCipher = ncs.bulkCipher.createWriteCipher(
writeAuthenticator,
hc.negotiatedProtocol, writeKey, iv,
hc.sslContext.getSecureRandom());
} catch (GeneralSecurityException gse) {
// unlikely
throw new SSLException("Algorithm missing: ", gse);
}
if (writeCipher == null) {
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Illegal cipher suite (" + ncs +
") and protocol version (" + hc.negotiatedProtocol + ")");
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced ChangeCipherSpec message");
}
hc.conContext.outputRecord.changeWriteCiphers(writeCipher, true);
// The handshake message has been delivered.
return null;
}
}
/**
* The "ChangeCipherSpec" message producer.
*/
private static final
class T10ChangeCipherSpecConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T10ChangeCipherSpecConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
TransportContext tc = (TransportContext)context;
// This consumer can be used only once.
tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
// parse
if (message.remaining() != 1 || message.get() != 1) {
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
"Malformed or unexpected ChangeCipherSpec message");
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Consuming ChangeCipherSpec message");
}
// validate
if (tc.handshakeContext == null) {
throw tc.fatal(Alert.HANDSHAKE_FAILURE,
"Unexpected ChangeCipherSpec message");
}
HandshakeContext hc = tc.handshakeContext;
if (hc.handshakeKeyDerivation == null) {
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ChangeCipherSpec message");
}
SSLKeyDerivation kd = hc.handshakeKeyDerivation;
if (kd instanceof LegacyTrafficKeyDerivation) {
LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
CipherSuite ncs = hc.negotiatedCipherSuite;
Authenticator readAuthenticator;
if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
readAuthenticator =
Authenticator.valueOf(hc.negotiatedProtocol);
} else {
try {
readAuthenticator = Authenticator.valueOf(
hc.negotiatedProtocol, ncs.macAlg,
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"serverMacKey" : "clientMacKey"));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
// unlikely
throw new SSLException("Algorithm missing: ", e);
}
}
SecretKey readKey =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"serverWriteKey" : "clientWriteKey");
SecretKey readIv =
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
"serverWriteIv" : "clientWriteIv");
IvParameterSpec iv = (readIv == null) ? null :
new IvParameterSpec(readIv.getEncoded());
SSLReadCipher readCipher;
try {
readCipher = ncs.bulkCipher.createReadCipher(
readAuthenticator,
hc.negotiatedProtocol, readKey, iv,
hc.sslContext.getSecureRandom());
} catch (GeneralSecurityException gse) {
// unlikely
throw new SSLException("Algorithm missing: ", gse);
}
if (readCipher == null) {
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
") and protocol version (" + hc.negotiatedProtocol +
")");
}
tc.inputRecord.changeReadCiphers(readCipher);
} else {
throw new UnsupportedOperationException("Not supported.");
}
}
}
private static final
class T13ChangeCipherSpecConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T13ChangeCipherSpecConsumer() {
// blank
}
// An implementation may receive an unencrypted record of type
// change_cipher_spec consisting of the single byte value 0x01
// at any time after the first ClientHello message has been
// sent or received and before the peer's Finished message has
// been received and MUST simply drop it without further
// processing.
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
TransportContext tc = (TransportContext)context;
// This consumer can be used only once.
tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
// parse
if (message.remaining() != 1 || message.get() != 1) {
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
"Malformed or unexpected ChangeCipherSpec message");
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Consuming ChangeCipherSpec message");
}
// no further processing
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, 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;
/**
* Enum for SSL/TLS cipher types.
*/
enum CipherType {
NULL_CIPHER, // null cipher
STREAM_CIPHER, // stream cipher
BLOCK_CIPHER, // block cipher in CBC mode
AEAD_CIPHER // AEAD cipher
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2015, 2018, 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 javax.net.ssl.SSLEngineResult.HandshakeStatus;
/*
* Ciphertext
*/
final class Ciphertext {
final byte contentType;
final byte handshakeType;
final long recordSN;
HandshakeStatus handshakeStatus; // null if not used or not handshaking
private Ciphertext() {
this.contentType = 0;
this.handshakeType = -1;
this.recordSN = -1L;
this.handshakeStatus = null;
}
Ciphertext(byte contentType, byte handshakeType, long recordSN) {
this.contentType = contentType;
this.handshakeType = handshakeType;
this.recordSN = recordSN;
this.handshakeStatus = null;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015, 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;
/**
* Client authentication type.
*/
enum ClientAuthType {
CLIENT_AUTH_NONE, // turn off client authentication
CLIENT_AUTH_REQUESTED, // need to request client authentication
CLIENT_AUTH_REQUIRED // require client authentication
}

View File

@@ -0,0 +1,115 @@
/*
* 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.io.IOException;
import java.security.cert.X509Certificate;
import sun.security.ssl.ClientHello.ClientHelloMessage;
class ClientHandshakeContext extends HandshakeContext {
/*
* Allow unsafe server certificate change?
*
* Server certificate change during SSL/TLS renegotiation may be considered
* unsafe, as described in the Triple Handshake attacks:
*
* https://secure-resumption.com/tlsauth.pdf
*
* Endpoint identification (See
* SSLParameters.getEndpointIdentificationAlgorithm()) is a pretty nice
* guarantee that the server certificate change in renegotiation is legal.
* However, endpoint identification is only enabled for HTTPS and LDAP
* over SSL/TLS by default. It is not enough to protect SSL/TLS
* connections other than HTTPS and LDAP.
*
* The renegotiation indication extension (See RFC 5746) is a pretty
* strong guarantee that the endpoints on both client and server sides
* are identical on the same connection. However, the Triple Handshake
* attacks can bypass this guarantee if there is a session-resumption
* handshake between the initial full handshake and the renegotiation
* full handshake.
*
* Server certificate change may be unsafe and should be restricted if
* endpoint identification is not enabled and the previous handshake is
* a session-resumption abbreviated initial handshake, unless the
* identities represented by both certificates can be regraded as the
* same (See isIdentityEquivalent()).
*
* Considering the compatibility impact and the actual requirements to
* support server certificate change in practice, the system property,
* jdk.tls.allowUnsafeServerCertChange, is used to define whether unsafe
* server certificate change in renegotiation is allowed or not. The
* default value of the system property is "false". To mitigate the
* compatibility impact, applications may want to set the system
* property to "true" at their own risk.
*
* If the value of the system property is "false", server certificate
* change in renegotiation after a session-resumption abbreviated initial
* handshake is restricted (See isIdentityEquivalent()).
*
* If the system property is set to "true" explicitly, the restriction on
* server certificate change in renegotiation is disabled.
*/
static final boolean allowUnsafeServerCertChange =
Utilities.getBooleanProperty(
"jdk.tls.allowUnsafeServerCertChange", false);
/*
* the reserved server certificate chain in previous handshaking
*
* The server certificate chain is only reserved if the previous
* handshake is a session-resumption abbreviated initial handshake.
*/
X509Certificate[] reservedServerCerts = null;
X509Certificate[] deferredCerts;
ClientHelloMessage initialClientHelloMsg = null;
// Flag to indicate receipt of a CertificateRequest message from
// the server. Because this is optional, we cannot guarantee
// the handshakeConsumers Map will always have it present there.
boolean receivedCertReq = false;
// PSK identity is selected in first Hello and used again after HRR
byte[] pskIdentity;
ClientHandshakeContext(SSLContextImpl sslContext,
TransportContext conContext) throws IOException {
super(sslContext, conContext);
}
@Override
void kickstart() throws IOException {
if (kickstartMessageDelivered) {
return;
}
SSLHandshake.kickstart(this);
kickstartMessageDelivered = true;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2003, 2018, 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.ByteBuffer;
import java.util.Map;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class ClientKeyExchange {
static final SSLConsumer handshakeConsumer =
new ClientKeyExchangeConsumer();
static final HandshakeProducer handshakeProducer =
new ClientKeyExchangeProducer();
/**
* The "ClientKeyExchange" handshake message producer.
*/
private static final
class ClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private ClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke != null) {
for (Map.Entry<Byte, HandshakeProducer> hp :
ke.getHandshakeProducers(chc)) {
if (hp.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
return hp.getValue().produce(context, message);
}
}
}
// not consumer defined.
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ClientKeyExchange handshake message.");
}
}
/**
* The "ClientKeyExchange" handshake message consumer.
*/
private static final
class ClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private ClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// clean up this consumer
shc.handshakeConsumers.remove(SSLHandshake.CLIENT_KEY_EXCHANGE.id);
// Check for an unprocessed client Certificate message. If that
// handshake consumer is still present then that expected message
// was not sent.
if (shc.handshakeConsumers.containsKey(
SSLHandshake.CERTIFICATE.id)) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ClientKeyExchange handshake message.");
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke != null) {
for (Map.Entry<Byte, SSLConsumer> hc :
ke.getHandshakeConsumers(shc)) {
if (hc.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
hc.getValue().consume(context, message);
return;
}
}
}
// not consumer defined.
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ClientKeyExchange handshake message.");
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018, 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;
/**
* SSL/TLS connection context.
*/
interface ConnectionContext {
// blank
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2018, 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;
/**
* Enum for SSL/TLS content types.
*/
enum ContentType {
INVALID ((byte)0, "invalid",
ProtocolVersion.PROTOCOLS_OF_13),
CHANGE_CIPHER_SPEC ((byte)20, "change_cipher_spec",
ProtocolVersion.PROTOCOLS_TO_12),
ALERT ((byte)21, "alert",
ProtocolVersion.PROTOCOLS_TO_13),
HANDSHAKE ((byte)22, "handshake",
ProtocolVersion.PROTOCOLS_TO_13),
APPLICATION_DATA ((byte)23, "application_data",
ProtocolVersion.PROTOCOLS_TO_13);
final byte id;
final String name;
final ProtocolVersion[] supportedProtocols;
private ContentType(byte id, String name,
ProtocolVersion[] supportedProtocols) {
this.id = id;
this.name = name;
this.supportedProtocols = supportedProtocols;
}
static ContentType valueOf(byte id) {
for (ContentType ct : ContentType.values()) {
if (ct.id == id) {
return ct;
}
}
return null;
}
static String nameOf(byte id) {
for (ContentType ct : ContentType.values()) {
if (ct.id == id) {
return ct.name;
}
}
return "<UNKNOWN CONTENT TYPE: " + (id & 0x0FF) + ">";
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.Locale;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.ServerHello.ServerHelloMessage;
import sun.misc.HexDumpEncoder;
public class CookieExtension {
static final HandshakeProducer chNetworkProducer =
new CHCookieProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHCookieConsumer();
static final HandshakeConsumer chOnTradeConsumer =
new CHCookieUpdate();
static final HandshakeProducer hrrNetworkProducer =
new HRRCookieProducer();
static final ExtensionConsumer hrrOnLoadConsumer =
new HRRCookieConsumer();
static final HandshakeProducer hrrNetworkReproducer =
new HRRCookieReproducer();
static final CookieStringizer cookieStringizer =
new CookieStringizer();
/**
* The "cookie" extension.
*/
static class CookieSpec implements SSLExtensionSpec {
final byte[] cookie;
private CookieSpec(ByteBuffer m) throws IOException {
// opaque cookie<1..2^16-1>;
if (m.remaining() < 3) {
throw new SSLProtocolException(
"Invalid cookie extension: insufficient data");
}
this.cookie = Record.getBytes16(m);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"cookie\": '{'\n" +
"{0}\n" +
"'}',", Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(hexEncoder.encode(cookie))
};
return messageFormat.format(messageFields);
}
}
private static final class CookieStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new CookieSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
private static final
class CHCookieProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHCookieProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext) context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable cookie extension");
}
return null;
}
// response to an HelloRetryRequest cookie
CookieSpec spec = (CookieSpec)chc.handshakeExtensions.get(
SSLExtension.HRR_COOKIE);
if (spec != null &&
spec.cookie != null && spec.cookie.length != 0) {
byte[] extData = new byte[spec.cookie.length + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putBytes16(m, spec.cookie);
return extData;
}
return null;
}
}
private static final
class CHCookieConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHCookieConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable cookie extension");
}
return; // ignore the extension
}
CookieSpec spec;
try {
spec = new CookieSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
shc.handshakeExtensions.put(SSLExtension.CH_COOKIE, spec);
// No impact on session resumption.
//
// Note that the protocol version negotiation happens before the
// session resumption negotiation. And the session resumption
// negotiation depends on the negotiated protocol version.
}
}
private static final
class CHCookieUpdate implements HandshakeConsumer {
// Prevent instantiation of this class.
private CHCookieUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ClientHelloMessage clientHello = (ClientHelloMessage)message;
CookieSpec spec = (CookieSpec)
shc.handshakeExtensions.get(SSLExtension.CH_COOKIE);
if (spec == null) {
// Ignore, no "cookie" extension requested.
return;
}
HelloCookieManager hcm =
shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
if (!hcm.isCookieValid(shc, clientHello, spec.cookie)) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"unrecognized cookie");
}
}
}
private static final
class HRRCookieProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private HRRCookieProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ServerHelloMessage hrrm = (ServerHelloMessage)message;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable cookie extension");
}
return null;
}
HelloCookieManager hcm =
shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
byte[] cookie = hcm.createCookie(shc, hrrm.clientHello);
byte[] extData = new byte[cookie.length + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putBytes16(m, cookie);
return extData;
}
}
private static final
class HRRCookieConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private HRRCookieConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable cookie extension");
}
return; // ignore the extension
}
CookieSpec spec;
try {
spec = new CookieSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
chc.handshakeExtensions.put(SSLExtension.HRR_COOKIE, spec);
}
}
private static final
class HRRCookieReproducer implements HandshakeProducer {
// Prevent instantiation of this class.
private HRRCookieReproducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext) context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable cookie extension");
}
return null;
}
// copy of the ClientHello cookie
CookieSpec spec = (CookieSpec)shc.handshakeExtensions.get(
SSLExtension.CH_COOKIE);
if (spec != null &&
spec.cookie != null && spec.cookie.length != 0) {
byte[] extData = new byte[spec.cookie.length + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putBytes16(m, spec.cookie);
return extData;
}
return null;
}
}
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright (c) 2003, 2018, 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.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.DHKeyExchange.DHECredentials;
import sun.security.ssl.DHKeyExchange.DHEPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.misc.HexDumpEncoder;
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class DHClientKeyExchange {
static final DHClientKeyExchangeConsumer dhHandshakeConsumer =
new DHClientKeyExchangeConsumer();
static final DHClientKeyExchangeProducer dhHandshakeProducer =
new DHClientKeyExchangeProducer();
/**
* The DiffieHellman ClientKeyExchange handshake message.
*
* If the client has sent a certificate which contains a suitable
* DiffieHellman key (for fixed_dh client authentication), then the
* client public value is implicit and does not need to be sent again.
* In this case, the client key exchange message will be sent, but it
* MUST be empty.
*
* Currently, we don't support cipher suite that requires implicit public
* key of client.
*/
private static final
class DHClientKeyExchangeMessage extends HandshakeMessage {
private byte[] y; // 1 to 2^16 - 1 bytes
DHClientKeyExchangeMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
// This happens in client side only.
ClientHandshakeContext chc =
(ClientHandshakeContext)handshakeContext;
DHEPossession dhePossession = null;
for (SSLPossession possession : chc.handshakePossessions) {
if (possession instanceof DHEPossession) {
dhePossession = (DHEPossession)possession;
break;
}
}
if (dhePossession == null) {
// unlikely
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No DHE credentials negotiated for client key exchange");
}
DHPublicKey publicKey = dhePossession.publicKey;
DHParameterSpec params = publicKey.getParams();
this.y = Utilities.toByteArray(publicKey.getY());
}
DHClientKeyExchangeMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// This happens in server side only.
ServerHandshakeContext shc =
(ServerHandshakeContext)handshakeContext;
if (m.remaining() < 3) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ClientKeyExchange message: insufficient data");
}
this.y = Record.getBytes16(m);
if (m.hasRemaining()) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ClientKeyExchange message: unknown extra data");
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
@Override
public int messageLength() {
return y.length + 2; // 2: length filed
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putBytes16(y);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"DH ClientKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"dh_Yc\": '{'\n" +
"{0}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(y), " "),
};
return messageFormat.format(messageFields);
}
}
/**
* The DiffieHellman "ClientKeyExchange" handshake message producer.
*/
private static final
class DHClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private DHClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
DHECredentials dheCredentials = null;
for (SSLCredentials cd : chc.handshakeCredentials) {
if (cd instanceof DHECredentials) {
dheCredentials = (DHECredentials)cd;
break;
}
}
if (dheCredentials == null) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No DHE credentials negotiated for client key exchange");
}
DHEPossession dhePossession = new DHEPossession(
dheCredentials, chc.sslContext.getSecureRandom());
chc.handshakePossessions.add(dhePossession);
DHClientKeyExchangeMessage ckem =
new DHClientKeyExchangeMessage(chc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced DH ClientKeyExchange handshake message", ckem);
}
// Output the handshake message.
ckem.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
chc.negotiatedProtocol);
} else {
chc.handshakeKeyDerivation =
kd.createKeyDerivation(chc, masterSecret);
}
}
// The handshake message has been delivered.
return null;
}
}
/**
* The DiffieHellman "ClientKeyExchange" handshake message consumer.
*/
private static final
class DHClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private DHClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
DHEPossession dhePossession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof DHEPossession) {
dhePossession = (DHEPossession)possession;
break;
}
}
if (dhePossession == null) {
// unlikely
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No expected DHE possessions for client key exchange");
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
}
DHClientKeyExchangeMessage ckem =
new DHClientKeyExchangeMessage(shc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming DH ClientKeyExchange handshake message", ckem);
}
// create the credentials
try {
DHParameterSpec params = dhePossession.publicKey.getParams();
DHPublicKeySpec spec = new DHPublicKeySpec(
new BigInteger(1, ckem.y),
params.getP(), params.getG());
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
DHPublicKey peerPublicKey =
(DHPublicKey)kf.generatePublic(spec);
// check constraints of peer DHPublicKey
if (!shc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
peerPublicKey)) {
throw new SSLHandshakeException(
"DHPublicKey does not comply to algorithm constraints");
}
NamedGroup namedGroup = NamedGroup.valueOf(params);
shc.handshakeCredentials.add(
new DHECredentials(peerPublicKey, namedGroup));
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException)(new SSLHandshakeException(
"Could not generate DHPublicKey").initCause(e));
}
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}

View File

@@ -0,0 +1,534 @@
/*
* 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.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.CipherSuite.HashAlg;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.KeyUtil;
final class DHKeyExchange {
static final SSLPossessionGenerator poGenerator =
new DHEPossessionGenerator(false);
static final SSLPossessionGenerator poExportableGenerator =
new DHEPossessionGenerator(true);
static final SSLKeyAgreementGenerator kaGenerator =
new DHEKAGenerator();
static final class DHECredentials implements SSLCredentials {
final DHPublicKey popPublicKey;
final NamedGroup namedGroup;
DHECredentials(DHPublicKey popPublicKey, NamedGroup namedGroup) {
this.popPublicKey = popPublicKey;
this.namedGroup = namedGroup;
}
static DHECredentials valueOf(NamedGroup ng,
byte[] encodedPublic) throws IOException, GeneralSecurityException {
if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
throw new RuntimeException(
"Credentials decoding: Not FFDHE named group");
}
if (encodedPublic == null || encodedPublic.length == 0) {
return null;
}
DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();
if (params == null) {
return null;
}
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
DHPublicKeySpec spec = new DHPublicKeySpec(
new BigInteger(1, encodedPublic),
params.getP(), params.getG());
DHPublicKey publicKey =
(DHPublicKey)kf.generatePublic(spec);
return new DHECredentials(publicKey, ng);
}
}
static final class DHEPossession implements SSLPossession {
final PrivateKey privateKey;
final DHPublicKey publicKey;
final NamedGroup namedGroup;
DHEPossession(NamedGroup namedGroup, SecureRandom random) {
try {
KeyPairGenerator kpg =
JsseJce.getKeyPairGenerator("DiffieHellman");
DHParameterSpec params =
(DHParameterSpec)namedGroup.getParameterSpec();
kpg.initialize(params, random);
KeyPair kp = generateDHKeyPair(kpg);
if (kp == null) {
throw new RuntimeException("Could not generate DH keypair");
}
privateKey = kp.getPrivate();
publicKey = (DHPublicKey)kp.getPublic();
} catch (GeneralSecurityException gse) {
throw new RuntimeException(
"Could not generate DH keypair", gse);
}
this.namedGroup = namedGroup;
}
DHEPossession(int keyLength, SecureRandom random) {
DHParameterSpec params =
PredefinedDHParameterSpecs.definedParams.get(keyLength);
try {
KeyPairGenerator kpg =
JsseJce.getKeyPairGenerator("DiffieHellman");
if (params != null) {
kpg.initialize(params, random);
} else {
kpg.initialize(keyLength, random);
}
KeyPair kp = generateDHKeyPair(kpg);
if (kp == null) {
throw new RuntimeException(
"Could not generate DH keypair of " +
keyLength + " bits");
}
privateKey = kp.getPrivate();
publicKey = (DHPublicKey)kp.getPublic();
} catch (GeneralSecurityException gse) {
throw new RuntimeException(
"Could not generate DH keypair", gse);
}
this.namedGroup = NamedGroup.valueOf(publicKey.getParams());
}
DHEPossession(DHECredentials credentials, SecureRandom random) {
try {
KeyPairGenerator kpg =
JsseJce.getKeyPairGenerator("DiffieHellman");
kpg.initialize(credentials.popPublicKey.getParams(), random);
KeyPair kp = generateDHKeyPair(kpg);
if (kp == null) {
throw new RuntimeException("Could not generate DH keypair");
}
privateKey = kp.getPrivate();
publicKey = (DHPublicKey)kp.getPublic();
} catch (GeneralSecurityException gse) {
throw new RuntimeException(
"Could not generate DH keypair", gse);
}
this.namedGroup = credentials.namedGroup;
}
// Generate and validate DHPublicKeySpec
private KeyPair generateDHKeyPair(
KeyPairGenerator kpg) throws GeneralSecurityException {
boolean doExtraValiadtion =
(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
boolean isRecovering = false;
for (int i = 0; i <= 2; i++) { // Try to recover from failure.
KeyPair kp = kpg.generateKeyPair();
// validate the Diffie-Hellman public key
if (doExtraValiadtion) {
DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
try {
KeyUtil.validate(spec);
} catch (InvalidKeyException ivke) {
if (isRecovering) {
throw ivke;
}
// otherwise, ignore the exception and try again
isRecovering = true;
continue;
}
}
return kp;
}
return null;
}
private static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
if (key instanceof DHPublicKey) {
DHPublicKey dhKey = (DHPublicKey)key;
DHParameterSpec params = dhKey.getParams();
return new DHPublicKeySpec(dhKey.getY(),
params.getP(), params.getG());
}
try {
KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
return factory.getKeySpec(key, DHPublicKeySpec.class);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
// unlikely
throw new RuntimeException("Unable to get DHPublicKeySpec", e);
}
}
@Override
public byte[] encode() {
// Note: the DH public value is encoded as a big-endian integer
// and padded to the left with zeros to the size of p in bytes.
byte[] encoded = Utilities.toByteArray(publicKey.getY());
int pSize = (KeyUtil.getKeySize(publicKey) + 7) >>> 3;
if (pSize > 0 && encoded.length < pSize) {
byte[] buffer = new byte[pSize];
System.arraycopy(encoded, 0,
buffer, pSize - encoded.length, encoded.length);
encoded = buffer;
}
return encoded;
}
}
private static final class
DHEPossessionGenerator implements SSLPossessionGenerator {
// Flag to use smart ephemeral DH key which size matches the
// corresponding authentication key
private static final boolean useSmartEphemeralDHKeys;
// Flag to use legacy ephemeral DH key which size is 512 bits for
// exportable cipher suites, and 768 bits for others
private static final boolean useLegacyEphemeralDHKeys;
// The customized ephemeral DH key size for non-exportable
// cipher suites.
private static final int customizedDHKeySize;
// Is it for exportable cipher suite?
private final boolean exportable;
static {
String property = GetPropertyAction.privilegedGetProperty(
"jdk.tls.ephemeralDHKeySize");
if (property == null || property.isEmpty()) {
useLegacyEphemeralDHKeys = false;
useSmartEphemeralDHKeys = false;
customizedDHKeySize = -1;
} else if ("matched".equals(property)) {
useLegacyEphemeralDHKeys = false;
useSmartEphemeralDHKeys = true;
customizedDHKeySize = -1;
} else if ("legacy".equals(property)) {
useLegacyEphemeralDHKeys = true;
useSmartEphemeralDHKeys = false;
customizedDHKeySize = -1;
} else {
useLegacyEphemeralDHKeys = false;
useSmartEphemeralDHKeys = false;
try {
// DH parameter generation can be extremely slow, best to
// use one of the supported pre-computed DH parameters
// (see DHCrypt class).
customizedDHKeySize = Integer.parseUnsignedInt(property);
if (customizedDHKeySize < 1024 ||
customizedDHKeySize > 8192 ||
(customizedDHKeySize & 0x3f) != 0) {
throw new IllegalArgumentException(
"Unsupported customized DH key size: " +
customizedDHKeySize + ". " +
"The key size must be multiple of 64, " +
"and range from 1024 to 8192 (inclusive)");
}
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"Invalid system property jdk.tls.ephemeralDHKeySize");
}
}
}
// Prevent instantiation of this class.
private DHEPossessionGenerator(boolean exportable) {
this.exportable = exportable;
}
// Used for ServerKeyExchange, TLS 1.2 and prior versions.
@Override
public SSLPossession createPossession(HandshakeContext context) {
NamedGroup preferableNamedGroup = null;
if (!useLegacyEphemeralDHKeys &&
(context.clientRequestedNamedGroups != null) &&
(!context.clientRequestedNamedGroups.isEmpty())) {
preferableNamedGroup =
SupportedGroups.getPreferredGroup(
context.negotiatedProtocol,
context.algorithmConstraints,
NamedGroupType.NAMED_GROUP_FFDHE,
context.clientRequestedNamedGroups);
if (preferableNamedGroup != null) {
return new DHEPossession(preferableNamedGroup,
context.sslContext.getSecureRandom());
}
}
/*
* 768 bits ephemeral DH private keys were used to be used in
* ServerKeyExchange except that exportable ciphers max out at 512
* bits modulus values. We still adhere to this behavior in legacy
* mode (system property "jdk.tls.ephemeralDHKeySize" is defined
* as "legacy").
*
* Old JDK (JDK 7 and previous) releases don't support DH keys
* bigger than 1024 bits. We have to consider the compatibility
* requirement. 1024 bits DH key is always used for non-exportable
* cipher suites in default mode (system property
* "jdk.tls.ephemeralDHKeySize" is not defined).
*
* However, if applications want more stronger strength, setting
* system property "jdk.tls.ephemeralDHKeySize" to "matched"
* is a workaround to use ephemeral DH key which size matches the
* corresponding authentication key. For example, if the public key
* size of an authentication certificate is 2048 bits, then the
* ephemeral DH key size should be 2048 bits accordingly unless
* the cipher suite is exportable. This key sizing scheme keeps
* the cryptographic strength consistent between authentication
* keys and key-exchange keys.
*
* Applications may also want to customize the ephemeral DH key
* size to a fixed length for non-exportable cipher suites. This
* can be approached by setting system property
* "jdk.tls.ephemeralDHKeySize" to a valid positive integer between
* 1024 and 8192 bits, inclusive.
*
* Note that the minimum acceptable key size is 1024 bits except
* exportable cipher suites or legacy mode.
*
* Note that per RFC 2246, the key size limit of DH is 512 bits for
* exportable cipher suites. Because of the weakness, exportable
* cipher suites are deprecated since TLS v1.1 and they are not
* enabled by default in Oracle provider. The legacy behavior is
* reserved and 512 bits DH key is always used for exportable
* cipher suites.
*/
int keySize = exportable ? 512 : 1024; // default mode
if (!exportable) {
if (useLegacyEphemeralDHKeys) { // legacy mode
keySize = 768;
} else if (useSmartEphemeralDHKeys) { // matched mode
PrivateKey key = null;
ServerHandshakeContext shc =
(ServerHandshakeContext)context;
if (shc.interimAuthn instanceof X509Possession) {
key = ((X509Possession)shc.interimAuthn).popPrivateKey;
}
if (key != null) {
int ks = KeyUtil.getKeySize(key);
// DH parameter generation can be extremely slow, make
// sure to use one of the supported pre-computed DH
// parameters.
//
// Old deployed applications may not be ready to
// support DH key sizes bigger than 2048 bits. Please
// DON'T use value other than 1024 and 2048 at present.
// May improve the underlying providers and key size
// limit in the future when the compatibility and
// interoperability impact is limited.
keySize = ks <= 1024 ? 1024 : 2048;
} // Otherwise, anonymous cipher suites, 1024-bit is used.
} else if (customizedDHKeySize > 0) { // customized mode
keySize = customizedDHKeySize;
}
}
return new DHEPossession(
keySize, context.sslContext.getSecureRandom());
}
}
private static final
class DHEKAGenerator implements SSLKeyAgreementGenerator {
static private DHEKAGenerator instance = new DHEKAGenerator();
// Prevent instantiation of this class.
private DHEKAGenerator() {
// blank
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
DHEPossession dhePossession = null;
DHECredentials dheCredentials = null;
for (SSLPossession poss : context.handshakePossessions) {
if (!(poss instanceof DHEPossession)) {
continue;
}
DHEPossession dhep = (DHEPossession)poss;
for (SSLCredentials cred : context.handshakeCredentials) {
if (!(cred instanceof DHECredentials)) {
continue;
}
DHECredentials dhec = (DHECredentials)cred;
if (dhep.namedGroup != null && dhec.namedGroup != null) {
if (dhep.namedGroup.equals(dhec.namedGroup)) {
dheCredentials = (DHECredentials)cred;
break;
}
} else {
DHParameterSpec pps = dhep.publicKey.getParams();
DHParameterSpec cps = dhec.popPublicKey.getParams();
if (pps.getP().equals(cps.getP()) &&
pps.getG().equals(cps.getG())) {
dheCredentials = (DHECredentials)cred;
break;
}
}
}
if (dheCredentials != null) {
dhePossession = (DHEPossession)poss;
break;
}
}
if (dhePossession == null || dheCredentials == null) {
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient DHE key agreement parameters negotiated");
}
return new DHEKAKeyDerivation(context,
dhePossession.privateKey, dheCredentials.popPublicKey);
}
private static final
class DHEKAKeyDerivation implements SSLKeyDerivation {
private final HandshakeContext context;
private final PrivateKey localPrivateKey;
private final PublicKey peerPublicKey;
DHEKAKeyDerivation(HandshakeContext context,
PrivateKey localPrivateKey,
PublicKey peerPublicKey) {
this.context = context;
this.localPrivateKey = localPrivateKey;
this.peerPublicKey = peerPublicKey;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
return t12DeriveKey(algorithm, params);
} else {
return t13DeriveKey(algorithm, params);
}
}
private SecretKey t12DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
try {
KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey preMasterSecret =
ka.generateSecret("TlsPremasterSecret");
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
if (mskd == null) {
// unlikely
throw new SSLHandshakeException(
"No expected master key derivation for protocol: " +
context.negotiatedProtocol.name);
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
private SecretKey t13DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
try {
KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey sharedSecret =
ka.generateSecret("TlsPremasterSecret");
HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
SSLKeyDerivation kd = context.handshakeKeyDerivation;
HKDF hkdf = new HKDF(hashAlg.name);
if (kd == null) { // No PSK is in use.
// If PSK is not in use Early Secret will still be
// HKDF-Extract(0, 0).
byte[] zeros = new byte[hashAlg.hashLength];
SecretKeySpec ikm =
new SecretKeySpec(zeros, "TlsPreSharedSecret");
SecretKey earlySecret =
hkdf.extract(zeros, ikm, "TlsEarlySecret");
kd = new SSLSecretDerivation(context, earlySecret);
}
// derive salt secret
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
// derive handshake secret
return hkdf.extract(saltSecret, sharedSecret, algorithm);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
}
}
}

View File

@@ -0,0 +1,557 @@
/*
* Copyright (c) 2015, 2018, 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.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;
import sun.security.ssl.DHKeyExchange.DHECredentials;
import sun.security.ssl.DHKeyExchange.DHEPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;
import sun.security.util.KeyUtil;
/**
* Pack of the ServerKeyExchange handshake message.
*/
final class DHServerKeyExchange {
static final SSLConsumer dhHandshakeConsumer =
new DHServerKeyExchangeConsumer();
static final HandshakeProducer dhHandshakeProducer =
new DHServerKeyExchangeProducer();
/**
* The DiffieHellman ServerKeyExchange handshake message.
*/
private static final
class DHServerKeyExchangeMessage extends HandshakeMessage {
// public key encapsulated in this message
private final byte[] p; // 1 to 2^16 - 1 bytes
private final byte[] g; // 1 to 2^16 - 1 bytes
private final byte[] y; // 1 to 2^16 - 1 bytes
// the signature algorithm used by this ServerKeyExchange message
private final boolean useExplicitSigAlgorithm;
private final SignatureScheme signatureScheme;
// signature bytes, or null if anonymous
private final byte[] paramsSignature;
DHServerKeyExchangeMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
// This happens in server side only.
ServerHandshakeContext shc =
(ServerHandshakeContext)handshakeContext;
DHEPossession dhePossession = null;
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof DHEPossession) {
dhePossession = (DHEPossession)possession;
if (x509Possession != null) {
break;
}
} else if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
if (dhePossession != null) {
break;
}
}
}
if (dhePossession == null) {
// unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No DHE credentials negotiated for server key exchange");
}
DHPublicKey publicKey = dhePossession.publicKey;
DHParameterSpec params = publicKey.getParams();
this.p = Utilities.toByteArray(params.getP());
this.g = Utilities.toByteArray(params.getG());
this.y = Utilities.toByteArray(publicKey.getY());
if (x509Possession == null) {
// anonymous, no authentication, no signature
paramsSignature = null;
signatureScheme = null;
useExplicitSigAlgorithm = false;
} else {
useExplicitSigAlgorithm =
shc.negotiatedProtocol.useTLS12PlusSpec();
Signature signer = null;
if (useExplicitSigAlgorithm) {
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
shc.peerRequestedSignatureSchemes,
x509Possession,
shc.negotiatedProtocol);
if (schemeAndSigner == null) {
// Unlikely, the credentials generator should have
// selected the preferable signature algorithm properly.
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No supported signature algorithm for " +
x509Possession.popPrivateKey.getAlgorithm() +
" key");
} else {
signatureScheme = schemeAndSigner.getKey();
signer = schemeAndSigner.getValue();
}
} else {
signatureScheme = null;
try {
signer = getSignature(
x509Possession.popPrivateKey.getAlgorithm(),
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Possession.popPrivateKey.getAlgorithm(), e);
}
}
byte[] signature = null;
try {
updateSignature(signer, shc.clientHelloRandom.randomBytes,
shc.serverHelloRandom.randomBytes);
signature = signer.sign();
} catch (SignatureException ex) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failed to sign dhe parameters: " +
x509Possession.popPrivateKey.getAlgorithm(), ex);
}
paramsSignature = signature;
}
}
DHServerKeyExchangeMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// This happens in client side only.
ClientHandshakeContext chc =
(ClientHandshakeContext)handshakeContext;
this.p = Record.getBytes16(m);
this.g = Record.getBytes16(m);
this.y = Record.getBytes16(m);
try {
KeyUtil.validate(new DHPublicKeySpec(
new BigInteger(1, y),
new BigInteger(1, p),
new BigInteger(1, p)));
} catch (InvalidKeyException ike) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ServerKeyExchange: invalid parameters", ike);
}
X509Credentials x509Credentials = null;
for (SSLCredentials cd : chc.handshakeCredentials) {
if (cd instanceof X509Credentials) {
x509Credentials = (X509Credentials)cd;
break;
}
}
if (x509Credentials == null) {
// anonymous, no authentication, no signature
if (m.hasRemaining()) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ServerKeyExchange: unknown extra data");
}
this.signatureScheme = null;
this.paramsSignature = null;
this.useExplicitSigAlgorithm = false;
return;
}
this.useExplicitSigAlgorithm =
chc.negotiatedProtocol.useTLS12PlusSpec();
if (useExplicitSigAlgorithm) {
int ssid = Record.getInt16(m);
signatureScheme = SignatureScheme.valueOf(ssid);
if (signatureScheme == null) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature algorithm (" + ssid +
") used in DH ServerKeyExchange handshake message");
}
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsupported signature algorithm (" +
signatureScheme.name +
") used in DH ServerKeyExchange handshake message");
}
} else {
this.signatureScheme = null;
}
// read and verify the signature
this.paramsSignature = Record.getBytes16(m);
Signature signer;
if (useExplicitSigAlgorithm) {
try {
signer = signatureScheme.getVerifier(
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
}
} else {
try {
signer = getSignature(
x509Credentials.popPublicKey.getAlgorithm(),
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Credentials.popPublicKey.getAlgorithm(), e);
}
}
try {
updateSignature(signer,
chc.clientHelloRandom.randomBytes,
chc.serverHelloRandom.randomBytes);
if (!signer.verify(paramsSignature)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature on DH ServerKeyExchange message");
}
} catch (SignatureException ex) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Cannot verify DH ServerKeyExchange signature", ex);
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.SERVER_KEY_EXCHANGE;
}
@Override
public int messageLength() {
int sigLen = 0;
if (paramsSignature != null) {
sigLen = 2 + paramsSignature.length;
if (useExplicitSigAlgorithm) {
sigLen += SignatureScheme.sizeInRecord();
}
}
return 6 + p.length + g.length + y.length + sigLen;
// 6: overhead for p, g, y values
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putBytes16(p);
hos.putBytes16(g);
hos.putBytes16(y);
if (paramsSignature != null) {
if (useExplicitSigAlgorithm) {
hos.putInt16(signatureScheme.id);
}
hos.putBytes16(paramsSignature);
}
}
@Override
public String toString() {
if (paramsSignature == null) { // anonymous
MessageFormat messageFormat = new MessageFormat(
"\"DH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"dh_p\": '{'\n" +
"{0}\n" +
" '}',\n" +
" \"dh_g\": '{'\n" +
"{1}\n" +
" '}',\n" +
" \"dh_Ys\": '{'\n" +
"{2}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(p), " "),
Utilities.indent(
hexEncoder.encodeBuffer(g), " "),
Utilities.indent(
hexEncoder.encodeBuffer(y), " "),
};
return messageFormat.format(messageFields);
}
if (useExplicitSigAlgorithm) {
MessageFormat messageFormat = new MessageFormat(
"\"DH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"dh_p\": '{'\n" +
"{0}\n" +
" '}',\n" +
" \"dh_g\": '{'\n" +
"{1}\n" +
" '}',\n" +
" \"dh_Ys\": '{'\n" +
"{2}\n" +
" '}',\n" +
" '}',\n" +
" \"digital signature\": '{'\n" +
" \"signature algorithm\": \"{3}\"\n" +
" \"signature\": '{'\n" +
"{4}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(p), " "),
Utilities.indent(
hexEncoder.encodeBuffer(g), " "),
Utilities.indent(
hexEncoder.encodeBuffer(y), " "),
signatureScheme.name,
Utilities.indent(
hexEncoder.encodeBuffer(paramsSignature), " ")
};
return messageFormat.format(messageFields);
} else {
MessageFormat messageFormat = new MessageFormat(
"\"DH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"dh_p\": '{'\n" +
"{0}\n" +
" '}',\n" +
" \"dh_g\": '{'\n" +
"{1}\n" +
" '}',\n" +
" \"dh_Ys\": '{'\n" +
"{2}\n" +
" '}',\n" +
" '}',\n" +
" \"signature\": '{'\n" +
"{3}\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(p), " "),
Utilities.indent(
hexEncoder.encodeBuffer(g), " "),
Utilities.indent(
hexEncoder.encodeBuffer(y), " "),
Utilities.indent(
hexEncoder.encodeBuffer(paramsSignature), " ")
};
return messageFormat.format(messageFields);
}
}
private static Signature getSignature(String keyAlgorithm,
Key key) throws NoSuchAlgorithmException, InvalidKeyException {
Signature signer = null;
switch (keyAlgorithm) {
case "DSA":
signer = JsseJce.getSignature(JsseJce.SIGNATURE_DSA);
break;
case "RSA":
signer = RSASignature.getInstance();
break;
default:
throw new NoSuchAlgorithmException(
"neither an RSA or a DSA key : " + keyAlgorithm);
}
if (signer != null) {
if (key instanceof PublicKey) {
signer.initVerify((PublicKey)(key));
} else {
signer.initSign((PrivateKey)key);
}
}
return signer;
}
/*
* Update sig with nonces and Diffie-Hellman public key.
*/
private void updateSignature(Signature sig, byte[] clntNonce,
byte[] svrNonce) throws SignatureException {
int tmp;
sig.update(clntNonce);
sig.update(svrNonce);
sig.update((byte)(p.length >> 8));
sig.update((byte)(p.length & 0x0ff));
sig.update(p);
sig.update((byte)(g.length >> 8));
sig.update((byte)(g.length & 0x0ff));
sig.update(g);
sig.update((byte)(y.length >> 8));
sig.update((byte)(y.length & 0x0ff));
sig.update(y);
}
}
/**
* The DiffieHellman "ServerKeyExchange" handshake message producer.
*/
static final class DHServerKeyExchangeProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private DHServerKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
DHServerKeyExchangeMessage skem =
new DHServerKeyExchangeMessage(shc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced DH ServerKeyExchange handshake message", skem);
}
// Output the handshake message.
skem.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The handshake message has been delivered.
return null;
}
}
/**
* The DiffieHellman "ServerKeyExchange" handshake message consumer.
*/
static final class DHServerKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private DHServerKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
DHServerKeyExchangeMessage skem =
new DHServerKeyExchangeMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming DH ServerKeyExchange handshake message", skem);
}
//
// validate
//
// check constraints of EC PublicKey
DHPublicKey publicKey;
try {
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
DHPublicKeySpec spec = new DHPublicKeySpec(
new BigInteger(1, skem.y),
new BigInteger(1, skem.p),
new BigInteger(1, skem.g));
publicKey = (DHPublicKey)kf.generatePublic(spec);
} catch (GeneralSecurityException gse) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"Could not generate DHPublicKey", gse);
}
if (!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"DH ServerKeyExchange does not comply to " +
"algorithm constraints");
}
//
// update
//
NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams());
chc.handshakeCredentials.add(
new DHECredentials(publicKey, namedGroup));
//
// produce
//
// Need no new handshake message producers here.
}
}
}

View File

@@ -0,0 +1,536 @@
/*
* Copyright (c) 2003, 2018, 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.ByteBuffer;
import java.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class ECDHClientKeyExchange {
static final SSLConsumer ecdhHandshakeConsumer =
new ECDHClientKeyExchangeConsumer();
static final HandshakeProducer ecdhHandshakeProducer =
new ECDHClientKeyExchangeProducer();
static final SSLConsumer ecdheHandshakeConsumer =
new ECDHEClientKeyExchangeConsumer();
static final HandshakeProducer ecdheHandshakeProducer =
new ECDHEClientKeyExchangeProducer();
/**
* The ECDH/ECDHE ClientKeyExchange handshake message.
*/
private static final
class ECDHClientKeyExchangeMessage extends HandshakeMessage {
private final byte[] encodedPoint;
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
ECPublicKey publicKey) {
super(handshakeContext);
ECPoint point = publicKey.getW();
ECParameterSpec params = publicKey.getParams();
encodedPoint = JsseJce.encodePoint(point, params.getCurve());
}
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
if (m.remaining() != 0) { // explicit PublicValueEncoding
this.encodedPoint = Record.getBytes8(m);
} else {
this.encodedPoint = new byte[0];
}
}
// Check constraints of the specified EC public key.
static void checkConstraints(AlgorithmConstraints constraints,
ECPublicKey publicKey,
byte[] encodedPoint) throws SSLHandshakeException {
try {
ECParameterSpec params = publicKey.getParams();
ECPoint point =
JsseJce.decodePoint(encodedPoint, params.getCurve());
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
KeyFactory kf = JsseJce.getKeyFactory("EC");
ECPublicKey peerPublicKey =
(ECPublicKey)kf.generatePublic(spec);
// check constraints of ECPublicKey
if (constraints != null &&
!constraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
peerPublicKey)) {
throw new SSLHandshakeException(
"ECPublicKey does not comply to algorithm constraints");
}
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate ECPublicKey").initCause(e);
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
@Override
public int messageLength() {
if (encodedPoint == null || encodedPoint.length == 0) {
return 0;
} else {
return 1 + encodedPoint.length;
}
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
if (encodedPoint != null && encodedPoint.length != 0) {
hos.putBytes8(encodedPoint);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"ECDH ClientKeyExchange\": '{'\n" +
" \"ecdh public\": '{'\n" +
"{0}\n" +
" '}',\n" +
"'}'",
Locale.ENGLISH);
if (encodedPoint == null || encodedPoint.length == 0) {
Object[] messageFields = {
" <implicit>"
};
return messageFormat.format(messageFields);
} else {
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(encodedPoint), " "),
};
return messageFormat.format(messageFields);
}
}
}
/**
* The ECDH "ClientKeyExchange" handshake message producer.
*/
private static final
class ECDHClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private ECDHClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
X509Credentials x509Credentials = null;
for (SSLCredentials credential : chc.handshakeCredentials) {
if (credential instanceof X509Credentials) {
x509Credentials = (X509Credentials)credential;
break;
}
}
if (x509Credentials == null) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"No server certificate for ECDH client key exchange");
}
PublicKey publicKey = x509Credentials.popPublicKey;
if (!publicKey.getAlgorithm().equals("EC")) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Not EC server certificate for ECDH client key exchange");
}
ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported EC server cert for ECDH client key exchange");
}
ECDHEPossession ecdhePossession = new ECDHEPossession(
namedGroup, chc.sslContext.getSecureRandom());
chc.handshakePossessions.add(ecdhePossession);
ECDHClientKeyExchangeMessage cke =
new ECDHClientKeyExchangeMessage(
chc, ecdhePossession.publicKey);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced ECDH ClientKeyExchange handshake message", cke);
}
// Output the handshake message.
cke.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
chc.negotiatedProtocol);
} else {
chc.handshakeKeyDerivation =
kd.createKeyDerivation(chc, masterSecret);
}
}
// The handshake message has been delivered.
return null;
}
}
/**
* The ECDH "ClientKeyExchange" handshake message consumer.
*/
private static final
class ECDHClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private ECDHClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
break;
}
}
if (x509Possession == null) {
// unlikely, have been checked during cipher suite negotiation.
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No expected EC server cert for ECDH client key exchange");
}
ECParameterSpec params = x509Possession.getECParameterSpec();
if (params == null) {
// unlikely, have been checked during cipher suite negotiation.
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Not EC server cert for ECDH client key exchange");
}
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
// unlikely, have been checked during cipher suite negotiation.
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported EC server cert for ECDH client key exchange");
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
}
// parse the handshake message
ECDHClientKeyExchangeMessage cke =
new ECDHClientKeyExchangeMessage(shc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming ECDH ClientKeyExchange handshake message", cke);
}
// create the credentials
try {
ECPoint point =
JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
KeyFactory kf = JsseJce.getKeyFactory("EC");
ECPublicKey peerPublicKey =
(ECPublicKey)kf.generatePublic(spec);
// check constraints of peer ECPublicKey
if (shc.algorithmConstraints != null &&
!shc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
peerPublicKey)) {
throw new SSLHandshakeException(
"ECPublicKey does not comply to algorithm constraints");
}
shc.handshakeCredentials.add(new ECDHECredentials(
peerPublicKey, namedGroup));
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException)(new SSLHandshakeException(
"Could not generate ECPublicKey").initCause(e));
}
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
/**
* The ECDHE "ClientKeyExchange" handshake message producer.
*/
private static final
class ECDHEClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private ECDHEClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
ECDHECredentials ecdheCredentials = null;
for (SSLCredentials cd : chc.handshakeCredentials) {
if (cd instanceof ECDHECredentials) {
ecdheCredentials = (ECDHECredentials)cd;
break;
}
}
if (ecdheCredentials == null) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"No ECDHE credentials negotiated for client key exchange");
}
ECDHEPossession ecdhePossession = new ECDHEPossession(
ecdheCredentials, chc.sslContext.getSecureRandom());
chc.handshakePossessions.add(ecdhePossession);
ECDHClientKeyExchangeMessage cke =
new ECDHClientKeyExchangeMessage(
chc, ecdhePossession.publicKey);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced ECDHE ClientKeyExchange handshake message", cke);
}
// Output the handshake message.
cke.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
chc.negotiatedProtocol);
} else {
chc.handshakeKeyDerivation =
kd.createKeyDerivation(chc, masterSecret);
}
}
// The handshake message has been delivered.
return null;
}
}
/**
* The ECDHE "ClientKeyExchange" handshake message consumer.
*/
private static final
class ECDHEClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private ECDHEClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ECDHEPossession ecdhePossession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof ECDHEPossession) {
ecdhePossession = (ECDHEPossession)possession;
break;
}
}
if (ecdhePossession == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No expected ECDHE possessions for client key exchange");
}
ECParameterSpec params = ecdhePossession.publicKey.getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
// unlikely, have been checked during cipher suite negotiation.
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported EC server cert for ECDHE client key exchange");
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
}
// parse the handshake message
ECDHClientKeyExchangeMessage cke =
new ECDHClientKeyExchangeMessage(shc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming ECDHE ClientKeyExchange handshake message", cke);
}
// create the credentials
try {
ECPoint point =
JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
KeyFactory kf = JsseJce.getKeyFactory("EC");
ECPublicKey peerPublicKey =
(ECPublicKey)kf.generatePublic(spec);
// check constraints of peer ECPublicKey
if (shc.algorithmConstraints != null &&
!shc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
peerPublicKey)) {
throw new SSLHandshakeException(
"ECPublicKey does not comply to algorithm constraints");
}
shc.handshakeCredentials.add(new ECDHECredentials(
peerPublicKey, namedGroup));
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException)(new SSLHandshakeException(
"Could not generate ECPublicKey").initCause(e));
}
// update the states
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}

View File

@@ -0,0 +1,484 @@
/*
* Copyright (c) 2018, 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.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.ECPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.EnumSet;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.CipherSuite.HashAlg;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.ECUtil;
final class ECDHKeyExchange {
static final SSLPossessionGenerator poGenerator =
new ECDHEPossessionGenerator();
static final SSLKeyAgreementGenerator ecdheKAGenerator =
new ECDHEKAGenerator();
static final SSLKeyAgreementGenerator ecdhKAGenerator =
new ECDHKAGenerator();
static final class ECDHECredentials implements SSLCredentials {
final ECPublicKey popPublicKey;
final NamedGroup namedGroup;
ECDHECredentials(ECPublicKey popPublicKey, NamedGroup namedGroup) {
this.popPublicKey = popPublicKey;
this.namedGroup = namedGroup;
}
static ECDHECredentials valueOf(NamedGroup namedGroup,
byte[] encodedPoint) throws IOException, GeneralSecurityException {
if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
throw new RuntimeException(
"Credentials decoding: Not ECDHE named group");
}
if (encodedPoint == null || encodedPoint.length == 0) {
return null;
}
ECParameterSpec parameters =
JsseJce.getECParameterSpec(namedGroup.oid);
if (parameters == null) {
return null;
}
ECPoint point = JsseJce.decodePoint(
encodedPoint, parameters.getCurve());
KeyFactory factory = JsseJce.getKeyFactory("EC");
ECPublicKey publicKey = (ECPublicKey)factory.generatePublic(
new ECPublicKeySpec(point, parameters));
return new ECDHECredentials(publicKey, namedGroup);
}
}
static final class ECDHEPossession implements SSLPossession {
final PrivateKey privateKey;
final ECPublicKey publicKey;
final NamedGroup namedGroup;
ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {
try {
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
ECGenParameterSpec params =
(ECGenParameterSpec)namedGroup.getParameterSpec();
kpg.initialize(params, random);
KeyPair kp = kpg.generateKeyPair();
privateKey = kp.getPrivate();
publicKey = (ECPublicKey)kp.getPublic();
} catch (GeneralSecurityException e) {
throw new RuntimeException(
"Could not generate ECDH keypair", e);
}
this.namedGroup = namedGroup;
}
ECDHEPossession(ECDHECredentials credentials, SecureRandom random) {
ECParameterSpec params = credentials.popPublicKey.getParams();
try {
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
kpg.initialize(params, random);
KeyPair kp = kpg.generateKeyPair();
privateKey = kp.getPrivate();
publicKey = (ECPublicKey)kp.getPublic();
} catch (GeneralSecurityException e) {
throw new RuntimeException(
"Could not generate ECDH keypair", e);
}
this.namedGroup = credentials.namedGroup;
}
@Override
public byte[] encode() {
return ECUtil.encodePoint(
publicKey.getW(), publicKey.getParams().getCurve());
}
// called by ClientHandshaker with either the server's static or
// ephemeral public key
SecretKey getAgreedSecret(
PublicKey peerPublicKey) throws SSLHandshakeException {
try {
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
ka.init(privateKey);
ka.doPhase(peerPublicKey, true);
return ka.generateSecret("TlsPremasterSecret");
} catch (GeneralSecurityException e) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(e);
}
}
// called by ServerHandshaker
SecretKey getAgreedSecret(
byte[] encodedPoint) throws SSLHandshakeException {
try {
ECParameterSpec params = publicKey.getParams();
ECPoint point =
JsseJce.decodePoint(encodedPoint, params.getCurve());
KeyFactory kf = JsseJce.getKeyFactory("EC");
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
PublicKey peerPublicKey = kf.generatePublic(spec);
return getAgreedSecret(peerPublicKey);
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(e);
}
}
// Check constraints of the specified EC public key.
void checkConstraints(AlgorithmConstraints constraints,
byte[] encodedPoint) throws SSLHandshakeException {
try {
ECParameterSpec params = publicKey.getParams();
ECPoint point =
JsseJce.decodePoint(encodedPoint, params.getCurve());
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
KeyFactory kf = JsseJce.getKeyFactory("EC");
ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec);
// check constraints of ECPublicKey
if (!constraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) {
throw new SSLHandshakeException(
"ECPublicKey does not comply to algorithm constraints");
}
} catch (GeneralSecurityException | java.io.IOException e) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate ECPublicKey").initCause(e);
}
}
}
private static final
class ECDHEPossessionGenerator implements SSLPossessionGenerator {
// Prevent instantiation of this class.
private ECDHEPossessionGenerator() {
// blank
}
@Override
public SSLPossession createPossession(HandshakeContext context) {
NamedGroup preferableNamedGroup = null;
if ((context.clientRequestedNamedGroups != null) &&
(!context.clientRequestedNamedGroups.isEmpty())) {
preferableNamedGroup = SupportedGroups.getPreferredGroup(
context.negotiatedProtocol,
context.algorithmConstraints,
NamedGroupType.NAMED_GROUP_ECDHE,
context.clientRequestedNamedGroups);
} else {
preferableNamedGroup = SupportedGroups.getPreferredGroup(
context.negotiatedProtocol,
context.algorithmConstraints,
NamedGroupType.NAMED_GROUP_ECDHE);
}
if (preferableNamedGroup != null) {
return new ECDHEPossession(preferableNamedGroup,
context.sslContext.getSecureRandom());
}
// no match found, cannot use this cipher suite.
//
return null;
}
}
private static final
class ECDHKAGenerator implements SSLKeyAgreementGenerator {
// Prevent instantiation of this class.
private ECDHKAGenerator() {
// blank
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
if (context instanceof ServerHandshakeContext) {
return createServerKeyDerivation(
(ServerHandshakeContext)context);
} else {
return createClientKeyDerivation(
(ClientHandshakeContext)context);
}
}
private SSLKeyDerivation createServerKeyDerivation(
ServerHandshakeContext shc) throws IOException {
X509Possession x509Possession = null;
ECDHECredentials ecdheCredentials = null;
for (SSLPossession poss : shc.handshakePossessions) {
if (!(poss instanceof X509Possession)) {
continue;
}
ECParameterSpec params =
((X509Possession)poss).getECParameterSpec();
if (params == null) {
continue;
}
NamedGroup ng = NamedGroup.valueOf(params);
if (ng == null) {
// unlikely, have been checked during cipher suite negotiation.
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported EC server cert for ECDH key exchange");
}
for (SSLCredentials cred : shc.handshakeCredentials) {
if (!(cred instanceof ECDHECredentials)) {
continue;
}
if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
ecdheCredentials = (ECDHECredentials)cred;
break;
}
}
if (ecdheCredentials != null) {
x509Possession = (X509Possession)poss;
break;
}
}
if (x509Possession == null || ecdheCredentials == null) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient ECDHE key agreement parameters negotiated");
}
return new ECDHEKAKeyDerivation(shc,
x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
}
private SSLKeyDerivation createClientKeyDerivation(
ClientHandshakeContext chc) throws IOException {
ECDHEPossession ecdhePossession = null;
X509Credentials x509Credentials = null;
for (SSLPossession poss : chc.handshakePossessions) {
if (!(poss instanceof ECDHEPossession)) {
continue;
}
NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
for (SSLCredentials cred : chc.handshakeCredentials) {
if (!(cred instanceof X509Credentials)) {
continue;
}
PublicKey publicKey = ((X509Credentials)cred).popPublicKey;
if (!publicKey.getAlgorithm().equals("EC")) {
continue;
}
ECParameterSpec params =
((ECPublicKey)publicKey).getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
// unlikely, should have been checked previously
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported EC server cert for ECDH key exchange");
}
if (ng.equals(namedGroup)) {
x509Credentials = (X509Credentials)cred;
break;
}
}
if (x509Credentials != null) {
ecdhePossession = (ECDHEPossession)poss;
break;
}
}
if (ecdhePossession == null || x509Credentials == null) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient ECDH key agreement parameters negotiated");
}
return new ECDHEKAKeyDerivation(chc,
ecdhePossession.privateKey, x509Credentials.popPublicKey);
}
}
private static final
class ECDHEKAGenerator implements SSLKeyAgreementGenerator {
// Prevent instantiation of this class.
private ECDHEKAGenerator() {
// blank
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
ECDHEPossession ecdhePossession = null;
ECDHECredentials ecdheCredentials = null;
for (SSLPossession poss : context.handshakePossessions) {
if (!(poss instanceof ECDHEPossession)) {
continue;
}
NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
for (SSLCredentials cred : context.handshakeCredentials) {
if (!(cred instanceof ECDHECredentials)) {
continue;
}
if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
ecdheCredentials = (ECDHECredentials)cred;
break;
}
}
if (ecdheCredentials != null) {
ecdhePossession = (ECDHEPossession)poss;
break;
}
}
if (ecdhePossession == null || ecdheCredentials == null) {
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient ECDHE key agreement parameters negotiated");
}
return new ECDHEKAKeyDerivation(context,
ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
}
}
private static final
class ECDHEKAKeyDerivation implements SSLKeyDerivation {
private final HandshakeContext context;
private final PrivateKey localPrivateKey;
private final PublicKey peerPublicKey;
ECDHEKAKeyDerivation(HandshakeContext context,
PrivateKey localPrivateKey,
PublicKey peerPublicKey) {
this.context = context;
this.localPrivateKey = localPrivateKey;
this.peerPublicKey = peerPublicKey;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
return t12DeriveKey(algorithm, params);
} else {
return t13DeriveKey(algorithm, params);
}
}
private SecretKey t12DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
try {
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey preMasterSecret =
ka.generateSecret("TlsPremasterSecret");
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
if (mskd == null) {
// unlikely
throw new SSLHandshakeException(
"No expected master key derivation for protocol: " +
context.negotiatedProtocol.name);
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
private SecretKey t13DeriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
try {
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
ka.init(localPrivateKey);
ka.doPhase(peerPublicKey, true);
SecretKey sharedSecret =
ka.generateSecret("TlsPremasterSecret");
HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
SSLKeyDerivation kd = context.handshakeKeyDerivation;
HKDF hkdf = new HKDF(hashAlg.name);
if (kd == null) { // No PSK is in use.
// If PSK is not in use Early Secret will still be
// HKDF-Extract(0, 0).
byte[] zeros = new byte[hashAlg.hashLength];
SecretKeySpec ikm =
new SecretKeySpec(zeros, "TlsPreSharedSecret");
SecretKey earlySecret =
hkdf.extract(zeros, ikm, "TlsEarlySecret");
kd = new SSLSecretDerivation(context, earlySecret);
}
// derive salt secret
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
// derive handshake secret
return hkdf.extract(saltSecret, sharedSecret, algorithm);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
}
}

View File

@@ -0,0 +1,560 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;
/**
* Pack of the ServerKeyExchange handshake message.
*/
final class ECDHServerKeyExchange {
static final SSLConsumer ecdheHandshakeConsumer =
new ECDHServerKeyExchangeConsumer();
static final HandshakeProducer ecdheHandshakeProducer =
new ECDHServerKeyExchangeProducer();
/**
* The ECDH ServerKeyExchange handshake message.
*/
private static final
class ECDHServerKeyExchangeMessage extends HandshakeMessage {
private static final byte CURVE_NAMED_CURVE = (byte)0x03;
// id of the named curve
private final NamedGroup namedGroup;
// encoded public point
private final byte[] publicPoint;
// signature bytes, or null if anonymous
private final byte[] paramsSignature;
// public key object encapsulated in this message
private final ECPublicKey publicKey;
private final boolean useExplicitSigAlgorithm;
// the signature algorithm used by this ServerKeyExchange message
private final SignatureScheme signatureScheme;
ECDHServerKeyExchangeMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
// This happens in server side only.
ServerHandshakeContext shc =
(ServerHandshakeContext)handshakeContext;
ECDHEPossession ecdhePossession = null;
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof ECDHEPossession) {
ecdhePossession = (ECDHEPossession)possession;
if (x509Possession != null) {
break;
}
} else if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
if (ecdhePossession != null) {
break;
}
}
}
if (ecdhePossession == null) {
// unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No ECDHE credentials negotiated for server key exchange");
}
publicKey = ecdhePossession.publicKey;
ECParameterSpec params = publicKey.getParams();
ECPoint point = publicKey.getW();
publicPoint = JsseJce.encodePoint(point, params.getCurve());
this.namedGroup = NamedGroup.valueOf(params);
if ((namedGroup == null) || (namedGroup.oid == null) ) {
// unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unnamed EC parameter spec: " + params);
}
if (x509Possession == null) {
// anonymous, no authentication, no signature
paramsSignature = null;
signatureScheme = null;
useExplicitSigAlgorithm = false;
} else {
useExplicitSigAlgorithm =
shc.negotiatedProtocol.useTLS12PlusSpec();
Signature signer = null;
if (useExplicitSigAlgorithm) {
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
SignatureScheme.getSignerOfPreferableAlgorithm(
shc.peerRequestedSignatureSchemes,
x509Possession,
shc.negotiatedProtocol);
if (schemeAndSigner == null) {
// Unlikely, the credentials generator should have
// selected the preferable signature algorithm properly.
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No supported signature algorithm for " +
x509Possession.popPrivateKey.getAlgorithm() +
" key");
} else {
signatureScheme = schemeAndSigner.getKey();
signer = schemeAndSigner.getValue();
}
} else {
signatureScheme = null;
try {
signer = getSignature(
x509Possession.popPrivateKey.getAlgorithm(),
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Possession.popPrivateKey.getAlgorithm(), e);
}
}
byte[] signature = null;
try {
updateSignature(signer, shc.clientHelloRandom.randomBytes,
shc.serverHelloRandom.randomBytes,
namedGroup.id, publicPoint);
signature = signer.sign();
} catch (SignatureException ex) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failed to sign ecdhe parameters: " +
x509Possession.popPrivateKey.getAlgorithm(), ex);
}
paramsSignature = signature;
}
}
ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// This happens in client side only.
ClientHandshakeContext chc =
(ClientHandshakeContext)handshakeContext;
byte curveType = (byte)Record.getInt8(m);
if (curveType != CURVE_NAMED_CURVE) {
// Unlikely as only the named curves should be negotiated.
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported ECCurveType: " + curveType);
}
int namedGroupId = Record.getInt16(m);
this.namedGroup = NamedGroup.valueOf(namedGroupId);
if (namedGroup == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unknown named group ID: " + namedGroupId);
}
if (!SupportedGroups.isSupported(namedGroup)) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported named group: " + namedGroup);
}
if (namedGroup.oid == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unknown named EC curve: " + namedGroup);
}
ECParameterSpec parameters =
JsseJce.getECParameterSpec(namedGroup.oid);
if (parameters == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No supported EC parameter: " + namedGroup);
}
publicPoint = Record.getBytes8(m);
if (publicPoint.length == 0) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Insufficient ECPoint data: " + namedGroup);
}
ECPublicKey ecPublicKey = null;
try {
ECPoint point =
JsseJce.decodePoint(publicPoint, parameters.getCurve());
KeyFactory factory = JsseJce.getKeyFactory("EC");
ecPublicKey = (ECPublicKey)factory.generatePublic(
new ECPublicKeySpec(point, parameters));
} catch (NoSuchAlgorithmException |
InvalidKeySpecException | IOException ex) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid ECPoint: " + namedGroup, ex);
}
publicKey = ecPublicKey;
X509Credentials x509Credentials = null;
for (SSLCredentials cd : chc.handshakeCredentials) {
if (cd instanceof X509Credentials) {
x509Credentials = (X509Credentials)cd;
break;
}
}
if (x509Credentials == null) {
// anonymous, no authentication, no signature
if (m.hasRemaining()) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ServerKeyExchange: unknown extra data");
}
this.signatureScheme = null;
this.paramsSignature = null;
this.useExplicitSigAlgorithm = false;
return;
}
this.useExplicitSigAlgorithm =
chc.negotiatedProtocol.useTLS12PlusSpec();
if (useExplicitSigAlgorithm) {
int ssid = Record.getInt16(m);
signatureScheme = SignatureScheme.valueOf(ssid);
if (signatureScheme == null) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature algorithm (" + ssid +
") used in ECDH ServerKeyExchange handshake message");
}
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsupported signature algorithm (" +
signatureScheme.name +
") used in ECDH ServerKeyExchange handshake message");
}
} else {
signatureScheme = null;
}
// read and verify the signature
paramsSignature = Record.getBytes16(m);
Signature signer;
if (useExplicitSigAlgorithm) {
try {
signer = signatureScheme.getVerifier(
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
}
} else {
try {
signer = getSignature(
x509Credentials.popPublicKey.getAlgorithm(),
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Credentials.popPublicKey.getAlgorithm(), e);
}
}
try {
updateSignature(signer,
chc.clientHelloRandom.randomBytes,
chc.serverHelloRandom.randomBytes,
namedGroup.id, publicPoint);
if (!signer.verify(paramsSignature)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid ECDH ServerKeyExchange signature");
}
} catch (SignatureException ex) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Cannot verify ECDH ServerKeyExchange signature", ex);
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.SERVER_KEY_EXCHANGE;
}
@Override
public int messageLength() {
int sigLen = 0;
if (paramsSignature != null) {
sigLen = 2 + paramsSignature.length;
if (useExplicitSigAlgorithm) {
sigLen += SignatureScheme.sizeInRecord();
}
}
return 4 + publicPoint.length + sigLen;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putInt8(CURVE_NAMED_CURVE);
hos.putInt16(namedGroup.id);
hos.putBytes8(publicPoint);
if (paramsSignature != null) {
if (useExplicitSigAlgorithm) {
hos.putInt16(signatureScheme.id);
}
hos.putBytes16(paramsSignature);
}
}
@Override
public String toString() {
if (useExplicitSigAlgorithm) {
MessageFormat messageFormat = new MessageFormat(
"\"ECDH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"named group\": \"{0}\"\n" +
" \"ecdh public\": '{'\n" +
"{1}\n" +
" '}',\n" +
" '}',\n" +
" \"digital signature\": '{'\n" +
" \"signature algorithm\": \"{2}\"\n" +
" \"signature\": '{'\n" +
"{3}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
namedGroup.name,
Utilities.indent(
hexEncoder.encodeBuffer(publicPoint), " "),
signatureScheme.name,
Utilities.indent(
hexEncoder.encodeBuffer(paramsSignature), " ")
};
return messageFormat.format(messageFields);
} else if (paramsSignature != null) {
MessageFormat messageFormat = new MessageFormat(
"\"ECDH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"named group\": \"{0}\"\n" +
" \"ecdh public\": '{'\n" +
"{1}\n" +
" '}',\n" +
" '}',\n" +
" \"signature\": '{'\n" +
"{2}\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
namedGroup.name,
Utilities.indent(
hexEncoder.encodeBuffer(publicPoint), " "),
Utilities.indent(
hexEncoder.encodeBuffer(paramsSignature), " ")
};
return messageFormat.format(messageFields);
} else { // anonymous
MessageFormat messageFormat = new MessageFormat(
"\"ECDH ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"named group\": \"{0}\"\n" +
" \"ecdh public\": '{'\n" +
"{1}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
namedGroup.name,
Utilities.indent(
hexEncoder.encodeBuffer(publicPoint), " "),
};
return messageFormat.format(messageFields);
}
}
private static Signature getSignature(String keyAlgorithm,
Key key) throws NoSuchAlgorithmException, InvalidKeyException {
Signature signer = null;
switch (keyAlgorithm) {
case "EC":
signer = JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA);
break;
case "RSA":
signer = RSASignature.getInstance();
break;
default:
throw new NoSuchAlgorithmException(
"neither an RSA or a EC key : " + keyAlgorithm);
}
if (signer != null) {
if (key instanceof PublicKey) {
signer.initVerify((PublicKey)(key));
} else {
signer.initSign((PrivateKey)key);
}
}
return signer;
}
private static void updateSignature(Signature sig,
byte[] clntNonce, byte[] svrNonce, int namedGroupId,
byte[] publicPoint) throws SignatureException {
sig.update(clntNonce);
sig.update(svrNonce);
sig.update(CURVE_NAMED_CURVE);
sig.update((byte)((namedGroupId >> 8) & 0xFF));
sig.update((byte)(namedGroupId & 0xFF));
sig.update((byte)publicPoint.length);
sig.update(publicPoint);
}
}
/**
* The ECDH "ServerKeyExchange" handshake message producer.
*/
private static final
class ECDHServerKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private ECDHServerKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ECDHServerKeyExchangeMessage skem =
new ECDHServerKeyExchangeMessage(shc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced ECDH ServerKeyExchange handshake message", skem);
}
// Output the handshake message.
skem.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The handshake message has been delivered.
return null;
}
}
/**
* The ECDH "ServerKeyExchange" handshake message consumer.
*/
private static final
class ECDHServerKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private ECDHServerKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
ECDHServerKeyExchangeMessage skem =
new ECDHServerKeyExchangeMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming ECDH ServerKeyExchange handshake message", skem);
}
//
// validate
//
// check constraints of EC PublicKey
if (chc.algorithmConstraints != null &&
!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
skem.publicKey)) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"ECDH ServerKeyExchange does not comply " +
"to algorithm constraints");
}
//
// update
//
chc.handshakeCredentials.add(
new ECDHECredentials(skem.publicKey, skem.namedGroup));
//
// produce
//
// Need no new handshake message producers here.
}
}
}

View File

@@ -0,0 +1,300 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.Locale;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
/**
* Pack of the "ec_point_formats" extensions [RFC 4492].
*/
final class ECPointFormatsExtension {
static final HandshakeProducer chNetworkProducer =
new CHECPointFormatsProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHECPointFormatsConsumer();
static final ExtensionConsumer shOnLoadConsumer =
new SHECPointFormatsConsumer();
static final SSLStringizer epfStringizer =
new ECPointFormatsStringizer();
/**
* The "ec_point_formats" extension.
*/
static class ECPointFormatsSpec implements SSLExtensionSpec {
static final ECPointFormatsSpec DEFAULT =
new ECPointFormatsSpec(new byte[] {ECPointFormat.UNCOMPRESSED.id});
final byte[] formats;
ECPointFormatsSpec(byte[] formats) {
this.formats = formats;
}
private ECPointFormatsSpec(ByteBuffer m) throws IOException {
if (!m.hasRemaining()) {
throw new SSLProtocolException(
"Invalid ec_point_formats extension: " +
"insufficient data");
}
this.formats = Record.getBytes8(m);
}
private boolean hasUncompressedFormat() {
for (byte format : formats) {
if (format == ECPointFormat.UNCOMPRESSED.id) {
return true;
}
}
return false;
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"formats\": '['{0}']'", Locale.ENGLISH);
if (formats == null || formats.length == 0) {
Object[] messageFields = {
"<no EC point format specified>"
};
return messageFormat.format(messageFields);
} else {
StringBuilder builder = new StringBuilder(512);
boolean isFirst = true;
for (byte pf : formats) {
if (isFirst) {
isFirst = false;
} else {
builder.append(", ");
}
builder.append(ECPointFormat.nameOf(pf));
}
Object[] messageFields = {
builder.toString()
};
return messageFormat.format(messageFields);
}
}
}
private static final class ECPointFormatsStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new ECPointFormatsSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
private static enum ECPointFormat {
UNCOMPRESSED ((byte)0, "uncompressed"),
ANSIX962_COMPRESSED_PRIME ((byte)1, "ansiX962_compressed_prime"),
FMT_ANSIX962_COMPRESSED_CHAR2 ((byte)2, "ansiX962_compressed_char2");
final byte id;
final String name;
private ECPointFormat(byte id, String name) {
this.id = id;
this.name = name;
}
static String nameOf(int id) {
for (ECPointFormat pf: ECPointFormat.values()) {
if (pf.id == id) {
return pf.name;
}
}
return "UNDEFINED-EC-POINT-FORMAT(" + id + ")";
}
}
/**
* Network data producer of a "ec_point_formats" extension in
* the ClientHello handshake message.
*/
private static final
class CHECPointFormatsProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHECPointFormatsProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable ec_point_formats extension");
}
return null;
}
// Produce the extension.
//
// produce the extension only if EC cipher suite is activated.
if (NamedGroupType.NAMED_GROUP_ECDHE.isSupported(
chc.activeCipherSuites)) {
// We are using uncompressed ECPointFormat only at present.
byte[] extData = new byte[] {0x01, 0x00};
// Update the context.
chc.handshakeExtensions.put(
CH_EC_POINT_FORMATS, ECPointFormatsSpec.DEFAULT);
return extData;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Need no ec_point_formats extension");
}
return null;
}
}
/**
* Network data consumer of a "ec_point_formats" extension in
* the ClientHello handshake message.
*/
private static final
class CHECPointFormatsConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHECPointFormatsConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable ec_point_formats extension");
}
return; // ignore the extension
}
// Parse the extension.
ECPointFormatsSpec spec;
try {
spec = new ECPointFormatsSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// per RFC 4492, uncompressed points must always be supported.
if (!spec.hasUncompressedFormat()) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid ec_point_formats extension data: " +
"peer does not support uncompressed points");
}
// Update the context.
shc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
// No impact on session resumption, as only uncompressed points
// are supported at present.
}
}
/**
* Network data consumer of a "ec_point_formats" extension in
* the ServerHello handshake message.
*/
private static final
class SHECPointFormatsConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHECPointFormatsConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to "ec_point_formats" extension request only
ECPointFormatsSpec requestedSpec = (ECPointFormatsSpec)
chc.handshakeExtensions.get(CH_EC_POINT_FORMATS);
if (requestedSpec == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ec_point_formats extension in ServerHello");
}
// Parse the extension.
ECPointFormatsSpec spec;
try {
spec = new ECPointFormatsSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// per RFC 4492, uncompressed points must always be supported.
if (!spec.hasUncompressedFormat()) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid ec_point_formats extension data: " +
"peer does not support uncompressed points");
}
// Update the context.
chc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
// No impact on session resumption, as only uncompressed points
// are supported at present.
}
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.Locale;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the EncryptedExtensions handshake message.
*/
final class EncryptedExtensions {
static final HandshakeProducer handshakeProducer =
new EncryptedExtensionsProducer();
static final SSLConsumer handshakeConsumer =
new EncryptedExtensionsConsumer();
/**
* The EncryptedExtensions handshake message.
*/
static final class EncryptedExtensionsMessage extends HandshakeMessage {
private final SSLExtensions extensions;
EncryptedExtensionsMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
this.extensions = new SSLExtensions(this);
}
EncryptedExtensionsMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// struct {
// Extension extensions<0..2^16-1>;
// } EncryptedExtensions;
if (m.remaining() < 2) {
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid EncryptedExtensions handshake message: " +
"no sufficient data");
}
SSLExtension[] encryptedExtensions =
handshakeContext.sslConfig.getEnabledExtensions(
SSLHandshake.ENCRYPTED_EXTENSIONS);
this.extensions = new SSLExtensions(this, m, encryptedExtensions);
}
@Override
SSLHandshake handshakeType() {
return SSLHandshake.ENCRYPTED_EXTENSIONS;
}
@Override
int messageLength() {
int extLen = extensions.length();
if (extLen == 0) {
extLen = 2; // empty extensions
}
return extLen;
}
@Override
void send(HandshakeOutStream hos) throws IOException {
// Is it an empty extensions?
if (extensions.length() == 0) {
hos.putInt16(0);
} else {
extensions.send(hos);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"EncryptedExtensions\": [\n" +
"{0}\n" +
"]",
Locale.ENGLISH);
Object[] messageFields = {
Utilities.indent(extensions.toString())
};
return messageFormat.format(messageFields);
}
}
/**
* The EncryptedExtensions handshake message consumer.
*/
private static final class EncryptedExtensionsProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private EncryptedExtensionsProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
EncryptedExtensionsMessage eem =
new EncryptedExtensionsMessage(shc);
SSLExtension[] extTypes =
shc.sslConfig.getEnabledExtensions(
SSLHandshake.ENCRYPTED_EXTENSIONS,
shc.negotiatedProtocol);
eem.extensions.produce(shc, extTypes);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced EncryptedExtensions message", eem);
}
// Output the handshake message.
eem.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The handshake message has been delivered.
return null;
}
}
/**
* The EncryptedExtensions handshake message consumer.
*/
private static final class EncryptedExtensionsConsumer
implements SSLConsumer {
// Prevent instantiation of this class.
private EncryptedExtensionsConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// clean up this consumer
chc.handshakeConsumers.remove(SSLHandshake.ENCRYPTED_EXTENSIONS.id);
EncryptedExtensionsMessage eem =
new EncryptedExtensionsMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming EncryptedExtensions handshake message", eem);
}
//
// validate
//
SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
SSLHandshake.ENCRYPTED_EXTENSIONS);
eem.extensions.consumeOnLoad(chc, extTypes);
//
// update
//
eem.extensions.consumeOnTrade(chc, extTypes);
//
// produce
//
// Need no new handshake message producers here.
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2002, 2018, 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.*;
/**
* The "KeyManager" for ephemeral RSA keys. Ephemeral DH and ECDH keys
* are handled by the DHCrypt and ECDHCrypt classes, respectively.
*
* @author Andreas Sterbenz
*/
final class EphemeralKeyManager {
// indices for the keys array below
private static final int INDEX_RSA512 = 0;
private static final int INDEX_RSA1024 = 1;
/*
* Current cached RSA KeyPairs. Elements are never null.
* Indexed via the constants above.
*/
private final EphemeralKeyPair[] keys = new EphemeralKeyPair[] {
new EphemeralKeyPair(null),
new EphemeralKeyPair(null),
};
EphemeralKeyManager() {
// empty
}
/*
* Get a temporary RSA KeyPair.
*/
KeyPair getRSAKeyPair(boolean export, SecureRandom random) {
int length, index;
if (export) {
length = 512;
index = INDEX_RSA512;
} else {
length = 1024;
index = INDEX_RSA1024;
}
synchronized (keys) {
KeyPair kp = keys[index].getKeyPair();
if (kp == null) {
try {
KeyPairGenerator kgen = JsseJce.getKeyPairGenerator("RSA");
kgen.initialize(length, random);
keys[index] = new EphemeralKeyPair(kgen.genKeyPair());
kp = keys[index].getKeyPair();
} catch (Exception e) {
// ignore
}
}
return kp;
}
}
/**
* Inner class to handle storage of ephemeral KeyPairs.
*/
private static class EphemeralKeyPair {
// maximum number of times a KeyPair is used
private static final int MAX_USE = 200;
// maximum time interval in which the keypair is used (1 hour in ms)
private static final long USE_INTERVAL = 3600*1000;
private KeyPair keyPair;
private int uses;
private long expirationTime;
private EphemeralKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
expirationTime = System.currentTimeMillis() + USE_INTERVAL;
}
/*
* Check if the KeyPair can still be used.
*/
private boolean isValid() {
return (keyPair != null) && (uses < MAX_USE)
&& (System.currentTimeMillis() < expirationTime);
}
/*
* Return the KeyPair or null if it is invalid.
*/
private KeyPair getKeyPair() {
if (isValid() == false) {
keyPair = null;
return null;
}
uses++;
return keyPair;
}
}
}

View File

@@ -0,0 +1,389 @@
/*
* Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
* Copyright (c) 2018, 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.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.SSLExtension.CH_EXTENDED_MASTER_SECRET;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import static sun.security.ssl.SSLExtension.SH_EXTENDED_MASTER_SECRET;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "extended_master_secret" extensions [RFC 7627].
*/
final class ExtendedMasterSecretExtension {
static final HandshakeProducer chNetworkProducer =
new CHExtendedMasterSecretProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHExtendedMasterSecretConsumer();
static final HandshakeAbsence chOnLoadAbsence =
new CHExtendedMasterSecretAbsence();
static final HandshakeProducer shNetworkProducer =
new SHExtendedMasterSecretProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHExtendedMasterSecretConsumer();
static final HandshakeAbsence shOnLoadAbsence =
new SHExtendedMasterSecretAbsence();
static final SSLStringizer emsStringizer =
new ExtendedMasterSecretStringizer();
/**
* The "extended_master_secret" extension.
*/
static final class ExtendedMasterSecretSpec implements SSLExtensionSpec {
// A nominal object that does not holding any real renegotiation info.
static final ExtendedMasterSecretSpec NOMINAL =
new ExtendedMasterSecretSpec();
private ExtendedMasterSecretSpec() {
// blank
}
private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException {
// Parse the extension.
if (m.hasRemaining()) {
throw new SSLProtocolException(
"Invalid extended_master_secret extension data: " +
"not empty");
}
}
@Override
public String toString() {
return "<empty>";
}
}
private static final
class ExtendedMasterSecretStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new ExtendedMasterSecretSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of a "extended_master_secret" extension in
* the ClientHello handshake message.
*/
private static final
class CHExtendedMasterSecretProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHExtendedMasterSecretProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
!SSLConfiguration.useExtendedMasterSecret ||
!chc.conContext.protocolVersion.useTLS10PlusSpec()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable extended_master_secret extension");
}
return null;
}
if (chc.handshakeSession == null ||
chc.handshakeSession.useExtendedMasterSecret) {
byte[] extData = new byte[0];
chc.handshakeExtensions.put(CH_EXTENDED_MASTER_SECRET,
ExtendedMasterSecretSpec.NOMINAL);
return extData;
}
return null;
}
}
/**
* Network data producer of a "extended_master_secret" extension in
* the ServerHello handshake message.
*/
private static final
class CHExtendedMasterSecretConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHExtendedMasterSecretConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
!SSLConfiguration.useExtendedMasterSecret ||
!shc.negotiatedProtocol.useTLS10PlusSpec()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Ignore unavailable extension: " +
CH_EXTENDED_MASTER_SECRET.name);
}
return; // ignore the extension
}
// Parse the extension.
ExtendedMasterSecretSpec spec;
try {
spec = new ExtendedMasterSecretSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (shc.isResumption && shc.resumingSession != null &&
!shc.resumingSession.useExtendedMasterSecret) {
// For abbreviated handshake request, If the original
// session did not use the "extended_master_secret"
// extension but the new ClientHello contains the
// extension, then the server MUST NOT perform the
// abbreviated handshake. Instead, it SHOULD continue
// with a full handshake.
shc.isResumption = false;
shc.resumingSession = null;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"abort session resumption which did not use " +
"Extended Master Secret extension");
}
}
// Update the context.
//
shc.handshakeExtensions.put(
CH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
// No impact on session resumption.
}
}
/**
* The absence processing if a "extended_master_secret" extension is
* not present in the ClientHello handshake message.
*/
private static final
class CHExtendedMasterSecretAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
!SSLConfiguration.useExtendedMasterSecret) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Ignore unavailable extension: " +
CH_EXTENDED_MASTER_SECRET.name);
}
return; // ignore the extension
}
if (shc.negotiatedProtocol.useTLS10PlusSpec() &&
!SSLConfiguration.allowLegacyMasterSecret) {
// For full handshake, if the server receives a ClientHello
// without the extension, it SHOULD abort the handshake if
// it does not wish to interoperate with legacy clients.
//
// As if extended master extension is required for full
// handshake, it MUST be used in abbreviated handshake too.
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Extended Master Secret extension is required");
}
if (shc.isResumption && shc.resumingSession != null) {
if (shc.resumingSession.useExtendedMasterSecret) {
// For abbreviated handshake request, if the original
// session used the "extended_master_secret" extension
// but the new ClientHello does not contain it, the
// server MUST abort the abbreviated handshake.
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Missing Extended Master Secret extension " +
"on session resumption");
} else {
// For abbreviated handshake request, if neither the
// original session nor the new ClientHello uses the
// extension, the server SHOULD abort the handshake.
if (!SSLConfiguration.allowLegacyResumption) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Missing Extended Master Secret extension " +
"on session resumption");
} else { // Otherwise, continue with a full handshake.
shc.isResumption = false;
shc.resumingSession = null;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"abort session resumption, " +
"missing Extended Master Secret extension");
}
}
}
}
}
}
/**
* Network data producer of a "extended_master_secret" extension in
* the ServerHello handshake message.
*/
private static final
class SHExtendedMasterSecretProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHExtendedMasterSecretProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (shc.handshakeSession.useExtendedMasterSecret) {
byte[] extData = new byte[0];
shc.handshakeExtensions.put(SH_EXTENDED_MASTER_SECRET,
ExtendedMasterSecretSpec.NOMINAL);
return extData;
}
return null;
}
}
/**
* Network data consumer of a "extended_master_secret" extension in
* the ServerHello handshake message.
*/
private static final
class SHExtendedMasterSecretConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHExtendedMasterSecretConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to the client extended_master_secret extension
// request, which is mandatory for ClientHello message.
ExtendedMasterSecretSpec requstedSpec = (ExtendedMasterSecretSpec)
chc.handshakeExtensions.get(CH_EXTENDED_MASTER_SECRET);
if (requstedSpec == null) {
throw chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
"Server sent the extended_master_secret " +
"extension improperly");
}
// Parse the extension.
ExtendedMasterSecretSpec spec;
try {
spec = new ExtendedMasterSecretSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (chc.isResumption && chc.resumingSession != null &&
!chc.resumingSession.useExtendedMasterSecret) {
throw chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
"Server sent an unexpected extended_master_secret " +
"extension on session resumption");
}
// Update the context.
chc.handshakeExtensions.put(
SH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
// No impact on session resumption.
}
}
/**
* The absence processing if a "extended_master_secret" extension is
* not present in the ServerHello handshake message.
*/
private static final
class SHExtendedMasterSecretAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
if (SSLConfiguration.useExtendedMasterSecret &&
!SSLConfiguration.allowLegacyMasterSecret) {
// For full handshake, if a client receives a ServerHello
// without the extension, it SHOULD abort the handshake if
// it does not wish to interoperate with legacy servers.
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Extended Master Secret extension is required");
}
if (chc.isResumption && chc.resumingSession != null) {
if (chc.resumingSession.useExtendedMasterSecret) {
// For abbreviated handshake, if the original session used
// the "extended_master_secret" extension but the new
// ServerHello does not contain the extension, the client
// MUST abort the handshake.
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Missing Extended Master Secret extension " +
"on session resumption");
} else if (SSLConfiguration.useExtendedMasterSecret &&
!SSLConfiguration.allowLegacyResumption &&
chc.negotiatedProtocol.useTLS10PlusSpec()) {
// Unlikely, abbreviated handshake should be discarded.
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Extended Master Secret extension is required");
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 2018, 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.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import java.util.Objects;
/**
* An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
* specific to the needs of TLS 1.3 key derivation in JSSE. This is not a
* general purpose HKDF implementation and is suited only to single-key output
* derivations.
*
* HKDF objects are created by specifying a message digest algorithm. That
* digest algorithm will be used by the HMAC function as part of the HKDF
* derivation process.
*/
final class HKDF {
private final String hmacAlg;
private final Mac hmacObj;
private final int hmacLen;
/**
* Create an HDKF object, specifying the underlying message digest
* algorithm.
*
* @param hashAlg a standard name corresponding to a supported message
* digest algorithm.
*
* @throws NoSuchAlgorithmException if that message digest algorithm does
* not have an HMAC variant supported on any available provider.
*/
HKDF(String hashAlg) throws NoSuchAlgorithmException {
Objects.requireNonNull(hashAlg,
"Must provide underlying HKDF Digest algorithm.");
hmacAlg = "Hmac" + hashAlg.replace("-", "");
hmacObj = JsseJce.getMac(hmacAlg);
hmacLen = hmacObj.getMacLength();
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value, implemented as a {@code SecretKey}. A
* {@code null} value is allowed, which will internally use an array of
* zero bytes the same size as the underlying hash output length.
* @param inputKey the input keying material provided as a
* {@code SecretKey}.
* @param keyAlg the algorithm name assigned to the resulting
* {@code SecretKey} object.
*
* @return a {@code SecretKey} that is the result of the HKDF extract
* operation.
*
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
}
hmacObj.init(salt);
return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
keyAlg);
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value as cleartext bytes. A {@code null} value is
* allowed, which will internally use an array of zero bytes the same
* size as the underlying hash output length.
* @param inputKey the input keying material provided as a
* {@code SecretKey}.
* @param keyAlg the algorithm name assigned to the resulting
* {@code SecretKey} object.
*
* @return a {@code SecretKey} that is the result of the HKDF extract
* operation.
*
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new byte[hmacLen];
}
return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
}
/**
* Perform the HKDF-Expand derivation for a single-key output.
*
* @param pseudoRandKey the pseudo random key (PRK).
* @param info optional context-specific info. A {@code null} value is
* allowed in which case a zero-length byte array will be used.
* @param outLen the length of the resulting {@code SecretKey}
* @param keyAlg the algorithm name applied to the resulting
* {@code SecretKey}
*
* @return the resulting key derivation as a {@code SecretKey} object
*
* @throws InvalidKeyException if the underlying HMAC operation cannot
* be initialized using the provided {@code pseudoRandKey} object.
*/
SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
String keyAlg) throws InvalidKeyException {
byte[] kdfOutput;
// Calculate the number of rounds of HMAC that are needed to
// meet the requested data. Then set up the buffers we will need.
Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
// Output from the expand operation must be <= 255 * hmac length
if (outLen > 255 * hmacLen) {
throw new IllegalArgumentException("Requested output length " +
"exceeds maximum length allowed for HKDF expansion");
}
hmacObj.init(pseudoRandKey);
if (info == null) {
info = new byte[0];
}
int rounds = (outLen + hmacLen - 1) / hmacLen;
kdfOutput = new byte[rounds * hmacLen];
int offset = 0;
int tLength = 0;
for (int i = 0; i < rounds ; i++) {
// Calculate this round
try {
// Add T(i). This will be an empty string on the first
// iteration since tLength starts at zero. After the first
// iteration, tLength is changed to the HMAC length for the
// rest of the loop.
hmacObj.update(kdfOutput,
Math.max(0, offset - hmacLen), tLength);
hmacObj.update(info); // Add info
hmacObj.update((byte)(i + 1)); // Add round number
hmacObj.doFinal(kdfOutput, offset);
tLength = hmacLen;
offset += hmacLen; // For next iteration
} catch (ShortBufferException sbe) {
// This really shouldn't happen given that we've
// sized the buffers to their largest possible size up-front,
// but just in case...
throw new RuntimeException(sbe);
}
}
return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, 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 sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Interface for handshake message or extension absence on handshake
* message processing.
*
* This is typically used after the SSLSession object created, so that the
* extension can update/impact the session object.
*/
interface HandshakeAbsence {
void absent(ConnectionContext context,
HandshakeMessage message) throws IOException;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, 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 sun.security.ssl.SSLHandshake.HandshakeMessage;
interface HandshakeConsumer {
// message: the handshake message to be consumed.
void consume(ConnectionContext context,
HandshakeMessage message) throws IOException;
}

View File

@@ -0,0 +1,551 @@
/*
* 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.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
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<Byte, SSLConsumer> handshakeConsumers;
final HashMap<Byte, HandshakeProducer> handshakeProducers;
// context
final SSLContextImpl sslContext;
final TransportContext conContext;
final SSLConfiguration sslConfig;
// consolidated parameters
final List<ProtocolVersion> activeProtocols;
final List<CipherSuite> 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;
final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
volatile boolean taskDelegated = false;
volatile Exception delegatedThrown = null;
ProtocolVersion negotiatedProtocol;
CipherSuite negotiatedCipherSuite;
final List<SSLPossession> handshakePossessions;
final List<SSLCredentials> 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<SSLExtension, SSLExtension.SSLExtensionSpec>
handshakeExtensions;
// MaxFragmentLength
int maxFragmentLength;
// SignatureScheme
List<SignatureScheme> localSupportedSignAlgs;
List<SignatureScheme> peerRequestedSignatureSchemes;
List<SignatureScheme> peerRequestedCertSignSchemes;
// Known authorities
X500Principal[] peerSupportedAuthorities = null;
// SupportedGroups
List<NamedGroup> clientRequestedNamedGroups;
// HelloRetryRequest
NamedGroup serverSelectedNamedGroup;
// if server name indicator is negotiated
//
// May need a public API for the indication in the future.
List<SNIServerName> 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.algorithmConstraints = new SSLAlgorithmConstraints(
sslConfig.userSpecifiedAlgorithmConstraints);
this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
sslConfig.enabledCipherSuites, 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, algorithmConstraints);
if (activeCipherSuites.isEmpty()) {
throw new SSLHandshakeException("No appropriate cipher suite");
}
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
*/
protected 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.handshakeConsumers = new LinkedHashMap<>();
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<ProtocolVersion> getActiveProtocols(
List<ProtocolVersion> enabledProtocols,
List<CipherSuite> enabledCipherSuites,
AlgorithmConstraints algorithmConstraints) {
boolean enabledSSL20Hello = false;
ArrayList<ProtocolVersion> 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<NamedGroupType, Boolean> cachedStatus =
new EnumMap<>(NamedGroupType.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<CipherSuite> getActiveCipherSuites(
List<ProtocolVersion> enabledProtocols,
List<CipherSuite> enabledCipherSuites,
AlgorithmConstraints algorithmConstraints) {
List<CipherSuite> suites = new LinkedList<>();
if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
Map<NamedGroupType, Boolean> cachedStatus =
new EnumMap<>(NamedGroupType.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 = (ByteBuffer)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<CipherSuite> 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<CipherSuite> 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);
}
private static boolean isActivatable(CipherSuite suite,
AlgorithmConstraints algorithmConstraints,
Map<NamedGroupType, Boolean> 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;
}
boolean available;
NamedGroupType groupType = suite.keyExchange.groupType;
if (groupType != NAMED_GROUP_NONE) {
Boolean checkedStatus = cachedStatus.get(groupType);
if (checkedStatus == null) {
available = SupportedGroups.isActivatable(
algorithmConstraints, groupType);
cachedStatus.put(groupType, available);
if (!available &&
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("No activated named group");
}
} else {
available = checkedStatus;
}
if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"No active named group, ignore " + suite);
}
return available;
} else {
return true;
}
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Ignore disabled cipher suite: " + suite);
}
return false;
}
List<SNIServerName> getRequestedServerNames() {
if (requestedServerNames == null) {
return Collections.<SNIServerName>emptyList();
}
return requestedServerNames;
}
}

View File

@@ -0,0 +1,634 @@
/*
* Copyright (c) 2003, 2018, 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.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.LinkedList;
import javax.crypto.SecretKey;
import sun.security.util.MessageDigestSpi2;
final class HandshakeHash {
private TranscriptHash transcriptHash;
private LinkedList<byte[]> reserves; // one handshake message per entry
private boolean hasBeenUsed;
HandshakeHash() {
this.transcriptHash = new CacheOnlyHash();
this.reserves = new LinkedList<>();
this.hasBeenUsed = false;
}
// fix the negotiated protocol version and cipher suite
void determine(ProtocolVersion protocolVersion,
CipherSuite cipherSuite) {
if (!(transcriptHash instanceof CacheOnlyHash)) {
throw new IllegalStateException(
"Not expected instance of transcript hash");
}
CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;
if (protocolVersion.useTLS13PlusSpec()) {
transcriptHash = new T13HandshakeHash(cipherSuite);
} else if (protocolVersion.useTLS12PlusSpec()) {
transcriptHash = new T12HandshakeHash(cipherSuite);
} else if (protocolVersion.useTLS10PlusSpec()) {
transcriptHash = new T10HandshakeHash(cipherSuite);
} else {
transcriptHash = new S30HandshakeHash(cipherSuite);
}
byte[] reserved = coh.baos.toByteArray();
if (reserved.length != 0) {
transcriptHash.update(reserved, 0, reserved.length);
}
}
HandshakeHash copy() {
if (transcriptHash instanceof CacheOnlyHash) {
HandshakeHash result = new HandshakeHash();
result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();
result.reserves = new LinkedList<>(reserves);
result.hasBeenUsed = hasBeenUsed;
return result;
} else {
throw new IllegalStateException("Hash does not support copying");
}
}
void receive(byte[] input) {
reserves.add(Arrays.copyOf(input, input.length));
}
void receive(ByteBuffer input, int length) {
if (input.hasArray()) {
int from = input.position() + input.arrayOffset();
int to = from + length;
reserves.add(Arrays.copyOfRange(input.array(), from, to));
} else {
int inPos = input.position();
byte[] holder = new byte[length];
input.get(holder);
input.position(inPos);
reserves.add(Arrays.copyOf(holder, holder.length));
}
}
void receive(ByteBuffer input) {
receive(input, input.remaining());
}
// For HelloRetryRequest only! Please use this method very carefully!
void push(byte[] input) {
reserves.push(Arrays.copyOf(input, input.length));
}
// For PreSharedKey to modify the state of the PSK binder hash
byte[] removeLastReceived() {
return reserves.removeLast();
}
void deliver(byte[] input) {
update();
transcriptHash.update(input, 0, input.length);
}
void deliver(byte[] input, int offset, int length) {
update();
transcriptHash.update(input, offset, length);
}
void deliver(ByteBuffer input) {
update();
if (input.hasArray()) {
transcriptHash.update(input.array(),
input.position() + input.arrayOffset(), input.remaining());
} else {
int inPos = input.position();
byte[] holder = new byte[input.remaining()];
input.get(holder);
input.position(inPos);
transcriptHash.update(holder, 0, holder.length);
}
}
// Use one handshake message if it has not been used.
void utilize() {
if (hasBeenUsed) {
return;
}
if (reserves.size() != 0) {
byte[] holder = reserves.remove();
transcriptHash.update(holder, 0, holder.length);
hasBeenUsed = true;
}
}
// Consume one handshake message if it has not been consumed.
void consume() {
if (hasBeenUsed) {
hasBeenUsed = false;
return;
}
if (reserves.size() != 0) {
byte[] holder = reserves.remove();
transcriptHash.update(holder, 0, holder.length);
}
}
void update() {
while (reserves.size() != 0) {
byte[] holder = reserves.remove();
transcriptHash.update(holder, 0, holder.length);
}
hasBeenUsed = false;
}
byte[] digest() {
// Note that the reserve handshake message may be not a part of
// the expected digest.
return transcriptHash.digest();
}
void finish() {
this.transcriptHash = new CacheOnlyHash();
this.reserves = new LinkedList<>();
this.hasBeenUsed = false;
}
// Optional
byte[] archived() {
// Note that the reserve handshake message may be not a part of
// the expected digest.
return transcriptHash.archived();
}
// Optional, TLS 1.0/1.1 only
byte[] digest(String algorithm) {
T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;
return hh.digest(algorithm);
}
// Optional, SSL 3.0 only
byte[] digest(String algorithm, SecretKey masterSecret) {
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
return hh.digest(algorithm, masterSecret);
}
// Optional, SSL 3.0 only
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
return hh.digest(useClientLabel, masterSecret);
}
public boolean isHashable(byte handshakeType) {
return handshakeType != SSLHandshake.HELLO_REQUEST.id;
}
interface TranscriptHash {
void update(byte[] input, int offset, int length);
byte[] digest();
byte[] archived(); // optional
}
// For cache only.
private static final class CacheOnlyHash implements TranscriptHash {
private final ByteArrayOutputStream baos;
CacheOnlyHash() {
this.baos = new ByteArrayOutputStream();
}
@Override
public void update(byte[] input, int offset, int length) {
baos.write(input, offset, length);
}
@Override
public byte[] digest() {
throw new IllegalStateException(
"Not expected call to handshake hash digest");
}
@Override
public byte[] archived() {
return baos.toByteArray();
}
CacheOnlyHash copy() {
CacheOnlyHash result = new CacheOnlyHash();
try {
baos.writeTo(result.baos);
} catch (IOException ex) {
throw new RuntimeException("unable to to clone hash state");
}
return result;
}
}
static final class S30HandshakeHash implements TranscriptHash {
static final byte[] MD5_pad1 = genPad(0x36, 48);
static final byte[] MD5_pad2 = genPad(0x5c, 48);
static final byte[] SHA_pad1 = genPad(0x36, 40);
static final byte[] SHA_pad2 = genPad(0x5c, 40);
private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
private final MessageDigest mdMD5;
private final MessageDigest mdSHA;
private final TranscriptHash md5;
private final TranscriptHash sha;
private final ByteArrayOutputStream baos;
S30HandshakeHash(CipherSuite cipherSuite) {
this.mdMD5 = JsseJce.getMessageDigest("MD5");
this.mdSHA = JsseJce.getMessageDigest("SHA");
boolean hasArchived = false;
if (mdMD5 instanceof Cloneable) {
md5 = new CloneableHash(mdMD5);
} else {
hasArchived = true;
md5 = new NonCloneableHash(mdMD5);
}
if (mdSHA instanceof Cloneable) {
sha = new CloneableHash(mdSHA);
} else {
hasArchived = true;
sha = new NonCloneableHash(mdSHA);
}
if (hasArchived) {
this.baos = null;
} else {
this.baos = new ByteArrayOutputStream();
}
}
@Override
public void update(byte[] input, int offset, int length) {
md5.update(input, offset, length);
sha.update(input, offset, length);
if (baos != null) {
baos.write(input, offset, length);
}
}
@Override
public byte[] digest() {
byte[] digest = new byte[36];
System.arraycopy(md5.digest(), 0, digest, 0, 16);
System.arraycopy(sha.digest(), 0, digest, 16, 20);
return digest;
}
@Override
public byte[] archived() {
if (baos != null) {
return baos.toByteArray();
} else if (md5 instanceof NonCloneableHash) {
return md5.archived();
} else {
return sha.archived();
}
}
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
MessageDigest md5Clone = cloneMd5();
MessageDigest shaClone = cloneSha();
if (useClientLabel) {
md5Clone.update(SSL_CLIENT);
shaClone.update(SSL_CLIENT);
} else {
md5Clone.update(SSL_SERVER);
shaClone.update(SSL_SERVER);
}
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
byte[] digest = new byte[36];
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
return digest;
}
byte[] digest(String algorithm, SecretKey masterSecret) {
if ("RSA".equalsIgnoreCase(algorithm)) {
MessageDigest md5Clone = cloneMd5();
MessageDigest shaClone = cloneSha();
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
byte[] digest = new byte[36];
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
return digest;
} else {
MessageDigest shaClone = cloneSha();
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
return shaClone.digest();
}
}
private static byte[] genPad(int b, int count) {
byte[] padding = new byte[count];
Arrays.fill(padding, (byte)b);
return padding;
}
private MessageDigest cloneMd5() {
MessageDigest md5Clone;
if (mdMD5 instanceof Cloneable) {
try {
md5Clone = (MessageDigest)mdMD5.clone();
} catch (CloneNotSupportedException ex) { // unlikely
throw new RuntimeException(
"MessageDigest does no support clone operation");
}
} else {
md5Clone = JsseJce.getMessageDigest("MD5");
md5Clone.update(md5.archived());
}
return md5Clone;
}
private MessageDigest cloneSha() {
MessageDigest shaClone;
if (mdSHA instanceof Cloneable) {
try {
shaClone = (MessageDigest)mdSHA.clone();
} catch (CloneNotSupportedException ex) { // unlikely
throw new RuntimeException(
"MessageDigest does no support clone operation");
}
} else {
shaClone = JsseJce.getMessageDigest("SHA");
shaClone.update(sha.archived());
}
return shaClone;
}
private static void updateDigest(MessageDigest md,
byte[] pad1, byte[] pad2, SecretKey masterSecret) {
byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
? masterSecret.getEncoded() : null;
if (keyBytes != null) {
md.update(keyBytes);
} else {
digestKey(md, masterSecret);
}
md.update(pad1);
byte[] temp = md.digest();
if (keyBytes != null) {
md.update(keyBytes);
} else {
digestKey(md, masterSecret);
}
md.update(pad2);
md.update(temp);
}
private static void digestKey(MessageDigest md, SecretKey key) {
try {
if (md instanceof MessageDigestSpi2) {
((MessageDigestSpi2)md).engineUpdate(key);
} else {
throw new Exception(
"Digest does not support implUpdate(SecretKey)");
}
} catch (Exception e) {
throw new RuntimeException(
"Could not obtain encoded key and "
+ "MessageDigest cannot digest key", e);
}
}
}
// TLS 1.0 and TLS 1.1
static final class T10HandshakeHash implements TranscriptHash {
private final TranscriptHash md5;
private final TranscriptHash sha;
private final ByteArrayOutputStream baos;
T10HandshakeHash(CipherSuite cipherSuite) {
MessageDigest mdMD5 = JsseJce.getMessageDigest("MD5");
MessageDigest mdSHA = JsseJce.getMessageDigest("SHA");
boolean hasArchived = false;
if (mdMD5 instanceof Cloneable) {
md5 = new CloneableHash(mdMD5);
} else {
hasArchived = true;
md5 = new NonCloneableHash(mdMD5);
}
if (mdSHA instanceof Cloneable) {
sha = new CloneableHash(mdSHA);
} else {
hasArchived = true;
sha = new NonCloneableHash(mdSHA);
}
if (hasArchived) {
this.baos = null;
} else {
this.baos = new ByteArrayOutputStream();
}
}
@Override
public void update(byte[] input, int offset, int length) {
md5.update(input, offset, length);
sha.update(input, offset, length);
if (baos != null) {
baos.write(input, offset, length);
}
}
@Override
public byte[] digest() {
byte[] digest = new byte[36];
System.arraycopy(md5.digest(), 0, digest, 0, 16);
System.arraycopy(sha.digest(), 0, digest, 16, 20);
return digest;
}
byte[] digest(String algorithm) {
if ("RSA".equalsIgnoreCase(algorithm)) {
return digest();
} else {
return sha.digest();
}
}
@Override
public byte[] archived() {
if (baos != null) {
return baos.toByteArray();
} else if (md5 instanceof NonCloneableHash) {
return md5.archived();
} else {
return sha.archived();
}
}
}
static final class T12HandshakeHash implements TranscriptHash {
private final TranscriptHash transcriptHash;
private final ByteArrayOutputStream baos;
T12HandshakeHash(CipherSuite cipherSuite) {
MessageDigest md =
JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
if (md instanceof Cloneable) {
transcriptHash = new CloneableHash(md);
this.baos = new ByteArrayOutputStream();
} else {
transcriptHash = new NonCloneableHash(md);
this.baos = null;
}
}
@Override
public void update(byte[] input, int offset, int length) {
transcriptHash.update(input, offset, length);
if (baos != null) {
baos.write(input, offset, length);
}
}
@Override
public byte[] digest() {
return transcriptHash.digest();
}
@Override
public byte[] archived() {
if (baos != null) {
return baos.toByteArray();
} else {
return transcriptHash.archived();
}
}
}
static final class T13HandshakeHash implements TranscriptHash {
private final TranscriptHash transcriptHash;
T13HandshakeHash(CipherSuite cipherSuite) {
MessageDigest md =
JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
if (md instanceof Cloneable) {
transcriptHash = new CloneableHash(md);
} else {
transcriptHash = new NonCloneableHash(md);
}
}
@Override
public void update(byte[] input, int offset, int length) {
transcriptHash.update(input, offset, length);
}
@Override
public byte[] digest() {
return transcriptHash.digest();
}
@Override
public byte[] archived() {
// This method is not necessary in T13
throw new UnsupportedOperationException(
"TLS 1.3 does not require archived.");
}
}
static final class CloneableHash implements TranscriptHash {
private final MessageDigest md;
CloneableHash(MessageDigest md) {
this.md = md;
}
@Override
public void update(byte[] input, int offset, int length) {
md.update(input, offset, length);
}
@Override
public byte[] digest() {
try {
return ((MessageDigest)md.clone()).digest();
} catch (CloneNotSupportedException ex) {
// unlikely
return new byte[0];
}
}
@Override
public byte[] archived() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
static final class NonCloneableHash implements TranscriptHash {
private final MessageDigest md;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
NonCloneableHash(MessageDigest md) {
this.md = md;
}
@Override
public void update(byte[] input, int offset, int length) {
baos.write(input, offset, length);
}
@Override
public byte[] digest() {
byte[] bytes = baos.toByteArray();
md.reset();
return md.digest(bytes);
}
@Override
public byte[] archived() {
return baos.toByteArray();
}
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 1996, 2018, 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.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Output stream for handshake data. This is used only internally
* to the SSL classes.
*
* MT note: one thread at a time is presumed be writing handshake
* messages, but (after initial connection setup) it's possible to
* have other threads reading/writing application data. It's the
* SSLSocketImpl class that synchronizes record writes.
*
* @author David Brownell
*/
public class HandshakeOutStream extends ByteArrayOutputStream {
OutputRecord outputRecord; // May be null if not actually used to
// output handshake message records.
HandshakeOutStream(OutputRecord outputRecord) {
super();
this.outputRecord = outputRecord;
}
// Complete a handshaking message write. Called by HandshakeMessage.
void complete() throws IOException {
if (size() < 4) { // 4: handshake message header size
// internal_error alert will be triggered
throw new RuntimeException("handshake message is not available");
}
if (outputRecord != null) {
if (!outputRecord.isClosed()) {
outputRecord.encodeHandshake(buf, 0, count);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"handshake messages", ByteBuffer.wrap(buf, 0, count));
}
}
// reset the byte array output stream
reset();
} // otherwise, the handshake outstream is temporarily used only.
}
//
// overridden ByteArrayOutputStream methods
//
@Override
public void write(byte[] b, int off, int len) {
// The maximum fragment size is 24 bytes.
checkOverflow(len, Record.OVERFLOW_OF_INT24);
super.write(b, off, len);
}
@Override
public void flush() throws IOException {
if (outputRecord != null) {
outputRecord.flush();
}
}
//
// handshake output stream management functions
//
/*
* Put integers encoded in standard 8, 16, 24, and 32 bit
* big endian formats. Note that OutputStream.write(int) only
* writes the least significant 8 bits and ignores the rest.
*/
void putInt8(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT08);
super.write(i);
}
void putInt16(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT16);
super.write(i >> 8);
super.write(i);
}
void putInt24(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT24);
super.write(i >> 16);
super.write(i >> 8);
super.write(i);
}
void putInt32(int i) throws IOException {
super.write(i >> 24);
super.write(i >> 16);
super.write(i >> 8);
super.write(i);
}
/*
* Put byte arrays with length encoded as 8, 16, 24 bit
* integers in big-endian format.
*/
void putBytes8(byte[] b) throws IOException {
if (b == null) {
putInt8(0);
} else {
putInt8(b.length);
super.write(b, 0, b.length);
}
}
public void putBytes16(byte[] b) throws IOException {
if (b == null) {
putInt16(0);
} else {
putInt16(b.length);
super.write(b, 0, b.length);
}
}
void putBytes24(byte[] b) throws IOException {
if (b == null) {
putInt24(0);
} else {
putInt24(b.length);
super.write(b, 0, b.length);
}
}
/*
* Does the specified length overflow the limitation?
*/
private static void checkOverflow(int length, int limit) {
if (length >= limit) {
// internal_error alert will be triggered
throw new RuntimeException(
"Field length overflow, the field length (" +
length + ") should be less than " + limit);
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018, 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 sun.security.ssl.SSLHandshake.HandshakeMessage;
interface HandshakeProducer {
// return the encoded producing if it has not been dumped to the context
//
// message: the handshake message responded to, can be null for producing
// of kickstart handshake message
byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException;
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 2018, 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.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import static sun.security.ssl.ClientHello.ClientHelloMessage;
/**
* TLS handshake cookie manager
*/
abstract class HelloCookieManager {
static class Builder {
final SecureRandom secureRandom;
private volatile T13HelloCookieManager t13HelloCookieManager;
Builder(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
}
HelloCookieManager valueOf(ProtocolVersion protocolVersion) {
if (protocolVersion.useTLS13PlusSpec()) {
if (t13HelloCookieManager != null) {
return t13HelloCookieManager;
}
synchronized (this) {
if (t13HelloCookieManager == null) {
t13HelloCookieManager =
new T13HelloCookieManager(secureRandom);
}
}
return t13HelloCookieManager;
}
return null;
}
}
abstract byte[] createCookie(ServerHandshakeContext context,
ClientHelloMessage clientHello) throws IOException;
abstract boolean isCookieValid(ServerHandshakeContext context,
ClientHelloMessage clientHello, byte[] cookie) throws IOException;
private static final
class T13HelloCookieManager extends HelloCookieManager {
final SecureRandom secureRandom;
private int cookieVersion; // version + sequence
private final byte[] cookieSecret;
private final byte[] legacySecret;
T13HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
this.cookieVersion = secureRandom.nextInt();
this.cookieSecret = new byte[64];
this.legacySecret = new byte[64];
secureRandom.nextBytes(cookieSecret);
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
}
@Override
byte[] createCookie(ServerHandshakeContext context,
ClientHelloMessage clientHello) throws IOException {
int version;
byte[] secret;
synchronized (this) {
version = cookieVersion;
secret = cookieSecret;
// the cookie secret usage limit is 2^24
if ((cookieVersion & 0xFFFFFF) == 0) { // reset the secret
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
secureRandom.nextBytes(cookieSecret);
}
cookieVersion++; // allow wrapped version number
}
MessageDigest md = JsseJce.getMessageDigest(
context.negotiatedCipherSuite.hashAlg.name);
byte[] headerBytes = clientHello.getHeaderBytes();
md.update(headerBytes);
byte[] headerCookie = md.digest(secret);
// hash of ClientHello handshake message
context.handshakeHash.update();
byte[] clientHelloHash = context.handshakeHash.digest();
// version and cipher suite
//
// Store the negotiated cipher suite in the cookie as well.
// cookie[0]/[1]: cipher suite
// cookie[2]: cookie version
// + (hash length): Mac(ClientHello header)
// + (hash length): Hash(ClientHello)
byte[] prefix = new byte[] {
(byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF),
(byte)(context.negotiatedCipherSuite.id & 0xFF),
(byte)((version >> 24) & 0xFF)
};
byte[] cookie = Arrays.copyOf(prefix,
prefix.length + headerCookie.length + clientHelloHash.length);
System.arraycopy(headerCookie, 0, cookie,
prefix.length, headerCookie.length);
System.arraycopy(clientHelloHash, 0, cookie,
prefix.length + headerCookie.length, clientHelloHash.length);
return cookie;
}
@Override
boolean isCookieValid(ServerHandshakeContext context,
ClientHelloMessage clientHello, byte[] cookie) throws IOException {
// no cookie exchange or not a valid cookie length
if ((cookie == null) || (cookie.length <= 32)) { // 32: roughly
return false;
}
int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF);
CipherSuite cs = CipherSuite.valueOf(csId);
if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) {
return false;
}
int hashLen = cs.hashAlg.hashLength;
if (cookie.length != (3 + hashLen * 2)) {
return false;
}
byte[] prevHeadCookie =
Arrays.copyOfRange(cookie, 3, 3 + hashLen);
byte[] prevClientHelloHash =
Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);
byte[] secret;
synchronized (this) {
if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {
secret = cookieSecret;
} else {
secret = legacySecret; // including out of window cookies
}
}
MessageDigest md = JsseJce.getMessageDigest(cs.hashAlg.name);
byte[] headerBytes = clientHello.getHeaderBytes();
md.update(headerBytes);
byte[] headerCookie = md.digest(secret);
if (!MessageDigest.isEqual(headerCookie, prevHeadCookie)) {
return false;
}
// Use the ClientHello hash in the cookie for transtript
// hash calculation for stateless HelloRetryRequest.
//
// Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
// Hash(message_hash || /* Handshake type */
// 00 00 Hash.length || /* Handshake message length (bytes) */
// Hash(ClientHello1) || /* Hash of ClientHello1 */
// HelloRetryRequest || ... || Mn)
// Reproduce HelloRetryRequest handshake message
byte[] hrrMessage =
ServerHello.hrrReproducer.produce(context, clientHello);
context.handshakeHash.push(hrrMessage);
// Construct the 1st ClientHello message for transcript hash
byte[] hashedClientHello = new byte[4 + hashLen];
hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;
hashedClientHello[1] = (byte)0x00;
hashedClientHello[2] = (byte)0x00;
hashedClientHello[3] = (byte)(hashLen & 0xFF);
System.arraycopy(prevClientHelloHash, 0,
hashedClientHello, 4, hashLen);
context.handshakeHash.push(hashedClientHello);
return true;
}
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the HelloRequest handshake message.
*/
final class HelloRequest {
static final SSLProducer kickstartProducer =
new HelloRequestKickstartProducer();
static final SSLConsumer handshakeConsumer =
new HelloRequestConsumer();
static final HandshakeProducer handshakeProducer =
new HelloRequestProducer();
/**
* The HelloRequest handshake message.
*
* [RFC 5246] The HelloRequest message MAY be sent by the server at any
* time. HelloRequest is a simple notification that the client should
* begin the negotiation process anew.
*
* struct { } HelloRequest;
*/
static final class HelloRequestMessage extends HandshakeMessage {
HelloRequestMessage(HandshakeContext handshakeContext) {
super(handshakeContext);
}
HelloRequestMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
if (m.hasRemaining()) {
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Error parsing HelloRequest message: not empty");
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.HELLO_REQUEST;
}
@Override
public int messageLength() {
return 0;
}
@Override
public void send(HandshakeOutStream s) throws IOException {
// empty, nothing to send
}
@Override
public String toString() {
return "<empty>";
}
}
/**
* The "HelloRequest" handshake message kick start producer.
*/
private static final
class HelloRequestKickstartProducer implements SSLProducer {
// Prevent instantiation of this class.
private HelloRequestKickstartProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
HelloRequestMessage hrm = new HelloRequestMessage(shc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced HelloRequest handshake message", hrm);
}
// Output the handshake message.
hrm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// update the context
// What's the expected response?
shc.handshakeConsumers.put(
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
// The handshake message has been delivered.
return null;
}
}
/**
* The "HelloRequest" handshake message producer.
*/
private static final class HelloRequestProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private HelloRequestProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
HelloRequestMessage hrm = new HelloRequestMessage(shc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced HelloRequest handshake message", hrm);
}
// Output the handshake message.
hrm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// update the context
// What's the expected response?
shc.handshakeConsumers.put(
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
// The handshake message has been delivered.
return null;
}
}
/**
* The "HelloRequest" handshake message consumer.
*/
private static final class HelloRequestConsumer
implements SSLConsumer {
// Prevent instantiation of this class.
private HelloRequestConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// For TLS 1.2 and prior versions, the HelloRequest message MAY
// be sent by the server at any time. Please don't clean up this
// handshake consumer.
HelloRequestMessage hrm = new HelloRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming HelloRequest handshake message", hrm);
}
if (!chc.kickstartMessageDelivered) {
if (!chc.conContext.secureRenegotiation &&
!HandshakeContext.allowUnsafeRenegotiation) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsafe renegotiation is not allowed");
}
if (!chc.conContext.secureRenegotiation) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Continue with insecure renegotiation");
}
}
// update the responders
chc.handshakeProducers.put(
SSLHandshake.CLIENT_HELLO.id,
SSLHandshake.CLIENT_HELLO);
//
// produce response handshake message
//
SSLHandshake.CLIENT_HELLO.produce(context, hrm);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ingore HelloRequest, handshaking is in progress");
}
}
}
}
}

View File

@@ -0,0 +1,369 @@
/*
* Copyright (c) 1996, 2018, 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.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import javax.crypto.BadPaddingException;
import sun.security.ssl.SSLCipher.SSLReadCipher;
/**
* {@code InputRecord} takes care of the management of SSL/TLS input
* records, including buffering, decryption, handshake messages marshal, etc.
*
* @author David Brownell
*/
abstract class InputRecord implements Record, Closeable {
SSLReadCipher readCipher;
// Needed for KeyUpdate, used after Handshake.Finished
TransportContext tc;
final HandshakeHash handshakeHash;
boolean isClosed;
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
// and the first message we read is a ClientHello in V2 format, we convert
// it to V3. Otherwise we throw an exception when encountering a V2 hello.
ProtocolVersion helloVersion;
// fragment size
int fragmentSize;
InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
this.readCipher = readCipher;
this.helloVersion = ProtocolVersion.TLS10;
this.handshakeHash = handshakeHash;
this.isClosed = false;
this.fragmentSize = Record.maxDataSize;
}
void setHelloVersion(ProtocolVersion helloVersion) {
this.helloVersion = helloVersion;
}
boolean seqNumIsHuge() {
return (readCipher.authenticator != null) &&
readCipher.authenticator.seqNumIsHuge();
}
/**
* Prevent any more data from being read into this record,
* and flag the record as holding no data.
*/
@Override
public synchronized void close() throws IOException {
if (!isClosed) {
isClosed = true;
readCipher.dispose();
}
}
synchronized boolean isClosed() {
return isClosed;
}
// apply to SSLSocket and SSLEngine
void changeReadCiphers(SSLReadCipher readCipher) {
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
readCipher.dispose();
this.readCipher = readCipher;
}
// change fragment size
void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
/*
* Check if there is enough inbound data in the ByteBuffer to make
* a inbound packet.
*
* @return -1 if there are not enough bytes to tell (small header),
*/
// apply to SSLEngine only
int bytesInCompletePacket(
ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
// apply to SSLSocket only
int bytesInCompletePacket() throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void setReceiverStream(InputStream inputStream) {
throw new UnsupportedOperationException();
}
// read, decrypt and decompress the network record.
//
abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
int srcsLength) throws IOException, BadPaddingException;
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
throw new UnsupportedOperationException();
}
// calculate plaintext fragment size
//
// apply to SSLEngine only
int estimateFragmentSize(int packetSize) {
throw new UnsupportedOperationException();
}
//
// shared helpers
//
static ByteBuffer convertToClientHello(ByteBuffer packet) {
int srcPos = packet.position();
byte firstByte = packet.get();
byte secondByte = packet.get();
int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
packet.position(srcPos + 3); // the V2ClientHello record header
byte majorVersion = packet.get();
byte minorVersion = packet.get();
int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
(packet.get() & 0xFF);
int sessionIdLen = ((packet.get() & 0xFF) << 8) +
(packet.get() & 0xFF);
int nonceLen = ((packet.get() & 0xFF) << 8) +
(packet.get() & 0xFF);
// Required space for the target SSLv3 ClientHello message.
// 5: record header size
// 4: handshake header size
// 2: ClientHello.client_version
// 32: ClientHello.random
// 1: length byte of ClientHello.session_id
// 2: length bytes of ClientHello.cipher_suites
// 2: empty ClientHello.compression_methods
int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
byte[] converted = new byte[requiredSize];
/*
* Build the first part of the V3 record header from the V2 one
* that's now buffered up. (Lengths are fixed up later).
*/
// Note: need not to set the header actually.
converted[0] = ContentType.HANDSHAKE.id;
converted[1] = majorVersion;
converted[2] = minorVersion;
// header [3..4] for handshake message length
// required size is 5;
/*
* Store the generic V3 handshake header: 4 bytes
*/
converted[5] = 1; // HandshakeMessage.ht_client_hello
// buf [6..8] for length of ClientHello (int24)
// required size += 4;
/*
* ClientHello header starts with SSL version
*/
converted[9] = majorVersion;
converted[10] = minorVersion;
// required size += 2;
int pointer = 11;
/*
* Copy Random value/nonce ... if less than the 32 bytes of
* a V3 "Random", right justify and zero pad to the left. Else
* just take the last 32 bytes.
*/
int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
if (nonceLen < 32) {
for (int i = 0; i < (32 - nonceLen); i++) {
converted[pointer++] = 0;
}
packet.position(offset);
packet.get(converted, pointer, nonceLen);
pointer += nonceLen;
} else {
packet.position(offset + nonceLen - 32);
packet.get(converted, pointer, 32);
pointer += 32;
}
/*
* Copy session ID (only one byte length!)
*/
offset -= sessionIdLen;
converted[pointer++] = (byte)(sessionIdLen & 0xFF);
packet.position(offset);
packet.get(converted, pointer, sessionIdLen);
/*
* Copy and translate cipher suites ... V2 specs with first byte zero
* are really V3 specs (in the last 2 bytes), just copy those and drop
* the other ones. Preference order remains unchanged.
*
* Example: Netscape Navigator 3.0 (exportable) says:
*
* 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
* 0/6, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
*
* Microsoft Internet Explorer 3.0 (exportable) supports only
*
* 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
*/
int j;
offset -= cipherSpecLen;
packet.position(offset);
j = pointer + 2;
for (int i = 0; i < cipherSpecLen; i += 3) {
if (packet.get() != 0) {
// Ignore version 2.0 specific cipher suite. Clients
// should also include the version 3.0 equivalent in
// the V2ClientHello message.
packet.get(); // ignore the 2nd byte
packet.get(); // ignore the 3rd byte
continue;
}
converted[j++] = packet.get();
converted[j++] = packet.get();
}
j -= pointer + 2;
converted[pointer++] = (byte)((j >>> 8) & 0xFF);
converted[pointer++] = (byte)(j & 0xFF);
pointer += j;
/*
* Append compression methods (default/null only)
*/
converted[pointer++] = 1;
converted[pointer++] = 0; // Session.compression_null
/*
* Fill in lengths of the messages we synthesized (nested:
* V3 handshake message within V3 record).
*/
// Note: need not to set the header actually.
int fragLen = pointer - 5; // TLSPlaintext.length
converted[3] = (byte)((fragLen >>> 8) & 0xFF);
converted[4] = (byte)(fragLen & 0xFF);
/*
* Handshake.length, length of ClientHello message
*/
fragLen = pointer - 9; // Handshake.length
converted[6] = (byte)((fragLen >>> 16) & 0xFF);
converted[7] = (byte)((fragLen >>> 8) & 0xFF);
converted[8] = (byte)(fragLen & 0xFF);
// consume the full record
packet.position(srcPos + recordLen);
// Need no header bytes.
return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size
}
// Extract an SSL/TLS record from the specified source buffers.
static ByteBuffer extract(
ByteBuffer[] buffers, int offset, int length, int headerSize) {
boolean hasFullHeader = false;
int contentLen = -1;
for (int i = offset, j = 0;
i < (offset + length) && j < headerSize; i++) {
int remains = buffers[i].remaining();
int pos = buffers[i].position();
for (int k = 0; k < remains && j < headerSize; j++, k++) {
byte b = buffers[i].get(pos + k);
if (j == (headerSize - 2)) {
contentLen = ((b & 0xFF) << 8);
} else if (j == (headerSize -1)) {
contentLen |= (b & 0xFF);
hasFullHeader = true;
break;
}
}
}
if (!hasFullHeader) {
throw new BufferUnderflowException();
}
int packetLen = headerSize + contentLen;
int remains = 0;
for (int i = offset; i < offset + length; i++) {
remains += buffers[i].remaining();
if (remains >= packetLen) {
break;
}
}
if (remains < packetLen) {
throw new BufferUnderflowException();
}
byte[] packet = new byte[packetLen];
int packetOffset = 0;
int packetSpaces = packetLen;
for (int i = offset; i < offset + length; i++) {
if (buffers[i].hasRemaining()) {
int len = Math.min(packetSpaces, buffers[i].remaining());
buffers[i].get(packet, packetOffset, len);
packetOffset += len;
packetSpaces -= len;
}
if (packetSpaces <= 0) {
break;
}
}
return ByteBuffer.wrap(packet);
}
}

View File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 2001, 2018, 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.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.*;
import javax.crypto.*;
import sun.security.jca.ProviderList;
import sun.security.jca.Providers;
import static sun.security.ssl.SunJSSE.cryptoProvider;
import sun.security.util.ECUtil;
import static sun.security.util.SecurityConstants.PROVIDER_VER;
/**
* This class contains a few static methods for interaction with the JCA/JCE
* to obtain implementations, etc.
*
* @author Andreas Sterbenz
*/
final class JsseJce {
static final boolean ALLOW_ECC =
Utilities.getBooleanProperty("com.sun.net.ssl.enableECC", true);
private static final ProviderList fipsProviderList;
// Flag indicating whether Kerberos crypto is available.
// If true, then all the Kerberos-based crypto we need is available.
private final static boolean kerberosAvailable;
static {
boolean temp;
try {
AccessController.doPrivileged(
new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
// Test for Kerberos using the bootstrap class loader
Class.forName("sun.security.krb5.PrincipalName", true,
null);
return null;
}
});
temp = true;
} catch (Exception e) {
temp = false;
}
kerberosAvailable = temp;
}
static {
// force FIPS flag initialization
// Because isFIPS() is synchronized and cryptoProvider is not modified
// after it completes, this also eliminates the need for any further
// synchronization when accessing cryptoProvider
if (SunJSSE.isFIPS() == false) {
fipsProviderList = null;
} else {
// Setup a ProviderList that can be used by the trust manager
// during certificate chain validation. All the crypto must be
// from the FIPS provider, but we also allow the required
// certificate related services from the SUN provider.
Provider sun = Security.getProvider("SUN");
if (sun == null) {
throw new RuntimeException
("FIPS mode: SUN provider must be installed");
}
Provider sunCerts = new SunCertificates(sun);
fipsProviderList = ProviderList.newList(cryptoProvider, sunCerts);
}
}
private static final class SunCertificates extends Provider {
private static final long serialVersionUID = -3284138292032213752L;
SunCertificates(final Provider p) {
super("SunCertificates", PROVIDER_VER, "SunJSSE internal");
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
// copy certificate related services from the Sun provider
for (Map.Entry<Object,Object> entry : p.entrySet()) {
String key = (String)entry.getKey();
if (key.startsWith("CertPathValidator.")
|| key.startsWith("CertPathBuilder.")
|| key.startsWith("CertStore.")
|| key.startsWith("CertificateFactory.")) {
put(key, entry.getValue());
}
}
return null;
}
});
}
}
/**
* JCE transformation string for RSA with PKCS#1 v1.5 padding.
* Can be used for encryption, decryption, signing, verifying.
*/
static final String CIPHER_RSA_PKCS1 = "RSA/ECB/PKCS1Padding";
/**
* JCE transformation string for the stream cipher RC4.
*/
static final String CIPHER_RC4 = "RC4";
/**
* JCE transformation string for DES in CBC mode without padding.
*/
static final String CIPHER_DES = "DES/CBC/NoPadding";
/**
* JCE transformation string for (3-key) Triple DES in CBC mode
* without padding.
*/
static final String CIPHER_3DES = "DESede/CBC/NoPadding";
/**
* JCE transformation string for AES in CBC mode
* without padding.
*/
static final String CIPHER_AES = "AES/CBC/NoPadding";
/**
* JCE transformation string for AES in GCM mode
* without padding.
*/
static final String CIPHER_AES_GCM = "AES/GCM/NoPadding";
/**
* JCA identifier string for DSA, i.e. a DSA with SHA-1.
*/
static final String SIGNATURE_DSA = "DSA";
/**
* JCA identifier string for ECDSA, i.e. a ECDSA with SHA-1.
*/
static final String SIGNATURE_ECDSA = "SHA1withECDSA";
/**
* JCA identifier string for Raw DSA, i.e. a DSA signature without
* hashing where the application provides the SHA-1 hash of the data.
* Note that the standard name is "NONEwithDSA" but we use "RawDSA"
* for compatibility.
*/
static final String SIGNATURE_RAWDSA = "RawDSA";
/**
* JCA identifier string for Raw ECDSA, i.e. a DSA signature without
* hashing where the application provides the SHA-1 hash of the data.
*/
static final String SIGNATURE_RAWECDSA = "NONEwithECDSA";
/**
* JCA identifier string for Raw RSA, i.e. a RSA PKCS#1 v1.5 signature
* without hashing where the application provides the hash of the data.
* Used for RSA client authentication with a 36 byte hash.
*/
static final String SIGNATURE_RAWRSA = "NONEwithRSA";
/**
* JCA identifier string for the SSL/TLS style RSA Signature. I.e.
* an signature using RSA with PKCS#1 v1.5 padding signing a
* concatenation of an MD5 and SHA-1 digest.
*/
static final String SIGNATURE_SSLRSA = "MD5andSHA1withRSA";
private JsseJce() {
// no instantiation of this class
}
static boolean isEcAvailable() {
return EcAvailability.isAvailable;
}
static boolean isKerberosAvailable() {
return kerberosAvailable;
}
/**
* Return an JCE cipher implementation for the specified algorithm.
*/
static Cipher getCipher(String transformation)
throws NoSuchAlgorithmException {
try {
if (cryptoProvider == null) {
return Cipher.getInstance(transformation);
} else {
return Cipher.getInstance(transformation, cryptoProvider);
}
} catch (NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(e);
}
}
/**
* Return an JCA signature implementation for the specified algorithm.
* The algorithm string should be one of the constants defined
* in this class.
*/
static Signature getSignature(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return Signature.getInstance(algorithm);
} else {
// reference equality
if (algorithm == SIGNATURE_SSLRSA) {
// The SunPKCS11 provider currently does not support this
// special algorithm. We allow a fallback in this case because
// the SunJSSE implementation does the actual crypto using
// a NONEwithRSA signature obtained from the cryptoProvider.
if (cryptoProvider.getService("Signature", algorithm) == null) {
// Calling Signature.getInstance() and catching the
// exception would be cleaner, but exceptions are a little
// expensive. So we check directly via getService().
try {
return Signature.getInstance(algorithm, "SunJSSE");
} catch (NoSuchProviderException e) {
throw new NoSuchAlgorithmException(e);
}
}
}
return Signature.getInstance(algorithm, cryptoProvider);
}
}
static KeyGenerator getKeyGenerator(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return KeyGenerator.getInstance(algorithm);
} else {
return KeyGenerator.getInstance(algorithm, cryptoProvider);
}
}
static KeyPairGenerator getKeyPairGenerator(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return KeyPairGenerator.getInstance(algorithm);
} else {
return KeyPairGenerator.getInstance(algorithm, cryptoProvider);
}
}
static KeyAgreement getKeyAgreement(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return KeyAgreement.getInstance(algorithm);
} else {
return KeyAgreement.getInstance(algorithm, cryptoProvider);
}
}
static Mac getMac(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return Mac.getInstance(algorithm);
} else {
return Mac.getInstance(algorithm, cryptoProvider);
}
}
static KeyFactory getKeyFactory(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return KeyFactory.getInstance(algorithm);
} else {
return KeyFactory.getInstance(algorithm, cryptoProvider);
}
}
static AlgorithmParameters getAlgorithmParameters(String algorithm)
throws NoSuchAlgorithmException {
if (cryptoProvider == null) {
return AlgorithmParameters.getInstance(algorithm);
} else {
return AlgorithmParameters.getInstance(algorithm, cryptoProvider);
}
}
static SecureRandom getSecureRandom() throws KeyManagementException {
if (cryptoProvider == null) {
return new SecureRandom();
}
// Try "PKCS11" first. If that is not supported, iterate through
// the provider and return the first working implementation.
try {
return SecureRandom.getInstance("PKCS11", cryptoProvider);
} catch (NoSuchAlgorithmException e) {
// ignore
}
for (Provider.Service s : cryptoProvider.getServices()) {
if (s.getType().equals("SecureRandom")) {
try {
return SecureRandom.getInstance(
s.getAlgorithm(), cryptoProvider);
} catch (NoSuchAlgorithmException ee) {
// ignore
}
}
}
throw new KeyManagementException("FIPS mode: no SecureRandom "
+ " implementation found in provider " + cryptoProvider.getName());
}
static MessageDigest getMD5() {
return getMessageDigest("MD5");
}
static MessageDigest getSHA() {
return getMessageDigest("SHA");
}
static MessageDigest getMessageDigest(String algorithm) {
try {
if (cryptoProvider == null) {
return MessageDigest.getInstance(algorithm);
} else {
return MessageDigest.getInstance(algorithm, cryptoProvider);
}
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException
("Algorithm " + algorithm + " not available", e);
}
}
static int getRSAKeyLength(PublicKey key) {
BigInteger modulus;
if (key instanceof RSAPublicKey) {
modulus = ((RSAPublicKey)key).getModulus();
} else {
RSAPublicKeySpec spec = getRSAPublicKeySpec(key);
modulus = spec.getModulus();
}
return modulus.bitLength();
}
static RSAPublicKeySpec getRSAPublicKeySpec(PublicKey key) {
if (key instanceof RSAPublicKey) {
RSAPublicKey rsaKey = (RSAPublicKey)key;
return new RSAPublicKeySpec(rsaKey.getModulus(),
rsaKey.getPublicExponent());
}
try {
KeyFactory factory = JsseJce.getKeyFactory("RSA");
return factory.getKeySpec(key, RSAPublicKeySpec.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static ECParameterSpec getECParameterSpec(String namedCurveOid) {
return ECUtil.getECParameterSpec(cryptoProvider, namedCurveOid);
}
static String getNamedCurveOid(ECParameterSpec params) {
return ECUtil.getCurveName(cryptoProvider, params);
}
static ECPoint decodePoint(byte[] encoded, EllipticCurve curve)
throws java.io.IOException {
return ECUtil.decodePoint(encoded, curve);
}
static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
return ECUtil.encodePoint(point, curve);
}
// In FIPS mode, set thread local providers; otherwise a no-op.
// Must be paired with endFipsProvider.
static Object beginFipsProvider() {
if (fipsProviderList == null) {
return null;
} else {
return Providers.beginThreadProviderList(fipsProviderList);
}
}
static void endFipsProvider(Object o) {
if (fipsProviderList != null) {
Providers.endThreadProviderList((ProviderList)o);
}
}
// lazy initialization holder class idiom for static default parameters
//
// See Effective Java Second Edition: Item 71.
private static class EcAvailability {
// Is EC crypto available?
private static final boolean isAvailable;
static {
boolean mediator = true;
try {
JsseJce.getSignature(SIGNATURE_ECDSA);
JsseJce.getSignature(SIGNATURE_RAWECDSA);
JsseJce.getKeyAgreement("ECDH");
JsseJce.getKeyFactory("EC");
JsseJce.getKeyPairGenerator("EC");
JsseJce.getAlgorithmParameters("EC");
} catch (Exception e) {
mediator = false;
}
isAvailable = mediator;
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 1999, 2018, 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.util.List;
import java.util.Collections;
import java.security.*;
import java.security.KeyStore.*;
import javax.net.ssl.*;
abstract class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
X509ExtendedKeyManager keyManager;
boolean isInitialized;
KeyManagerFactoryImpl() {
// empty
}
/**
* Returns one key manager for each type of key material.
*/
@Override
protected KeyManager[] engineGetKeyManagers() {
if (!isInitialized) {
throw new IllegalStateException(
"KeyManagerFactoryImpl is not initialized");
}
return new KeyManager[] { keyManager };
}
// Factory for the SunX509 keymanager
public static final class SunX509 extends KeyManagerFactoryImpl {
@Override
protected void engineInit(KeyStore ks, char[] password) throws
KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException {
if ((ks != null) && SunJSSE.isFIPS()) {
if (ks.getProvider() != SunJSSE.cryptoProvider) {
throw new KeyStoreException("FIPS mode: KeyStore must be "
+ "from provider " + SunJSSE.cryptoProvider.getName());
}
}
keyManager = new SunX509KeyManagerImpl(ks, password);
isInitialized = true;
}
@Override
protected void engineInit(ManagerFactoryParameters spec) throws
InvalidAlgorithmParameterException {
throw new InvalidAlgorithmParameterException(
"SunX509KeyManager does not use ManagerFactoryParameters");
}
}
// Factory for the X509 keymanager
public static final class X509 extends KeyManagerFactoryImpl {
@Override
protected void engineInit(KeyStore ks, char[] password) throws
KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException {
if (ks == null) {
keyManager = new X509KeyManagerImpl(
Collections.<Builder>emptyList());
} else {
if (SunJSSE.isFIPS() &&
(ks.getProvider() != SunJSSE.cryptoProvider)) {
throw new KeyStoreException(
"FIPS mode: KeyStore must be " +
"from provider " + SunJSSE.cryptoProvider.getName());
}
try {
Builder builder = Builder.newInstance(ks,
new PasswordProtection(password));
keyManager = new X509KeyManagerImpl(builder);
} catch (RuntimeException e) {
throw new KeyStoreException("initialization failed", e);
}
}
isInitialized = true;
}
@Override
protected void engineInit(ManagerFactoryParameters params) throws
InvalidAlgorithmParameterException {
if (params instanceof KeyStoreBuilderParameters == false) {
throw new InvalidAlgorithmParameterException(
"Parameters must be instance of KeyStoreBuilderParameters");
}
if (SunJSSE.isFIPS()) {
throw new InvalidAlgorithmParameterException
("FIPS mode: KeyStoreBuilderParameters not supported");
}
List<Builder> builders =
((KeyStoreBuilderParameters)params).getParameters();
keyManager = new X509KeyManagerImpl(builders);
isInitialized = true;
}
}
}

View File

@@ -0,0 +1,954 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.DHKeyExchange.DHECredentials;
import sun.security.ssl.DHKeyExchange.DHEPossession;
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.misc.HexDumpEncoder;
/**
* Pack of the "key_share" extensions.
*/
final class KeyShareExtension {
static final HandshakeProducer chNetworkProducer =
new CHKeyShareProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHKeyShareConsumer();
static final SSLStringizer chStringizer =
new CHKeyShareStringizer();
static final HandshakeProducer shNetworkProducer =
new SHKeyShareProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHKeyShareConsumer();
static final HandshakeAbsence shOnLoadAbsence =
new SHKeyShareAbsence();
static final SSLStringizer shStringizer =
new SHKeyShareStringizer();
static final HandshakeProducer hrrNetworkProducer =
new HRRKeyShareProducer();
static final ExtensionConsumer hrrOnLoadConsumer =
new HRRKeyShareConsumer();
static final HandshakeProducer hrrNetworkReproducer =
new HRRKeyShareReproducer();
static final SSLStringizer hrrStringizer =
new HRRKeyShareStringizer();
/**
* The key share entry used in "key_share" extensions.
*/
private static final class KeyShareEntry {
final int namedGroupId;
final byte[] keyExchange;
private KeyShareEntry(int namedGroupId, byte[] keyExchange) {
this.namedGroupId = namedGroupId;
this.keyExchange = keyExchange;
}
private byte[] getEncoded() {
byte[] buffer = new byte[keyExchange.length + 4];
// 2: named group id
// +2: key exchange length
ByteBuffer m = ByteBuffer.wrap(buffer);
try {
Record.putInt16(m, namedGroupId);
Record.putBytes16(m, keyExchange);
} catch (IOException ioe) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unlikely IOException", ioe);
}
}
return buffer;
}
private int getEncodedSize() {
return keyExchange.length + 4; // 2: named group id
// +2: key exchange length
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\n'{'\n" +
" \"named group\": {0}\n" +
" \"key_exchange\": '{'\n" +
"{1}\n" +
" '}'\n" +
"'}',", Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
NamedGroup.nameOf(namedGroupId),
Utilities.indent(hexEncoder.encode(keyExchange), " ")
};
return messageFormat.format(messageFields);
}
}
/**
* The "key_share" extension in a ClientHello handshake message.
*/
static final class CHKeyShareSpec implements SSLExtensionSpec {
final List<KeyShareEntry> clientShares;
private CHKeyShareSpec(List<KeyShareEntry> clientShares) {
this.clientShares = clientShares;
}
private CHKeyShareSpec(ByteBuffer buffer) throws IOException {
// struct {
// KeyShareEntry client_shares<0..2^16-1>;
// } KeyShareClientHello;
if (buffer.remaining() < 2) {
throw new SSLProtocolException(
"Invalid key_share extension: " +
"insufficient data (length=" + buffer.remaining() + ")");
}
int listLen = Record.getInt16(buffer);
if (listLen != buffer.remaining()) {
throw new SSLProtocolException(
"Invalid key_share extension: " +
"incorrect list length (length=" + listLen + ")");
}
List<KeyShareEntry> keyShares = new LinkedList<>();
while (buffer.hasRemaining()) {
int namedGroupId = Record.getInt16(buffer);
byte[] keyExchange = Record.getBytes16(buffer);
if (keyExchange.length == 0) {
throw new SSLProtocolException(
"Invalid key_share extension: empty key_exchange");
}
keyShares.add(new KeyShareEntry(namedGroupId, keyExchange));
}
this.clientShares = Collections.unmodifiableList(keyShares);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"client_shares\": '['{0}\n']'", Locale.ENGLISH);
StringBuilder builder = new StringBuilder(512);
for (KeyShareEntry entry : clientShares) {
builder.append(entry.toString());
}
Object[] messageFields = {
Utilities.indent(builder.toString())
};
return messageFormat.format(messageFields);
}
}
private static final class CHKeyShareStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new CHKeyShareSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of the extension in a ClientHello
* handshake message.
*/
private static final
class CHKeyShareProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHKeyShareProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable key_share extension");
}
return null;
}
List<NamedGroup> namedGroups;
if (chc.serverSelectedNamedGroup != null) {
// Response to HelloRetryRequest
namedGroups = Arrays.asList(chc.serverSelectedNamedGroup);
} else {
namedGroups = chc.clientRequestedNamedGroups;
if (namedGroups == null || namedGroups.isEmpty()) {
// No supported groups.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore key_share extension, no supported groups");
}
return null;
}
}
List<KeyShareEntry> keyShares = new LinkedList<>();
for (NamedGroup ng : namedGroups) {
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
if (ke == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No key exchange for named group " + ng.name);
}
continue;
}
SSLPossession[] poses = ke.createPossessions(chc);
for (SSLPossession pos : poses) {
// update the context
chc.handshakePossessions.add(pos);
if (!(pos instanceof ECDHEPossession) &&
!(pos instanceof DHEPossession)) {
// May need more possesion types in the future.
continue;
}
keyShares.add(new KeyShareEntry(ng.id, pos.encode()));
}
// One key share entry only. Too much key share entries makes
// the ClientHello handshake message really big.
if (!keyShares.isEmpty()) {
break;
}
}
int listLen = 0;
for (KeyShareEntry entry : keyShares) {
listLen += entry.getEncodedSize();
}
byte[] extData = new byte[listLen + 2]; // 2: list length
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, listLen);
for (KeyShareEntry entry : keyShares) {
m.put(entry.getEncoded());
}
// update the context
chc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE,
new CHKeyShareSpec(keyShares));
return extData;
}
}
/**
* Network data consumer of the extension in a ClientHello
* handshake message.
*/
private static final class CHKeyShareConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHKeyShareConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"The key_share extension has been loaded");
}
return;
}
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable key_share extension");
}
return; // ignore the extension
}
// Parse the extension
CHKeyShareSpec spec;
try {
spec = new CHKeyShareSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
List<SSLCredentials> credentials = new LinkedList<>();
for (KeyShareEntry entry : spec.clientShares) {
NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
shc.algorithmConstraints, ng)) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unsupported named group: " +
NamedGroup.nameOf(entry.namedGroupId));
}
continue;
}
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
try {
ECDHECredentials ecdhec =
ECDHECredentials.valueOf(ng, entry.keyExchange);
if (ecdhec != null) {
if (shc.algorithmConstraints != null &&
!shc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
ecdhec.popPublicKey)) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"ECDHE key share entry does not " +
"comply to algorithm constraints");
}
} else {
credentials.add(ecdhec);
}
}
} catch (IOException | GeneralSecurityException ex) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Cannot decode named group: " +
NamedGroup.nameOf(entry.namedGroupId));
}
}
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
try {
DHECredentials dhec =
DHECredentials.valueOf(ng, entry.keyExchange);
if (dhec != null) {
if (shc.algorithmConstraints != null &&
!shc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
dhec.popPublicKey)) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"DHE key share entry does not " +
"comply to algorithm constraints");
}
} else {
credentials.add(dhec);
}
}
} catch (IOException | GeneralSecurityException ex) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Cannot decode named group: " +
NamedGroup.nameOf(entry.namedGroupId));
}
}
}
}
if (!credentials.isEmpty()) {
shc.handshakeCredentials.addAll(credentials);
} else {
// New handshake credentials are required from the client side.
shc.handshakeProducers.put(
SSLHandshake.HELLO_RETRY_REQUEST.id,
SSLHandshake.HELLO_RETRY_REQUEST);
}
// update the context
shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec);
}
}
/**
* The key share entry used in ServerHello "key_share" extensions.
*/
static final class SHKeyShareSpec implements SSLExtensionSpec {
final KeyShareEntry serverShare;
SHKeyShareSpec(KeyShareEntry serverShare) {
this.serverShare = serverShare;
}
private SHKeyShareSpec(ByteBuffer buffer) throws IOException {
// struct {
// KeyShareEntry server_share;
// } KeyShareServerHello;
if (buffer.remaining() < 5) { // 5: minimal server_share
throw new SSLProtocolException(
"Invalid key_share extension: " +
"insufficient data (length=" + buffer.remaining() + ")");
}
int namedGroupId = Record.getInt16(buffer);
byte[] keyExchange = Record.getBytes16(buffer);
if (buffer.hasRemaining()) {
throw new SSLProtocolException(
"Invalid key_share extension: unknown extra data");
}
this.serverShare = new KeyShareEntry(namedGroupId, keyExchange);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"server_share\": '{'\n" +
" \"named group\": {0}\n" +
" \"key_exchange\": '{'\n" +
"{1}\n" +
" '}'\n" +
"'}',", Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
NamedGroup.nameOf(serverShare.namedGroupId),
Utilities.indent(
hexEncoder.encode(serverShare.keyExchange), " ")
};
return messageFormat.format(messageFields);
}
}
private static final class SHKeyShareStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new SHKeyShareSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of the extension in a ServerHello
* handshake message.
*/
private static final class SHKeyShareProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHKeyShareProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// In response to key_share request only
CHKeyShareSpec kss =
(CHKeyShareSpec)shc.handshakeExtensions.get(
SSLExtension.CH_KEY_SHARE);
if (kss == null) {
// Unlikely, no key_share extension requested.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore, no client key_share extension");
}
return null;
}
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore, no available server key_share extension");
}
return null;
}
// use requested key share entries
if ((shc.handshakeCredentials == null) ||
shc.handshakeCredentials.isEmpty()) {
// Unlikely, HelloRetryRequest should be used ealier.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No available client key share entries");
}
return null;
}
KeyShareEntry keyShare = null;
for (SSLCredentials cd : shc.handshakeCredentials) {
NamedGroup ng = null;
if (cd instanceof ECDHECredentials) {
ng = ((ECDHECredentials)cd).namedGroup;
} else if (cd instanceof DHECredentials) {
ng = ((DHECredentials)cd).namedGroup;
}
if (ng == null) {
continue;
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
if (ke == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No key exchange for named group " + ng.name);
}
continue;
}
SSLPossession[] poses = ke.createPossessions(shc);
for (SSLPossession pos : poses) {
if (!(pos instanceof ECDHEPossession) &&
!(pos instanceof DHEPossession)) {
// May need more possesion types in the future.
continue;
}
// update the context
shc.handshakeKeyExchange = ke;
shc.handshakePossessions.add(pos);
keyShare = new KeyShareEntry(ng.id, pos.encode());
break;
}
if (keyShare != null) {
for (Map.Entry<Byte, HandshakeProducer> me :
ke.getHandshakeProducers(shc)) {
shc.handshakeProducers.put(
me.getKey(), me.getValue());
}
// We have got one! Don't forgor to break.
break;
}
}
if (keyShare == null) {
// Unlikely, HelloRetryRequest should be used instead ealier.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No available server key_share extension");
}
return null;
}
byte[] extData = keyShare.getEncoded();
// update the context
SHKeyShareSpec spec = new SHKeyShareSpec(keyShare);
shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
return extData;
}
}
/**
* Network data consumer of the extension in a ServerHello
* handshake message.
*/
private static final class SHKeyShareConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHKeyShareConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// Happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
if (chc.clientRequestedNamedGroups == null ||
chc.clientRequestedNamedGroups.isEmpty()) {
// No supported groups.
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in ServerHello");
}
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in ServerHello");
}
// Parse the extension
SHKeyShareSpec spec;
try {
spec = new SHKeyShareSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
KeyShareEntry keyShare = spec.serverShare;
NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
chc.algorithmConstraints, ng)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
if (ke == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"No key exchange for named group " + ng.name);
}
SSLCredentials credentials = null;
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
try {
ECDHECredentials ecdhec =
ECDHECredentials.valueOf(ng, keyShare.keyExchange);
if (ecdhec != null) {
if (chc.algorithmConstraints != null &&
!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
ecdhec.popPublicKey)) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"ECDHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials = ecdhec;
}
}
} catch (IOException | GeneralSecurityException ex) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Cannot decode named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
try {
DHECredentials dhec =
DHECredentials.valueOf(ng, keyShare.keyExchange);
if (dhec != null) {
if (chc.algorithmConstraints != null &&
!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
dhec.popPublicKey)) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"DHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials = dhec;
}
}
} catch (IOException | GeneralSecurityException ex) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Cannot decode named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
} else {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
if (credentials == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " + ng.name);
}
// update the context
chc.handshakeKeyExchange = ke;
chc.handshakeCredentials.add(credentials);
chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
}
}
/**
* The absence processing if the extension is not present in
* the ServerHello handshake message.
*/
private static final class SHKeyShareAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Cannot use the previous requested key shares any more.
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
SSLLogger.fine(
"No key_share extension in ServerHello, " +
"cleanup the key shares if necessary");
}
chc.handshakePossessions.clear();
}
}
/**
* The key share entry used in HelloRetryRequest "key_share" extensions.
*/
static final class HRRKeyShareSpec implements SSLExtensionSpec {
final int selectedGroup;
HRRKeyShareSpec(NamedGroup serverGroup) {
this.selectedGroup = serverGroup.id;
}
private HRRKeyShareSpec(ByteBuffer buffer) throws IOException {
// struct {
// NamedGroup selected_group;
// } KeyShareHelloRetryRequest;
if (buffer.remaining() != 2) {
throw new SSLProtocolException(
"Invalid key_share extension: " +
"improper data (length=" + buffer.remaining() + ")");
}
this.selectedGroup = Record.getInt16(buffer);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"selected group\": '['{0}']'", Locale.ENGLISH);
Object[] messageFields = {
NamedGroup.nameOf(selectedGroup)
};
return messageFormat.format(messageFields);
}
}
private static final class HRRKeyShareStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new HRRKeyShareSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of the extension in a HelloRetryRequest
* handshake message.
*/
private static final
class HRRKeyShareProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private HRRKeyShareProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext) context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
}
if (shc.clientRequestedNamedGroups == null ||
shc.clientRequestedNamedGroups.isEmpty()) {
// No supported groups.
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in HelloRetryRequest");
}
NamedGroup selectedGroup = null;
for (NamedGroup ng : shc.clientRequestedNamedGroups) {
if (SupportedGroups.isActivatable(
shc.algorithmConstraints, ng)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"HelloRetryRequest selected named group: " +
ng.name);
}
selectedGroup = ng;
break;
}
}
if (selectedGroup == null) {
throw shc.conContext.fatal(
Alert.UNEXPECTED_MESSAGE, "No common named group");
}
byte[] extdata = new byte[] {
(byte)((selectedGroup.id >> 8) & 0xFF),
(byte)(selectedGroup.id & 0xFF)
};
// update the context
shc.serverSelectedNamedGroup = selectedGroup;
shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE,
new HRRKeyShareSpec(selectedGroup));
return extdata;
}
}
/**
* Network data producer of the extension for stateless
* HelloRetryRequest reconstruction.
*/
private static final
class HRRKeyShareReproducer implements HandshakeProducer {
// Prevent instantiation of this class.
private HRRKeyShareReproducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext) context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
}
CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get(
SSLExtension.CH_KEY_SHARE);
if (spec != null && spec.clientShares != null &&
spec.clientShares.size() == 1) {
int namedGroupId = spec.clientShares.get(0).namedGroupId;
byte[] extdata = new byte[] {
(byte)((namedGroupId >> 8) & 0xFF),
(byte)(namedGroupId & 0xFF)
};
return extdata;
}
return null;
}
}
/**
* Network data consumer of the extension in a HelloRetryRequest
* handshake message.
*/
private static final
class HRRKeyShareConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private HRRKeyShareConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
}
if (chc.clientRequestedNamedGroups == null ||
chc.clientRequestedNamedGroups.isEmpty()) {
// No supported groups.
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in HelloRetryRequest");
}
// Parse the extension
HRRKeyShareSpec spec;
try {
spec = new HRRKeyShareSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
if (serverGroup == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported HelloRetryRequest selected group: " +
NamedGroup.nameOf(spec.selectedGroup));
}
if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected HelloRetryRequest selected group: " +
serverGroup.name);
}
// update the context
// When sending the new ClientHello, the client MUST replace the
// original "key_share" extension with one containing only a new
// KeyShareEntry for the group indicated in the selected_group
// field of the triggering HelloRetryRequest.
//
chc.serverSelectedNamedGroup = serverGroup;
chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec);
}
}
}

View File

@@ -0,0 +1,335 @@
/*
* Copyright (c) 2018, 2023, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.Locale;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SSLCipher.SSLReadCipher;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
/**
* Pack of the KeyUpdate handshake message.
*/
final class KeyUpdate {
static final SSLProducer kickstartProducer =
new KeyUpdateKickstartProducer();
static final SSLConsumer handshakeConsumer =
new KeyUpdateConsumer();
static final HandshakeProducer handshakeProducer =
new KeyUpdateProducer();
/**
* The KeyUpdate handshake message.
*
* The KeyUpdate handshake message is used to indicate that the sender is
* updating its sending cryptographic keys.
*
* enum {
* update_not_requested(0), update_requested(1), (255)
* } KeyUpdateRequest;
*
* struct {
* KeyUpdateRequest request_update;
* } KeyUpdate;
*/
static final class KeyUpdateMessage extends HandshakeMessage {
private final KeyUpdateRequest status;
KeyUpdateMessage(PostHandshakeContext context,
KeyUpdateRequest status) {
super(context);
this.status = status;
}
KeyUpdateMessage(PostHandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
if (m.remaining() != 1) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"KeyUpdate has an unexpected length of "+
m.remaining());
}
byte request = m.get();
this.status = KeyUpdateRequest.valueOf(request);
if (status == null) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid KeyUpdate message value: " +
KeyUpdateRequest.nameOf(request));
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.KEY_UPDATE;
}
@Override
public int messageLength() {
// one byte enum
return 1;
}
@Override
public void send(HandshakeOutStream s) throws IOException {
s.putInt8(status.id);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"KeyUpdate\": '{'\n" +
" \"request_update\": {0}\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
status.name
};
return messageFormat.format(messageFields);
}
}
enum KeyUpdateRequest {
NOTREQUESTED ((byte)0, "update_not_requested"),
REQUESTED ((byte)1, "update_requested");
final byte id;
final String name;
private KeyUpdateRequest(byte id, String name) {
this.id = id;
this.name = name;
}
static KeyUpdateRequest valueOf(byte id) {
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
if (kur.id == id) {
return kur;
}
}
return null;
}
static String nameOf(byte id) {
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
if (kur.id == id) {
return kur.name;
}
}
return "<UNKNOWN KeyUpdateRequest TYPE: " + (id & 0x0FF) + ">";
}
}
private static final
class KeyUpdateKickstartProducer implements SSLProducer {
// Prevent instantiation of this class.
private KeyUpdateKickstartProducer() {
// blank
}
// Produce kickstart handshake message.
@Override
public byte[] produce(ConnectionContext context) throws IOException {
PostHandshakeContext hc = (PostHandshakeContext)context;
return handshakeProducer.produce(context,
new KeyUpdateMessage(hc, hc.conContext.isInboundClosed() ?
KeyUpdateRequest.NOTREQUESTED :
KeyUpdateRequest.REQUESTED));
}
}
/**
* The "KeyUpdate" handshake message consumer.
*/
private static final class KeyUpdateConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private KeyUpdateConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
PostHandshakeContext hc = (PostHandshakeContext)context;
KeyUpdateMessage km = new KeyUpdateMessage(hc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming KeyUpdate post-handshake message", km);
}
// Update read key and IV.
SSLTrafficKeyDerivation kdg =
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
if (kdg == null) {
// unlikely
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
hc.conContext.protocolVersion);
}
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
hc.conContext.inputRecord.readCipher.baseSecret);
if (skd == null) {
// unlikely
throw hc.conContext.fatal(
Alert.INTERNAL_ERROR, "no key derivation");
}
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
SecretKey key = kd.deriveKey("TlsKey", null);
IvParameterSpec ivSpec = new IvParameterSpec(
kd.deriveKey("TlsIv", null).getEncoded());
try {
SSLReadCipher rc =
hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
Authenticator.valueOf(hc.conContext.protocolVersion),
hc.conContext.protocolVersion, key, ivSpec,
hc.sslContext.getSecureRandom());
if (rc == null) {
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
") and protocol version (" + hc.negotiatedProtocol +
")");
}
rc.baseSecret = nplus1;
hc.conContext.inputRecord.changeReadCiphers(rc);
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("KeyUpdate: read key updated");
}
} catch (GeneralSecurityException gse) {
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive read secrets", gse);
}
if (km.status == KeyUpdateRequest.REQUESTED) {
// Update the write key and IV.
handshakeProducer.produce(hc,
new KeyUpdateMessage(hc, KeyUpdateRequest.NOTREQUESTED));
return;
}
// clean handshake context
hc.conContext.finishPostHandshake();
}
}
/**
* The "KeyUpdate" handshake message producer.
*/
private static final class KeyUpdateProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private KeyUpdateProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
PostHandshakeContext hc = (PostHandshakeContext)context;
KeyUpdateMessage km = (KeyUpdateMessage)message;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced KeyUpdate post-handshake message", km);
}
// Update the write key and IV.
SSLTrafficKeyDerivation kdg =
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
if (kdg == null) {
// unlikely
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
hc.conContext.protocolVersion);
}
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
hc.conContext.outputRecord.writeCipher.baseSecret);
if (skd == null) {
// unlikely
throw hc.conContext.fatal(
Alert.INTERNAL_ERROR, "no key derivation");
}
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
SecretKey key = kd.deriveKey("TlsKey", null);
IvParameterSpec ivSpec = new IvParameterSpec(
kd.deriveKey("TlsIv", null).getEncoded());
SSLWriteCipher wc;
try {
wc = hc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
Authenticator.valueOf(hc.conContext.protocolVersion),
hc.conContext.protocolVersion, key, ivSpec,
hc.sslContext.getSecureRandom());
} catch (GeneralSecurityException gse) {
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive write secrets", gse);
}
if (wc == null) {
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
") and protocol version (" + hc.negotiatedProtocol + ")");
}
// Output the handshake message and change the write cipher.
//
// The KeyUpdate handshake message SHALL be delivered in the
// changeWriteCiphers() implementation.
wc.baseSecret = nplus1;
hc.conContext.outputRecord.changeWriteCiphers(wc, km.status.id);
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("KeyUpdate: write key updated");
}
// clean handshake context
hc.conContext.finishPostHandshake();
// The handshake message has been delivered.
return null;
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2009, 2020, 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.Permission;
import java.security.Principal;
import java.security.PrivilegedAction;
import javax.crypto.SecretKey;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
/**
* A helper class for Kerberos APIs.
*/
public final class Krb5Helper {
private Krb5Helper() { }
// loads Krb5Proxy implementation class if available
private static final String IMPL_CLASS =
"sun.security.ssl.krb5.Krb5ProxyImpl";
private static final Krb5Proxy proxy =
AccessController.doPrivileged(new PrivilegedAction<Krb5Proxy>() {
@Override
public Krb5Proxy run() {
try {
Class<?> c = Class.forName(IMPL_CLASS, true, null);
return (Krb5Proxy)c.newInstance();
} catch (ClassNotFoundException cnf) {
return null;
} catch (InstantiationException e) {
throw new AssertionError(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}});
private static void ensureAvailable() {
if (proxy == null)
throw new AssertionError("Kerberos should be available");
}
/**
* Returns the Subject associated with client-side of the SSL socket.
*/
public static Subject getClientSubject(AccessControlContext acc)
throws LoginException {
ensureAvailable();
return proxy.getClientSubject(acc);
}
/**
* Returns the Subject associated with server-side of the SSL socket.
*/
public static Subject getServerSubject(AccessControlContext acc)
throws LoginException {
ensureAvailable();
return proxy.getServerSubject(acc);
}
/**
* Returns the KerberosKeys for the default server-side principal.
*/
public static Object getServiceCreds(AccessControlContext acc)
throws LoginException {
ensureAvailable();
return proxy.getServiceCreds(acc);
}
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
public static String getServerPrincipalName(Object serviceCreds) {
ensureAvailable();
return proxy.getServerPrincipalName(serviceCreds);
}
/**
* Returns the hostname embedded in the principal name.
*/
public static String getPrincipalHostName(Principal principal) {
ensureAvailable();
return proxy.getPrincipalHostName(principal);
}
/**
* Returns a ServicePermission for the principal name and action.
*/
public static Permission getServicePermission(String principalName,
String action) {
ensureAvailable();
return proxy.getServicePermission(principalName, action);
}
/**
* Determines if the Subject might contain creds for princ.
*/
public static boolean isRelated(Subject subject, Principal princ) {
ensureAvailable();
return proxy.isRelated(subject, princ);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2009, 2013, 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.Permission;
import java.security.Principal;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
/**
* An interface to a subset of the Kerberos APIs to avoid a static dependency
* on the types defined by these APIs.
*/
public interface Krb5Proxy {
/**
* Returns the Subject associated with the client-side of the SSL socket.
*/
Subject getClientSubject(AccessControlContext acc) throws LoginException;
/**
* Returns the Subject associated with the server-side of the SSL socket.
*/
Subject getServerSubject(AccessControlContext acc) throws LoginException;
/**
* Returns the Kerberos ServiceCreds for the default server-side principal.
*/
Object getServiceCreds(AccessControlContext acc) throws LoginException;
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
String getServerPrincipalName(Object serviceCreds);
/**
* Returns the hostname embedded in the principal name.
*/
String getPrincipalHostName(Principal principal);
/**
* Returns a ServicePermission for the principal name and action.
*/
Permission getServicePermission(String principalName, String action);
/**
* Determines if the Subject might contain creds for princ.
*/
boolean isRelated(Subject subject, Principal princ);
}

View File

@@ -0,0 +1,379 @@
/*
* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2020, Red Hat, Inc.
* 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.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.StandardConstants;
import sun.misc.HexDumpEncoder;
import sun.security.ssl.KrbKeyExchange.KrbPremasterSecret;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class KrbClientKeyExchange {
static final SSLConsumer krbHandshakeConsumer =
new KrbClientKeyExchangeConsumer();
static final HandshakeProducer krbHandshakeProducer =
new KrbClientKeyExchangeProducer();
/**
* The KRB5 ClientKeyExchange handshake message (CLIENT -> SERVER).
* It holds the Kerberos ticket and the encrypted pre-master secret
* encrypted with the session key sealed in the ticket.
*
* From RFC 2712:
*
* struct
* {
* opaque Ticket;
* opaque authenticator; // optional, ignored
* opaque EncryptedPreMasterSecret; // encrypted with the session key
* // sealed in the ticket
* } KerberosWrapper;
*
*/
private static final
class KrbClientKeyExchangeMessage extends HandshakeMessage {
private static final String KRB5_CLASS_NAME =
"sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl";
private static final Class<?> krb5Class = AccessController.doPrivileged(
new PrivilegedAction<Class<?>>() {
@Override
public Class<?> run() {
try {
return Class.forName(KRB5_CLASS_NAME, true, null);
} catch (ClassNotFoundException cnf) {
return null;
}
}
});
private static KrbClientKeyExchangeHelper newKrb5Instance() {
if (krb5Class != null) {
try {
return (KrbClientKeyExchangeHelper)krb5Class.
getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
throw new AssertionError(e);
}
}
return null;
}
private final KrbClientKeyExchangeHelper krb5Helper;
private KrbClientKeyExchangeMessage(HandshakeContext context) {
super(context);
if ((krb5Helper = newKrb5Instance()) == null)
throw new IllegalStateException("Kerberos is unavailable");
}
KrbClientKeyExchangeMessage(HandshakeContext context,
byte[] preMaster, String serverName,
AccessControlContext acc) throws IOException {
this(context);
krb5Helper.init(preMaster, serverName, acc);
}
KrbClientKeyExchangeMessage(HandshakeContext context,
ByteBuffer message, Object serverKeys,
AccessControlContext acc) throws IOException {
this(context);
byte[] encodedTicket = Record.getBytes16(message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("encoded Kerberos service ticket",
encodedTicket);
}
// Read and ignore the authenticator
Record.getBytes16(message);
byte[] encryptedPreMasterSecret = Record.getBytes16(message);
if (encryptedPreMasterSecret != null && SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("encrypted Kerberos pre-master secret",
encryptedPreMasterSecret);
}
krb5Helper.init(encodedTicket, encryptedPreMasterSecret,
serverKeys, acc);
}
@Override
SSLHandshake handshakeType() {
return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
@Override
int messageLength() {
return (6 + krb5Helper.getEncodedTicket().length +
krb5Helper.getEncryptedPreMasterSecret().length);
}
@Override
void send(HandshakeOutStream hos) throws IOException {
hos.putBytes16(krb5Helper.getEncodedTicket());
hos.putBytes16(null); // XXX no authenticator
hos.putBytes16(krb5Helper.getEncryptedPreMasterSecret());
}
byte[] getPlainPreMasterSecret() {
return krb5Helper.getPlainPreMasterSecret();
}
Principal getPeerPrincipal() {
return krb5Helper.getPeerPrincipal();
}
Principal getLocalPrincipal() {
return krb5Helper.getLocalPrincipal();
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"KRB5 ClientKeyExchange\": '{'\n" +
" \"ticket\": '{'\n" +
"{0}\n" +
" '}'\n" +
" \"pre-master\": '{'\n" +
" \"plain\": '{'\n" +
"{1}\n" +
" '}'\n" +
" \"encrypted\": '{'\n" +
"{2}\n" +
" '}'\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(
krb5Helper.getEncodedTicket()), " "),
Utilities.indent(
hexEncoder.encodeBuffer(
krb5Helper.getPlainPreMasterSecret()), " "),
Utilities.indent(
hexEncoder.encodeBuffer(
krb5Helper.getEncryptedPreMasterSecret()), " "),
};
return messageFormat.format(messageFields);
}
}
/**
* The KRB5 "ClientKeyExchange" handshake message producer.
*/
private static final
class KrbClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private KrbClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// This happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
KrbClientKeyExchangeMessage kerberosMsg = null;
String hostName = null;
if (chc.negotiatedServerName != null) {
if (chc.negotiatedServerName.getType() ==
StandardConstants.SNI_HOST_NAME) {
SNIHostName sniHostName = null;
if (chc.negotiatedServerName instanceof SNIHostName) {
sniHostName = (SNIHostName) chc.negotiatedServerName;
} else {
try {
sniHostName = new SNIHostName(
chc.negotiatedServerName.getEncoded());
} catch (IllegalArgumentException iae) {
// unlikely to happen, just in case ...
}
}
if (sniHostName != null)
hostName = sniHostName.getAsciiName();
}
} else {
hostName = chc.handshakeSession.getPeerHost();
}
try {
KrbPremasterSecret premasterSecret =
KrbPremasterSecret.createPremasterSecret(
chc.negotiatedProtocol,
chc.sslContext.getSecureRandom());
kerberosMsg = new KrbClientKeyExchangeMessage(chc,
premasterSecret.preMaster, hostName,
chc.conContext.acc);
chc.handshakePossessions.add(premasterSecret);
} catch (IOException e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Error generating KRB premaster secret." +
" Hostname: " + hostName + " - Negotiated" +
" server name: " + chc.negotiatedServerName);
}
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Cannot generate KRB premaster secret", e);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced KRB5 ClientKeyExchange handshake message",
kerberosMsg);
}
// Record the principals involved in the exchange
chc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
chc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
// Output the handshake message.
kerberosMsg.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kd == null) {
// unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
chc.negotiatedProtocol);
} else {
chc.handshakeKeyDerivation =
kd.createKeyDerivation(chc, masterSecret);
}
}
// The handshake message has been delivered.
return null;
}
}
/**
* The KRB5 "ClientKeyExchange" handshake message consumer.
*/
private static final
class KrbClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private KrbClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
Object serviceCreds = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof KrbKeyExchange.KrbServiceCreds) {
serviceCreds = ((KrbKeyExchange.KrbServiceCreds) possession)
.serviceCreds;
break;
}
}
if (serviceCreds == null) { // unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No Kerberos service credentials for KRB Client Key Exchange");
}
KrbClientKeyExchangeMessage kerberosMsg =
new KrbClientKeyExchangeMessage(shc,
message, serviceCreds, shc.conContext.acc);
KrbPremasterSecret premasterSecret = KrbPremasterSecret.decode(
shc.negotiatedProtocol,
ProtocolVersion.valueOf(shc.clientHelloVersion),
kerberosMsg.getPlainPreMasterSecret(),
shc.sslContext.getSecureRandom());
shc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
shc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
shc.handshakeCredentials.add(premasterSecret);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming KRB5 ClientKeyExchange handshake message",
kerberosMsg);
}
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke == null) { // unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
// update the states
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
if (kd == null) { // unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2003, 2020, 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.security.AccessControlContext;
import java.security.Principal;
/**
* Helper interface for KrbClientKeyExchange SSL classes to access
* the Kerberos implementation without static-linking. This enables
* Java SE Embedded 8 compilation using 'compact1' profile -where
* SSL classes are available but Kerberos are not-.
*/
public interface KrbClientKeyExchangeHelper {
void init(byte[] preMaster, String serverName,
AccessControlContext acc) throws IOException;
void init(byte[] encodedTicket, byte[] preMasterEnc,
Object serviceCreds, AccessControlContext acc)
throws IOException;
byte[] getEncodedTicket();
byte[] getEncryptedPreMasterSecret();
byte[] getPlainPreMasterSecret();
Principal getPeerPrincipal();
Principal getLocalPrincipal();
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
* Copyright (c) 2020, Red Hat, Inc.
* 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 javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
final class KrbKeyExchange {
static final SSLPossessionGenerator poGenerator =
new KrbPossessionGenerator();
static final SSLKeyAgreementGenerator kaGenerator =
new KrbKAGenerator();
static final
class KrbPossessionGenerator implements SSLPossessionGenerator {
/**
* Kerberos service credentials. Having this possession in
* the server side will enable the use of a KRB cipher suite in
* T12ServerHelloProducer::chooseCipherSuite.
*/
@Override
public SSLPossession createPossession(HandshakeContext handshakeContext) {
Object serviceCreds = null;
try {
final AccessControlContext acc = handshakeContext.conContext.acc;
serviceCreds = AccessController.doPrivileged(
// Eliminate dependency on KerberosKey
new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
// get kerberos key for the default principal
return Krb5Helper.getServiceCreds(acc);
}});
// check permission to access and use the secret key of the
// Kerberized "host" service
if (serviceCreds != null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Using Kerberos creds");
}
String serverPrincipal =
Krb5Helper.getServerPrincipalName(serviceCreds);
if (serverPrincipal != null) {
// When service is bound, we check ASAP. Otherwise,
// will check after client request is received
// in Kerberos ClientKeyExchange
SecurityManager sm = System.getSecurityManager();
try {
if (sm != null) {
// Eliminate dependency on ServicePermission
sm.checkPermission(Krb5Helper.getServicePermission(
serverPrincipal, "accept"), acc);
}
} catch (SecurityException se) {
// Do not destroy keys. Will affect Subject
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Permission to access Kerberos"
+ " secret key denied");
}
return null;
}
}
return new KrbServiceCreds(serviceCreds);
}
} catch (PrivilegedActionException e) {
// Likely exception here is LoginException
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Attempt to obtain Kerberos key failed: "
+ e.toString());
}
}
return null;
}
}
static final
class KrbServiceCreds implements SSLPossession {
final Object serviceCreds;
KrbServiceCreds(Object serviceCreds) {
this.serviceCreds = serviceCreds;
}
}
static final
class KrbPremasterSecret implements SSLPossession, SSLCredentials {
final byte[] preMaster; // 48 bytes
KrbPremasterSecret(byte[] premasterSecret) {
preMaster = premasterSecret;
}
static KrbPremasterSecret createPremasterSecret(
ProtocolVersion protocolVersion, SecureRandom rand) {
byte[] pm = new byte[48];
rand.nextBytes(pm);
pm[0] = protocolVersion.major;
pm[1] = protocolVersion.minor;
return new KrbPremasterSecret(pm);
}
static KrbPremasterSecret decode(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion, byte[] preMaster,
SecureRandom rand) {
KrbPremasterSecret preMasterSecret = null;
boolean versionMismatch = true;
ProtocolVersion preMasterProtocolVersion = null;
if (preMaster != null && preMaster.length == 48) {
preMasterProtocolVersion =
ProtocolVersion.valueOf(preMaster[0], preMaster[1]);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Kerberos pre-master secret protocol" +
" version: " + preMasterProtocolVersion);
}
// Check if the pre-master secret protocol version is valid.
// The specification states that it must be the maximum version
// supported by the client from its ClientHello message. However,
// many old implementations send the negotiated version, so accept
// both for SSL v3.0 and TLS v1.0.
versionMismatch =
(preMasterProtocolVersion.compare(clientVersion) != 0);
if (versionMismatch &&
(clientVersion.compare(ProtocolVersion.TLS10) <= 0)) {
versionMismatch =
(preMasterProtocolVersion.compare(protocolVersion) != 0);
}
}
if (versionMismatch) {
/*
* Bogus decrypted ClientKeyExchange? If so, conjure a
* a random pre-master secret that will fail later during
* Finished message processing. This is a countermeasure against
* the "interactive RSA PKCS#1 encryption envelop attack" reported
* in June 1998. Preserving the execution path will
* mitigate timing attacks and force consistent error handling.
*/
preMasterSecret = createPremasterSecret(clientVersion, rand);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Kerberos pre-master secret error," +
" generating random secret for safe failure.");
}
} else {
preMasterSecret = new KrbPremasterSecret(preMaster);
}
return preMasterSecret;
}
}
private static final class KrbKAGenerator implements SSLKeyAgreementGenerator {
// Prevent instantiation of this class.
private KrbKAGenerator() {
// blank
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
KrbPremasterSecret premaster = null;
if (context instanceof ClientHandshakeContext) {
for (SSLPossession possession : context.handshakePossessions) {
if (possession instanceof KrbPremasterSecret) {
premaster = (KrbPremasterSecret)possession;
break;
}
}
} else {
for (SSLCredentials credential : context.handshakeCredentials) {
if (credential instanceof KrbPremasterSecret) {
premaster = (KrbPremasterSecret)credential;
break;
}
}
}
if (premaster == null) {
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient KRB key agreement parameters negotiated");
}
return new KRBKAKeyDerivation(context, premaster.preMaster);
}
private static final
class KRBKAKeyDerivation implements SSLKeyDerivation {
private final HandshakeContext context;
private final byte[] secretBytes;
KRBKAKeyDerivation(HandshakeContext context,
byte[] secret) {
this.context = context;
this.secretBytes = secret;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
try {
SecretKey preMasterSecret = new SecretKeySpec(secretBytes,
"TlsPremasterSecret");
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
if (mskd == null) {
// unlikely
throw new SSLHandshakeException(
"No expected master key derivation for protocol: " +
context.negotiatedProtocol.name);
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
} catch (Exception gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
}
}
}

View File

@@ -0,0 +1,609 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.SSLExtension.CH_MAX_FRAGMENT_LENGTH;
import static sun.security.ssl.SSLExtension.EE_MAX_FRAGMENT_LENGTH;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import static sun.security.ssl.SSLExtension.SH_MAX_FRAGMENT_LENGTH;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "max_fragment_length" extensions [RFC6066].
*/
final class MaxFragExtension {
static final HandshakeProducer chNetworkProducer =
new CHMaxFragmentLengthProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHMaxFragmentLengthConsumer();
static final HandshakeProducer shNetworkProducer =
new SHMaxFragmentLengthProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHMaxFragmentLengthConsumer();
static final HandshakeConsumer shOnTradeConsumer =
new SHMaxFragmentLengthUpdate();
static final HandshakeProducer eeNetworkProducer =
new EEMaxFragmentLengthProducer();
static final ExtensionConsumer eeOnLoadConsumer =
new EEMaxFragmentLengthConsumer();
static final HandshakeConsumer eeOnTradeConsumer =
new EEMaxFragmentLengthUpdate();
static final SSLStringizer maxFragLenStringizer =
new MaxFragLenStringizer();
/**
* The "max_fragment_length" extension [RFC 6066].
*/
static final class MaxFragLenSpec implements SSLExtensionSpec {
byte id;
private MaxFragLenSpec(byte id) {
this.id = id;
}
private MaxFragLenSpec(ByteBuffer buffer) throws IOException {
if (buffer.remaining() != 1) {
throw new SSLProtocolException(
"Invalid max_fragment_length extension data");
}
this.id = buffer.get();
}
@Override
public String toString() {
return MaxFragLenEnum.nameOf(id);
}
}
private static final class MaxFragLenStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new MaxFragLenSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
static enum MaxFragLenEnum {
MFL_512 ((byte)0x01, 512, "2^9"),
MFL_1024 ((byte)0x02, 1024, "2^10"),
MFL_2048 ((byte)0x03, 2048, "2^11"),
MFL_4096 ((byte)0x04, 4096, "2^12");
final byte id;
final int fragmentSize;
final String description;
private MaxFragLenEnum(byte id, int fragmentSize, String description) {
this.id = id;
this.fragmentSize = fragmentSize;
this.description = description;
}
private static MaxFragLenEnum valueOf(byte id) {
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
if (mfl.id == id) {
return mfl;
}
}
return null;
}
private static String nameOf(byte id) {
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
if (mfl.id == id) {
return mfl.description;
}
}
return "UNDEFINED-MAX-FRAGMENT-LENGTH(" + id + ")";
}
/**
* Returns the best match enum constant of the specified
* fragment size.
*/
static MaxFragLenEnum valueOf(int fragmentSize) {
if (fragmentSize <= 0) {
return null;
} else if (fragmentSize < 1024) {
return MFL_512;
} else if (fragmentSize < 2048) {
return MFL_1024;
} else if (fragmentSize < 4096) {
return MFL_2048;
} else if (fragmentSize == 4096) {
return MFL_4096;
}
return null;
}
}
/**
* Network data producer of a "max_fragment_length" extension in
* the ClientHello handshake message.
*/
private static final
class CHMaxFragmentLengthProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHMaxFragmentLengthProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable max_fragment_length extension");
}
return null;
}
// Produce the extension and update the context.
int requestedMFLength;
if (chc.isResumption && (chc.resumingSession != null)) {
// The same extension should be sent for resumption.
requestedMFLength =
chc.resumingSession.getNegotiatedMaxFragSize();
} else if (chc.sslConfig.maximumPacketSize != 0) {
// Maybe we can calculate the fragment size more accurate
// by condering the enabled cipher suites in the future.
requestedMFLength = chc.sslConfig.maximumPacketSize -
SSLRecord.maxPlaintextPlusSize;
} else {
// Need no max_fragment_length extension.
requestedMFLength = -1;
}
MaxFragLenEnum mfl = MaxFragLenEnum.valueOf(requestedMFLength);
if (mfl != null) {
// update the context.
chc.handshakeExtensions.put(
CH_MAX_FRAGMENT_LENGTH, new MaxFragLenSpec(mfl.id));
return new byte[] { mfl.id };
} else {
// log and ignore, no MFL extension.
chc.maxFragmentLength = -1;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No available max_fragment_length extension can " +
"be used for fragment size of " +
requestedMFLength + "bytes");
}
}
return null;
}
}
/**
* Network data consumer of a "max_fragment_length" extension in
* the ClientHello handshake message.
*/
private static final
class CHMaxFragmentLengthConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHMaxFragmentLengthConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable max_fragment_length extension");
}
return; // ignore the extension
}
// Parse the extension.
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
// Update the context.
shc.maxFragmentLength = mfle.fragmentSize;
shc.handshakeExtensions.put(CH_MAX_FRAGMENT_LENGTH, spec);
// No impact on session resumption.
}
}
/**
* Network data producer of a "max_fragment_length" extension in
* the ServerHello handshake message.
*/
private static final
class SHMaxFragmentLengthProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHMaxFragmentLengthProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// In response to "max_fragment_length" extension request only
MaxFragLenSpec spec = (MaxFragLenSpec)
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Ignore unavailable max_fragment_length extension");
}
return null; // ignore the extension
}
if ((shc.maxFragmentLength > 0) &&
(shc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
shc.negotiatedCipherSuite.calculatePacketSize(
shc.maxFragmentLength, shc.negotiatedProtocol);
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
shc.maxFragmentLength = -1;
}
}
// update the context
if (shc.maxFragmentLength > 0) {
shc.handshakeSession.setNegotiatedMaxFragSize(
shc.maxFragmentLength);
shc.conContext.inputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.conContext.outputRecord.changeFragmentSize(
shc.maxFragmentLength);
// The response extension data is the same as the requested one.
shc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
return new byte[] { spec.id };
}
return null;
}
}
/**
* Network data consumer of a "max_fragment_length" extension in
* the ServerHello handshake message.
*/
private static final
class SHMaxFragmentLengthConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHMaxFragmentLengthConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to "max_fragment_length" extension request only
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (requestedSpec == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected max_fragment_length extension in ServerHello");
}
// Parse the extension.
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (spec.id != requestedSpec.id) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
// update the context
chc.maxFragmentLength = mfle.fragmentSize;
chc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
}
}
/**
* After session creation consuming of a "max_fragment_length"
* extension in the ClientHello handshake message.
*/
private static final class SHMaxFragmentLengthUpdate
implements HandshakeConsumer {
// Prevent instantiation of this class.
private SHMaxFragmentLengthUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
chc.handshakeExtensions.get(SH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
// Ignore, no "max_fragment_length" extension response.
return;
}
if ((chc.maxFragmentLength > 0) &&
(chc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
chc.negotiatedCipherSuite.calculatePacketSize(
chc.maxFragmentLength, chc.negotiatedProtocol);
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
chc.maxFragmentLength = -1;
}
}
// update the context
if (chc.maxFragmentLength > 0) {
chc.handshakeSession.setNegotiatedMaxFragSize(
chc.maxFragmentLength);
chc.conContext.inputRecord.changeFragmentSize(
chc.maxFragmentLength);
chc.conContext.outputRecord.changeFragmentSize(
chc.maxFragmentLength);
}
}
}
/**
* Network data producer of a "max_fragment_length" extension in
* the EncryptedExtensions handshake message.
*/
private static final
class EEMaxFragmentLengthProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private EEMaxFragmentLengthProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// In response to "max_fragment_length" extension request only
MaxFragLenSpec spec = (MaxFragLenSpec)
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (spec == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Ignore unavailable max_fragment_length extension");
}
return null; // ignore the extension
}
if ((shc.maxFragmentLength > 0) &&
(shc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
shc.negotiatedCipherSuite.calculatePacketSize(
shc.maxFragmentLength, shc.negotiatedProtocol);
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
shc.maxFragmentLength = -1;
}
}
// update the context
if (shc.maxFragmentLength > 0) {
shc.handshakeSession.setNegotiatedMaxFragSize(
shc.maxFragmentLength);
shc.conContext.inputRecord.changeFragmentSize(
shc.maxFragmentLength);
shc.conContext.outputRecord.changeFragmentSize(
shc.maxFragmentLength);
// The response extension data is the same as the requested one.
shc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
return new byte[] { spec.id };
}
return null;
}
}
/**
* Network data consumer of a "max_fragment_length" extension in the
* EncryptedExtensions handshake message.
*/
private static final
class EEMaxFragmentLengthConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private EEMaxFragmentLengthConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to "max_fragment_length" extension request only
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
if (requestedSpec == null) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected max_fragment_length extension in ServerHello");
}
// Parse the extension.
MaxFragLenSpec spec;
try {
spec = new MaxFragLenSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (spec.id != requestedSpec.id) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"the requested maximum fragment length is other " +
"than the allowed values");
}
// update the context
chc.maxFragmentLength = mfle.fragmentSize;
chc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
}
}
/**
* After session creation consuming of a "max_fragment_length"
* extension in the EncryptedExtensions handshake message.
*/
private static final
class EEMaxFragmentLengthUpdate implements HandshakeConsumer {
// Prevent instantiation of this class.
private EEMaxFragmentLengthUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
MaxFragLenSpec spec = (MaxFragLenSpec)
chc.handshakeExtensions.get(EE_MAX_FRAGMENT_LENGTH);
if (spec == null) {
// Ignore, no "max_fragment_length" extension response.
return;
}
if ((chc.maxFragmentLength > 0) &&
(chc.sslConfig.maximumPacketSize != 0)) {
int estimatedMaxFragSize =
chc.negotiatedCipherSuite.calculatePacketSize(
chc.maxFragmentLength, chc.negotiatedProtocol);
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Abort the maximum fragment length negotiation, " +
"may overflow the maximum packet size limit.");
}
chc.maxFragmentLength = -1;
}
}
// update the context
if (chc.maxFragmentLength > 0) {
chc.handshakeSession.setNegotiatedMaxFragSize(
chc.maxFragmentLength);
chc.conContext.inputRecord.changeFragmentSize(
chc.maxFragmentLength);
chc.conContext.outputRecord.changeFragmentSize(
chc.maxFragmentLength);
}
}
}
}

View File

@@ -0,0 +1,389 @@
/*
* 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.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the NewSessionTicket handshake message.
*/
final class NewSessionTicket {
private static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
static final SSLConsumer handshakeConsumer =
new NewSessionTicketConsumer();
static final SSLProducer kickstartProducer =
new NewSessionTicketKickstartProducer();
static final HandshakeProducer handshakeProducer =
new NewSessionTicketProducer();
/**
* The NewSessionTicketMessage handshake message.
*/
static final class NewSessionTicketMessage extends HandshakeMessage {
final int ticketLifetime;
final int ticketAgeAdd;
final byte[] ticketNonce;
final byte[] ticket;
final SSLExtensions extensions;
NewSessionTicketMessage(HandshakeContext context,
int ticketLifetime, SecureRandom generator,
byte[] ticketNonce, byte[] ticket) {
super(context);
this.ticketLifetime = ticketLifetime;
this.ticketAgeAdd = generator.nextInt();
this.ticketNonce = ticketNonce;
this.ticket = ticket;
this.extensions = new SSLExtensions(this);
}
NewSessionTicketMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
// struct {
// uint32 ticket_lifetime;
// uint32 ticket_age_add;
// opaque ticket_nonce<0..255>;
// opaque ticket<1..2^16-1>;
// Extension extensions<0..2^16-2>;
// } NewSessionTicket;
if (m.remaining() < 14) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
}
this.ticketLifetime = Record.getInt32(m);
this.ticketAgeAdd = Record.getInt32(m);
this.ticketNonce = Record.getBytes8(m);
if (m.remaining() < 5) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
}
this.ticket = Record.getBytes16(m);
if (ticket.length == 0) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No ticket in the NewSessionTicket handshake message");
}
if (m.remaining() < 2) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
}
SSLExtension[] supportedExtensions =
context.sslConfig.getEnabledExtensions(
SSLHandshake.NEW_SESSION_TICKET);
this.extensions = new SSLExtensions(this, m, supportedExtensions);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.NEW_SESSION_TICKET;
}
@Override
public int messageLength() {
int extLen = extensions.length();
if (extLen == 0) {
extLen = 2; // empty extensions
}
return 8 + ticketNonce.length + 1 +
ticket.length + 2 + extLen;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putInt32(ticketLifetime);
hos.putInt32(ticketAgeAdd);
hos.putBytes8(ticketNonce);
hos.putBytes16(ticket);
// Is it an empty extensions?
if (extensions.length() == 0) {
hos.putInt16(0);
} else {
extensions.send(hos);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"NewSessionTicket\": '{'\n" +
" \"ticket_lifetime\" : \"{0}\",\n" +
" \"ticket_age_add\" : \"{1}\",\n" +
" \"ticket_nonce\" : \"{2}\",\n" +
" \"ticket\" : \"{3}\",\n" +
" \"extensions\" : [\n" +
"{4}\n" +
" ]\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
ticketLifetime,
"<omitted>", //ticketAgeAdd should not be logged
Utilities.toHexString(ticketNonce),
Utilities.toHexString(ticket),
Utilities.indent(extensions.toString(), " ")
};
return messageFormat.format(messageFields);
}
}
private static SecretKey derivePreSharedKey(CipherSuite.HashAlg hashAlg,
SecretKey resumptionMasterSecret, byte[] nonce) throws IOException {
try {
HKDF hkdf = new HKDF(hashAlg.name);
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
"tls13 resumption".getBytes(), nonce, hashAlg.hashLength);
return hkdf.expand(resumptionMasterSecret, hkdfInfo,
hashAlg.hashLength, "TlsPreSharedKey");
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not derive PSK").initCause(gse);
}
}
private static final
class NewSessionTicketKickstartProducer implements SSLProducer {
// Prevent instantiation of this class.
private NewSessionTicketKickstartProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is this session resumable?
if (!shc.handshakeSession.isRejoinable()) {
return null;
}
// What's the requested PSK key exchange modes?
//
// Note that currently, the NewSessionTicket post-handshake is
// produced and delivered only in the current handshake context
// if required.
PskKeyExchangeModesSpec pkemSpec =
(PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
SSLExtension.PSK_KEY_EXCHANGE_MODES);
if (pkemSpec == null || !pkemSpec.contains(
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
// Client doesn't support PSK with (EC)DHE key establishment.
return null;
}
// get a new session ID
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
SessionId newId = new SessionId(true,
shc.sslContext.getSecureRandom());
SecretKey resumptionMasterSecret =
shc.handshakeSession.getResumptionMasterSecret();
if (resumptionMasterSecret == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session has no resumption secret. No ticket sent.");
}
return null;
}
// construct the PSK and handshake message
BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
byte[] nonceArr = nonce.toByteArray();
SecretKey psk = derivePreSharedKey(
shc.negotiatedCipherSuite.hashAlg,
resumptionMasterSecret, nonceArr);
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session timeout is too long. No ticket sent.");
}
return null;
}
NewSessionTicketMessage nstm = new NewSessionTicketMessage(shc,
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
nonceArr, newId.getId());
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket handshake message", nstm);
}
// create and cache the new session
// The new session must be a child of the existing session so
// they will be invalidated together, etc.
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
shc.handshakeSession.addChild(sessionCopy);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setPskIdentity(newId.getId());
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
sessionCache.put(sessionCopy);
// Output the handshake message.
nstm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The message has been delivered.
return null;
}
}
/**
* The "NewSessionTicket" handshake message producer.
*/
private static final class NewSessionTicketProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private NewSessionTicketProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// NSTM may be sent in response to handshake messages.
// For example: key update
throw new ProviderException(
"NewSessionTicket handshake producer not implemented");
}
}
private static final
class NewSessionTicketConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private NewSessionTicketConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// Note: Although the resumption master secret depends on the
// client's second flight, servers which do not request client
// authentication MAY compute the remainder of the transcript
// independently and then send a NewSessionTicket immediately
// upon sending its Finished rather than waiting for the client
// Finished.
//
// The consuming happens in client side only. As the server
// may send the NewSessionTicket before handshake complete, the
// context may be a PostHandshakeContext or HandshakeContext
// instance.
HandshakeContext hc = (HandshakeContext)context;
NewSessionTicketMessage nstm =
new NewSessionTicketMessage(hc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming NewSessionTicket message", nstm);
}
// discard tickets with timeout 0
if (nstm.ticketLifetime <= 0 ||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Discarding NewSessionTicket with lifetime "
+ nstm.ticketLifetime, nstm);
}
return;
}
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
hc.sslContext.engineGetClientSessionContext();
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session cache lifetime is too long. Discarding ticket.");
}
return;
}
SSLSessionImpl sessionToSave = hc.conContext.conSession;
SecretKey resumptionMasterSecret =
sessionToSave.getResumptionMasterSecret();
if (resumptionMasterSecret == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session has no resumption master secret. Ignoring ticket.");
}
return;
}
// derive the PSK
SecretKey psk = derivePreSharedKey(
sessionToSave.getSuite().hashAlg, resumptionMasterSecret,
nstm.ticketNonce);
// create and cache the new session
// The new session must be a child of the existing session so
// they will be invalidated together, etc.
SessionId newId =
new SessionId(true, hc.sslContext.getSecureRandom());
SSLSessionImpl sessionCopy = new SSLSessionImpl(sessionToSave,
newId);
sessionToSave.addChild(sessionCopy);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
sessionCopy.setPskIdentity(nstm.ticket);
sessionCache.put(sessionCopy);
// clean handshake context
hc.conContext.finishPostHandshake();
}
}
}

View File

@@ -0,0 +1,521 @@
/*
* Copyright (c) 1996, 2018, 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.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
/**
* {@code OutputRecord} takes care of the management of SSL/TLS
* output records, including buffering, encryption, handshake
* messages marshal, etc.
*
* @author David Brownell
*/
abstract class OutputRecord
extends ByteArrayOutputStream implements Record, Closeable {
SSLWriteCipher writeCipher;
// Needed for KeyUpdate, used after Handshake.Finished
TransportContext tc;
final HandshakeHash handshakeHash;
boolean firstMessage;
// current protocol version, sent as record version
ProtocolVersion protocolVersion;
// version for the ClientHello message. Only relevant if this is a
// client handshake record. If set to ProtocolVersion.SSL20Hello,
// the V3 client hello is converted to V2 format.
ProtocolVersion helloVersion;
// Is it the first application record to write?
boolean isFirstAppOutputRecord = true;
// packet size
int packetSize;
// fragment size
private int fragmentSize;
// closed or not?
volatile boolean isClosed;
/*
* Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* This is taken from the SSL V3 specification, Appendix E.
*/
private static final int[] V3toV2CipherMap1 =
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static final int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
private static final byte[] HANDSHAKE_MESSAGE_KEY_UPDATE =
{SSLHandshake.KEY_UPDATE.id, 0x00, 0x00, 0x01, 0x00};
OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
this.writeCipher = writeCipher;
this.firstMessage = true;
this.fragmentSize = Record.maxDataSize;
this.handshakeHash = handshakeHash;
// Please set packetSize and protocolVersion in the implementation.
}
synchronized void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
}
/*
* Updates helloVersion of this record.
*/
synchronized void setHelloVersion(ProtocolVersion helloVersion) {
this.helloVersion = helloVersion;
}
/*
* Return true iff the record is empty -- to avoid doing the work
* of sending empty records over the network.
*/
boolean isEmpty() {
return false;
}
synchronized boolean seqNumIsHuge() {
return (writeCipher.authenticator != null) &&
writeCipher.authenticator.seqNumIsHuge();
}
// SSLEngine and SSLSocket
abstract void encodeAlert(byte level, byte description) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeHandshake(byte[] buffer,
int offset, int length) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeChangeCipherSpec() throws IOException;
// apply to SSLEngine only
Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
void encodeV2NoCipher() throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void deliver(
byte[] source, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
throw new UnsupportedOperationException();
}
// Change write ciphers, may use change_cipher_spec record.
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
boolean useChangeCipherSpec) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"change_cipher_spec message");
}
return;
}
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
// Change write ciphers using key_update handshake message.
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
byte keyUpdateRequest) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"key_update handshake message");
}
return;
}
// encode the handshake message, KeyUpdate
byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
hm[hm.length - 1] = keyUpdateRequest;
encodeHandshake(hm, 0, hm.length);
flush();
// Dispose of any intermediate state in the underlying cipher.
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
synchronized void changePacketSize(int packetSize) {
this.packetSize = packetSize;
}
synchronized void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
synchronized int getMaxPacketSize() {
return packetSize;
}
@Override
public synchronized void close() throws IOException {
if (isClosed) {
return;
}
isClosed = true;
writeCipher.dispose();
}
boolean isClosed() {
return isClosed;
}
//
// shared helpers
//
private static final class T13PaddingHolder {
private static final byte[] zeros = new byte[16];
}
int calculateFragmentSize(int fragmentLimit) {
if (fragmentSize > 0) {
fragmentLimit = Math.min(fragmentLimit, fragmentSize);
}
if (protocolVersion.useTLS13PlusSpec()) {
// No negative integer checking as the fragment capacity should
// have been ensured.
return fragmentLimit - T13PaddingHolder.zeros.length - 1;
}
return fragmentLimit;
}
// Encrypt a fragment and wrap up a record.
//
// To be consistent with the spec of SSLEngine.wrap() methods, the
// destination ByteBuffer's position is updated to reflect the amount
// of data produced. The limit remains the same.
static long encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
if (protocolVersion.useTLS13PlusSpec()) {
return t13Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
} else {
return t10Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
}
}
private static long t13Encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
if (!encCipher.isNullCipher()) {
// inner plaintext, using zero length padding.
int endOfPt = destination.limit();
int startOfPt = destination.position();
destination.position(endOfPt);
destination.limit(endOfPt + 1 + T13PaddingHolder.zeros.length);
destination.put(contentType);
destination.put(T13PaddingHolder.zeros);
destination.position(startOfPt);
}
// use the right TLSCiphertext.opaque_type and legacy_record_version
ProtocolVersion pv = protocolVersion;
if (!encCipher.isNullCipher()) {
pv = ProtocolVersion.TLS12;
contentType = ContentType.APPLICATION_DATA.id;
} else if (protocolVersion.useTLS13PlusSpec()) {
pv = ProtocolVersion.TLS12;
}
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
encCipher.encrypt(contentType, destination);
// Finish out the record header.
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType);
destination.put(headerOffset + 1, pv.major);
destination.put(headerOffset + 2, pv.minor);
// fragment length
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
// Update destination position to reflect the amount of data produced.
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
private static long t10Encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
encCipher.encrypt(contentType, destination);
// Finish out the record header.
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType); // content type
destination.put(headerOffset + 1, protocolVersion.major);
destination.put(headerOffset + 2, protocolVersion.minor);
// fragment length
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
// Update destination position to reflect the amount of data produced.
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
// Encrypt a fragment and wrap up a record.
//
// Uses the internal expandable buf variable and the current
// protocolVersion variable.
long encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (protocolVersion.useTLS13PlusSpec()) {
return t13Encrypt(encCipher, contentType, headerSize);
} else {
return t10Encrypt(encCipher, contentType, headerSize);
}
}
private long t13Encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (!encCipher.isNullCipher()) {
// inner plaintext
write(contentType);
write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
}
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
int position = headerSize;
int contentLen = count - position;
// ensure the capacity
int requiredPacketSize =
encCipher.calculatePacketSize(contentLen, headerSize);
if (requiredPacketSize > buf.length) {
byte[] newBuf = new byte[requiredPacketSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
// use the right TLSCiphertext.opaque_type and legacy_record_version
ProtocolVersion pv = protocolVersion;
if (!encCipher.isNullCipher()) {
pv = ProtocolVersion.TLS12;
contentType = ContentType.APPLICATION_DATA.id;
} else {
pv = ProtocolVersion.TLS12;
}
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
count = headerSize + encCipher.encrypt(contentType, destination);
// Fill out the header, write it and the message.
int fragLen = count - headerSize;
buf[0] = contentType;
buf[1] = pv.major;
buf[2] = pv.minor;
buf[3] = (byte)((fragLen >> 8) & 0xFF);
buf[4] = (byte)(fragLen & 0xFF);
return Authenticator.toLong(sequenceNumber);
}
private long t10Encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
int position = headerSize + writeCipher.getExplicitNonceSize();
int contentLen = count - position;
// ensure the capacity
int requiredPacketSize =
encCipher.calculatePacketSize(contentLen, headerSize);
if (requiredPacketSize > buf.length) {
byte[] newBuf = new byte[requiredPacketSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
count = headerSize + encCipher.encrypt(contentType, destination);
// Fill out the header, write it and the message.
int fragLen = count - headerSize;
buf[0] = contentType;
buf[1] = protocolVersion.major;
buf[2] = protocolVersion.minor;
buf[3] = (byte)((fragLen >> 8) & 0xFF);
buf[4] = (byte)(fragLen & 0xFF);
return Authenticator.toLong(sequenceNumber);
}
static ByteBuffer encodeV2ClientHello(
byte[] fragment, int offset, int length) throws IOException {
int v3SessIdLenOffset = offset + 34; // 2: client_version
// 32: random
int v3SessIdLen = fragment[v3SessIdLenOffset];
int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
(fragment[v3CSLenOffset + 1] & 0xff);
int cipherSpecs = v3CSLen / 2; // 2: cipher spec size
// Estimate the max V2ClientHello message length
//
// 11: header size
// (cipherSpecs * 6): cipher_specs
// 6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
// 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
// signaling cipher suite
// 32: challenge size
int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
// Create a ByteBuffer backed by an accessible byte array.
byte[] dstBytes = new byte[v2MaxMsgLen];
ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
/*
* Copy over the cipher specs. We don't care about actually
* translating them for use with an actual V2 server since
* we only talk V3. Therefore, just copy over the V3 cipher
* spec values with a leading 0.
*/
int v3CSOffset = v3CSLenOffset + 2; // skip length field
int v2CSLen = 0;
dstBuf.position(11);
boolean containsRenegoInfoSCSV = false;
for (int i = 0; i < cipherSpecs; i++) {
byte byte1, byte2;
byte1 = fragment[v3CSOffset++];
byte2 = fragment[v3CSOffset++];
v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
if (!containsRenegoInfoSCSV &&
byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
containsRenegoInfoSCSV = true;
}
}
if (!containsRenegoInfoSCSV) {
v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
}
/*
* Copy in the nonce.
*/
dstBuf.put(fragment, (offset + 2), 32);
/*
* Build the first part of the V3 record header from the V2 one
* that's now buffered up. (Lengths are fixed up later).
*/
int msgLen = dstBuf.position() - 2; // Exclude the legth field itself
dstBuf.position(0);
dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
dstBuf.put(SSLHandshake.CLIENT_HELLO.id); // pos: 2
dstBuf.put(fragment[offset]); // major version, pos: 3
dstBuf.put(fragment[offset + 1]); // minor version, pos: 4
dstBuf.put((byte)(v2CSLen >>> 8)); // pos: 5
dstBuf.put((byte)(v2CSLen & 0xFF)); // pos: 6
dstBuf.put((byte)0x00); // session_id_length, pos: 7
dstBuf.put((byte)0x00); // pos: 8
dstBuf.put((byte)0x00); // challenge_length, pos: 9
dstBuf.put((byte)32); // pos: 10
dstBuf.position(0);
dstBuf.limit(msgLen + 2);
return dstBuf;
}
private static int V3toV2CipherSuite(ByteBuffer dstBuf,
byte byte1, byte byte2) {
dstBuf.put((byte)0);
dstBuf.put(byte1);
dstBuf.put(byte2);
if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
return 3;
}
dstBuf.put((byte)V3toV2CipherMap1[byte2]);
dstBuf.put((byte)0);
dstBuf.put((byte)V3toV2CipherMap3[byte2]);
return 6;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2015, 2018, 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.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
/*
* Plaintext
*/
final class Plaintext {
static final Plaintext PLAINTEXT_NULL = new Plaintext();
final byte contentType;
final byte majorVersion;
final byte minorVersion;
final int recordEpoch; // increments on every cipher state change
final long recordSN; // epoch | sequence number
final ByteBuffer fragment; // null if need to be reassembled
HandshakeStatus handshakeStatus; // null if not used or not handshaking
private Plaintext() {
this.contentType = 0;
this.majorVersion = 0;
this.minorVersion = 0;
this.recordEpoch = -1;
this.recordSN = -1;
this.fragment = null;
this.handshakeStatus = null;
}
Plaintext(byte contentType,
byte majorVersion, byte minorVersion,
int recordEpoch, long recordSN, ByteBuffer fragment) {
this.contentType = contentType;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.recordEpoch = recordEpoch;
this.recordSN = recordSN;
this.fragment = fragment;
this.handshakeStatus = null;
}
@Override
public String toString() {
return "contentType: " + contentType + "/" +
"majorVersion: " + majorVersion + "/" +
"minorVersion: " + minorVersion + "/" +
"recordEpoch: " + recordEpoch + "/" +
"recordSN: 0x" + Long.toHexString(recordSN) + "/" +
"fragment: " + fragment;
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.util.ArrayList;
/**
* A compact implementation of HandshakeContext for post-handshake messages
*/
final class PostHandshakeContext extends HandshakeContext {
PostHandshakeContext(TransportContext context) throws IOException {
super(context);
if (!negotiatedProtocol.useTLS13PlusSpec()) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Post-handshake not supported in " + negotiatedProtocol.name);
}
this.localSupportedSignAlgs = new ArrayList<>(
context.conSession.getLocalSupportedSignatureSchemes());
// Add the potential post-handshake consumers.
if (context.sslConfig.isClientMode) {
handshakeConsumers.putIfAbsent(
SSLHandshake.KEY_UPDATE.id,
SSLHandshake.KEY_UPDATE);
handshakeConsumers.putIfAbsent(
SSLHandshake.NEW_SESSION_TICKET.id,
SSLHandshake.NEW_SESSION_TICKET);
} else {
handshakeConsumers.putIfAbsent(
SSLHandshake.KEY_UPDATE.id,
SSLHandshake.KEY_UPDATE);
}
handshakeFinished = true;
}
@Override
void kickstart() throws IOException {
SSLHandshake.kickstart(this);
}
@Override
void dispatch(byte handshakeType, ByteBuffer fragment) throws IOException {
SSLConsumer consumer = handshakeConsumers.get(handshakeType);
if (consumer == null) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected post-handshake message: " +
SSLHandshake.nameOf(handshakeType));
}
try {
consumer.consume(this, fragment);
} catch (UnsupportedOperationException unsoe) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported post-handshake message: " +
SSLHandshake.nameOf(handshakeType), unsoe);
} catch (BufferUnderflowException | BufferOverflowException be) {
throw conContext.fatal(Alert.DECODE_ERROR,
"Illegal handshake message: " +
SSLHandshake.nameOf(handshakeType), be);
}
}
static boolean isConsumable(TransportContext context, byte handshakeType) {
if (handshakeType == SSLHandshake.KEY_UPDATE.id) {
// The KeyUpdate handshake message does not apply to TLS 1.2 and
// previous protocols.
return context.protocolVersion.useTLS13PlusSpec();
}
if (handshakeType == SSLHandshake.NEW_SESSION_TICKET.id) {
// The new session ticket handshake message could be consumer in
// client side only.
return context.sslConfig.isClientMode;
}
// No more post-handshake message supported currently.
return false;
}
}

View File

@@ -0,0 +1,892 @@
/*
* Copyright (c) 2015, 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.ByteBuffer;
import java.security.*;
import java.text.MessageFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Objects;
import java.util.Collection;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLPeerUnverifiedException;
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import static sun.security.ssl.SSLExtension.*;
/**
* Pack of the "pre_shared_key" extension.
*/
final class PreSharedKeyExtension {
static final HandshakeProducer chNetworkProducer =
new CHPreSharedKeyProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHPreSharedKeyConsumer();
static final HandshakeAbsence chOnLoadAbsence =
new CHPreSharedKeyAbsence();
static final HandshakeConsumer chOnTradeConsumer =
new CHPreSharedKeyUpdate();
static final SSLStringizer chStringizer =
new CHPreSharedKeyStringizer();
static final HandshakeProducer shNetworkProducer =
new SHPreSharedKeyProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHPreSharedKeyConsumer();
static final HandshakeAbsence shOnLoadAbsence =
new SHPreSharedKeyAbsence();
static final SSLStringizer shStringizer =
new SHPreSharedKeyStringizer();
private static final class PskIdentity {
final byte[] identity;
final int obfuscatedAge;
PskIdentity(byte[] identity, int obfuscatedAge) {
this.identity = identity;
this.obfuscatedAge = obfuscatedAge;
}
int getEncodedLength() {
return 2 + identity.length + 4;
}
void writeEncoded(ByteBuffer m) throws IOException {
Record.putBytes16(m, identity);
Record.putInt32(m, obfuscatedAge);
}
@Override
public String toString() {
return "{" + Utilities.toHexString(identity) + "," +
obfuscatedAge + "}";
}
}
private static final
class CHPreSharedKeySpec implements SSLExtensionSpec {
final List<PskIdentity> identities;
final List<byte[]> binders;
CHPreSharedKeySpec(List<PskIdentity> identities, List<byte[]> binders) {
this.identities = identities;
this.binders = binders;
}
CHPreSharedKeySpec(HandshakeContext context,
ByteBuffer m) throws IOException {
// struct {
// PskIdentity identities<7..2^16-1>;
// PskBinderEntry binders<33..2^16-1>;
// } OfferedPsks;
if (m.remaining() < 44) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient data (length=" + m.remaining() + ")");
}
int idEncodedLength = Record.getInt16(m);
if (idEncodedLength < 7) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient identities (length=" + idEncodedLength + ")");
}
identities = new ArrayList<>();
int idReadLength = 0;
while (idReadLength < idEncodedLength) {
byte[] id = Record.getBytes16(m);
if (id.length < 1) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient identity (length=" + id.length + ")");
}
int obfuscatedTicketAge = Record.getInt32(m);
PskIdentity pskId = new PskIdentity(id, obfuscatedTicketAge);
identities.add(pskId);
idReadLength += pskId.getEncodedLength();
}
if (m.remaining() < 35) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient binders data (length=" +
m.remaining() + ")");
}
int bindersEncodedLen = Record.getInt16(m);
if (bindersEncodedLen < 33) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient binders (length=" +
bindersEncodedLen + ")");
}
binders = new ArrayList<>();
int bindersReadLength = 0;
while (bindersReadLength < bindersEncodedLen) {
byte[] binder = Record.getBytes8(m);
if (binder.length < 32) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient binder entry (length=" +
binder.length + ")");
}
binders.add(binder);
bindersReadLength += 1 + binder.length;
}
}
int getIdsEncodedLength() {
int idEncodedLength = 0;
for (PskIdentity curId : identities) {
idEncodedLength += curId.getEncodedLength();
}
return idEncodedLength;
}
int getBindersEncodedLength() {
int binderEncodedLength = 0;
for (byte[] curBinder : binders) {
binderEncodedLength += 1 + curBinder.length;
}
return binderEncodedLength;
}
byte[] getEncoded() throws IOException {
int idsEncodedLength = getIdsEncodedLength();
int bindersEncodedLength = getBindersEncodedLength();
int encodedLength = 4 + idsEncodedLength + bindersEncodedLength;
byte[] buffer = new byte[encodedLength];
ByteBuffer m = ByteBuffer.wrap(buffer);
Record.putInt16(m, idsEncodedLength);
for (PskIdentity curId : identities) {
curId.writeEncoded(m);
}
Record.putInt16(m, bindersEncodedLength);
for (byte[] curBinder : binders) {
Record.putBytes8(m, curBinder);
}
return buffer;
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"PreSharedKey\": '{'\n" +
" \"identities\" : \"{0}\",\n" +
" \"binders\" : \"{1}\",\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
Utilities.indent(identitiesString()),
Utilities.indent(bindersString())
};
return messageFormat.format(messageFields);
}
String identitiesString() {
StringBuilder result = new StringBuilder();
for (PskIdentity curId : identities) {
result.append(curId.toString() + "\n");
}
return result.toString();
}
String bindersString() {
StringBuilder result = new StringBuilder();
for (byte[] curBinder : binders) {
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
}
return result.toString();
}
}
private static final
class CHPreSharedKeyStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
// As the HandshakeContext parameter of CHPreSharedKeySpec
// constructor is used for fatal alert only, we can use
// null HandshakeContext here as we don't care about exception.
//
// Please take care of this code if the CHPreSharedKeySpec
// constructor is updated in the future.
return (new CHPreSharedKeySpec(null, buffer)).toString();
} catch (Exception ex) {
// For debug logging only, so please swallow exceptions.
return ex.getMessage();
}
}
}
private static final
class SHPreSharedKeySpec implements SSLExtensionSpec {
final int selectedIdentity;
SHPreSharedKeySpec(int selectedIdentity) {
this.selectedIdentity = selectedIdentity;
}
SHPreSharedKeySpec(HandshakeContext context,
ByteBuffer m) throws IOException {
if (m.remaining() < 2) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient selected_identity (length=" +
m.remaining() + ")");
}
this.selectedIdentity = Record.getInt16(m);
}
byte[] getEncoded() throws IOException {
return new byte[] {
(byte)((selectedIdentity >> 8) & 0xFF),
(byte)(selectedIdentity & 0xFF)
};
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"PreSharedKey\": '{'\n" +
" \"selected_identity\" : \"{0}\",\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
Utilities.byte16HexString(selectedIdentity)
};
return messageFormat.format(messageFields);
}
}
private static final
class SHPreSharedKeyStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
// As the HandshakeContext parameter of SHPreSharedKeySpec
// constructor is used for fatal alert only, we can use
// null HandshakeContext here as we don't care about exception.
//
// Please take care of this code if the SHPreSharedKeySpec
// constructor is updated in the future.
return (new SHPreSharedKeySpec(null, buffer)).toString();
} catch (Exception ex) {
// For debug logging only, so please swallow exceptions.
return ex.getMessage();
}
}
}
private static final
class CHPreSharedKeyConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHPreSharedKeyConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message,
ByteBuffer buffer) throws IOException {
ClientHelloMessage clientHello = (ClientHelloMessage) message;
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable pre_shared_key extension");
}
return; // ignore the extension
}
// Parse the extension.
CHPreSharedKeySpec pskSpec = null;
try {
pskSpec = new CHPreSharedKeySpec(shc, buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// The "psk_key_exchange_modes" extension should have been loaded.
if (!shc.handshakeExtensions.containsKey(
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Client sent PSK but not PSK modes, or the PSK " +
"extension is not the last extension");
}
// error if id and binder lists are not the same length
if (pskSpec.identities.size() != pskSpec.binders.size()) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"PSK extension has incorrect number of binders");
}
if (shc.isResumption) { // resumingSession may not be set
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
int idIndex = 0;
for (PskIdentity requestedId : pskSpec.identities) {
SSLSessionImpl s = sessionCache.pull(requestedId.identity);
if (s != null && canRejoin(clientHello, shc, s)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Resuming session: ", s);
}
// binder will be checked later
shc.resumingSession = s;
shc.handshakeExtensions.put(SH_PRE_SHARED_KEY,
new SHPreSharedKeySpec(idIndex)); // for the index
break;
}
++idIndex;
}
if (idIndex == pskSpec.identities.size()) {
// no resumable session
shc.isResumption = false;
shc.resumingSession = null;
}
}
// update the context
shc.handshakeExtensions.put(
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
}
}
private static boolean canRejoin(ClientHelloMessage clientHello,
ServerHandshakeContext shc, SSLSessionImpl s) {
boolean result = s.isRejoinable() && (s.getPreSharedKey() != null);
// Check protocol version
if (result && s.getProtocolVersion() != shc.negotiatedProtocol) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest("Can't resume, incorrect protocol version");
}
result = false;
}
// Make sure that the server handshake context's localSupportedSignAlgs
// field is populated. This is particularly important when
// client authentication was used in an initial session and it is
// now being resumed.
if (shc.localSupportedSignAlgs == null) {
shc.localSupportedSignAlgs =
SignatureScheme.getSupportedAlgorithms(
shc.sslConfig,
shc.algorithmConstraints, shc.activeProtocols);
}
// Validate the required client authentication.
if (result &&
(shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
try {
s.getPeerPrincipal();
} catch (SSLPeerUnverifiedException e) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Can't resume, " +
"client authentication is required");
}
result = false;
}
// Make sure the list of supported signature algorithms matches
Collection<SignatureScheme> sessionSigAlgs =
s.getLocalSupportedSignatureSchemes();
if (result &&
!shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Can't resume. Session uses different " +
"signature algorithms");
}
result = false;
}
}
// ensure that the endpoint identification algorithm matches the
// one in the session
String identityAlg = shc.sslConfig.identificationProtocol;
if (result && identityAlg != null) {
String sessionIdentityAlg = s.getIdentificationProtocol();
if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest("Can't resume, endpoint id" +
" algorithm does not match, requested: " +
identityAlg + ", cached: " + sessionIdentityAlg);
}
result = false;
}
}
// Ensure cipher suite can be negotiated
if (result && (!shc.isNegotiable(s.getSuite()) ||
!clientHello.cipherSuites.contains(s.getSuite()))) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Can't resume, unavailable session cipher suite");
}
result = false;
}
return result;
}
private static final
class CHPreSharedKeyUpdate implements HandshakeConsumer {
// Prevent instantiation of this class.
private CHPreSharedKeyUpdate() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (!shc.isResumption || shc.resumingSession == null) {
// not resuming---nothing to do
return;
}
CHPreSharedKeySpec chPsk = (CHPreSharedKeySpec)
shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
SHPreSharedKeySpec shPsk = (SHPreSharedKeySpec)
shc.handshakeExtensions.get(SSLExtension.SH_PRE_SHARED_KEY);
if (chPsk == null || shPsk == null) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Required extensions are unavailable");
}
byte[] binder = chPsk.binders.get(shPsk.selectedIdentity);
// set up PSK binder hash
HandshakeHash pskBinderHash = shc.handshakeHash.copy();
byte[] lastMessage = pskBinderHash.removeLastReceived();
ByteBuffer messageBuf = ByteBuffer.wrap(lastMessage);
// skip the type and length
messageBuf.position(4);
// read to find the beginning of the binders
ClientHelloMessage.readPartial(shc.conContext, messageBuf);
int length = messageBuf.position();
messageBuf.position(0);
pskBinderHash.receive(messageBuf, length);
checkBinder(shc, shc.resumingSession, pskBinderHash, binder);
}
}
private static void checkBinder(ServerHandshakeContext shc,
SSLSessionImpl session,
HandshakeHash pskBinderHash, byte[] binder) throws IOException {
SecretKey psk = session.getPreSharedKey();
if (psk == null) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Session has no PSK");
}
SecretKey binderKey = deriveBinderKey(shc, psk, session);
byte[] computedBinder =
computeBinder(shc, binderKey, session, pskBinderHash);
if (!MessageDigest.isEqual(binder, computedBinder)) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Incorect PSK binder value");
}
}
// Class that produces partial messages used to compute binder hash
static final class PartialClientHelloMessage extends HandshakeMessage {
private final ClientHello.ClientHelloMessage msg;
private final CHPreSharedKeySpec psk;
PartialClientHelloMessage(HandshakeContext ctx,
ClientHello.ClientHelloMessage msg,
CHPreSharedKeySpec psk) {
super(ctx);
this.msg = msg;
this.psk = psk;
}
@Override
SSLHandshake handshakeType() {
return msg.handshakeType();
}
private int pskTotalLength() {
return psk.getIdsEncodedLength() +
psk.getBindersEncodedLength() + 8;
}
@Override
int messageLength() {
if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) != null) {
return msg.messageLength();
} else {
return msg.messageLength() + pskTotalLength();
}
}
@Override
void send(HandshakeOutStream hos) throws IOException {
msg.sendCore(hos);
// complete extensions
int extsLen = msg.extensions.length();
if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) == null) {
extsLen += pskTotalLength();
}
hos.putInt16(extsLen - 2);
// write the complete extensions
for (SSLExtension ext : SSLExtension.values()) {
byte[] extData = msg.extensions.get(ext);
if (extData == null) {
continue;
}
// the PSK could be there from an earlier round
if (ext == SSLExtension.CH_PRE_SHARED_KEY) {
continue;
}
int extID = ext.id;
hos.putInt16(extID);
hos.putBytes16(extData);
}
// partial PSK extension
int extID = SSLExtension.CH_PRE_SHARED_KEY.id;
hos.putInt16(extID);
byte[] encodedPsk = psk.getEncoded();
hos.putInt16(encodedPsk.length);
hos.write(encodedPsk, 0, psk.getIdsEncodedLength() + 2);
}
}
private static final
class CHPreSharedKeyProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHPreSharedKeyProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
if (!chc.isResumption || chc.resumingSession == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("No session to resume.");
}
return null;
}
// Make sure the list of supported signature algorithms matches
Collection<SignatureScheme> sessionSigAlgs =
chc.resumingSession.getLocalSupportedSignatureSchemes();
if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Existing session uses different " +
"signature algorithms");
}
return null;
}
// The session must have a pre-shared key
SecretKey psk = chc.resumingSession.getPreSharedKey();
if (psk == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Existing session has no PSK.");
}
return null;
}
// The PSK ID can only be used in one connections, but this method
// may be called twice in a connection if the server sends HRR.
// ID is saved in the context so it can be used in the second call.
if (chc.pskIdentity == null) {
chc.pskIdentity = chc.resumingSession.consumePskIdentity();
}
if (chc.pskIdentity == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"PSK has no identity, or identity was already used");
}
return null;
}
//The session cannot be used again. Remove it from the cache.
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
chc.sslContext.engineGetClientSessionContext();
sessionCache.remove(chc.resumingSession.getSessionId());
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Found resumable session. Preparing PSK message.");
}
List<PskIdentity> identities = new ArrayList<>();
int ageMillis = (int)(System.currentTimeMillis() -
chc.resumingSession.getTicketCreationTime());
int obfuscatedAge =
ageMillis + chc.resumingSession.getTicketAgeAdd();
identities.add(new PskIdentity(chc.pskIdentity, obfuscatedAge));
SecretKey binderKey =
deriveBinderKey(chc, psk, chc.resumingSession);
ClientHelloMessage clientHello = (ClientHelloMessage)message;
CHPreSharedKeySpec pskPrototype = createPskPrototype(
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
chc.resumingSession, chc, clientHello, pskPrototype);
List<byte[]> binders = new ArrayList<>();
binders.add(binder);
CHPreSharedKeySpec pskMessage =
new CHPreSharedKeySpec(identities, binders);
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
return pskMessage.getEncoded();
}
private CHPreSharedKeySpec createPskPrototype(
int hashLength, List<PskIdentity> identities) {
List<byte[]> binders = new ArrayList<>();
byte[] binderProto = new byte[hashLength];
for (PskIdentity curId : identities) {
binders.add(binderProto);
}
return new CHPreSharedKeySpec(identities, binders);
}
}
private static byte[] computeBinder(
HandshakeContext context, SecretKey binderKey,
SSLSessionImpl session,
HandshakeHash pskBinderHash) throws IOException {
pskBinderHash.determine(
session.getProtocolVersion(), session.getSuite());
pskBinderHash.update();
byte[] digest = pskBinderHash.digest();
return computeBinder(context, binderKey, session, digest);
}
private static byte[] computeBinder(
HandshakeContext context, SecretKey binderKey,
HandshakeHash hash, SSLSessionImpl session,
HandshakeContext ctx, ClientHello.ClientHelloMessage hello,
CHPreSharedKeySpec pskPrototype) throws IOException {
PartialClientHelloMessage partialMsg =
new PartialClientHelloMessage(ctx, hello, pskPrototype);
SSLEngineOutputRecord record = new SSLEngineOutputRecord(hash);
HandshakeOutStream hos = new HandshakeOutStream(record);
partialMsg.write(hos);
hash.determine(session.getProtocolVersion(), session.getSuite());
hash.update();
byte[] digest = hash.digest();
return computeBinder(context, binderKey, session, digest);
}
private static byte[] computeBinder(HandshakeContext context,
SecretKey binderKey,
SSLSessionImpl session, byte[] digest) throws IOException {
try {
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
byte[] label = ("tls13 finished").getBytes();
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
label, new byte[0], hashAlg.hashLength);
SecretKey finishedKey = hkdf.expand(
binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
String hmacAlg =
"Hmac" + hashAlg.name.replace("-", "");
try {
Mac hmac = JsseJce.getMac(hmacAlg);
hmac.init(finishedKey);
return hmac.doFinal(digest);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
}
} catch (GeneralSecurityException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
}
}
private static SecretKey deriveBinderKey(HandshakeContext context,
SecretKey psk, SSLSessionImpl session) throws IOException {
try {
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
HKDF hkdf = new HKDF(hashAlg.name);
byte[] zeros = new byte[hashAlg.hashLength];
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
byte[] label = ("tls13 res binder").getBytes();
MessageDigest md = MessageDigest.getInstance(hashAlg.name);
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
label, md.digest(new byte[0]), hashAlg.hashLength);
return hkdf.expand(earlySecret,
hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
} catch (GeneralSecurityException ex) {
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
}
}
private static final
class CHPreSharedKeyAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Handling pre_shared_key absence.");
}
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Resumption is only determined by PSK, when enabled
shc.resumingSession = null;
shc.isResumption = false;
}
}
private static final
class SHPreSharedKeyConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHPreSharedKeyConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a response of the specific request?
if (!chc.handshakeExtensions.containsKey(
SSLExtension.CH_PRE_SHARED_KEY)) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Server sent unexpected pre_shared_key extension");
}
SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Received pre_shared_key extension: ", shPsk);
}
if (shPsk.selectedIdentity != 0) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Selected identity index is not in correct range.");
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Resuming session: ", chc.resumingSession);
}
}
}
private static final
class SHPreSharedKeyAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Handling pre_shared_key absence.");
}
// The server refused to resume, or the client did not
// request 1.3 resumption.
chc.resumingSession = null;
chc.isResumption = false;
}
}
private static final
class SHPreSharedKeyProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHPreSharedKeyProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
SHPreSharedKeySpec psk = (SHPreSharedKeySpec)
shc.handshakeExtensions.get(SH_PRE_SHARED_KEY);
if (psk == null) {
return null;
}
return psk.getEncoded();
}
}
}

View File

@@ -0,0 +1,314 @@
/*
* Copyright (c) 2017, 2018, 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.math.BigInteger;
import java.security.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.spec.DHParameterSpec;
import sun.security.util.SafeDHParameterSpec;
/**
* Predefined default DH ephemeral parameters.
*/
final class PredefinedDHParameterSpecs {
//
// Default DH ephemeral parameters
//
private static final BigInteger p512 = new BigInteger( // generated
"D87780E15FF50B4ABBE89870188B049406B5BEA98AB23A02" +
"41D88EA75B7755E669C08093D3F0CA7FC3A5A25CF067DCB9" +
"A43DD89D1D90921C6328884461E0B6D3", 16);
private static final BigInteger p768 = new BigInteger( // RFC 2409
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p1024 = new BigInteger( // RFC 2409
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" +
"FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p1536 = new BigInteger( // RFC 3526
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p2048 = new BigInteger( // TLS FFDHE
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
"886B423861285C97FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p3072 = new BigInteger( // TLS FFDHE
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
"3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p4096 = new BigInteger( // TLS FFDHE
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A" +
"FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p6144 = new BigInteger( // TLS FFDHE
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
"0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
"3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
"CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
"A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
"0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
"763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
"B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
"D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
"E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
"5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
"A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", 16);
private static final BigInteger p8192 = new BigInteger( // TLS FFDHE
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
"0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
"3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
"CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
"A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
"0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
"763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
"B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
"D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
"E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
"5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
"A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838" +
"1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E" +
"0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665" +
"CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282" +
"2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022" +
"BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C" +
"51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9" +
"D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457" +
"1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30" +
"FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D" +
"97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C" +
"D68C8BB7C5C6424CFFFFFFFFFFFFFFFF", 16);
private static final BigInteger[] supportedPrimes = {
p512, p768, p1024, p1536, p2048, p3072, p4096, p6144, p8192};
private static final BigInteger[] ffdhePrimes = {
p2048, p3072, p4096, p6144, p8192};
// a measure of the uncertainty that prime modulus p is not a prime
//
// see BigInteger.isProbablePrime(int certainty)
private static final int PRIME_CERTAINTY = 120;
// the known security property, jdk.tls.server.defaultDHEParameters
private static final String PROPERTY_NAME =
"jdk.tls.server.defaultDHEParameters";
private static final Pattern spacesPattern = Pattern.compile("\\s+");
private static final Pattern syntaxPattern = Pattern.compile(
"(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
"(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
private static final Pattern paramsPattern = Pattern.compile(
"\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
// cache of predefined default DH ephemeral parameters
static final Map<Integer, DHParameterSpec> definedParams;
// cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE)
static final Map<Integer, DHParameterSpec> ffdheParams;
static {
String property = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
return Security.getProperty(PROPERTY_NAME);
}
});
if (property != null && !property.isEmpty()) {
// remove double quote marks from beginning/end of the property
if (property.length() >= 2 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
property = property.trim();
}
if (property != null && !property.isEmpty()) {
Matcher spacesMatcher = spacesPattern.matcher(property);
property = spacesMatcher.replaceAll("");
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine(
"The Security Property " +
PROPERTY_NAME + ": " + property);
}
}
Map<Integer,DHParameterSpec> defaultParams = new HashMap<>();
if (property != null && !property.isEmpty()) {
Matcher syntaxMatcher = syntaxPattern.matcher(property);
if (syntaxMatcher.matches()) {
Matcher paramsFinder = paramsPattern.matcher(property);
while(paramsFinder.find()) {
String primeModulus = paramsFinder.group(1);
BigInteger p = new BigInteger(primeModulus, 16);
if (!p.isProbablePrime(PRIME_CERTAINTY)) {
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine(
"Prime modulus p in Security Property, " +
PROPERTY_NAME + ", is not a prime: " +
primeModulus);
}
continue;
}
String baseGenerator = paramsFinder.group(2);
BigInteger g = new BigInteger(baseGenerator, 16);
int primeLen = p.bitLength();
DHParameterSpec spec = new DHParameterSpec(p, g);
defaultParams.put(primeLen, spec);
}
} else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine("Invalid Security Property, " +
PROPERTY_NAME + ", definition");
}
}
BigInteger TWO = BigInteger.valueOf(2);
Map<Integer,DHParameterSpec> tempFFDHEs = new HashMap<>();
for (BigInteger p : ffdhePrimes) {
int primeLen = p.bitLength();
DHParameterSpec dhps = new SafeDHParameterSpec(p, TWO);
tempFFDHEs.put(primeLen, dhps);
defaultParams.putIfAbsent(primeLen, dhps);
}
for (BigInteger p : supportedPrimes) {
int primeLen = p.bitLength();
if (defaultParams.get(primeLen) == null) {
defaultParams.put(primeLen, new SafeDHParameterSpec(p, TWO));
}
}
ffdheParams =
Collections.<Integer,DHParameterSpec>unmodifiableMap(tempFFDHEs);
definedParams =
Collections.<Integer,DHParameterSpec>unmodifiableMap(defaultParams);
}
}

View File

@@ -0,0 +1,395 @@
/*
* Copyright (c) 2002, 2018, 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.CryptoPrimitive;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
/**
* Enum for an SSL/TLS protocol version.
*
* @author Andreas Sterbenz
* @since 1.4.1
*/
public enum ProtocolVersion {
TLS13 (0x0304, "TLSv1.3"),
TLS12 (0x0303, "TLSv1.2"),
TLS11 (0x0302, "TLSv1.1"),
TLS10 (0x0301, "TLSv1"),
SSL30 (0x0300, "SSLv3"),
SSL20Hello (0x0002, "SSLv2Hello"),
// Dummy protocol version value for invalid SSLSession
NONE (-1, "NONE");
final int id;
final String name;
final byte major;
final byte minor;
final boolean isAvailable;
// The limit of maximum protocol version
static final int LIMIT_MAX_VALUE = 0xFFFF;
// The limit of minimum protocol version
static final int LIMIT_MIN_VALUE = 0x0000;
// TLS ProtocolVersion array for TLS 1.0 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_10 = new ProtocolVersion[] {
TLS10, SSL30
};
// TLS ProtocolVersion array for TLS 1.1 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_11 = new ProtocolVersion[] {
TLS11, TLS10, SSL30
};
// TLS ProtocolVersion array for TLS 1.2 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10, SSL30
};
// TLS ProtocolVersion array for TLS 1.3 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_13 = new ProtocolVersion[] {
TLS13, TLS12, TLS11, TLS10, SSL30
};
// No protocol version specified.
static final ProtocolVersion[] PROTOCOLS_OF_NONE = new ProtocolVersion[] {
NONE
};
// TLS ProtocolVersion array for SSL 3.0.
static final ProtocolVersion[] PROTOCOLS_OF_30 = new ProtocolVersion[] {
SSL30
};
// TLS ProtocolVersion array for TLS 1.1.
static final ProtocolVersion[] PROTOCOLS_OF_11 = new ProtocolVersion[] {
TLS11
};
// TLS ProtocolVersion array for TLS 1.2.
static final ProtocolVersion[] PROTOCOLS_OF_12 = new ProtocolVersion[] {
TLS12
};
// TLS ProtocolVersion array for TLS 1.3.
static final ProtocolVersion[] PROTOCOLS_OF_13 = new ProtocolVersion[] {
TLS13
};
// TLS ProtocolVersion array for TSL 1.0/1.1.
static final ProtocolVersion[] PROTOCOLS_10_11 = new ProtocolVersion[] {
TLS11, TLS10
};
// TLS ProtocolVersion array for TSL 1.1/1.2.
static final ProtocolVersion[] PROTOCOLS_11_12 = new ProtocolVersion[] {
TLS12, TLS11
};
// TLS ProtocolVersion array for TSL 1.2/1.3.
static final ProtocolVersion[] PROTOCOLS_12_13 = new ProtocolVersion[] {
TLS13, TLS12
};
// TLS ProtocolVersion array for TSL 1.0/1.1/1.2.
static final ProtocolVersion[] PROTOCOLS_10_12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10
};
// TLS ProtocolVersion array for TLS 1.2 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10, SSL30
};
// TLS ProtocolVersion array for TLS 1.1 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS11 = new ProtocolVersion[] {
TLS11, TLS10, SSL30
};
// TLS ProtocolVersion array for TLS 1.0 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS10 = new ProtocolVersion[] {
TLS10, SSL30
};
// Empty ProtocolVersion array
static final ProtocolVersion[] PROTOCOLS_EMPTY = new ProtocolVersion[0];
private ProtocolVersion(int id, String name) {
this.id = id;
this.name = name;
this.major = (byte)((id >>> 8) & 0xFF);
this.minor = (byte)(id & 0xFF);
this.isAvailable = SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT),
name, null);
}
/**
* Return a ProtocolVersion with the specified major and minor
* version numbers.
*/
static ProtocolVersion valueOf(byte major, byte minor) {
for (ProtocolVersion pv : ProtocolVersion.values()) {
if ((pv.major == major) && (pv.minor == minor)) {
return pv;
}
}
return null;
}
/**
* Return a ProtocolVersion with the specified version number.
*/
static ProtocolVersion valueOf(int id) {
for (ProtocolVersion pv : ProtocolVersion.values()) {
if (pv.id == id) {
return pv;
}
}
return null;
}
/**
* Return name of a TLS protocol specified by major and
* minor version numbers.
*/
static String nameOf(byte major, byte minor) {
for (ProtocolVersion pv : ProtocolVersion.values()) {
if ((pv.major == major) && (pv.minor == minor)) {
return pv.name;
}
}
return "TLS-" + major + "." + minor;
}
/**
* Return name of a TLS protocol specified by a protocol number.
*/
static String nameOf(int id) {
return nameOf((byte)((id >>> 8) & 0xFF), (byte)(id & 0xFF));
}
/**
* Return a ProtocolVersion for the given TLS protocol name.
*/
static ProtocolVersion nameOf(String name) {
for (ProtocolVersion pv : ProtocolVersion.values()) {
if (pv.name.equals(name)) {
return pv;
}
}
return null;
}
/**
* Return true if the specific TLS protocol is negotiable.
*
* Used to filter out SSLv2Hello and protocol numbers less than the
* minimal supported protocol versions.
*/
static boolean isNegotiable(
byte major, byte minor, boolean allowSSL20Hello) {
int v = ((major & 0xFF) << 8) | (minor & 0xFF);
if (v < SSL30.id) {
if (!allowSSL20Hello || (v != SSL20Hello.id)) {
return false;
}
}
return true;
}
/**
* Get names of a list of ProtocolVersion objects.
*/
static String[] toStringArray(List<ProtocolVersion> protocolVersions) {
if ((protocolVersions != null) && !protocolVersions.isEmpty()) {
String[] protocolNames = new String[protocolVersions.size()];
int i = 0;
for (ProtocolVersion pv : protocolVersions) {
protocolNames[i++] = pv.name;
}
return protocolNames;
}
return new String[0];
}
/**
* Get names of a list of protocol version identifiers.
*/
static String[] toStringArray(int[] protocolVersions) {
if ((protocolVersions != null) && protocolVersions.length != 0) {
String[] protocolNames = new String[protocolVersions.length];
int i = 0;
for (int pv : protocolVersions) {
protocolNames[i++] = ProtocolVersion.nameOf(pv);
}
return protocolNames;
}
return new String[0];
}
/**
* Get a list of ProtocolVersion objects of an array protocol
* version names.
*/
static List<ProtocolVersion> namesOf(String[] protocolNames) {
if (protocolNames == null || protocolNames.length == 0) {
return Collections.<ProtocolVersion>emptyList();
}
List<ProtocolVersion> pvs = new ArrayList<>(protocolNames.length);
for (String pn : protocolNames) {
ProtocolVersion pv = ProtocolVersion.nameOf(pn);
if (pv == null) {
throw new IllegalArgumentException(
"Unsupported protocol" + pn);
}
pvs.add(pv);
}
return Collections.unmodifiableList(pvs);
}
/**
* Return true if the specific protocol version name is
* of TLS 1.2 or newer version.
*/
static boolean useTLS12PlusSpec(String name) {
ProtocolVersion pv = ProtocolVersion.nameOf(name);
if (pv != null && pv != NONE) {
return pv.id >= TLS12.id;
}
return false;
}
/**
* Compares this object with the specified ProtocolVersion.
*
* @see java.lang.Comparable
*/
int compare(ProtocolVersion that) {
if (this == that) {
return 0;
}
if (this == ProtocolVersion.NONE) {
return -1;
} else if (that == ProtocolVersion.NONE) {
return 1;
}
return this.id - that.id;
}
/**
* Return true if this ProtocolVersion object is of TLS 1.3 or
* newer version.
*/
boolean useTLS13PlusSpec() {
return this.id >= TLS13.id;
}
/**
* Return true if this ProtocolVersion object is of TLS 1.2 or
* newer version.
*/
boolean useTLS12PlusSpec() {
return this.id >= TLS12.id;
}
/**
* Return true if this ProtocolVersion object is of
* TLS 1.1 or newer version.
*/
boolean useTLS11PlusSpec() {
return this.id >= TLS11.id;
}
/**
* Return true if this ProtocolVersion object is of TLS 1.0 or
* newer version.
*/
boolean useTLS10PlusSpec() {
return this.id >= TLS10.id;
}
/**
* Return true if this ProtocolVersion object is of TLS 1.0 or
* newer version.
*/
static boolean useTLS10PlusSpec(int id) {
return id >= TLS10.id;
}
/**
* Return true if this ProtocolVersion object is of TLS 1.3 or
* newer version.
*/
static boolean useTLS13PlusSpec(int id) {
return id >= TLS13.id;
}
/**
* Select the lower of that suggested protocol version and
* the highest of the listed protocol versions.
*
* @param listedVersions the listed protocol version
* @param suggestedVersion the suggested protocol version
*/
static ProtocolVersion selectedFrom(
List<ProtocolVersion> listedVersions, int suggestedVersion) {
ProtocolVersion selectedVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : listedVersions) {
if (pv.id == suggestedVersion) {
return pv;
} else {
if (pv.id < suggestedVersion && pv.id > selectedVersion.id) {
selectedVersion = pv;
}
}
}
return selectedVersion;
}
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.*;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the "psk_key_exchange_modes" extensions.
*/
final class PskKeyExchangeModesExtension {
static final HandshakeProducer chNetworkProducer =
new PskKeyExchangeModesProducer();
static final ExtensionConsumer chOnLoadConsumer =
new PskKeyExchangeModesConsumer();
static final HandshakeAbsence chOnLoadAbsence =
new PskKeyExchangeModesOnLoadAbsence();
static final HandshakeAbsence chOnTradeAbsence =
new PskKeyExchangeModesOnTradeAbsence();
static final SSLStringizer pkemStringizer =
new PskKeyExchangeModesStringizer();
enum PskKeyExchangeMode {
PSK_KE ((byte)0, "psk_ke"),
PSK_DHE_KE ((byte)1, "psk_dhe_ke");
final byte id;
final String name;
PskKeyExchangeMode(byte id, String name) {
this.id = id;
this.name = name;
}
static PskKeyExchangeMode valueOf(byte id) {
for(PskKeyExchangeMode pkem : values()) {
if (pkem.id == id) {
return pkem;
}
}
return null;
}
static String nameOf(byte id) {
for (PskKeyExchangeMode pkem : PskKeyExchangeMode.values()) {
if (pkem.id == id) {
return pkem.name;
}
}
return "<UNKNOWN PskKeyExchangeMode TYPE: " + (id & 0x0FF) + ">";
}
}
static final
class PskKeyExchangeModesSpec implements SSLExtensionSpec {
private static final PskKeyExchangeModesSpec DEFAULT =
new PskKeyExchangeModesSpec(new byte[] {
PskKeyExchangeMode.PSK_DHE_KE.id});
final byte[] modes;
PskKeyExchangeModesSpec(byte[] modes) {
this.modes = modes;
}
PskKeyExchangeModesSpec(ByteBuffer m) throws IOException {
if (m.remaining() < 2) {
throw new SSLProtocolException(
"Invalid psk_key_exchange_modes extension: " +
"insufficient data");
}
this.modes = Record.getBytes8(m);
}
boolean contains(PskKeyExchangeMode mode) {
if (modes != null) {
for (byte m : modes) {
if (mode.id == m) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"ke_modes\": '['{0}']'", Locale.ENGLISH);
if (modes == null || modes.length == 0) {
Object[] messageFields = {
"<no PSK key exchange modes specified>"
};
return messageFormat.format(messageFields);
} else {
StringBuilder builder = new StringBuilder(64);
boolean isFirst = true;
for (byte mode : modes) {
if (isFirst) {
isFirst = false;
} else {
builder.append(", ");
}
builder.append(PskKeyExchangeMode.nameOf(mode));
}
Object[] messageFields = {
builder.toString()
};
return messageFormat.format(messageFields);
}
}
}
private static final
class PskKeyExchangeModesStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new PskKeyExchangeModesSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data consumer of a "psk_key_exchange_modes" extension in
* the ClientHello handshake message.
*/
private static final
class PskKeyExchangeModesConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private PskKeyExchangeModesConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable psk_key_exchange_modes extension");
}
// No session resumption is allowed.
if (shc.isResumption && shc.resumingSession != null) {
shc.isResumption = false;
shc.resumingSession = null;
}
return; // ignore the extension
}
// Parse the extension.
PskKeyExchangeModesSpec spec;
try {
spec = new PskKeyExchangeModesSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
// Update the context.
shc.handshakeExtensions.put(
SSLExtension.PSK_KEY_EXCHANGE_MODES, spec);
// Impact on session resumption.
//
// Do the requested modes support session resumption?
if (shc.isResumption) { // resumingSession may not be set
// Note: psk_dhe_ke is the only supported mode now. If the
// psk_ke mode is supported in the future, may need an update
// here.
if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) {
shc.isResumption = false;
shc.resumingSession = null;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"abort session resumption, " +
"no supported psk_dhe_ke PSK key exchange mode");
}
}
}
}
}
/**
* Network data producer of a "psk_key_exchange_modes" extension in the
* ClientHello handshake message.
*/
private static final
class PskKeyExchangeModesProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private PskKeyExchangeModesProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore unavailable psk_key_exchange_modes extension");
}
return null;
}
byte[] extData = new byte[] {0x01, 0x01}; // psk_dhe_ke
// Update the context.
chc.handshakeExtensions.put(
SSLExtension.PSK_KEY_EXCHANGE_MODES,
PskKeyExchangeModesSpec.DEFAULT);
return extData;
}
}
/**
* The absence processing if a "psk_key_exchange_modes" extension is
* not present in the ClientHello handshake message.
*/
private static final
class PskKeyExchangeModesOnLoadAbsence implements HandshakeAbsence {
// Prevent instantiation of this class.
private PskKeyExchangeModesOnLoadAbsence() {
// blank
}
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// No session resumptio is allowed.
if (shc.isResumption) { // resumingSession may not be set
shc.isResumption = false;
shc.resumingSession = null;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"abort session resumption, " +
"no supported psk_dhe_ke PSK key exchange mode");
}
}
}
}
/**
* The absence processing if a "signature_algorithms" extension is
* not present in the ClientHello handshake message.
*/
private static final
class PskKeyExchangeModesOnTradeAbsence implements HandshakeAbsence {
// Prevent instantiation of this class.
private PskKeyExchangeModesOnTradeAbsence() {
// blank
}
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// A client MUST provide a "psk_key_exchange_modes" extension if
// it offers a "pre_shared_key" extension. If clients offer
// "pre_shared_key" without a "psk_key_exchange_modes" extension,
// servers MUST abort the handshake.
SSLExtensionSpec spec =
shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
if (spec != null) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"pre_shared_key key extension is offered " +
"without a psk_key_exchange_modes extension");
}
}
}
}

View File

@@ -0,0 +1,319 @@
/*
* Copyright (c) 2003, 2018, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.MessageFormat;
import java.util.Locale;
import javax.crypto.SecretKey;
import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
import sun.security.ssl.RSAKeyExchange.RSAPremasterSecret;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class RSAClientKeyExchange {
static final SSLConsumer rsaHandshakeConsumer =
new RSAClientKeyExchangeConsumer();
static final HandshakeProducer rsaHandshakeProducer =
new RSAClientKeyExchangeProducer();
/**
* The RSA ClientKeyExchange handshake message.
*/
private static final
class RSAClientKeyExchangeMessage extends HandshakeMessage {
final int protocolVersion;
final boolean useTLS10PlusSpec;
final byte[] encrypted;
RSAClientKeyExchangeMessage(HandshakeContext context,
RSAPremasterSecret premaster,
PublicKey publicKey) throws GeneralSecurityException {
super(context);
this.protocolVersion = context.clientHelloVersion;
this.encrypted = premaster.getEncoded(
publicKey, context.sslContext.getSecureRandom());
this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
protocolVersion);
}
RSAClientKeyExchangeMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
if (m.remaining() < 2) {
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid RSA ClientKeyExchange message: insufficient data");
}
this.protocolVersion = context.clientHelloVersion;
this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
protocolVersion);
if (useTLS10PlusSpec) {
this.encrypted = Record.getBytes16(m);
} else { // SSL 3.0
this.encrypted = new byte[m.remaining()];
m.get(encrypted);
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
@Override
public int messageLength() {
if (useTLS10PlusSpec) {
return encrypted.length + 2;
} else {
return encrypted.length;
}
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
if (useTLS10PlusSpec) {
hos.putBytes16(encrypted);
} else {
hos.write(encrypted);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"RSA ClientKeyExchange\": '{'\n" +
" \"client_version\": {0}\n" +
" \"encncrypted\": '{'\n" +
"{1}\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
ProtocolVersion.nameOf(protocolVersion),
Utilities.indent(
hexEncoder.encodeBuffer(encrypted), " "),
};
return messageFormat.format(messageFields);
}
}
/**
* The RSA "ClientKeyExchange" handshake message producer.
*/
private static final
class RSAClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private RSAClientKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// This happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
EphemeralRSACredentials rsaCredentials = null;
X509Credentials x509Credentials = null;
for (SSLCredentials credential : chc.handshakeCredentials) {
if (credential instanceof EphemeralRSACredentials) {
rsaCredentials = (EphemeralRSACredentials)credential;
if (x509Credentials != null) {
break;
}
} else if (credential instanceof X509Credentials) {
x509Credentials = (X509Credentials)credential;
if (rsaCredentials != null) {
break;
}
}
}
if (rsaCredentials == null && x509Credentials == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No RSA credentials negotiated for client key exchange");
}
PublicKey publicKey = (rsaCredentials != null) ?
rsaCredentials.popPublicKey : x509Credentials.popPublicKey;
if (!publicKey.getAlgorithm().equals("RSA")) { // unlikely
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Not RSA public key for client key exchange");
}
RSAPremasterSecret premaster;
RSAClientKeyExchangeMessage ckem;
try {
premaster = RSAPremasterSecret.createPremasterSecret(chc);
chc.handshakePossessions.add(premaster);
ckem = new RSAClientKeyExchangeMessage(
chc, premaster, publicKey);
} catch (GeneralSecurityException gse) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Cannot generate RSA premaster secret", gse);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced RSA ClientKeyExchange handshake message", ckem);
}
// Output the handshake message.
ckem.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
chc.negotiatedCipherSuite.keyExchange,
chc.negotiatedProtocol);
if (ke == null) { // unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
// update the states
chc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
if (kd == null) { // unlikely
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
chc.negotiatedProtocol);
} else {
chc.handshakeKeyDerivation =
kd.createKeyDerivation(chc, masterSecret);
}
}
// The handshake message has been delivered.
return null;
}
}
/**
* The RSA "ClientKeyExchange" handshake message consumer.
*/
private static final
class RSAClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private RSAClientKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
EphemeralRSAPossession rsaPossession = null;
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof EphemeralRSAPossession) {
rsaPossession = (EphemeralRSAPossession)possession;
break;
} else if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
if (rsaPossession != null) {
break;
}
}
}
if (rsaPossession == null && x509Possession == null) { // unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No RSA possessions negotiated for client key exchange");
}
PrivateKey privateKey = (rsaPossession != null) ?
rsaPossession.popPrivateKey : x509Possession.popPrivateKey;
if (!privateKey.getAlgorithm().equals("RSA")) { // unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Not RSA private key for client key exchange");
}
RSAClientKeyExchangeMessage ckem =
new RSAClientKeyExchangeMessage(shc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming RSA ClientKeyExchange handshake message", ckem);
}
// create the credentials
RSAPremasterSecret premaster;
try {
premaster =
RSAPremasterSecret.decode(shc, privateKey, ckem.encrypted);
shc.handshakeCredentials.add(premaster);
} catch (GeneralSecurityException gse) {
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Cannot decode RSA premaster secret", gse);
}
// update the states
SSLKeyExchange ke = SSLKeyExchange.valueOf(
shc.negotiatedCipherSuite.keyExchange,
shc.negotiatedProtocol);
if (ke == null) { // unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
} else {
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
SecretKey masterSecret =
masterKD.deriveKey("MasterSecret", null);
// update the states
shc.handshakeSession.setMasterSecret(masterSecret);
SSLTrafficKeyDerivation kd =
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
if (kd == null) { // unlikely
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}
}

View File

@@ -0,0 +1,313 @@
/*
* Copyright (c) 2018, 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.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
final class RSAKeyExchange {
static final SSLPossessionGenerator poGenerator =
new EphemeralRSAPossessionGenerator();
static final SSLKeyAgreementGenerator kaGenerator =
new RSAKAGenerator();
static final class EphemeralRSAPossession implements SSLPossession {
// Proof of possession of the private key corresponding to the public
// key for which a certificate is being provided for authentication.
final RSAPublicKey popPublicKey;
final PrivateKey popPrivateKey;
EphemeralRSAPossession(PrivateKey popPrivateKey,
RSAPublicKey popPublicKey) {
this.popPublicKey = popPublicKey;
this.popPrivateKey = popPrivateKey;
}
}
static final class EphemeralRSACredentials implements SSLCredentials {
final RSAPublicKey popPublicKey;
EphemeralRSACredentials(RSAPublicKey popPublicKey) {
this.popPublicKey = popPublicKey;
}
}
private static final class EphemeralRSAPossessionGenerator
implements SSLPossessionGenerator {
// Prevent instantiation of this class.
private EphemeralRSAPossessionGenerator() {
// blank
}
@Override
public SSLPossession createPossession(HandshakeContext context) {
try {
EphemeralKeyManager ekm =
context.sslContext.getEphemeralKeyManager();
KeyPair kp = ekm.getRSAKeyPair(
true, context.sslContext.getSecureRandom());
if (kp != null) {
return new EphemeralRSAPossession(
kp.getPrivate(), (RSAPublicKey)kp.getPublic());
} else {
// Could not generate the ephemeral key, ignore.
return null;
}
} catch (RuntimeException rte) {
// Could not determine keylength, ignore.
return null;
}
}
}
static final
class RSAPremasterSecret implements SSLPossession, SSLCredentials {
final SecretKey premasterSecret;
RSAPremasterSecret(SecretKey premasterSecret) {
this.premasterSecret = premasterSecret;
}
byte[] getEncoded(PublicKey publicKey,
SecureRandom secureRandom) throws GeneralSecurityException {
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
cipher.init(Cipher.WRAP_MODE, publicKey, secureRandom);
return cipher.wrap(premasterSecret);
}
@SuppressWarnings("deprecation")
static RSAPremasterSecret createPremasterSecret(
ClientHandshakeContext chc) throws GeneralSecurityException {
String algorithm = chc.negotiatedProtocol.useTLS12PlusSpec() ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
KeyGenerator kg = JsseJce.getKeyGenerator(algorithm);
TlsRsaPremasterSecretParameterSpec spec =
new TlsRsaPremasterSecretParameterSpec(
chc.clientHelloVersion,
chc.negotiatedProtocol.id);
kg.init(spec, chc.sslContext.getSecureRandom());
return new RSAPremasterSecret(kg.generateKey());
}
@SuppressWarnings("deprecation")
static RSAPremasterSecret decode(ServerHandshakeContext shc,
PrivateKey privateKey,
byte[] encrypted) throws GeneralSecurityException {
byte[] encoded = null;
boolean needFailover = false;
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
try {
// Try UNWRAP_MODE mode firstly.
cipher.init(Cipher.UNWRAP_MODE, privateKey,
new TlsRsaPremasterSecretParameterSpec(
shc.clientHelloVersion,
shc.negotiatedProtocol.id),
shc.sslContext.getSecureRandom());
// The provider selection can be delayed, please don't call
// any Cipher method before the call to Cipher.init().
needFailover = !KeyUtil.isOracleJCEProvider(
cipher.getProvider().getName());
} catch (InvalidKeyException | UnsupportedOperationException iue) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("The Cipher provider "
+ safeProviderName(cipher)
+ " caused exception: " + iue.getMessage());
}
needFailover = true;
}
SecretKey preMaster;
if (needFailover) {
// The cipher might be spoiled by unsuccessful call to init(),
// so request a fresh instance
cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
// Use DECRYPT_MODE and dispose the previous initialization.
cipher.init(Cipher.DECRYPT_MODE, privateKey);
boolean failed = false;
try {
encoded = cipher.doFinal(encrypted);
} catch (BadPaddingException bpe) {
// Note: encoded == null
failed = true;
}
encoded = KeyUtil.checkTlsPreMasterSecretKey(
shc.clientHelloVersion, shc.negotiatedProtocol.id,
shc.sslContext.getSecureRandom(), encoded, failed);
preMaster = generatePremasterSecret(
shc.clientHelloVersion, shc.negotiatedProtocol.id,
encoded, shc.sslContext.getSecureRandom());
} else {
// the cipher should have been initialized
preMaster = (SecretKey)cipher.unwrap(encrypted,
"TlsRsaPremasterSecret", Cipher.SECRET_KEY);
}
return new RSAPremasterSecret(preMaster);
}
/*
* Retrieving the cipher's provider name for the debug purposes
* can throw an exception by itself.
*/
private static String safeProviderName(Cipher cipher) {
try {
return cipher.getProvider().toString();
} catch (Exception e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Retrieving The Cipher provider name" +
" caused exception ", e);
}
}
try {
return cipher.toString() + " (provider name not available)";
} catch (Exception e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Retrieving The Cipher name" +
" caused exception ", e);
}
}
return "(cipher/provider names not available)";
}
// generate a premaster secret with the specified version number
@SuppressWarnings("deprecation")
private static SecretKey generatePremasterSecret(
int clientVersion, int serverVersion, byte[] encodedSecret,
SecureRandom generator) throws GeneralSecurityException {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Generating a premaster secret");
}
try {
String s = ((clientVersion >= ProtocolVersion.TLS12.id) ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(
clientVersion, serverVersion, encodedSecret),
generator);
return kg.generateKey();
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException iae) {
// unlikely to happen, otherwise, must be a provider exception
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("RSA premaster secret generation error:");
iae.printStackTrace(System.out);
}
throw new GeneralSecurityException(
"Could not generate premaster secret", iae);
}
}
}
private static final
class RSAKAGenerator implements SSLKeyAgreementGenerator {
// Prevent instantiation of this class.
private RSAKAGenerator() {
// blank
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
RSAPremasterSecret premaster = null;
if (context instanceof ClientHandshakeContext) {
for (SSLPossession possession : context.handshakePossessions) {
if (possession instanceof RSAPremasterSecret) {
premaster = (RSAPremasterSecret)possession;
break;
}
}
} else {
for (SSLCredentials credential : context.handshakeCredentials) {
if (credential instanceof RSAPremasterSecret) {
premaster = (RSAPremasterSecret)credential;
break;
}
}
}
if (premaster == null) {
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No sufficient RSA key agreement parameters negotiated");
}
return new RSAKAKeyDerivation(context, premaster.premasterSecret);
}
private static final
class RSAKAKeyDerivation implements SSLKeyDerivation {
private final HandshakeContext context;
private final SecretKey preMasterSecret;
RSAKAKeyDerivation(
HandshakeContext context, SecretKey preMasterSecret) {
this.context = context;
this.preMasterSecret = preMasterSecret;
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
SSLMasterKeyDerivation mskd =
SSLMasterKeyDerivation.valueOf(
context.negotiatedProtocol);
if (mskd == null) {
// unlikely
throw new SSLHandshakeException(
"No expected master key derivation for protocol: " +
context.negotiatedProtocol.name);
}
SSLKeyDerivation kd = mskd.createKeyDerivation(
context, preMasterSecret);
return kd.deriveKey("MasterSecret", params);
}
}
}
}

View File

@@ -0,0 +1,339 @@
/*
* Copyright (c) 2018, 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.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;
/**
* Pack of the ServerKeyExchange handshake message.
*/
final class RSAServerKeyExchange {
static final SSLConsumer rsaHandshakeConsumer =
new RSAServerKeyExchangeConsumer();
static final HandshakeProducer rsaHandshakeProducer =
new RSAServerKeyExchangeProducer();
/**
* The ephemeral RSA ServerKeyExchange handshake message.
*
* Used for RSA_EXPORT, SSL 3.0 and TLS 1.0 only.
*/
private static final
class RSAServerKeyExchangeMessage extends HandshakeMessage {
// public key encapsulated in this message
private final byte[] modulus; // 1 to 2^16 - 1 bytes
private final byte[] exponent; // 1 to 2^16 - 1 bytes
// signature bytes, none-null as no anonymous RSA key exchange.
private final byte[] paramsSignature;
private RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
X509Possession x509Possession,
EphemeralRSAPossession rsaPossession) throws IOException {
super(handshakeContext);
// This happens in server side only.
ServerHandshakeContext shc =
(ServerHandshakeContext)handshakeContext;
RSAPublicKey publicKey = rsaPossession.popPublicKey;
RSAPublicKeySpec spec = JsseJce.getRSAPublicKeySpec(publicKey);
this.modulus = Utilities.toByteArray(spec.getModulus());
this.exponent = Utilities.toByteArray(spec.getPublicExponent());
byte[] signature = null;
try {
Signature signer = RSASignature.getInstance();
signer.initSign(x509Possession.popPrivateKey,
shc.sslContext.getSecureRandom());
updateSignature(signer,
shc.clientHelloRandom.randomBytes,
shc.serverHelloRandom.randomBytes);
signature = signer.sign();
} catch (NoSuchAlgorithmException |
InvalidKeyException | SignatureException ex) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failed to sign ephemeral RSA parameters", ex);
}
this.paramsSignature = signature;
}
RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// This happens in client side only.
ClientHandshakeContext chc =
(ClientHandshakeContext)handshakeContext;
this.modulus = Record.getBytes16(m);
this.exponent = Record.getBytes16(m);
this.paramsSignature = Record.getBytes16(m);
X509Credentials x509Credentials = null;
for (SSLCredentials cd : chc.handshakeCredentials) {
if (cd instanceof X509Credentials) {
x509Credentials = (X509Credentials)cd;
break;
}
}
if (x509Credentials == null) {
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No RSA credentials negotiated for server key exchange");
}
try {
Signature signer = RSASignature.getInstance();
signer.initVerify(x509Credentials.popPublicKey);
updateSignature(signer,
chc.clientHelloRandom.randomBytes,
chc.serverHelloRandom.randomBytes);
if (!signer.verify(paramsSignature)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature of RSA ServerKeyExchange message");
}
} catch (NoSuchAlgorithmException |
InvalidKeyException | SignatureException ex) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failed to sign ephemeral RSA parameters", ex);
}
}
@Override
SSLHandshake handshakeType() {
return SSLHandshake.SERVER_KEY_EXCHANGE;
}
@Override
int messageLength() {
return 6 + modulus.length + exponent.length
+ paramsSignature.length;
}
@Override
void send(HandshakeOutStream hos) throws IOException {
hos.putBytes16(modulus);
hos.putBytes16(exponent);
hos.putBytes16(paramsSignature);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"RSA ServerKeyExchange\": '{'\n" +
" \"parameters\": '{'\n" +
" \"rsa_modulus\": '{'\n" +
"{0}\n" +
" '}',\n" +
" \"rsa_exponent\": '{'\n" +
"{1}\n" +
" '}'\n" +
" '}',\n" +
" \"digital signature\": '{'\n" +
" \"signature\": '{'\n" +
"{2}\n" +
" '}',\n" +
" '}'\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(
hexEncoder.encodeBuffer(modulus), " "),
Utilities.indent(
hexEncoder.encodeBuffer(exponent), " "),
Utilities.indent(
hexEncoder.encodeBuffer(paramsSignature), " ")
};
return messageFormat.format(messageFields);
}
/*
* Hash the nonces and the ephemeral RSA public key.
*/
private void updateSignature(Signature signature,
byte[] clntNonce, byte[] svrNonce) throws SignatureException {
signature.update(clntNonce);
signature.update(svrNonce);
signature.update((byte)(modulus.length >> 8));
signature.update((byte)(modulus.length & 0x0ff));
signature.update(modulus);
signature.update((byte)(exponent.length >> 8));
signature.update((byte)(exponent.length & 0x0ff));
signature.update(exponent);
}
}
/**
* The RSA "ServerKeyExchange" handshake message producer.
*/
private static final
class RSAServerKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private RSAServerKeyExchangeProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
EphemeralRSAPossession rsaPossession = null;
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof EphemeralRSAPossession) {
rsaPossession = (EphemeralRSAPossession)possession;
if (x509Possession != null) {
break;
}
} else if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
if (rsaPossession != null) {
break;
}
}
}
if (rsaPossession == null) {
// The X.509 certificate itself should be used for RSA_EXPORT
// key exchange. The ServerKeyExchange handshake message is
// not needed.
return null;
} else if (x509Possession == null) {
// unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No RSA certificate negotiated for server key exchange");
} else if (!"RSA".equals(
x509Possession.popPrivateKey.getAlgorithm())) {
// unlikely
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No X.509 possession can be used for " +
"ephemeral RSA ServerKeyExchange");
}
RSAServerKeyExchangeMessage skem =
new RSAServerKeyExchangeMessage(
shc, x509Possession, rsaPossession);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced RSA ServerKeyExchange handshake message", skem);
}
// Output the handshake message.
skem.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The handshake message has been delivered.
return null;
}
}
/**
* The RSA "ServerKeyExchange" handshake message consumer.
*/
private static final
class RSAServerKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private RSAServerKeyExchangeConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
RSAServerKeyExchangeMessage skem =
new RSAServerKeyExchangeMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming RSA ServerKeyExchange handshake message", skem);
}
//
// validate
//
// check constraints of RSA PublicKey
RSAPublicKey publicKey;
try {
KeyFactory kf = JsseJce.getKeyFactory("RSA");
RSAPublicKeySpec spec = new RSAPublicKeySpec(
new BigInteger(1, skem.modulus),
new BigInteger(1, skem.exponent));
publicKey = (RSAPublicKey)kf.generatePublic(spec);
} catch (GeneralSecurityException gse) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"Could not generate RSAPublicKey", gse);
}
if (!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"RSA ServerKeyExchange does not comply to " +
"algorithm constraints");
}
//
// update
//
chc.handshakeCredentials.add(
new EphemeralRSACredentials(publicKey));
//
// produce
//
// Need no new handshake message producers here.
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright (c) 1996, 2018, 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.*;
import java.security.spec.AlgorithmParameterSpec;
/**
* Signature implementation for the SSL/TLS RSA Signature variant with both
* MD5 and SHA-1 MessageDigests. Used for explicit RSA server authentication
* (RSA signed server key exchange for RSA_EXPORT and DHE_RSA) and RSA client
* authentication (RSA signed certificate verify message).
*
* It conforms to the standard JCA Signature API. It is registered in the
* SunJSSE provider to avoid more complicated getInstance() code and
* negative interaction with the JCA mechanisms for hardware providers.
*
* The class should be instantiated via the getInstance() method in this class,
* which returns the implementation from the preferred provider. The internal
* implementation allows the hashes to be explicitly set, which is required
* for RSA client authentication. It can be obtained via the
* getInternalInstance() method.
*
* This class is not thread safe.
*/
public final class RSASignature extends SignatureSpi {
private final Signature rawRsa;
private final MessageDigest mdMD5;
private final MessageDigest mdSHA;
public RSASignature() throws NoSuchAlgorithmException {
super();
rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
this.mdMD5 = JsseJce.getMessageDigest("MD5");
this.mdSHA = JsseJce.getMessageDigest("SHA");
}
/**
* Get an implementation for the RSA signature.
*
* Follows the standard JCA getInstance() model, so it return the
* implementation from the provider with the highest precedence,
* which may be this class.
*/
static Signature getInstance() throws NoSuchAlgorithmException {
return JsseJce.getSignature(JsseJce.SIGNATURE_SSLRSA);
}
@Override
protected void engineInitVerify(PublicKey publicKey)
throws InvalidKeyException {
if (publicKey == null) {
throw new InvalidKeyException("Public key must not be null");
}
mdMD5.reset();
mdSHA.reset();
rawRsa.initVerify(publicKey);
}
@Override
protected void engineInitSign(PrivateKey privateKey)
throws InvalidKeyException {
engineInitSign(privateKey, null);
}
@Override
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
throws InvalidKeyException {
if (privateKey == null) {
throw new InvalidKeyException("Private key must not be null");
}
mdMD5.reset();
mdSHA.reset();
rawRsa.initSign(privateKey, random);
}
@Override
protected void engineUpdate(byte b) {
mdMD5.update(b);
mdSHA.update(b);
}
@Override
protected void engineUpdate(byte[] b, int off, int len) {
mdMD5.update(b, off, len);
mdSHA.update(b, off, len);
}
private byte[] getDigest() throws SignatureException {
try {
byte[] data = new byte[36];
mdMD5.digest(data, 0, 16);
mdSHA.digest(data, 16, 20);
return data;
} catch (DigestException e) {
// should never occur
throw new SignatureException(e);
}
}
@Override
protected byte[] engineSign() throws SignatureException {
rawRsa.update(getDigest());
return rawRsa.sign();
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
return engineVerify(sigBytes, 0, sigBytes.length);
}
@Override
protected boolean engineVerify(byte[] sigBytes, int offset, int length)
throws SignatureException {
rawRsa.update(getDigest());
return rawRsa.verify(sigBytes, offset, length);
}
@Override
@SuppressWarnings("deprecation")
protected void engineSetParameter(String param,
Object value) throws InvalidParameterException {
throw new InvalidParameterException("Parameters not supported");
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("No parameters accepted");
}
}
@Override
@SuppressWarnings("deprecation")
protected Object engineGetParameter(
String param) throws InvalidParameterException {
throw new InvalidParameterException("Parameters not supported");
}
@Override
protected AlgorithmParameters engineGetParameters() {
return null;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 1996, 2018, 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 sun.security.util.ByteArrays;
import java.io.*;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;
/*
* RandomCookie ... SSL hands standard format random cookies (nonces)
* around. These know how to encode/decode themselves on SSL streams,
* and can be created and printed.
*
* @author David Brownell
*/
final class RandomCookie {
final byte[] randomBytes = new byte[32]; // exactly 32 bytes
private static final byte[] hrrRandomBytes = new byte[] {
(byte)0xCF, (byte)0x21, (byte)0xAD, (byte)0x74,
(byte)0xE5, (byte)0x9A, (byte)0x61, (byte)0x11,
(byte)0xBE, (byte)0x1D, (byte)0x8C, (byte)0x02,
(byte)0x1E, (byte)0x65, (byte)0xB8, (byte)0x91,
(byte)0xC2, (byte)0xA2, (byte)0x11, (byte)0x16,
(byte)0x7A, (byte)0xBB, (byte)0x8C, (byte)0x5E,
(byte)0x07, (byte)0x9E, (byte)0x09, (byte)0xE2,
(byte)0xC8, (byte)0xA8, (byte)0x33, (byte)0x9C
};
private static final byte[] t12Protection = new byte[] {
(byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
(byte)0x47, (byte)0x52, (byte)0x44, (byte)0x01
};
private static final byte[] t11Protection = new byte[] {
(byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
(byte)0x47, (byte)0x52, (byte)0x44, (byte)0x00
};
static final RandomCookie hrrRandom = new RandomCookie(hrrRandomBytes);
RandomCookie(SecureRandom generator) {
generator.nextBytes(randomBytes);
}
// Used for server random generation with version downgrade protection.
RandomCookie(HandshakeContext context) {
SecureRandom generator = context.sslContext.getSecureRandom();
generator.nextBytes(randomBytes);
// TLS 1.3 has a downgrade protection mechanism embedded in the
// server's random value. TLS 1.3 servers which negotiate TLS 1.2
// or below in response to a ClientHello MUST set the last eight
// bytes of their Random value specially.
byte[] protection = null;
if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
if (context.negotiatedProtocol.useTLS12PlusSpec()) {
protection = t12Protection;
} else {
protection = t11Protection;
}
}
} else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
protection = t11Protection;
}
}
if (protection != null) {
System.arraycopy(protection, 0, randomBytes,
randomBytes.length - protection.length, protection.length);
}
}
RandomCookie(ByteBuffer m) throws IOException {
m.get(randomBytes);
}
private RandomCookie(byte[] randomBytes) {
System.arraycopy(randomBytes, 0, this.randomBytes, 0, 32);
}
@Override
public String toString() {
return "random_bytes = {" + Utilities.toHexString(randomBytes) + "}";
}
boolean isHelloRetryRequest() {
return MessageDigest.isEqual(hrrRandomBytes, randomBytes);
}
// Used for client random validation of version downgrade protection.
boolean isVersionDowngrade(HandshakeContext context) {
if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
return isT12Downgrade() || isT11Downgrade();
}
} else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
return isT11Downgrade();
}
}
return false;
}
private boolean isT12Downgrade() {
return ByteArrays.isEqual(randomBytes, 24, 32, t12Protection, 0, 8);
}
private boolean isT11Downgrade() {
return ByteArrays.isEqual(randomBytes, 24, 32, t11Protection, 0, 8);
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (c) 1996, 2018, 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.ByteBuffer;
import javax.net.ssl.SSLException;
/**
* SSL/TLS record.
*
* This is the base interface, which defines common information and interfaces
* used by both Input and Output records.
*
* @author David Brownell
*/
interface Record {
static final int maxMacSize = 48; // the max supported MAC or
// AEAD tag size
static final int maxDataSize = 16384; // 2^14 bytes of data
static final int maxPadding = 256; // block cipher padding
static final int maxIVLength = 16; // the max supported IV length
static final int maxFragmentSize = 18432; // the max fragment size
// 2^14 + 2048
/*
* System property to enable/disable CBC protection in SSL3/TLS1.
*/
static final boolean enableCBCProtection =
Utilities.getBooleanProperty("jsse.enableCBCProtection", true);
/*
* The overflow values of integers of 8, 16 and 24 bits.
*/
static final int OVERFLOW_OF_INT08 = (0x01 << 8);
static final int OVERFLOW_OF_INT16 = (0x01 << 16);
static final int OVERFLOW_OF_INT24 = (0x01 << 24);
/*
* Read 8, 16, 24, and 32 bit integer data types, encoded
* in standard big-endian form.
*/
static int getInt8(ByteBuffer m) throws IOException {
verifyLength(m, 1);
return (m.get() & 0xFF);
}
static int getInt16(ByteBuffer m) throws IOException {
verifyLength(m, 2);
return ((m.get() & 0xFF) << 8) |
(m.get() & 0xFF);
}
static int getInt24(ByteBuffer m) throws IOException {
verifyLength(m, 3);
return ((m.get() & 0xFF) << 16) |
((m.get() & 0xFF) << 8) |
(m.get() & 0xFF);
}
static int getInt32(ByteBuffer m) throws IOException {
verifyLength(m, 4);
return ((m.get() & 0xFF) << 24) |
((m.get() & 0xFF) << 16) |
((m.get() & 0xFF) << 8) |
(m.get() & 0xFF);
}
/*
* Read byte vectors with 8, 16, and 24 bit length encodings.
*/
static byte[] getBytes8(ByteBuffer m) throws IOException {
int len = Record.getInt8(m);
verifyLength(m, len);
byte[] b = new byte[len];
m.get(b);
return b;
}
static byte[] getBytes16(ByteBuffer m) throws IOException {
int len = Record.getInt16(m);
verifyLength(m, len);
byte[] b = new byte[len];
m.get(b);
return b;
}
static byte[] getBytes24(ByteBuffer m) throws IOException {
int len = Record.getInt24(m);
verifyLength(m, len);
byte[] b = new byte[len];
m.get(b);
return b;
}
/*
* Write 8, 16, 24, and 32 bit integer data types, encoded
* in standard big-endian form.
*/
static void putInt8(ByteBuffer m, int i) throws IOException {
verifyLength(m, 1);
m.put((byte)(i & 0xFF));
}
static void putInt16(ByteBuffer m, int i) throws IOException {
verifyLength(m, 2);
m.put((byte)((i >> 8) & 0xFF));
m.put((byte)(i & 0xFF));
}
static void putInt24(ByteBuffer m, int i) throws IOException {
verifyLength(m, 3);
m.put((byte)((i >> 16) & 0xFF));
m.put((byte)((i >> 8) & 0xFF));
m.put((byte)(i & 0xFF));
}
static void putInt32(ByteBuffer m, int i) throws IOException {
m.put((byte)((i >> 24) & 0xFF));
m.put((byte)((i >> 16) & 0xFF));
m.put((byte)((i >> 8) & 0xFF));
m.put((byte)(i & 0xFF));
}
/*
* Write byte vectors with 8, 16, and 24 bit length encodings.
*/
static void putBytes8(ByteBuffer m, byte[] s) throws IOException {
if (s == null || s.length == 0) {
verifyLength(m, 1);
putInt8(m, 0);
} else {
verifyLength(m, 1 + s.length);
putInt8(m, s.length);
m.put(s);
}
}
static void putBytes16(ByteBuffer m, byte[] s) throws IOException {
if (s == null || s.length == 0) {
verifyLength(m, 2);
putInt16(m, 0);
} else {
verifyLength(m, 2 + s.length);
putInt16(m, s.length);
m.put(s);
}
}
static void putBytes24(ByteBuffer m, byte[] s) throws IOException {
if (s == null || s.length == 0) {
verifyLength(m, 3);
putInt24(m, 0);
} else {
verifyLength(m, 3 + s.length);
putInt24(m, s.length);
m.put(s);
}
}
// Verify that the buffer has sufficient remaining.
static void verifyLength(
ByteBuffer m, int len) throws SSLException {
if (len > m.remaining()) {
throw new SSLException("Insufficient space in the buffer, " +
"may be cause by an unexpected end of handshake data.");
}
}
}

View File

@@ -0,0 +1,556 @@
/*
* Copyright (c) 2015, 2018, 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.ByteBuffer;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import static sun.security.ssl.SSLExtension.CH_RENEGOTIATION_INFO;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import static sun.security.ssl.SSLExtension.SH_RENEGOTIATION_INFO;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.util.ByteArrays;
/**
* Pack of the "renegotiation_info" extensions [RFC 5746].
*/
final class RenegoInfoExtension {
static final HandshakeProducer chNetworkProducer =
new CHRenegotiationInfoProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHRenegotiationInfoConsumer();
static final HandshakeAbsence chOnLoadAbsence =
new CHRenegotiationInfoAbsence();
static final HandshakeProducer shNetworkProducer =
new SHRenegotiationInfoProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHRenegotiationInfoConsumer();
static final HandshakeAbsence shOnLoadAbsence =
new SHRenegotiationInfoAbsence();
static final SSLStringizer rniStringizer =
new RenegotiationInfoStringizer();
/**
* The "renegotiation_info" extension.
*/
static final class RenegotiationInfoSpec implements SSLExtensionSpec {
// A nominal object that does not holding any real renegotiation info.
static final RenegotiationInfoSpec NOMINAL =
new RenegotiationInfoSpec(new byte[0]);
private final byte[] renegotiatedConnection;
private RenegotiationInfoSpec(byte[] renegotiatedConnection) {
this.renegotiatedConnection = Arrays.copyOf(
renegotiatedConnection, renegotiatedConnection.length);
}
private RenegotiationInfoSpec(ByteBuffer m) throws IOException {
// Parse the extension.
if (!m.hasRemaining() || m.remaining() < 1) {
throw new SSLProtocolException(
"Invalid renegotiation_info extension data: " +
"insufficient data");
}
this.renegotiatedConnection = Record.getBytes8(m);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"renegotiated connection\": '['{0}']'", Locale.ENGLISH);
if (renegotiatedConnection.length == 0) {
Object[] messageFields = {
"<no renegotiated connection>"
};
return messageFormat.format(messageFields);
} else {
Object[] messageFields = {
Utilities.toHexString(renegotiatedConnection)
};
return messageFormat.format(messageFields);
}
}
}
private static final
class RenegotiationInfoStringizer implements SSLStringizer {
@Override
public String toString(ByteBuffer buffer) {
try {
return (new RenegotiationInfoSpec(buffer)).toString();
} catch (IOException ioe) {
// For debug logging only, so please swallow exceptions.
return ioe.getMessage();
}
}
}
/**
* Network data producer of a "renegotiation_info" extension in
* the ClientHello handshake message.
*/
private static final
class CHRenegotiationInfoProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private CHRenegotiationInfoProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable renegotiation_info extension");
}
return null;
}
if (!chc.conContext.isNegotiated) {
if (chc.activeCipherSuites.contains(
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
// Using the the TLS_EMPTY_RENEGOTIATION_INFO_SCSV instead.
return null;
}
// initial handshaking.
//
// If this is the initial handshake for a connection, then the
// "renegotiated_connection" field is of zero length in both
// the ClientHello and the ServerHello. [RFC 5746]
byte[] extData = new byte[] { 0x00 };
chc.handshakeExtensions.put(
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
return extData;
} else if (chc.conContext.secureRenegotiation) {
// secure renegotiation
//
// For ClientHello handshake message in renegotiation, this
// field contains the "client_verify_data".
byte[] extData =
new byte[chc.conContext.clientVerifyData.length + 1];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putBytes8(m, chc.conContext.clientVerifyData);
// The conContext.clientVerifyData will be used for further
// processing, so it does not matter to save whatever in the
// RenegotiationInfoSpec object.
chc.handshakeExtensions.put(
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
return extData;
} else { // not secure renegotiation
if (HandshakeContext.allowUnsafeRenegotiation) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("Using insecure renegotiation");
}
return null;
} else {
// terminate the session.
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"insecure renegotiation is not allowed");
}
}
}
}
/**
* Network data producer of a "renegotiation_info" extension in
* the ServerHello handshake message.
*/
private static final
class CHRenegotiationInfoConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private CHRenegotiationInfoConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// Is it a supported and enabled extension?
if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Ignore unavailable extension: " +
CH_RENEGOTIATION_INFO.name);
}
return; // ignore the extension
}
// Parse the extension.
RenegotiationInfoSpec spec;
try {
spec = new RenegotiationInfoSpec(buffer);
} catch (IOException ioe) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (!shc.conContext.isNegotiated) {
// initial handshaking.
if (spec.renegotiatedConnection.length != 0) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid renegotiation_info extension data: not empty");
}
shc.conContext.secureRenegotiation = true;
} else {
if (!shc.conContext.secureRenegotiation) {
// Unexpected RI extension for insecure renegotiation,
// abort the handshake with a fatal handshake_failure alert.
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"The renegotiation_info is present in a insecure " +
"renegotiation");
} else {
// verify the client_verify_data value
if (!MessageDigest.isEqual(shc.conContext.clientVerifyData,
spec.renegotiatedConnection)) {
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid renegotiation_info extension data: " +
"incorrect verify data in ClientHello");
}
}
}
// Update the context.
//
// The conContext.clientVerifyData will be used for further
// processing, so it does not matter to save whatever in the
// RenegotiationInfoSpec object.
shc.handshakeExtensions.put(
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
// No impact on session resumption.
}
}
/**
* The absence processing if a "renegotiation_info" extension is
* not present in the ClientHello handshake message.
*/
private static final
class CHRenegotiationInfoAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ClientHelloMessage clientHello = (ClientHelloMessage)message;
if (!shc.conContext.isNegotiated) {
// initial handshaking.
for (int id : clientHello.cipherSuiteIds) {
if (id ==
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Safe renegotiation, using the SCSV signgling");
}
shc.conContext.secureRenegotiation = true;
return;
}
}
if (!HandshakeContext.allowLegacyHelloMessages) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Failed to negotiate the use of secure renegotiation");
} // otherwise, allow legacy hello message
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("Warning: No renegotiation " +
"indication in ClientHello, allow legacy ClientHello");
}
shc.conContext.secureRenegotiation = false;
} else if (shc.conContext.secureRenegotiation) {
// Require secure renegotiation, terminate the connection.
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Inconsistent secure renegotiation indication");
} else { // renegotiation, not secure
if (HandshakeContext.allowUnsafeRenegotiation) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("Using insecure renegotiation");
}
} else {
// Unsafe renegotiation should have been aborted in
// ealier processes.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Terminate insecure renegotiation");
}
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsafe renegotiation is not allowed");
}
}
}
}
/**
* Network data producer of a "renegotiation_info" extension in
* the ServerHello handshake message.
*/
private static final
class SHRenegotiationInfoProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private SHRenegotiationInfoProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// In response to "renegotiation_info" extension request only.
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
shc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
if (requestedSpec == null && !shc.conContext.secureRenegotiation) {
// Ignore, no renegotiation_info extension or SCSV signgling
// requested.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"Ignore unavailable renegotiation_info extension");
}
return null; // ignore the extension
}
if (!shc.conContext.secureRenegotiation) {
// Ignore, no secure renegotiation is negotiated.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest(
"No secure renegotiation has been negotiated");
}
return null; // ignore the extension
}
if (!shc.conContext.isNegotiated) {
// initial handshaking.
//
// If this is the initial handshake for a connection, then the
// "renegotiated_connection" field is of zero length in both
// the ClientHello and the ServerHello. [RFC 5746]
byte[] extData = new byte[] { 0x00 };
// The conContext.client/serverVerifyData will be used for
// further processing, so it does not matter to save whatever
// in the RenegotiationInfoSpec object.
shc.handshakeExtensions.put(
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
return extData;
} else {
// secure renegotiation
//
// For secure renegotiation, the server MUST include a
// "renegotiation_info" extension containing the saved
// client_verify_data and server_verify_data in the ServerHello.
int infoLen = shc.conContext.clientVerifyData.length +
shc.conContext.serverVerifyData.length;
byte[] extData = new byte[infoLen + 1];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt8(m, infoLen);
m.put(shc.conContext.clientVerifyData);
m.put(shc.conContext.serverVerifyData);
// The conContext.client/serverVerifyData will be used for
// further processing, so it does not matter to save whatever
// in the RenegotiationInfoSpec object.
shc.handshakeExtensions.put(
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
return extData;
}
}
}
/**
* Network data consumer of a "renegotiation_info" extension in
* the ServerHello handshake message.
*/
private static final
class SHRenegotiationInfoConsumer implements ExtensionConsumer {
// Prevent instantiation of this class.
private SHRenegotiationInfoConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to the client renegotiation_info extension request
// or SCSV signling, which is mandatory for ClientHello message.
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
if (requestedSpec == null &&
!chc.activeCipherSuites.contains(
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Missing renegotiation_info and SCSV detected in " +
"ClientHello");
}
// Parse the extension.
RenegotiationInfoSpec spec;
try {
spec = new RenegotiationInfoSpec(buffer);
} catch (IOException ioe) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
}
if (!chc.conContext.isNegotiated) { // initial handshake
// If the extension is present, set the secure_renegotiation
// flag to TRUE. The client MUST then verify that the
// length of the "renegotiated_connection" field is zero,
// and if it is not, MUST abort the handshake (by sending
// a fatal handshake_failure alert). [RFC 5746]
if (spec.renegotiatedConnection.length != 0) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"not empty renegotiated_connection");
}
chc.conContext.secureRenegotiation = true;
} else { // renegotiation
// The client MUST then verify that the first half of the
// "renegotiated_connection" field is equal to the saved
// client_verify_data value, and the second half is equal to the
// saved server_verify_data value. If they are not, the client
// MUST abort the handshake. [RFC 5746]
int infoLen = chc.conContext.clientVerifyData.length +
chc.conContext.serverVerifyData.length;
if (spec.renegotiatedConnection.length != infoLen) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"invalid renegotiated_connection length (" +
spec.renegotiatedConnection.length + ")");
}
byte[] cvd = chc.conContext.clientVerifyData;
if (!ByteArrays.isEqual(spec.renegotiatedConnection,
0, cvd.length, cvd, 0, cvd.length)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"unmatched client_verify_data value");
}
byte[] svd = chc.conContext.serverVerifyData;
if (!ByteArrays.isEqual(spec.renegotiatedConnection,
cvd.length, infoLen, svd, 0, svd.length)) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"unmatched server_verify_data value");
}
}
// Update the context.
chc.handshakeExtensions.put(
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
// No impact on session resumption.
}
}
/**
* The absence processing if a "renegotiation_info" extension is
* not present in the ServerHello handshake message.
*/
private static final
class SHRenegotiationInfoAbsence implements HandshakeAbsence {
@Override
public void absent(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// In response to the client renegotiation_info extension request
// or SCSV signling, which is mandatory for ClientHello message.
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
if (requestedSpec == null &&
!chc.activeCipherSuites.contains(
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Missing renegotiation_info and SCSV detected in " +
"ClientHello");
}
if (!chc.conContext.isNegotiated) {
// initial handshaking.
if (!HandshakeContext.allowLegacyHelloMessages) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Failed to negotiate the use of secure renegotiation");
} // otherwise, allow legacy hello message
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("Warning: No renegotiation " +
"indication in ServerHello, allow legacy ServerHello");
}
chc.conContext.secureRenegotiation = false;
} else if (chc.conContext.secureRenegotiation) {
// Require secure renegotiation, terminate the connection.
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Inconsistent secure renegotiation indication");
} else { // renegotiation, not secure
if (HandshakeContext.allowUnsafeRenegotiation) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("Using insecure renegotiation");
}
} else {
// Unsafe renegotiation should have been aborted in
// ealier processes.
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Terminate insecure renegotiation");
}
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsafe renegotiation is not allowed");
}
}
}
}
}

View File

@@ -0,0 +1,289 @@
/*
* Copyright (c) 2010, 2018, 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.AlgorithmConstraints;
import java.security.AlgorithmParameters;
import java.security.CryptoPrimitive;
import java.security.Key;
import java.util.Set;
import javax.net.ssl.*;
import sun.security.util.DisabledAlgorithmConstraints;
import static sun.security.util.DisabledAlgorithmConstraints.*;
/**
* Algorithm constraints for disabled algorithms property
*
* See the "jdk.certpath.disabledAlgorithms" specification in java.security
* for the syntax of the disabled algorithm string.
*/
final class SSLAlgorithmConstraints implements AlgorithmConstraints {
private static final AlgorithmConstraints tlsDisabledAlgConstraints =
new DisabledAlgorithmConstraints(PROPERTY_TLS_DISABLED_ALGS,
new SSLAlgorithmDecomposer());
private static final AlgorithmConstraints x509DisabledAlgConstraints =
new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS,
new SSLAlgorithmDecomposer(true));
private final AlgorithmConstraints userSpecifiedConstraints;
private final AlgorithmConstraints peerSpecifiedConstraints;
private final boolean enabledX509DisabledAlgConstraints;
// the default algorithm constraints
static final AlgorithmConstraints DEFAULT =
new SSLAlgorithmConstraints(null);
// the default SSL only algorithm constraints
static final AlgorithmConstraints DEFAULT_SSL_ONLY =
new SSLAlgorithmConstraints((SSLSocket)null, false);
SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints) {
this.userSpecifiedConstraints = userSpecifiedConstraints;
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = true;
}
SSLAlgorithmConstraints(SSLSocket socket,
boolean withDefaultCertPathConstraints) {
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLEngine engine,
boolean withDefaultCertPathConstraints) {
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms,
boolean withDefaultCertPathConstraints) {
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
this.peerSpecifiedConstraints =
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms,
boolean withDefaultCertPathConstraints) {
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
this.peerSpecifiedConstraints =
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
private static AlgorithmConstraints getUserSpecifiedConstraints(
SSLEngine engine) {
if (engine != null) {
// Note that the KeyManager or TrustManager implementation may be
// not implemented in the same provider as SSLSocket/SSLEngine.
// Please check the instance before casting to use SSLEngineImpl.
if (engine instanceof SSLEngineImpl) {
HandshakeContext hc =
((SSLEngineImpl)engine).conContext.handshakeContext;
if (hc != null) {
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
}
}
return engine.getSSLParameters().getAlgorithmConstraints();
}
return null;
}
private static AlgorithmConstraints getUserSpecifiedConstraints(
SSLSocket socket) {
if (socket != null) {
// Note that the KeyManager or TrustManager implementation may be
// not implemented in the same provider as SSLSocket/SSLEngine.
// Please check the instance before casting to use SSLSocketImpl.
if (socket instanceof SSLSocketImpl) {
HandshakeContext hc =
((SSLSocketImpl)socket).conContext.handshakeContext;
if (hc != null) {
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
}
}
return socket.getSSLParameters().getAlgorithmConstraints();
}
return null;
}
@Override
public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, AlgorithmParameters parameters) {
boolean permitted = true;
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(
primitives, algorithm, parameters);
}
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(
primitives, algorithm, parameters);
}
if (permitted) {
permitted = tlsDisabledAlgConstraints.permits(
primitives, algorithm, parameters);
}
if (permitted && enabledX509DisabledAlgConstraints) {
permitted = x509DisabledAlgConstraints.permits(
primitives, algorithm, parameters);
}
return permitted;
}
@Override
public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
boolean permitted = true;
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(primitives, key);
}
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(primitives, key);
}
if (permitted) {
permitted = tlsDisabledAlgConstraints.permits(primitives, key);
}
if (permitted && enabledX509DisabledAlgConstraints) {
permitted = x509DisabledAlgConstraints.permits(primitives, key);
}
return permitted;
}
@Override
public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, Key key, AlgorithmParameters parameters) {
boolean permitted = true;
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(
primitives, algorithm, key, parameters);
}
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(
primitives, algorithm, key, parameters);
}
if (permitted) {
permitted = tlsDisabledAlgConstraints.permits(
primitives, algorithm, key, parameters);
}
if (permitted && enabledX509DisabledAlgConstraints) {
permitted = x509DisabledAlgConstraints.permits(
primitives, algorithm, key, parameters);
}
return permitted;
}
private static class SupportedSignatureAlgorithmConstraints
implements AlgorithmConstraints {
// supported signature algorithms
private String[] supportedAlgorithms;
SupportedSignatureAlgorithmConstraints(String[] supportedAlgorithms) {
if (supportedAlgorithms != null) {
this.supportedAlgorithms = supportedAlgorithms.clone();
} else {
this.supportedAlgorithms = null;
}
}
@Override
public boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, AlgorithmParameters parameters) {
if (algorithm == null || algorithm.isEmpty()) {
throw new IllegalArgumentException(
"No algorithm name specified");
}
if (primitives == null || primitives.isEmpty()) {
throw new IllegalArgumentException(
"No cryptographic primitive specified");
}
if (supportedAlgorithms == null ||
supportedAlgorithms.length == 0) {
return false;
}
// trim the MGF part: <digest>with<encryption>and<mgf>
int position = algorithm.indexOf("and");
if (position > 0) {
algorithm = algorithm.substring(0, position);
}
for (String supportedAlgorithm : supportedAlgorithms) {
if (algorithm.equalsIgnoreCase(supportedAlgorithm)) {
return true;
}
}
return false;
}
@Override
public final boolean permits(Set<CryptoPrimitive> primitives, Key key) {
return true;
}
@Override
public final boolean permits(Set<CryptoPrimitive> primitives,
String algorithm, Key key, AlgorithmParameters parameters) {
if (algorithm == null || algorithm.isEmpty()) {
throw new IllegalArgumentException(
"No algorithm name specified");
}
return permits(primitives, algorithm, parameters);
}
}
}

View File

@@ -0,0 +1,280 @@
/*
* Copyright (c) 2015, 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.util.HashSet;
import java.util.Set;
import sun.security.ssl.CipherSuite.HashAlg;
import sun.security.ssl.CipherSuite.KeyExchange;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
import sun.security.ssl.CipherSuite.MacAlg;
import static sun.security.ssl.SSLCipher.*;
import sun.security.util.AlgorithmDecomposer;
/**
* The class decomposes standard SSL/TLS cipher suites into sub-elements.
*/
class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
// indicates that only certification path algorithms need to be used
private final boolean onlyX509;
SSLAlgorithmDecomposer(boolean onlyX509) {
this.onlyX509 = onlyX509;
}
SSLAlgorithmDecomposer() {
this(false);
}
private Set<String> decomposes(CipherSuite.KeyExchange keyExchange) {
Set<String> components = new HashSet<>();
switch (keyExchange) {
case K_NULL:
if (!onlyX509) {
components.add("K_NULL");
}
break;
case K_RSA:
components.add("RSA");
break;
case K_RSA_EXPORT:
components.add("RSA");
components.add("RSA_EXPORT");
break;
case K_DH_RSA:
components.add("RSA");
components.add("DH");
components.add("DiffieHellman");
components.add("DH_RSA");
break;
case K_DH_DSS:
components.add("DSA");
components.add("DSS");
components.add("DH");
components.add("DiffieHellman");
components.add("DH_DSS");
break;
case K_DHE_DSS:
components.add("DSA");
components.add("DSS");
components.add("DH");
components.add("DHE");
components.add("DiffieHellman");
components.add("DHE_DSS");
break;
case K_DHE_RSA:
components.add("RSA");
components.add("DH");
components.add("DHE");
components.add("DiffieHellman");
components.add("DHE_RSA");
break;
case K_DH_ANON:
if (!onlyX509) {
components.add("ANON");
components.add("DH");
components.add("DiffieHellman");
components.add("DH_ANON");
}
break;
case K_ECDH_ECDSA:
components.add("ECDH");
components.add("ECDSA");
components.add("ECDH_ECDSA");
break;
case K_ECDH_RSA:
components.add("ECDH");
components.add("RSA");
components.add("ECDH_RSA");
break;
case K_ECDHE_ECDSA:
components.add("ECDHE");
components.add("ECDSA");
components.add("ECDHE_ECDSA");
break;
case K_ECDHE_RSA:
components.add("ECDHE");
components.add("RSA");
components.add("ECDHE_RSA");
break;
case K_ECDH_ANON:
if (!onlyX509) {
components.add("ECDH");
components.add("ANON");
components.add("ECDH_ANON");
}
break;
default:
// otherwise ignore
}
return components;
}
private Set<String> decomposes(SSLCipher bulkCipher) {
Set<String> components = new HashSet<>();
if (bulkCipher.transformation != null) {
components.addAll(super.decompose(bulkCipher.transformation));
}
switch (bulkCipher) {
case B_NULL:
components.add("C_NULL");
break;
case B_RC2_40:
components.add("RC2_CBC_40");
break;
case B_RC4_40:
components.add("RC4_40");
break;
case B_RC4_128:
components.add("RC4_128");
break;
case B_DES_40:
components.add("DES40_CBC");
components.add("DES_CBC_40");
break;
case B_DES:
components.add("DES_CBC");
break;
case B_3DES:
components.add("3DES_EDE_CBC");
break;
case B_AES_128:
components.add("AES_128_CBC");
break;
case B_AES_256:
components.add("AES_256_CBC");
break;
case B_AES_128_GCM:
components.add("AES_128_GCM");
break;
case B_AES_256_GCM:
components.add("AES_256_GCM");
break;
}
return components;
}
private Set<String> decomposes(CipherSuite.MacAlg macAlg,
SSLCipher cipher) {
Set<String> components = new HashSet<>();
if (macAlg == CipherSuite.MacAlg.M_NULL
&& cipher.cipherType != CipherType.AEAD_CIPHER) {
components.add("M_NULL");
} else if (macAlg == CipherSuite.MacAlg.M_MD5) {
components.add("MD5");
components.add("HmacMD5");
} else if (macAlg == CipherSuite.MacAlg.M_SHA) {
components.add("SHA1");
components.add("SHA-1");
components.add("HmacSHA1");
} else if (macAlg == CipherSuite.MacAlg.M_SHA256) {
components.add("SHA256");
components.add("SHA-256");
components.add("HmacSHA256");
} else if (macAlg == CipherSuite.MacAlg.M_SHA384) {
components.add("SHA384");
components.add("SHA-384");
components.add("HmacSHA384");
}
return components;
}
private Set<String> decomposes(CipherSuite.HashAlg hashAlg) {
Set<String> components = new HashSet<>();
if (hashAlg == CipherSuite.HashAlg.H_SHA256) {
components.add("SHA256");
components.add("SHA-256");
components.add("HmacSHA256");
} else if (hashAlg == CipherSuite.HashAlg.H_SHA384) {
components.add("SHA384");
components.add("SHA-384");
components.add("HmacSHA384");
}
return components;
}
private Set<String> decompose(KeyExchange keyExchange,
SSLCipher cipher,
MacAlg macAlg,
HashAlg hashAlg) {
Set<String> components = new HashSet<>();
if (keyExchange != null) {
components.addAll(decomposes(keyExchange));
}
if (onlyX509) {
// Certification path algorithm constraints do not apply
// to cipher and macAlg.
return components;
}
if (cipher != null) {
components.addAll(decomposes(cipher));
}
if (macAlg != null) {
components.addAll(decomposes(macAlg, cipher));
}
if (hashAlg != null) {
components.addAll(decomposes(hashAlg));
}
return components;
}
@Override
public Set<String> decompose(String algorithm) {
if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) {
CipherSuite cipherSuite = null;
try {
cipherSuite = CipherSuite.nameOf(algorithm);
} catch (IllegalArgumentException iae) {
// ignore: unknown or unsupported ciphersuite
}
if (cipherSuite != null &&
cipherSuite != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
return decompose(cipherSuite.keyExchange,
cipherSuite.bulkCipher,
cipherSuite.macAlg,
cipherSuite.hashAlg);
}
}
return super.decompose(algorithm);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018, 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;
interface SSLAuthentication
extends SSLPossessionGenerator, SSLHandshakeBinding {
// blank
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
final class SSLBasicKeyDerivation implements SSLKeyDerivation {
private final String hashAlg;
private final SecretKey secret;
private final byte[] hkdfInfo;
SSLBasicKeyDerivation(SecretKey secret, String hashAlg,
byte[] label, byte[] context, int length) {
this.hashAlg = hashAlg.replace("-", "");
this.secret = secret;
this.hkdfInfo = createHkdfInfo(label, context, length);
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec keySpec) throws IOException {
try {
HKDF hkdf = new HKDF(hashAlg);
return hkdf.expand(secret, hkdfInfo,
((SecretSizeSpec)keySpec).length, algorithm);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
private static byte[] createHkdfInfo(
byte[] label, byte[] context, int length) {
byte[] info = new byte[4 + label.length + context.length];
ByteBuffer m = ByteBuffer.wrap(info);
try {
Record.putInt16(m, length);
Record.putBytes8(m, label);
Record.putBytes8(m, context);
} catch (IOException ioe) {
// unlikely
}
return info;
}
static class SecretSizeSpec implements AlgorithmParameterSpec {
final int length;
SecretSizeSpec(int length) {
this.length = length;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,491 @@
/*
* Copyright (c) 2018, 2020, 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.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.action.GetIntegerAction;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.SSLExtension.ClientExtensions;
import sun.security.ssl.SSLExtension.ServerExtensions;
/**
* SSL/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;
int maximumPacketSize = 0;
// 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);
// Use TLS1.3 middlebox compatibility mode.
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 = AccessController.doPrivileged(
new GetIntegerAction("jdk.tls.maxHandshakeMessageSize", 32768)).intValue();
// Set the max certificate chain length to 10
static final int maxCertificateChainLength = AccessController.doPrivileged(
new GetIntegerAction("jdk.tls.maxCertificateChainLength", 10)).intValue();
// Is the extended_master_secret extension supported?
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) {
// 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.<SNIServerName>emptyList();
this.sniMatchers = Collections.<SNIMatcher>emptyList();
this.preferLocalCipherSuites = false;
this.applicationProtocols = new String[0];
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);
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();
}
// 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, Arrays.asList(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 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 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();
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
interface SSLConsumer {
void consume(ConnectionContext context,
ByteBuffer message) throws IOException;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018, 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;
interface SSLCredentials {
}

View File

@@ -0,0 +1,988 @@
/*
* Copyright (c) 2003, 2023, 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.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLKeyException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
/**
* Implementation of an non-blocking SSLEngine.
*
* @author Brad Wetmore
*/
final class SSLEngineImpl extends SSLEngine implements SSLTransport {
private final SSLContextImpl sslContext;
final TransportContext conContext;
/**
* Constructor for an SSLEngine from SSLContext, without
* host/port hints.
*
* This Engine will not be able to cache sessions, but must renegotiate
* everything by hand.
*/
SSLEngineImpl(SSLContextImpl sslContext) {
this(sslContext, null, -1);
}
/**
* Constructor for an SSLEngine from SSLContext.
*/
SSLEngineImpl(SSLContextImpl sslContext,
String host, int port) {
super(host, port);
this.sslContext = sslContext;
HandshakeHash handshakeHash = new HandshakeHash();
this.conContext = new TransportContext(sslContext, this,
new SSLEngineInputRecord(handshakeHash),
new SSLEngineOutputRecord(handshakeHash));
// Server name indication is a connection scope extension.
if (host != null) {
this.conContext.sslConfig.serverNames =
Utilities.addToSNIServerNameList(
conContext.sslConfig.serverNames, host);
}
}
@Override
public synchronized void beginHandshake() throws SSLException {
if (conContext.isUnsureMode) {
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
try {
conContext.kickstart();
} catch (IOException ioe) {
throw conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Couldn't kickstart handshaking", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to begin handshake", ex);
}
}
@Override
public synchronized SSLEngineResult wrap(ByteBuffer[] appData,
int offset, int length, ByteBuffer netData) throws SSLException {
return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1);
}
// @Override
public synchronized SSLEngineResult wrap(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
if (conContext.isUnsureMode) {
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
// See if the handshaker needs to report back some SSLException.
checkTaskThrown();
// check parameters
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
try {
return writeRecord(
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
} catch (SSLProtocolException spe) {
// may be an unexpected handshake message
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe);
} catch (IOException ioe) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"problem wrapping app data", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to wrap application data", ex);
}
}
private SSLEngineResult writeRecord(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
// May need to deliver cached records.
if (isOutboundDone()) {
return new SSLEngineResult(
Status.CLOSED, getHandshakeStatus(), 0, 0);
}
HandshakeContext hc = conContext.handshakeContext;
HandshakeStatus hsStatus = null;
if (!conContext.isNegotiated && !conContext.isBroken &&
!conContext.isInboundClosed() &&
!conContext.isOutboundClosed()) {
conContext.kickstart();
hsStatus = getHandshakeStatus();
if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
}
}
if (hsStatus == null) {
hsStatus = getHandshakeStatus();
}
/*
* If we have a task outstanding, this *MUST* be done before
* doing any more wrapping, because we could be in the middle
* of receiving a handshake message, for example, a finished
* message which would change the ciphers.
*/
if (hsStatus == HandshakeStatus.NEED_TASK) {
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
}
int dstsRemains = 0;
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
dstsRemains += dsts[i].remaining();
}
// Check destination buffer size.
//
// We can be smarter about using smaller buffer sizes later. For
// now, force it to be large enough to handle any valid record.
if (dstsRemains < conContext.conSession.getPacketBufferSize()) {
return new SSLEngineResult(
Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
}
int srcsRemains = 0;
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
srcsRemains += srcs[i].remaining();
}
Ciphertext ciphertext = null;
try {
// Acquire the buffered to-be-delivered records or retransmissions.
//
// May have buffered records.
if (!conContext.outputRecord.isEmpty()) {
ciphertext = encode(null, 0, 0,
dsts, dstsOffset, dstsLength);
}
if (ciphertext == null && srcsRemains != 0) {
ciphertext = encode(srcs, srcsOffset, srcsLength,
dsts, dstsOffset, dstsLength);
}
} catch (IOException ioe) {
if (ioe instanceof SSLException) {
throw ioe;
} else {
throw new SSLException("Write problems", ioe);
}
}
/*
* Check for status.
*/
Status status = (isOutboundDone() ? Status.CLOSED : Status.OK);
if (ciphertext != null && ciphertext.handshakeStatus != null) {
hsStatus = ciphertext.handshakeStatus;
} else {
hsStatus = getHandshakeStatus();
if (ciphertext == null && !conContext.isNegotiated &&
conContext.isInboundClosed() &&
hsStatus == HandshakeStatus.NEED_WRAP) {
// Even the outboud is open, no futher data could be wrapped as:
// 1. the outbound is empty
// 2. no negotiated connection
// 3. the inbound has closed, cannot complete the handshake
//
// Mark the engine as closed if the handshake status is
// NEED_WRAP. Otherwise, it could lead to dead loops in
// applications.
status = Status.CLOSED;
}
}
int deltaSrcs = srcsRemains;
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
deltaSrcs -= srcs[i].remaining();
}
int deltaDsts = dstsRemains;
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
deltaDsts -= dsts[i].remaining();
}
return new SSLEngineResult(status, hsStatus, deltaSrcs, deltaDsts);
}
private Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
Ciphertext ciphertext = null;
try {
ciphertext = conContext.outputRecord.encode(
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
} catch (SSLHandshakeException she) {
// may be record sequence number overflow
throw conContext.fatal(Alert.HANDSHAKE_FAILURE, she);
} catch (IOException e) {
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
}
if (ciphertext == null) {
return null;
}
// Is the handshake completed?
HandshakeStatus hsStatus =
tryToFinishHandshake(ciphertext.contentType);
if (hsStatus == null) {
hsStatus = conContext.getHandshakeStatus();
}
// Is the sequence number is nearly overflow?
if (conContext.outputRecord.seqNumIsHuge() ||
conContext.outputRecord.writeCipher.atKeyLimit()) {
hsStatus = tryKeyUpdate(hsStatus);
}
// update context status
ciphertext.handshakeStatus = hsStatus;
return ciphertext;
}
private HandshakeStatus tryToFinishHandshake(byte contentType) {
HandshakeStatus hsStatus = null;
if ((contentType == ContentType.HANDSHAKE.id) &&
conContext.outputRecord.isEmpty()) {
if (conContext.handshakeContext == null) {
hsStatus = HandshakeStatus.FINISHED;
} else if (conContext.isPostHandshakeContext()) {
// unlikely, but just in case.
hsStatus = conContext.finishPostHandshake();
} else if (conContext.handshakeContext.handshakeFinished) {
hsStatus = conContext.finishHandshake();
}
} // Otherwise, the followed call to getHSStatus() will help.
return hsStatus;
}
/**
* Try key update for sequence number wrap or key usage limit.
*
* Note that in order to maintain the handshake status properly, we check
* the sequence number and key usage limit after the last record
* reading/writing process.
*
* As we request renegotiation or close the connection for wrapped sequence
* number when there is enough sequence number space left to handle a few
* more records, so the sequence number of the last record cannot be
* wrapped.
*/
private HandshakeStatus tryKeyUpdate(
HandshakeStatus currentHandshakeStatus) throws IOException {
// Don't bother to kickstart if handshaking is in progress, or if
// the write side of the connection is not open. We allow a half-
// duplex write-only connection for key updates.
if ((conContext.handshakeContext == null) &&
!conContext.isOutboundClosed() &&
!conContext.isBroken) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("trigger key update");
}
beginHandshake();
return conContext.getHandshakeStatus();
}
return currentHandshakeStatus;
}
private static void checkParams(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
if ((srcs == null) || (dsts == null)) {
throw new IllegalArgumentException(
"source or destination buffer is null");
}
if ((dstsOffset < 0) || (dstsLength < 0) ||
(dstsOffset > dsts.length - dstsLength)) {
throw new IndexOutOfBoundsException(
"index out of bound of the destination buffers");
}
if ((srcsOffset < 0) || (srcsLength < 0) ||
(srcsOffset > srcs.length - srcsLength)) {
throw new IndexOutOfBoundsException(
"index out of bound of the source buffers");
}
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
if (dsts[i] == null) {
throw new IllegalArgumentException(
"destination buffer[" + i + "] == null");
}
/*
* Make sure the destination bufffers are writable.
*/
if (dsts[i].isReadOnly()) {
throw new ReadOnlyBufferException();
}
}
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
if (srcs[i] == null) {
throw new IllegalArgumentException(
"source buffer[" + i + "] == null");
}
}
}
@Override
public synchronized SSLEngineResult unwrap(ByteBuffer src,
ByteBuffer[] dsts, int offset, int length) throws SSLException {
return unwrap(
new ByteBuffer[]{src}, 0, 1, dsts, offset, length);
}
// @Override
public synchronized SSLEngineResult unwrap(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
if (conContext.isUnsureMode) {
throw new IllegalStateException(
"Client/Server mode has not yet been set.");
}
// See if the handshaker needs to report back some SSLException.
checkTaskThrown();
// check parameters
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
try {
return readRecord(
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
} catch (SSLProtocolException spe) {
// may be an unexpected handshake message
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
spe.getMessage(), spe);
} catch (IOException ioe) {
/*
* Don't reset position so it looks like we didn't
* consume anything. We did consume something, and it
* got us into this situation, so report that much back.
* Our days of consuming are now over anyway.
*/
throw conContext.fatal(Alert.INTERNAL_ERROR,
"problem unwrapping net record", ioe);
} catch (Exception ex) { // including RuntimeException
throw conContext.fatal(Alert.INTERNAL_ERROR,
"Fail to unwrap network record", ex);
}
}
private SSLEngineResult readRecord(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
/*
* Check if we are closing/closed.
*/
if (isInboundDone()) {
return new SSLEngineResult(
Status.CLOSED, getHandshakeStatus(), 0, 0);
}
HandshakeStatus hsStatus = null;
if (!conContext.isNegotiated && !conContext.isBroken &&
!conContext.isInboundClosed() &&
!conContext.isOutboundClosed()) {
conContext.kickstart();
/*
* If there's still outbound data to flush, we
* can return without trying to unwrap anything.
*/
hsStatus = getHandshakeStatus();
if (hsStatus == HandshakeStatus.NEED_WRAP) {
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
}
}
if (hsStatus == null) {
hsStatus = getHandshakeStatus();
}
/*
* If we have a task outstanding, this *MUST* be done before
* doing any more unwrapping, because we could be in the middle
* of receiving a handshake message, for example, a finished
* message which would change the ciphers.
*/
if (hsStatus == HandshakeStatus.NEED_TASK) {
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
}
int srcsRemains = 0;
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
srcsRemains += srcs[i].remaining();
}
if (srcsRemains == 0) {
return new SSLEngineResult(
Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
}
/*
* Check the packet to make sure enough is here.
* This will also indirectly check for 0 len packets.
*/
int packetLen = conContext.inputRecord.bytesInCompletePacket(
srcs, srcsOffset, srcsLength);
// Is this packet bigger than SSL/TLS normally allows?
if (packetLen > conContext.conSession.getPacketBufferSize()) {
int largestRecordSize = SSLRecord.maxLargeRecordSize;
if (packetLen <= largestRecordSize) {
// Expand the expected maximum packet/application buffer
// sizes.
//
// Only apply to SSL/TLS protocols.
// Old behavior: shall we honor the System Property
// "jsse.SSLEngine.acceptLargeFragments" if it is "false"?
conContext.conSession.expandBufferSizes();
}
// check the packet again
largestRecordSize = conContext.conSession.getPacketBufferSize();
if (packetLen > largestRecordSize) {
throw new SSLProtocolException(
"Input record too big: max = " +
largestRecordSize + " len = " + packetLen);
}
}
/*
* Check for OVERFLOW.
*
* Delay enforcing the application buffer free space requirement
* until after the initial handshaking.
*/
int dstsRemains = 0;
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
dstsRemains += dsts[i].remaining();
}
if (conContext.isNegotiated) {
int FragLen =
conContext.inputRecord.estimateFragmentSize(packetLen);
if (FragLen > dstsRemains) {
return new SSLEngineResult(
Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
}
}
// check for UNDERFLOW.
if ((packetLen == -1) || (srcsRemains < packetLen)) {
return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
}
/*
* We're now ready to actually do the read.
*/
Plaintext plainText = null;
try {
plainText = decode(srcs, srcsOffset, srcsLength,
dsts, dstsOffset, dstsLength);
} catch (IOException ioe) {
if (ioe instanceof SSLException) {
throw ioe;
} else {
throw new SSLException("readRecord", ioe);
}
}
/*
* Check the various condition that we could be reporting.
*
* It's *possible* something might have happened between the
* above and now, but it was better to minimally lock "this"
* during the read process. We'll return the current
* status, which is more representative of the current state.
*
* status above should cover: FINISHED, NEED_TASK
*/
Status status = (isInboundDone() ? Status.CLOSED : Status.OK);
if (plainText.handshakeStatus != null) {
hsStatus = plainText.handshakeStatus;
} else {
hsStatus = getHandshakeStatus();
}
int deltaNet = srcsRemains;
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
deltaNet -= srcs[i].remaining();
}
int deltaApp = dstsRemains;
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
deltaApp -= dsts[i].remaining();
}
return new SSLEngineResult(status, hsStatus, deltaNet, deltaApp);
}
private Plaintext decode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
Plaintext pt = SSLTransport.decode(conContext,
srcs, srcsOffset, srcsLength,
dsts, dstsOffset, dstsLength);
// Is the handshake completed?
if (pt != Plaintext.PLAINTEXT_NULL) {
HandshakeStatus hsStatus = tryToFinishHandshake(pt.contentType);
if (hsStatus == null) {
pt.handshakeStatus = conContext.getHandshakeStatus();
} else {
pt.handshakeStatus = hsStatus;
}
// Is the sequence number is nearly overflow?
if (conContext.inputRecord.seqNumIsHuge() ||
conContext.inputRecord.readCipher.atKeyLimit()) {
pt.handshakeStatus =
tryKeyUpdate(pt.handshakeStatus);
}
}
return pt;
}
@Override
public synchronized Runnable getDelegatedTask() {
if (conContext.handshakeContext != null && // PRE or POST handshake
!conContext.handshakeContext.taskDelegated &&
!conContext.handshakeContext.delegatedActions.isEmpty()) {
conContext.handshakeContext.taskDelegated = true;
return new DelegatedTask(this);
}
return null;
}
@Override
public synchronized void closeInbound() throws SSLException {
if (isInboundDone()) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("Closing inbound of SSLEngine");
}
// Is it ready to close inbound?
//
// No need to throw exception if the initial handshake is not started.
if (!conContext.isInputCloseNotified &&
(conContext.isNegotiated || conContext.handshakeContext != null)) {
throw conContext.fatal(Alert.INTERNAL_ERROR,
"closing inbound before receiving peer's close_notify");
}
conContext.closeInbound();
}
@Override
public synchronized boolean isInboundDone() {
return conContext.isInboundClosed();
}
@Override
public synchronized void closeOutbound() {
if (conContext.isOutboundClosed()) {
return;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("Closing outbound of SSLEngine");
}
conContext.closeOutbound();
}
@Override
public synchronized boolean isOutboundDone() {
return conContext.isOutboundDone();
}
@Override
public String[] getSupportedCipherSuites() {
return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
}
@Override
public synchronized String[] getEnabledCipherSuites() {
return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites);
}
@Override
public synchronized void setEnabledCipherSuites(String[] suites) {
conContext.sslConfig.enabledCipherSuites =
CipherSuite.validValuesOf(suites);
}
@Override
public String[] getSupportedProtocols() {
return ProtocolVersion.toStringArray(
sslContext.getSupportedProtocolVersions());
}
@Override
public synchronized String[] getEnabledProtocols() {
return ProtocolVersion.toStringArray(
conContext.sslConfig.enabledProtocols);
}
@Override
public synchronized void setEnabledProtocols(String[] protocols) {
if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
}
conContext.sslConfig.enabledProtocols =
ProtocolVersion.namesOf(protocols);
}
@Override
public synchronized SSLSession getSession() {
return conContext.conSession;
}
@Override
public synchronized SSLSession getHandshakeSession() {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.handshakeSession;
}
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
return conContext.getHandshakeStatus();
}
@Override
public synchronized void setUseClientMode(boolean mode) {
conContext.setUseClientMode(mode);
}
@Override
public synchronized boolean getUseClientMode() {
return conContext.sslConfig.isClientMode;
}
@Override
public synchronized void setNeedClientAuth(boolean need) {
conContext.sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getNeedClientAuth() {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED);
}
@Override
public synchronized void setWantClientAuth(boolean want) {
conContext.sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getWantClientAuth() {
return (conContext.sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@Override
public synchronized void setEnableSessionCreation(boolean flag) {
conContext.sslConfig.enableSessionCreation = flag;
}
@Override
public synchronized boolean getEnableSessionCreation() {
return conContext.sslConfig.enableSessionCreation;
}
@Override
public synchronized SSLParameters getSSLParameters() {
return conContext.sslConfig.getSSLParameters();
}
@Override
public synchronized void setSSLParameters(SSLParameters params) {
conContext.sslConfig.setSSLParameters(params);
if (conContext.sslConfig.maximumPacketSize != 0) {
conContext.outputRecord.changePacketSize(
conContext.sslConfig.maximumPacketSize);
}
}
@Override
public synchronized String getApplicationProtocol() {
return conContext.applicationProtocol;
}
@Override
public synchronized String getHandshakeApplicationProtocol() {
return conContext.handshakeContext == null ?
null : conContext.handshakeContext.applicationProtocol;
}
@Override
public synchronized void setHandshakeApplicationProtocolSelector(
BiFunction<SSLEngine, List<String>, String> selector) {
conContext.sslConfig.engineAPSelector = selector;
}
@Override
public synchronized BiFunction<SSLEngine, List<String>, String>
getHandshakeApplicationProtocolSelector() {
return conContext.sslConfig.engineAPSelector;
}
@Override
public boolean useDelegatedTask() {
return true;
}
/*
* Depending on whether the error was just a warning and the
* handshaker wasn't closed, or fatal and the handshaker is now
* null, report back the Exception that happened in the delegated
* task(s).
*/
private synchronized void checkTaskThrown() throws SSLException {
Exception exc = null;
// First check the handshake context.
HandshakeContext hc = conContext.handshakeContext;
if ((hc != null) && (hc.delegatedThrown != null)) {
exc = hc.delegatedThrown;
hc.delegatedThrown = null;
}
/*
* hc.delegatedThrown and conContext.delegatedThrown are most likely
* the same, but it's possible we could have had a non-fatal
* exception and thus the new HandshakeContext is still valid
* (alert warning). If so, then we may have a secondary exception
* waiting to be reported from the TransportContext, so we will
* need to clear that on a successive call. Otherwise, clear it now.
*/
if (conContext.delegatedThrown != null) {
if (exc != null) {
// hc object comparison
if (conContext.delegatedThrown == exc) {
// clear if/only if both are the same
conContext.delegatedThrown = null;
} // otherwise report the hc delegatedThrown
} else {
// Nothing waiting in HandshakeContext, but one is in the
// TransportContext.
exc = conContext.delegatedThrown;
conContext.delegatedThrown = null;
}
}
// Anything to report?
if (exc == null) {
return;
}
// If it wasn't a RuntimeException/SSLException, need to wrap it.
if (exc instanceof SSLException) {
throw (SSLException)exc;
} else if (exc instanceof RuntimeException) {
throw (RuntimeException)exc;
} else {
throw getTaskThrown(exc);
}
}
private static SSLException getTaskThrown(Exception taskThrown) {
String msg = taskThrown.getMessage();
if (msg == null) {
msg = "Delegated task threw Exception or Error";
}
if (taskThrown instanceof RuntimeException) {
throw new RuntimeException(msg, taskThrown);
} else if (taskThrown instanceof SSLHandshakeException) {
return (SSLHandshakeException)
new SSLHandshakeException(msg).initCause(taskThrown);
} else if (taskThrown instanceof SSLKeyException) {
return (SSLKeyException)
new SSLKeyException(msg).initCause(taskThrown);
} else if (taskThrown instanceof SSLPeerUnverifiedException) {
return (SSLPeerUnverifiedException)
new SSLPeerUnverifiedException(msg).initCause(taskThrown);
} else if (taskThrown instanceof SSLProtocolException) {
return (SSLProtocolException)
new SSLProtocolException(msg).initCause(taskThrown);
} else if (taskThrown instanceof SSLException) {
return (SSLException)taskThrown;
} else {
return new SSLException(msg, taskThrown);
}
}
/**
* Implement a simple task delegator.
*/
private static class DelegatedTask implements Runnable {
private final SSLEngineImpl engine;
DelegatedTask(SSLEngineImpl engineInstance) {
this.engine = engineInstance;
}
@Override
public void run() {
synchronized (engine) {
HandshakeContext hc = engine.conContext.handshakeContext;
if (hc == null || hc.delegatedActions.isEmpty()) {
return;
}
try {
AccessController.doPrivileged(
new DelegatedAction(hc), engine.conContext.acc);
} catch (PrivilegedActionException pae) {
// Get the handshake context again in case the
// handshaking has completed.
Exception reportedException = pae.getException();
// Report to both the TransportContext...
if (engine.conContext.delegatedThrown == null) {
engine.conContext.delegatedThrown = reportedException;
}
// ...and the HandshakeContext in case condition
// wasn't fatal and the handshakeContext is still
// around.
hc = engine.conContext.handshakeContext;
if (hc != null) {
hc.delegatedThrown = reportedException;
} else if (engine.conContext.closeReason != null) {
// Update the reason in case there was a previous.
engine.conContext.closeReason =
getTaskThrown(reportedException);
}
} catch (RuntimeException rte) {
// Get the handshake context again in case the
// handshaking has completed.
// Report to both the TransportContext...
if (engine.conContext.delegatedThrown == null) {
engine.conContext.delegatedThrown = rte;
}
// ...and the HandshakeContext in case condition
// wasn't fatal and the handshakeContext is still
// around.
hc = engine.conContext.handshakeContext;
if (hc != null) {
hc.delegatedThrown = rte;
} else if (engine.conContext.closeReason != null) {
// Update the reason in case there was a previous.
engine.conContext.closeReason = rte;
}
}
// Get the handshake context again in case the
// handshaking has completed.
hc = engine.conContext.handshakeContext;
if (hc != null) {
hc.taskDelegated = false;
}
}
}
private static class DelegatedAction
implements PrivilegedExceptionAction<Void> {
final HandshakeContext context;
DelegatedAction(HandshakeContext context) {
this.context = context;
}
@Override
public Void run() throws Exception {
while (!context.delegatedActions.isEmpty()) {
Map.Entry<Byte, ByteBuffer> me =
context.delegatedActions.poll();
if (me != null) {
context.dispatch(me.getKey(), me.getValue());
}
}
return null;
}
}
}
}

View File

@@ -0,0 +1,427 @@
/*
* Copyright (c) 1996, 2020, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import javax.crypto.BadPaddingException;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
import sun.security.ssl.SSLCipher.SSLReadCipher;
/**
* {@code InputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
private boolean formatVerified = false; // SSLv2 ruled out?
// Cache for incomplete handshake messages.
private ByteBuffer handshakeBuffer = null;
SSLEngineInputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
}
@Override
int estimateFragmentSize(int packetSize) {
if (packetSize > 0) {
return readCipher.estimateFragmentSize(packetSize, headerSize);
} else {
return Record.maxDataSize;
}
}
@Override
int bytesInCompletePacket(
ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
return bytesInCompletePacket(srcs[srcsOffset]);
}
private int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
/*
* SSLv2 length field is in bytes 0/1
* SSLv3/TLS length field is in bytes 3/4
*/
if (packet.remaining() < 5) {
return -1;
}
int pos = packet.position();
byte byteZero = packet.get(pos);
int len = 0;
/*
* If we have already verified previous packets, we can
* ignore the verifications steps, and jump right to the
* determination. Otherwise, try one last heuristic to
* see if it's SSL/TLS.
*/
if (formatVerified ||
(byteZero == ContentType.HANDSHAKE.id) ||
(byteZero == ContentType.ALERT.id)) {
/*
* Last sanity check that it's not a wild record
*/
byte majorVersion = packet.get(pos + 1);
byte minorVersion = packet.get(pos + 2);
if (!ProtocolVersion.isNegotiable(
majorVersion, minorVersion, false)) {
throw new SSLException("Unrecognized record version " +
ProtocolVersion.nameOf(majorVersion, minorVersion) +
" , plaintext connection?");
}
/*
* Reasonably sure this is a V3, disable further checks.
* We can't do the same in the v2 check below, because
* read still needs to parse/handle the v2 clientHello.
*/
formatVerified = true;
/*
* One of the SSLv3/TLS message types.
*/
len = ((packet.get(pos + 3) & 0xFF) << 8) +
(packet.get(pos + 4) & 0xFF) + headerSize;
} else {
/*
* Must be SSLv2 or something unknown.
* Check if it's short (2 bytes) or
* long (3) header.
*
* Internals can warn about unsupported SSLv2
*/
boolean isShort = ((byteZero & 0x80) != 0);
if (isShort &&
((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
byte majorVersion = packet.get(pos + 3);
byte minorVersion = packet.get(pos + 4);
if (!ProtocolVersion.isNegotiable(
majorVersion, minorVersion, false)) {
throw new SSLException("Unrecognized record version " +
ProtocolVersion.nameOf(majorVersion, minorVersion) +
" , plaintext connection?");
}
/*
* Client or Server Hello
*/
int mask = (isShort ? 0x7F : 0x3F);
len = ((byteZero & mask) << 8) +
(packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
@Override
Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
int srcsLength) throws IOException, BadPaddingException {
if (srcs == null || srcs.length == 0 || srcsLength == 0) {
return new Plaintext[0];
} else if (srcsLength == 1) {
return decode(srcs[srcsOffset]);
} else {
ByteBuffer packet = extract(srcs,
srcsOffset, srcsLength, SSLRecord.headerSize);
return decode(packet);
}
}
private Plaintext[] decode(ByteBuffer packet)
throws IOException, BadPaddingException {
if (isClosed) {
return null;
}
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw read", packet);
}
// The caller should have validated the record.
if (!formatVerified) {
formatVerified = true;
/*
* The first record must either be a handshake record or an
* alert message. If it's not, it is either invalid or an
* SSLv2 message.
*/
int pos = packet.position();
byte byteZero = packet.get(pos);
if (byteZero != ContentType.HANDSHAKE.id &&
byteZero != ContentType.ALERT.id) {
return handleUnknownRecord(packet);
}
}
return decodeInputRecord(packet);
}
private Plaintext[] decodeInputRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record, or more.
//
int srcPos = packet.position();
int srcLim = packet.limit();
byte contentType = packet.get(); // pos: 0
byte majorVersion = packet.get(); // pos: 1
byte minorVersion = packet.get(); // pos: 2
int contentLen = Record.getInt16(packet); // pos: 3, 4
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"READ: " +
ProtocolVersion.nameOf(majorVersion, minorVersion) +
" " + ContentType.nameOf(contentType) + ", length = " +
contentLen);
}
//
// Check for upper bound.
//
// Note: May check packetSize limit in the future.
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
throw new SSLProtocolException(
"Bad input record size, TLSCiphertext.length = " + contentLen);
}
//
// Decrypt the fragment
//
int recLim = srcPos + SSLRecord.headerSize + contentLen;
packet.limit(recLim);
packet.position(srcPos + SSLRecord.headerSize);
ByteBuffer fragment;
try {
Plaintext plaintext =
readCipher.decrypt(contentType, packet, null);
fragment = plaintext.fragment;
contentType = plaintext.contentType;
} catch (BadPaddingException bpe) {
throw bpe;
} catch (GeneralSecurityException gse) {
throw (SSLProtocolException)(new SSLProtocolException(
"Unexpected exception")).initCause(gse);
} finally {
// consume a complete record
packet.limit(srcLim);
packet.position(recLim);
}
//
// check for handshake fragment
//
if (contentType != ContentType.HANDSHAKE.id &&
handshakeBuffer != null && handshakeBuffer.hasRemaining()) {
throw new SSLProtocolException(
"Expecting a handshake fragment, but received " +
ContentType.nameOf(contentType));
}
//
// parse handshake messages
//
if (contentType == ContentType.HANDSHAKE.id) {
ByteBuffer handshakeFrag = fragment;
if ((handshakeBuffer != null) &&
(handshakeBuffer.remaining() != 0)) {
ByteBuffer bb = ByteBuffer.wrap(new byte[
handshakeBuffer.remaining() + fragment.remaining()]);
bb.put(handshakeBuffer);
bb.put(fragment);
handshakeFrag = (ByteBuffer)bb.rewind();
handshakeBuffer = null;
}
ArrayList<Plaintext> plaintexts = new ArrayList<>(5);
while (handshakeFrag.hasRemaining()) {
int remaining = handshakeFrag.remaining();
if (remaining < handshakeHeaderSize) {
handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
handshakeBuffer.put(handshakeFrag);
handshakeBuffer.rewind();
break;
}
handshakeFrag.mark();
// Fail fast for unknown handshake message.
byte handshakeType = handshakeFrag.get();
if (!SSLHandshake.isKnown(handshakeType)) {
throw new SSLProtocolException(
"Unknown handshake type size, Handshake.msg_type = " +
(handshakeType & 0xFF));
}
int handshakeBodyLen = Record.getInt24(handshakeFrag);
if (handshakeBodyLen > SSLConfiguration.maxHandshakeMessageSize) {
throw new SSLProtocolException(
"The size of the handshake message ("
+ handshakeBodyLen
+ ") exceeds the maximum allowed size ("
+ SSLConfiguration.maxHandshakeMessageSize
+ ")");
}
handshakeFrag.reset();
int handshakeMessageLen =
handshakeHeaderSize + handshakeBodyLen;
if (remaining < handshakeMessageLen) {
handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
handshakeBuffer.put(handshakeFrag);
handshakeBuffer.rewind();
break;
} else if (remaining == handshakeMessageLen) {
if (handshakeHash.isHashable(handshakeType)) {
handshakeHash.receive(handshakeFrag);
}
plaintexts.add(
new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, handshakeFrag)
);
break;
} else {
int fragPos = handshakeFrag.position();
int fragLim = handshakeFrag.limit();
int nextPos = fragPos + handshakeMessageLen;
handshakeFrag.limit(nextPos);
if (handshakeHash.isHashable(handshakeType)) {
handshakeHash.receive(handshakeFrag);
}
plaintexts.add(
new Plaintext(contentType, majorVersion, minorVersion,
-1, -1L, handshakeFrag.slice())
);
handshakeFrag.position(nextPos);
handshakeFrag.limit(fragLim);
}
}
return plaintexts.toArray(new Plaintext[0]);
}
return new Plaintext[] {
new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, fragment)
};
}
private Plaintext[] handleUnknownRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record.
//
int srcPos = packet.position();
int srcLim = packet.limit();
byte firstByte = packet.get(srcPos);
byte thirdByte = packet.get(srcPos + 2);
// Does it look like a Version 2 client hello (V2ClientHello)?
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
/*
* If SSLv2Hello is not enabled, throw an exception.
*/
if (helloVersion != ProtocolVersion.SSL20Hello) {
throw new SSLHandshakeException("SSLv2Hello is not enabled");
}
byte majorVersion = packet.get(srcPos + 3);
byte minorVersion = packet.get(srcPos + 4);
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
/*
* Looks like a V2 client hello, but not one saying
* "let's talk SSLv3". So we need to send an SSLv2
* error message, one that's treated as fatal by
* clients (Otherwise we'll hang.)
*/
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"Requested to negotiate unsupported SSLv2!");
}
// hack code, the exception is caught in SSLEngineImpl
// so that SSLv2 error message can be delivered properly.
throw new UnsupportedOperationException( // SSLv2Hello
"Unsupported SSL v2.0 ClientHello");
}
/*
* If we can map this into a V3 ClientHello, read and
* hash the rest of the V2 handshake, turn it into a
* V3 ClientHello message, and pass it up.
*/
packet.position(srcPos + 2); // exclude the header
handshakeHash.receive(packet);
packet.position(srcPos);
ByteBuffer converted = convertToClientHello(packet);
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine(
"[Converted] ClientHello", converted);
}
return new Plaintext[] {
new Plaintext(ContentType.HANDSHAKE.id,
majorVersion, minorVersion, -1, -1L, converted)
};
} else {
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
throw new SSLException("SSL V2.0 servers are not supported.");
}
throw new SSLException("Unsupported or unrecognized SSL message");
}
}
}

View File

@@ -0,0 +1,587 @@
/*
* Copyright (c) 1996, 2018, 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.ByteBuffer;
import java.util.LinkedList;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
/**
* {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
private HandshakeFragment fragmenter = null;
private boolean isTalkingToV2 = false; // SSLv2Hello
private ByteBuffer v2ClientHello = null; // SSLv2Hello
private volatile boolean isCloseWaiting = false;
SSLEngineOutputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher());
this.packetSize = SSLRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.NONE;
}
@Override
public synchronized void close() throws IOException {
if (!isClosed) {
if (fragmenter != null && fragmenter.hasAlert()) {
isCloseWaiting = true;
} else {
super.close();
}
}
}
boolean isClosed() {
return isClosed || isCloseWaiting;
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"alert message: " + Alert.nameOf(description));
}
return;
}
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
fragmenter.queueUpAlert(level, description);
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"handshake message",
ByteBuffer.wrap(source, offset, length));
}
return;
}
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
if (firstMessage) {
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
// Double space should be big enough for the converted message.
v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4));
v2ClientHello.position(2); // exclude the header
handshakeHash.deliver(v2ClientHello);
v2ClientHello.position(0);
return;
}
}
byte handshakeType = source[offset];
if (handshakeHash.isHashable(handshakeType)) {
handshakeHash.deliver(source, offset, length);
}
fragmenter.queueUpFragment(source, offset, length);
}
@Override
void encodeChangeCipherSpec() throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"change_cipher_spec message");
}
return;
}
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
fragmenter.queueUpChangeCipherSpec();
}
@Override
void encodeV2NoCipher() throws IOException {
isTalkingToV2 = true;
}
@Override
Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
if (isClosed) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"application data or cached messages");
}
return null;
} else if (isCloseWaiting) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"application data");
}
srcs = null; // use no application data.
}
return encode(srcs, srcsOffset, srcsLength, dsts[0]);
}
private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
if (writeCipher.authenticator.seqNumOverflow()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine(
"sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
// Don't process the incoming record until all of the
// buffered records get handled.
Ciphertext ct = acquireCiphertext(destination);
if (ct != null) {
return ct;
}
if (sources == null || sources.length == 0) {
return null;
}
int srcsRemains = 0;
for (int i = offset; i < offset + length; i++) {
srcsRemains += sources[i].remaining();
}
if (srcsRemains == 0) {
return null;
}
int dstLim = destination.limit();
boolean isFirstRecordOfThePayload = true;
int packetLeftSize = Math.min(maxRecordSize, packetSize);
boolean needMorePayload = true;
long recordSN = 0L;
while (needMorePayload) {
int fragLen;
if (isFirstRecordOfThePayload && needToSplitPayload()) {
needMorePayload = true;
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
needMorePayload = false;
if (packetLeftSize > 0) {
fragLen = writeCipher.calculateFragmentSize(
packetLeftSize, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
// Calculate more impact, for example TLS 1.3 padding.
fragLen = calculateFragmentSize(fragLen);
}
int dstPos = destination.position();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
int remains = Math.min(fragLen, destination.remaining());
fragLen = 0;
int srcsLen = offset + length;
for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
int amount = Math.min(sources[i].remaining(), remains);
int srcLimit = sources[i].limit();
sources[i].limit(sources[i].position() + amount);
destination.put(sources[i]);
sources[i].limit(srcLimit); // restore the limit
remains -= amount;
fragLen += amount;
if (remains > 0) {
offset++;
length--;
}
}
destination.limit(destination.position());
destination.position(dstContent);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion + " " +
ContentType.APPLICATION_DATA.name +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
recordSN = encrypt(writeCipher,
ContentType.APPLICATION_DATA.id, destination,
dstPos, dstLim, headerSize,
protocolVersion);
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
SSLLogger.fine("Raw write", temporary);
}
packetLeftSize -= destination.position() - dstPos;
// remain the limit unchanged
destination.limit(dstLim);
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
}
return new Ciphertext(ContentType.APPLICATION_DATA.id,
SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
private Ciphertext acquireCiphertext(
ByteBuffer destination) throws IOException {
if (isTalkingToV2) { // SSLv2Hello
// We don't support SSLv2. Send an SSLv2 error message
// so that the connection can be closed gracefully.
//
// Please don't change the limit of the destination buffer.
destination.put(SSLRecord.v2NoCipher);
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
}
isTalkingToV2 = false;
return new Ciphertext(ContentType.ALERT.id,
SSLHandshake.NOT_APPLICABLE.id, -1L);
}
if (v2ClientHello != null) {
// deliver the SSLv2 format ClientHello message
//
// Please don't change the limit of the destination buffer.
if (SSLLogger.isOn) {
if (SSLLogger.isOn("record")) {
SSLLogger.fine(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + v2ClientHello.remaining());
}
if (SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw write", v2ClientHello);
}
}
destination.put(v2ClientHello);
v2ClientHello = null;
return new Ciphertext(ContentType.HANDSHAKE.id,
SSLHandshake.CLIENT_HELLO.id, -1L);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
return null;
}
@Override
boolean isEmpty() {
return (!isTalkingToV2) && (v2ClientHello == null) &&
((fragmenter == null) || fragmenter.isEmpty());
}
// buffered record fragment
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
SSLWriteCipher encodeCipher;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
byte handshakeType;
int acquireOffset;
}
final class HandshakeFragment {
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
void queueUpFragment(byte[] source,
int offset, int length) throws IOException {
HandshakeMemo memo = new HandshakeMemo();
memo.contentType = ContentType.HANDSHAKE.id;
memo.majorVersion = protocolVersion.major; // kick start version?
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.handshakeType = source[offset];
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
// 1: HandshakeType
// 3: message length
System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
handshakeMemos.add(memo);
}
void queueUpChangeCipherSpec() {
RecordMemo memo = new RecordMemo();
memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
}
void queueUpAlert(byte level, byte description) {
RecordMemo memo = new RecordMemo();
memo.contentType = ContentType.ALERT.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
handshakeMemos.add(memo);
}
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
if (isEmpty()) {
return null;
}
RecordMemo memo = handshakeMemos.getFirst();
HandshakeMemo hsMemo = null;
if (memo.contentType == ContentType.HANDSHAKE.id) {
hsMemo = (HandshakeMemo)memo;
}
// ChangeCipherSpec message is pretty small. Don't worry about
// the fragmentation of ChangeCipherSpec record.
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = memo.encodeCipher.calculateFragmentSize(
fragLen, headerSize);
} else {
fragLen = Record.maxDataSize;
}
// Calculate more impact, for example TLS 1.3 padding.
fragLen = calculateFragmentSize(fragLen);
int dstPos = dstBuf.position();
int dstLim = dstBuf.limit();
int dstContent = dstPos + headerSize +
memo.encodeCipher.getExplicitNonceSize();
dstBuf.position(dstContent);
if (hsMemo != null) {
int remainingFragLen = fragLen;
while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
int memoFragLen = hsMemo.fragment.length;
if (hsMemo.acquireOffset == 0) {
// Don't fragment handshake message header
if (remainingFragLen <= 4) {
break;
}
dstBuf.put(hsMemo.handshakeType);
dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
dstBuf.put((byte)(memoFragLen & 0xFF));
remainingFragLen -= 4;
} // Otherwise, handshake message is fragmented.
int chipLen = Math.min(remainingFragLen,
(memoFragLen - hsMemo.acquireOffset));
dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
hsMemo.acquireOffset += chipLen;
if (hsMemo.acquireOffset == memoFragLen) {
handshakeMemos.removeFirst();
// still have space for more records?
if ((remainingFragLen > chipLen) &&
!handshakeMemos.isEmpty()) {
// look for the next buffered record fragment
RecordMemo rm = handshakeMemos.getFirst();
if (rm.contentType == ContentType.HANDSHAKE.id &&
rm.encodeCipher == hsMemo.encodeCipher) {
hsMemo = (HandshakeMemo)rm;
} else {
// not of the flight, break the loop
break;
}
}
}
remainingFragLen -= chipLen;
}
} else {
fragLen = Math.min(fragLen, memo.fragment.length);
dstBuf.put(memo.fragment, 0, fragLen);
handshakeMemos.removeFirst();
}
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion + " " +
ContentType.nameOf(memo.contentType) +
", length = " + dstBuf.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(
memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion));
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
dstBuf.limit(dstLim);
// Reset the fragmentation offset.
if (hsMemo != null) {
return new Ciphertext(hsMemo.contentType,
hsMemo.handshakeType, recordSN);
} else {
if (isCloseWaiting &&
memo.contentType == ContentType.ALERT.id) {
close();
}
return new Ciphertext(memo.contentType,
SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
}
boolean isEmpty() {
return handshakeMemos.isEmpty();
}
boolean hasAlert() {
for (RecordMemo memo : handshakeMemos) {
if (memo.contentType == ContentType.ALERT.id) {
return true;
}
}
return false;
}
}
/*
* Need to split the payload except the following cases:
*
* 1. protocol version is TLS 1.1 or later;
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
* 3. the payload is the first application record of a freshly
* negotiated TLS session.
* 4. the CBC protection is disabled;
*
* By default, we counter chosen plaintext issues on CBC mode
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
* data in the first record of every payload, and the rest in
* subsequent record(s). Note that the issues have been solved in
* TLS 1.1 or later.
*
* It is not necessary to split the very first application record of
* a freshly negotiated TLS session, as there is no previous
* application data to guess. To improve compatibility, we will not
* split such records.
*
* This avoids issues in the outbound direction. For a full fix,
* the peer must have similar protections.
*/
boolean needToSplitPayload() {
return (!protocolVersion.useTLS11PlusSpec()) &&
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
Record.enableCBCProtection;
}
}

View File

@@ -0,0 +1,769 @@
/*
* Copyright (c) 2018, 2020, 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Locale;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.misc.HexDumpEncoder;
enum SSLExtension implements SSLStringizer {
// Extensions defined in RFC 6066
CH_SERVER_NAME (0x0000, "server_name",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
ServerNameExtension.chNetworkProducer,
ServerNameExtension.chOnLoadConsumer,
null,
null,
null,
ServerNameExtension.chStringizer),
SH_SERVER_NAME (0x0000, "server_name",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
ServerNameExtension.shNetworkProducer,
ServerNameExtension.shOnLoadConsumer,
null,
null,
null,
ServerNameExtension.shStringizer),
EE_SERVER_NAME (0x0000, "server_name",
SSLHandshake.ENCRYPTED_EXTENSIONS,
ProtocolVersion.PROTOCOLS_OF_13,
ServerNameExtension.eeNetworkProducer,
ServerNameExtension.eeOnLoadConsumer,
null,
null,
null,
ServerNameExtension.shStringizer),
CH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
MaxFragExtension.chNetworkProducer,
MaxFragExtension.chOnLoadConsumer,
null,
null,
null,
MaxFragExtension.maxFragLenStringizer),
SH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
MaxFragExtension.shNetworkProducer,
MaxFragExtension.shOnLoadConsumer,
null,
MaxFragExtension.shOnTradeConsumer,
null,
MaxFragExtension.maxFragLenStringizer),
EE_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
SSLHandshake.ENCRYPTED_EXTENSIONS,
ProtocolVersion.PROTOCOLS_OF_13,
MaxFragExtension.eeNetworkProducer,
MaxFragExtension.eeOnLoadConsumer,
null,
MaxFragExtension.eeOnTradeConsumer,
null,
MaxFragExtension.maxFragLenStringizer),
CLIENT_CERTIFICATE_URL (0x0002, "client_certificate_url"),
TRUSTED_CA_KEYS (0x0003, "trusted_ca_keys"),
TRUNCATED_HMAC (0x0004, "truncated_hmac"),
CH_STATUS_REQUEST (0x0005, "status_request",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
CertStatusExtension.chNetworkProducer,
CertStatusExtension.chOnLoadConsumer,
null,
null,
null,
CertStatusExtension.certStatusReqStringizer),
SH_STATUS_REQUEST (0x0005, "status_request",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
CertStatusExtension.shNetworkProducer,
CertStatusExtension.shOnLoadConsumer,
null,
null,
null,
CertStatusExtension.certStatusReqStringizer),
CR_STATUS_REQUEST (0x0005, "status_request"),
CT_STATUS_REQUEST (0x0005, "status_request",
SSLHandshake.CERTIFICATE,
ProtocolVersion.PROTOCOLS_OF_13,
CertStatusExtension.ctNetworkProducer,
CertStatusExtension.ctOnLoadConsumer,
null,
null,
null,
CertStatusExtension.certStatusRespStringizer),
// extensions defined in RFC 4681
USER_MAPPING (0x0006, "user_mapping"),
// extensions defined in RFC 5878
CLIENT_AUTHZ (0x0007, "client_authz"),
SERVER_AUTHZ (0x0008, "server_authz"),
// extensions defined in RFC 5081
CERT_TYPE (0x0009, "cert_type"),
// extensions defined in RFC 4492 (ECC)
CH_SUPPORTED_GROUPS (0x000A, "supported_groups",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
SupportedGroupsExtension.chNetworkProducer,
SupportedGroupsExtension.chOnLoadConsumer,
null,
null,
null,
SupportedGroupsExtension.sgsStringizer),
EE_SUPPORTED_GROUPS (0x000A, "supported_groups",
SSLHandshake.ENCRYPTED_EXTENSIONS,
ProtocolVersion.PROTOCOLS_OF_13,
SupportedGroupsExtension.eeNetworkProducer,
SupportedGroupsExtension.eeOnLoadConsumer,
null,
null,
null,
SupportedGroupsExtension.sgsStringizer),
CH_EC_POINT_FORMATS (0x000B, "ec_point_formats",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
ECPointFormatsExtension.chNetworkProducer,
ECPointFormatsExtension.chOnLoadConsumer,
null,
null,
null,
ECPointFormatsExtension.epfStringizer),
SH_EC_POINT_FORMATS (0x000B, "ec_point_formats",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
null, // not use of the producer
ECPointFormatsExtension.shOnLoadConsumer,
null,
null,
null,
ECPointFormatsExtension.epfStringizer),
// extensions defined in RFC 5054
SRP (0x000C, "srp"),
// extensions defined in RFC 5246
CH_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_12_13,
SignatureAlgorithmsExtension.chNetworkProducer,
SignatureAlgorithmsExtension.chOnLoadConsumer,
SignatureAlgorithmsExtension.chOnLoadAbsence,
SignatureAlgorithmsExtension.chOnTradeConsumer,
SignatureAlgorithmsExtension.chOnTradeAbsence,
SignatureAlgorithmsExtension.ssStringizer),
CR_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
SSLHandshake.CERTIFICATE_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
SignatureAlgorithmsExtension.crNetworkProducer,
SignatureAlgorithmsExtension.crOnLoadConsumer,
SignatureAlgorithmsExtension.crOnLoadAbsence,
SignatureAlgorithmsExtension.crOnTradeConsumer,
null,
SignatureAlgorithmsExtension.ssStringizer),
CH_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_12_13,
CertSignAlgsExtension.chNetworkProducer,
CertSignAlgsExtension.chOnLoadConsumer,
null,
CertSignAlgsExtension.chOnTradeConsumer,
null,
CertSignAlgsExtension.ssStringizer),
CR_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
SSLHandshake.CERTIFICATE_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
CertSignAlgsExtension.crNetworkProducer,
CertSignAlgsExtension.crOnLoadConsumer,
null,
CertSignAlgsExtension.crOnTradeConsumer,
null,
CertSignAlgsExtension.ssStringizer),
// extensions defined in RFC 5764
USE_SRTP (0x000E, "use_srtp"),
// extensions defined in RFC 6520
HEARTBEAT (0x000E, "heartbeat"),
// extension defined in RFC 7301 (ALPN)
CH_ALPN (0x0010, "application_layer_protocol_negotiation",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
AlpnExtension.chNetworkProducer,
AlpnExtension.chOnLoadConsumer,
AlpnExtension.chOnLoadAbsence,
null,
null,
AlpnExtension.alpnStringizer),
SH_ALPN (0x0010, "application_layer_protocol_negotiation",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
AlpnExtension.shNetworkProducer,
AlpnExtension.shOnLoadConsumer,
AlpnExtension.shOnLoadAbsence,
null,
null,
AlpnExtension.alpnStringizer),
EE_ALPN (0x0010, "application_layer_protocol_negotiation",
SSLHandshake.ENCRYPTED_EXTENSIONS,
ProtocolVersion.PROTOCOLS_OF_13,
AlpnExtension.shNetworkProducer,
AlpnExtension.shOnLoadConsumer,
AlpnExtension.shOnLoadAbsence,
null,
null,
AlpnExtension.alpnStringizer),
// extensions defined in RFC 6961
CH_STATUS_REQUEST_V2 (0x0011, "status_request_v2",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
CertStatusExtension.chV2NetworkProducer,
CertStatusExtension.chV2OnLoadConsumer,
null,
null,
null,
CertStatusExtension.certStatusReqV2Stringizer),
SH_STATUS_REQUEST_V2 (0x0011, "status_request_v2",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
CertStatusExtension.shV2NetworkProducer,
CertStatusExtension.shV2OnLoadConsumer,
null,
null,
null,
CertStatusExtension.certStatusReqV2Stringizer),
// extensions defined in RFC 6962
SIGNED_CERT_TIMESTAMP (0x0012, "signed_certificate_timestamp"),
// extensions defined in RFC 7250
CLIENT_CERT_TYPE (0x0013, "padding"),
SERVER_CERT_TYPE (0x0014, "server_certificate_type"),
// extensions defined in RFC 7685
PADDING (0x0015, "client_certificate_type"),
// extensions defined in RFC 7366
ENCRYPT_THEN_MAC (0x0016, "encrypt_then_mac"),
// extensions defined in RFC 7627
CH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
ExtendedMasterSecretExtension.chNetworkProducer,
ExtendedMasterSecretExtension.chOnLoadConsumer,
ExtendedMasterSecretExtension.chOnLoadAbsence,
null,
null,
ExtendedMasterSecretExtension.emsStringizer),
SH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
ExtendedMasterSecretExtension.shNetworkProducer,
ExtendedMasterSecretExtension.shOnLoadConsumer,
ExtendedMasterSecretExtension.shOnLoadAbsence,
null,
null,
ExtendedMasterSecretExtension.emsStringizer),
// extensions defined in RFC draft-ietf-tokbind-negotiation
TOKEN_BINDING (0x0018, "token_binding "),
// extensions defined in RFC 7924
CACHED_INFO (0x0019, "cached_info"),
// extensions defined in RFC 4507/5077
SESSION_TICKET (0x0023, "session_ticket"),
// extensions defined in TLS 1.3
CH_EARLY_DATA (0x002A, "early_data"),
EE_EARLY_DATA (0x002A, "early_data"),
NST_EARLY_DATA (0x002A, "early_data"),
CH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_13,
SupportedVersionsExtension.chNetworkProducer,
SupportedVersionsExtension.chOnLoadConsumer,
null,
null,
null,
SupportedVersionsExtension.chStringizer),
SH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
SSLHandshake.SERVER_HELLO,
// and HelloRetryRequest
ProtocolVersion.PROTOCOLS_OF_13,
SupportedVersionsExtension.shNetworkProducer,
SupportedVersionsExtension.shOnLoadConsumer,
null,
null,
null,
SupportedVersionsExtension.shStringizer),
HRR_SUPPORTED_VERSIONS (0x002B, "supported_versions",
SSLHandshake.HELLO_RETRY_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
SupportedVersionsExtension.hrrNetworkProducer,
SupportedVersionsExtension.hrrOnLoadConsumer,
null,
null,
null,
SupportedVersionsExtension.hrrStringizer),
MH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
SSLHandshake.MESSAGE_HASH,
ProtocolVersion.PROTOCOLS_OF_13,
SupportedVersionsExtension.hrrReproducer,
null, null, null,
null,
SupportedVersionsExtension.hrrStringizer),
CH_COOKIE (0x002C, "cookie",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
CookieExtension.chNetworkProducer,
CookieExtension.chOnLoadConsumer,
null,
CookieExtension.chOnTradeConsumer,
null,
CookieExtension.cookieStringizer),
HRR_COOKIE (0x002C, "cookie",
SSLHandshake.HELLO_RETRY_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
CookieExtension.hrrNetworkProducer,
CookieExtension.hrrOnLoadConsumer,
null, null,
null,
CookieExtension.cookieStringizer),
MH_COOKIE (0x002C, "cookie",
SSLHandshake.MESSAGE_HASH,
ProtocolVersion.PROTOCOLS_OF_13,
CookieExtension.hrrNetworkReproducer,
null, null, null,
null,
CookieExtension.cookieStringizer),
PSK_KEY_EXCHANGE_MODES (0x002D, "psk_key_exchange_modes",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
PskKeyExchangeModesExtension.chNetworkProducer,
PskKeyExchangeModesExtension.chOnLoadConsumer,
PskKeyExchangeModesExtension.chOnLoadAbsence,
null,
PskKeyExchangeModesExtension.chOnTradeAbsence,
PskKeyExchangeModesExtension.pkemStringizer),
CH_CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
CertificateAuthoritiesExtension.chNetworkProducer,
CertificateAuthoritiesExtension.chOnLoadConsumer,
null,
null,
null,
CertificateAuthoritiesExtension.ssStringizer),
CR_CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities",
SSLHandshake.CERTIFICATE_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
CertificateAuthoritiesExtension.crNetworkProducer,
CertificateAuthoritiesExtension.crOnLoadConsumer,
null,
null,
null,
CertificateAuthoritiesExtension.ssStringizer),
OID_FILTERS (0x0030, "oid_filters"),
POST_HANDSHAKE_AUTH (0x0030, "post_handshake_auth"),
CH_KEY_SHARE (0x0033, "key_share",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
KeyShareExtension.chNetworkProducer,
KeyShareExtension.chOnLoadConsumer,
null, null, null,
KeyShareExtension.chStringizer),
SH_KEY_SHARE (0x0033, "key_share",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
KeyShareExtension.shNetworkProducer,
KeyShareExtension.shOnLoadConsumer,
KeyShareExtension.shOnLoadAbsence,
null,
null,
KeyShareExtension.shStringizer),
HRR_KEY_SHARE (0x0033, "key_share",
SSLHandshake.HELLO_RETRY_REQUEST,
ProtocolVersion.PROTOCOLS_OF_13,
KeyShareExtension.hrrNetworkProducer,
KeyShareExtension.hrrOnLoadConsumer,
null, null, null,
KeyShareExtension.hrrStringizer),
MH_KEY_SHARE (0x0033, "key_share",
SSLHandshake.MESSAGE_HASH,
ProtocolVersion.PROTOCOLS_OF_13,
KeyShareExtension.hrrNetworkReproducer,
null, null, null, null,
KeyShareExtension.hrrStringizer),
// Extensions defined in RFC 5746
CH_RENEGOTIATION_INFO (0xff01, "renegotiation_info",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
RenegoInfoExtension.chNetworkProducer,
RenegoInfoExtension.chOnLoadConsumer,
RenegoInfoExtension.chOnLoadAbsence,
null,
null,
RenegoInfoExtension.rniStringizer),
SH_RENEGOTIATION_INFO (0xff01, "renegotiation_info",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_TO_12,
RenegoInfoExtension.shNetworkProducer,
RenegoInfoExtension.shOnLoadConsumer,
RenegoInfoExtension.shOnLoadAbsence,
null,
null,
RenegoInfoExtension.rniStringizer),
// TLS 1.3 PSK extension must be last
CH_PRE_SHARED_KEY (0x0029, "pre_shared_key",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
PreSharedKeyExtension.chNetworkProducer,
PreSharedKeyExtension.chOnLoadConsumer,
PreSharedKeyExtension.chOnLoadAbsence,
PreSharedKeyExtension.chOnTradeConsumer,
null,
PreSharedKeyExtension.chStringizer),
SH_PRE_SHARED_KEY (0x0029, "pre_shared_key",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_OF_13,
PreSharedKeyExtension.shNetworkProducer,
PreSharedKeyExtension.shOnLoadConsumer,
PreSharedKeyExtension.shOnLoadAbsence,
null, null,
PreSharedKeyExtension.shStringizer);
final int id;
final SSLHandshake handshakeType;
final String name;
final ProtocolVersion[] supportedProtocols;
final HandshakeProducer networkProducer;
final ExtensionConsumer onLoadConsumer;
final HandshakeAbsence onLoadAbsence;
final HandshakeConsumer onTradeConsumer;
final HandshakeAbsence onTradeAbsence;
final SSLStringizer stringizer;
// known but unsupported extension
private SSLExtension(int id, String name) {
this.id = id;
this.handshakeType = SSLHandshake.NOT_APPLICABLE;
this.name = name;
this.supportedProtocols = new ProtocolVersion[0];
this.networkProducer = null;
this.onLoadConsumer = null;
this.onLoadAbsence = null;
this.onTradeConsumer = null;
this.onTradeAbsence = null;
this.stringizer = null;
}
// supported extension
private SSLExtension(int id, String name, SSLHandshake handshakeType,
ProtocolVersion[] supportedProtocols,
HandshakeProducer producer,
ExtensionConsumer onLoadConsumer, HandshakeAbsence onLoadAbsence,
HandshakeConsumer onTradeConsumer, HandshakeAbsence onTradeAbsence,
SSLStringizer stringize) {
this.id = id;
this.handshakeType = handshakeType;
this.name = name;
this.supportedProtocols = supportedProtocols;
this.networkProducer = producer;
this.onLoadConsumer = onLoadConsumer;
this.onLoadAbsence = onLoadAbsence;
this.onTradeConsumer = onTradeConsumer;
this.onTradeAbsence = onTradeAbsence;
this.stringizer = stringize;
}
static SSLExtension valueOf(SSLHandshake handshakeType, int extensionType) {
for (SSLExtension ext : SSLExtension.values()) {
if (ext.id == extensionType &&
ext.handshakeType == handshakeType) {
return ext;
}
}
return null;
}
static String nameOf(int extensionType) {
for (SSLExtension ext : SSLExtension.values()) {
if (ext.id == extensionType) {
return ext.name;
}
}
return "unknown extension";
}
static boolean isConsumable(int extensionType) {
for (SSLExtension ext : SSLExtension.values()) {
if (ext.id == extensionType &&
ext.onLoadConsumer != null) {
return true;
}
}
return false;
}
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
if (networkProducer != null) {
return networkProducer.produce(context, message);
} else {
throw new UnsupportedOperationException(
"Not yet supported extension producing.");
}
}
public void consumeOnLoad(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException {
if (onLoadConsumer != null) {
onLoadConsumer.consume(context, message, buffer);
} else {
throw new UnsupportedOperationException(
"Not yet supported extension loading.");
}
}
public void consumeOnTrade(ConnectionContext context,
HandshakeMessage message) throws IOException {
if (onTradeConsumer != null) {
onTradeConsumer.consume(context, message);
} else {
throw new UnsupportedOperationException(
"Not yet supported extension processing.");
}
}
void absentOnLoad(ConnectionContext context,
HandshakeMessage message) throws IOException {
if (onLoadAbsence != null) {
onLoadAbsence.absent(context, message);
} else {
throw new UnsupportedOperationException(
"Not yet supported extension absence processing.");
}
}
void absentOnTrade(ConnectionContext context,
HandshakeMessage message) throws IOException {
if (onTradeAbsence != null) {
onTradeAbsence.absent(context, message);
} else {
throw new UnsupportedOperationException(
"Not yet supported extension absence processing.");
}
}
public boolean isAvailable(ProtocolVersion protocolVersion) {
for (int i = 0; i < supportedProtocols.length; i++) {
if (supportedProtocols[i] == protocolVersion) {
return true;
}
}
return false;
}
@Override
public String toString() {
return name;
}
@Override
public String toString(ByteBuffer byteBuffer) {
MessageFormat messageFormat = new MessageFormat(
"\"{0} ({1})\": '{'\n" +
"{2}\n" +
"'}'",
Locale.ENGLISH);
String extData;
if (stringizer == null) {
HexDumpEncoder hexEncoder = new HexDumpEncoder();
String encoded = hexEncoder.encode(byteBuffer.duplicate());
extData = encoded;
} else {
extData = stringizer.toString(byteBuffer);
}
Object[] messageFields = {
this.name,
this.id,
Utilities.indent(extData)
};
return messageFormat.format(messageFields);
}
//////////////////////////////////////////////////////
// Nested extension, consumer and producer interfaces.
static interface ExtensionConsumer {
void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer) throws IOException;
}
/**
* A (transparent) specification of extension data.
*
* This interface contains no methods or constants. Its only purpose is to
* group all extension data. All extension data should implement this
* interface if the data is expected to handle in the following handshake
* processes.
*/
static interface SSLExtensionSpec {
// blank
}
// Default enabled client extensions.
static final class ClientExtensions {
static final Collection<SSLExtension> defaults;
static {
Collection<SSLExtension> extensions = new LinkedList<>();
for (SSLExtension extension : SSLExtension.values()) {
if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
extensions.add(extension);
}
}
// Switch off SNI extention?
boolean enableExtension =
Utilities.getBooleanProperty("jsse.enableSNIExtension", true);
if (!enableExtension) {
extensions.remove(CH_SERVER_NAME);
}
// To switch off the max_fragment_length extension.
//
// Note that "jsse.enableMFLNExtension" is the CSR approved
// property name. However, "jsse.enableMFLExtension" was used
// in the original implementation. Temporarily, if either of
// the two properties set to true, the extension is switch on.
// We may remove the "jsse.enableMFLExtension" property in the
// future. Please don't continue to use the misspelling property.
enableExtension =
Utilities.getBooleanProperty(
"jsse.enableMFLNExtension", false) ||
Utilities.getBooleanProperty(
"jsse.enableMFLExtension", false);
if (!enableExtension) {
extensions.remove(CH_MAX_FRAGMENT_LENGTH);
}
// To switch on certificate_authorities extension in ClientHello.
//
// Note: Please be careful to enable this extension in ClientHello.
//
// In practice, if the server certificate cannot be validated by
// the underlying programs, the user may manually check the
// certificate in order to access the service. The certificate
// could be accepted manually, and the handshake continues. For
// example, the browsers provide the manual option to accept
// untrusted server certificate. If this extension is enabled in
// the ClientHello handshake message, and the server's certificate
// does not chain back to any of the CAs in the extension, then the
// server will terminate the handshake and close the connection.
// There is no chance for the client to perform the manual check.
// Therefore, enabling this extension in ClientHello may lead to
// unexpected compatibility issues for such cases.
//
// According to TLS 1.3 specification [RFC 8446] the maximum size
// of the certificate_authorities extension is 2^16 bytes. The
// maximum TLS record size is 2^14 bytes. If the handshake
// message is bigger than maximum TLS record size, it should be
// splitted into several records. In fact, some server
// implementations do not allow ClientHello messages bigger than
// the maximum TLS record size and will immediately abort the
// connection with a fatal alert. Therefore, if the client trusts
// too many certificate authorities, there may be unexpected
// interoperability issues.
//
// Furthermore, if the client trusts more CAs such that it exceeds
// the size limit of the extension, enabling this extension in
// client side does not really make sense any longer as there is
// no way to indicate the server certificate selection accurately.
//
// In general, a server does not use multiple certificates issued
// from different CAs. It is not expected to use this extension a
// lot in practice. When there is a need to use this extension
// in ClientHello handshake message, please take care of the
// potential compatibility and interoperability issues above.
enableExtension = Utilities.getBooleanProperty(
"jdk.tls.client.enableCAExtension", false);
if (!enableExtension) {
extensions.remove(CH_CERTIFICATE_AUTHORITIES);
}
defaults = Collections.unmodifiableCollection(extensions);
}
}
// Default enabled server extensions.
static final class ServerExtensions {
static final Collection<SSLExtension> defaults;
static {
Collection<SSLExtension> extensions = new LinkedList<>();
for (SSLExtension extension : SSLExtension.values()) {
if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
extensions.add(extension);
}
}
defaults = Collections.unmodifiableCollection(extensions);
}
}
}

View File

@@ -0,0 +1,389 @@
/*
* Copyright (c) 2018, 2020 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.ByteBuffer;
import java.text.MessageFormat;
import java.util.*;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.misc.HexDumpEncoder;
/**
* SSL/TLS extensions in a handshake message.
*/
final class SSLExtensions {
private final HandshakeMessage handshakeMessage;
private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>();
private int encodedLength;
// Extension map for debug logging
private final Map<Integer, byte[]> logMap =
SSLLogger.isOn ? new LinkedHashMap<>() : null;
SSLExtensions(HandshakeMessage handshakeMessage) {
this.handshakeMessage = handshakeMessage;
this.encodedLength = 2; // 2: the length of the extensions.
}
SSLExtensions(HandshakeMessage hm,
ByteBuffer m, SSLExtension[] extensions) throws IOException {
this.handshakeMessage = hm;
int len = Record.getInt16(m);
encodedLength = len + 2; // 2: the length of the extensions.
while (len > 0) {
int extId = Record.getInt16(m);
int extLen = Record.getInt16(m);
if (extLen > m.remaining()) {
throw hm.handshakeContext.conContext.fatal(
Alert.ILLEGAL_PARAMETER,
"Error parsing extension (" + extId +
"): no sufficient data");
}
boolean isSupported = true;
SSLHandshake handshakeType = hm.handshakeType();
if (SSLExtension.isConsumable(extId) &&
SSLExtension.valueOf(handshakeType, extId) == null) {
if (extId == SSLExtension.CH_SUPPORTED_GROUPS.id &&
handshakeType == SSLHandshake.SERVER_HELLO) {
// Note: It does not comply to the specification. However,
// there are servers that send the supported_groups
// extension in ServerHello handshake message.
//
// TLS 1.3 should not send this extension. We may want to
// limit the workaround for TLS 1.2 and prior version only.
// However, the implementation of the limit is complicated
// and inefficient, and may not worthy the maintenance.
isSupported = false;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Received buggy supported_groups extension " +
"in the ServerHello handshake message");
}
} else if (handshakeType == SSLHandshake.SERVER_HELLO) {
throw hm.handshakeContext.conContext.fatal(
Alert.UNSUPPORTED_EXTENSION, "extension (" +
extId + ") should not be presented in " +
handshakeType.name);
} else {
isSupported = false;
// debug log to ignore unknown extension for handshakeType
}
}
if (isSupported) {
isSupported = false;
for (SSLExtension extension : extensions) {
if ((extension.id != extId) ||
(extension.onLoadConsumer == null)) {
continue;
}
if (extension.handshakeType != handshakeType) {
throw hm.handshakeContext.conContext.fatal(
Alert.UNSUPPORTED_EXTENSION,
"extension (" + extId + ") should not be " +
"presented in " + handshakeType.name);
}
byte[] extData = new byte[extLen];
m.get(extData);
extMap.put(extension, extData);
if (logMap != null) {
logMap.put(extId, extData);
}
isSupported = true;
break;
}
}
if (!isSupported) {
if (logMap != null) {
// cache the extension for debug logging
byte[] extData = new byte[extLen];
m.get(extData);
logMap.put(extId, extData);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unknown or unsupported extension",
toString(extId, extData));
}
} else {
// ignore the extension
int pos = m.position() + extLen;
m.position(pos);
}
}
len -= extLen + 4;
}
}
byte[] get(SSLExtension ext) {
return extMap.get(ext);
}
/**
* Consume the specified extensions.
*/
void consumeOnLoad(HandshakeContext context,
SSLExtension[] extensions) throws IOException {
for (SSLExtension extension : extensions) {
if (context.negotiatedProtocol != null &&
!extension.isAvailable(context.negotiatedProtocol)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unsupported extension: " + extension.name);
}
continue;
}
if (!extMap.containsKey(extension)) {
if (extension.onLoadAbsence != null) {
extension.absentOnLoad(context, handshakeMessage);
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable extension: " + extension.name);
}
continue;
}
if (extension.onLoadConsumer == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore unsupported extension: " + extension.name);
}
continue;
}
ByteBuffer m = ByteBuffer.wrap(extMap.get(extension));
extension.consumeOnLoad(context, handshakeMessage, m);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Consumed extension: " + extension.name);
}
}
}
/**
* Consider impact of the specified extensions.
*/
void consumeOnTrade(HandshakeContext context,
SSLExtension[] extensions) throws IOException {
for (SSLExtension extension : extensions) {
if (!extMap.containsKey(extension)) {
if (extension.onTradeAbsence != null) {
extension.absentOnTrade(context, handshakeMessage);
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore unavailable extension: " + extension.name);
}
continue;
}
if (extension.onTradeConsumer == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore impact of unsupported extension: " +
extension.name);
}
continue;
}
extension.consumeOnTrade(context, handshakeMessage);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Populated with extension: " + extension.name);
}
}
}
/**
* Produce extension values for the specified extensions.
*/
void produce(HandshakeContext context,
SSLExtension[] extensions) throws IOException {
for (SSLExtension extension : extensions) {
if (extMap.containsKey(extension)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Ignore, duplicated extension: " +
extension.name);
}
continue;
}
if (extension.networkProducer == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore, no extension producer defined: " +
extension.name);
}
continue;
}
byte[] encoded = extension.produce(context, handshakeMessage);
if (encoded != null) {
extMap.put(extension, encoded);
encodedLength += encoded.length + 4; // extension_type (2)
// extension_data length(2)
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
// The extension is not available in the context.
SSLLogger.fine(
"Ignore, context unavailable extension: " +
extension.name);
}
}
}
/**
* Produce extension values for the specified extensions, replacing if
* there is an existing extension value for a specified extension.
*/
void reproduce(HandshakeContext context,
SSLExtension[] extensions) throws IOException {
for (SSLExtension extension : extensions) {
if (extension.networkProducer == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Ignore, no extension producer defined: " +
extension.name);
}
continue;
}
byte[] encoded = extension.produce(context, handshakeMessage);
if (encoded != null) {
if (extMap.containsKey(extension)) {
byte[] old = extMap.replace(extension, encoded);
if (old != null) {
encodedLength -= old.length + 4;
}
encodedLength += encoded.length + 4;
} else {
extMap.put(extension, encoded);
encodedLength += encoded.length + 4;
// extension_type (2)
// extension_data length(2)
}
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
// The extension is not available in the context.
SSLLogger.fine(
"Ignore, context unavailable extension: " +
extension.name);
}
}
}
// Note that TLS 1.3 may use empty extensions. Please consider it while
// using this method.
int length() {
if (extMap.isEmpty()) {
return 0;
} else {
return encodedLength;
}
}
// Note that TLS 1.3 may use empty extensions. Please consider it while
// using this method.
void send(HandshakeOutStream hos) throws IOException {
int extsLen = length();
if (extsLen == 0) {
return;
}
hos.putInt16(extsLen - 2);
// extensions must be sent in the order they appear in the enum
for (SSLExtension ext : SSLExtension.values()) {
byte[] extData = extMap.get(ext);
if (extData != null) {
hos.putInt16(ext.id);
hos.putBytes16(extData);
}
}
}
@Override
public String toString() {
if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) {
return "<no extension>";
} else {
StringBuilder builder = new StringBuilder(512);
if (logMap != null && !logMap.isEmpty()) {
for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) {
SSLExtension ext = SSLExtension.valueOf(
handshakeMessage.handshakeType(), en.getKey());
if (builder.length() != 0) {
builder.append(",\n");
}
if (ext != null) {
builder.append(
ext.toString(ByteBuffer.wrap(en.getValue())));
} else {
builder.append(toString(en.getKey(), en.getValue()));
}
}
return builder.toString();
} else {
for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) {
if (builder.length() != 0) {
builder.append(",\n");
}
builder.append(
en.getKey().toString(ByteBuffer.wrap(en.getValue())));
}
return builder.toString();
}
}
}
private static String toString(int extId, byte[] extData) {
String extName = SSLExtension.nameOf(extId);
MessageFormat messageFormat = new MessageFormat(
"\"{0} ({1})\": '{'\n" +
"{2}\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
String encoded = hexEncoder.encodeBuffer(extData);
Object[] messageFields = {
extName,
extId,
Utilities.indent(encoded)
};
return messageFormat.format(messageFields);
}
}

View File

@@ -0,0 +1,557 @@
/*
* Copyright (c) 2006, 2018, 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.ByteBuffer;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Map;
import javax.net.ssl.SSLException;
enum SSLHandshake implements SSLConsumer, HandshakeProducer {
@SuppressWarnings({"unchecked", "rawtypes"})
HELLO_REQUEST ((byte)0x00, "hello_request",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
HelloRequest.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
HelloRequest.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
CLIENT_HELLO ((byte)0x01, "client_hello",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ClientHello.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ClientHello.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
SERVER_HELLO ((byte)0x02, "server_hello",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ServerHello.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ServerHello.t12HandshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ServerHello.t13HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ServerHello.handshakeConsumer, // Use ServerHello consumer
ProtocolVersion.PROTOCOLS_TO_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ServerHello.hrrHandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
NewSessionTicket.handshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
NewSessionTicket.handshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"),
@SuppressWarnings({"unchecked", "rawtypes"})
ENCRYPTED_EXTENSIONS ((byte)0x08, "encrypted_extensions",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
EncryptedExtensions.handshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
EncryptedExtensions.handshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
CERTIFICATE ((byte)0x0B, "certificate",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateMessage.t12HandshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateMessage.t13HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateMessage.t12HandshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateMessage.t13HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
SERVER_KEY_EXCHANGE ((byte)0x0C, "server_key_exchange",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ServerKeyExchange.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ServerKeyExchange.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
CERTIFICATE_REQUEST ((byte)0x0D, "certificate_request",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateRequest.t10HandshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_11
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateRequest.t12HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_12
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateRequest.t13HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateRequest.t10HandshakeProducer,
ProtocolVersion.PROTOCOLS_TO_11
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateRequest.t12HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_12
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateRequest.t13HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
SERVER_HELLO_DONE ((byte)0x0E, "server_hello_done",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ServerHelloDone.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ServerHelloDone.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
CERTIFICATE_VERIFY ((byte)0x0F, "certificate_verify",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateVerify.s30HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_30
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateVerify.t10HandshakeConsumer,
ProtocolVersion.PROTOCOLS_10_11
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateVerify.t12HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_12
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateVerify.t13HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateVerify.s30HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_30
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateVerify.t10HandshakeProducer,
ProtocolVersion.PROTOCOLS_10_11
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateVerify.t12HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_12
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateVerify.t13HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
CLIENT_KEY_EXCHANGE ((byte)0x10, "client_key_exchange",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
ClientKeyExchange.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
ClientKeyExchange.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
@SuppressWarnings({"unchecked", "rawtypes"})
FINISHED ((byte)0x14, "finished",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
Finished.t12HandshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
Finished.t13HandshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
Finished.t12HandshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
Finished.t13HandshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
CERTIFICATE_URL ((byte)0x15, "certificate_url"),
@SuppressWarnings({"unchecked", "rawtypes"})
CERTIFICATE_STATUS ((byte)0x16, "certificate_status",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
CertificateStatus.handshakeConsumer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
CertificateStatus.handshakeProducer,
ProtocolVersion.PROTOCOLS_TO_12
)
}),
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeAbsence, ProtocolVersion[]>(
CertificateStatus.handshakeAbsence,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
SUPPLEMENTAL_DATA ((byte)0x17, "supplemental_data"),
@SuppressWarnings({"unchecked", "rawtypes"})
KEY_UPDATE ((byte)0x18, "key_update",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
KeyUpdate.handshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
)
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
KeyUpdate.handshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
)
})),
MESSAGE_HASH ((byte)0xFE, "message_hash"),
NOT_APPLICABLE ((byte)0xFF, "not_applicable");
final byte id;
final String name;
final Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers;
final Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers;
final Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsences;
@SuppressWarnings({"unchecked", "rawtypes"})
SSLHandshake(byte id, String name) {
this(id, name,
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(
new Map.Entry[0]),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(
new Map.Entry[0]),
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
new Map.Entry[0]));
}
@SuppressWarnings({"unchecked", "rawtypes"})
SSLHandshake(byte id, String name,
Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers) {
this(id, name, handshakeConsumers, handshakeProducers,
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
new Map.Entry[0]));
}
SSLHandshake(byte id, String name,
Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers,
Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsence) {
this.id = id;
this.name = name;
this.handshakeConsumers = handshakeConsumers;
this.handshakeProducers = handshakeProducers;
this.handshakeAbsences = handshakeAbsence;
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
SSLConsumer hc = getHandshakeConsumer(context);
if (hc != null) {
hc.consume(context, message);
} else {
throw new UnsupportedOperationException(
"Unsupported handshake consumer: " + this.name);
}
}
private SSLConsumer getHandshakeConsumer(ConnectionContext context) {
if (handshakeConsumers.length == 0) {
return null;
}
// The consuming happens in handshake context only.
HandshakeContext hc = (HandshakeContext)context;
ProtocolVersion protocolVersion;
if ((hc.negotiatedProtocol == null) ||
(hc.negotiatedProtocol == ProtocolVersion.NONE)) {
if (hc.conContext.isNegotiated &&
hc.conContext.protocolVersion != ProtocolVersion.NONE) {
protocolVersion = hc.conContext.protocolVersion;
} else {
protocolVersion = hc.maximumActiveProtocol;
}
} else {
protocolVersion = hc.negotiatedProtocol;
}
for (Map.Entry<SSLConsumer,
ProtocolVersion[]> phe : handshakeConsumers) {
for (ProtocolVersion pv : phe.getValue()) {
if (protocolVersion == pv) {
return phe.getKey();
}
}
}
return null;
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
HandshakeProducer hp = getHandshakeProducer(context);
if (hp != null) {
return hp.produce(context, message);
} else {
throw new UnsupportedOperationException(
"Unsupported handshake producer: " + this.name);
}
}
private HandshakeProducer getHandshakeProducer(
ConnectionContext context) {
if (handshakeConsumers.length == 0) {
return null;
}
// The consuming happens in handshake context only.
HandshakeContext hc = (HandshakeContext)context;
ProtocolVersion protocolVersion;
if ((hc.negotiatedProtocol == null) ||
(hc.negotiatedProtocol == ProtocolVersion.NONE)) {
if (hc.conContext.isNegotiated &&
hc.conContext.protocolVersion != ProtocolVersion.NONE) {
protocolVersion = hc.conContext.protocolVersion;
} else {
protocolVersion = hc.maximumActiveProtocol;
}
} else {
protocolVersion = hc.negotiatedProtocol;
}
for (Map.Entry<HandshakeProducer,
ProtocolVersion[]> phe : handshakeProducers) {
for (ProtocolVersion pv : phe.getValue()) {
if (protocolVersion == pv) {
return phe.getKey();
}
}
}
return null;
}
@Override
public String toString() {
return name;
}
static String nameOf(byte id) {
// If two handshake message share the same handshake type, returns
// the first handshake message name.
//
// It is not a big issue at present as only ServerHello and
// HellRetryRequest share a handshake type.
for (SSLHandshake hs : SSLHandshake.values()) {
if (hs.id == id) {
return hs.name;
}
}
return "UNKNOWN-HANDSHAKE-MESSAGE(" + id + ")";
}
static boolean isKnown(byte id) {
for (SSLHandshake hs : SSLHandshake.values()) {
if (hs.id == id && id != NOT_APPLICABLE.id) {
return true;
}
}
return false;
}
static final void kickstart(HandshakeContext context) throws IOException {
if (context instanceof ClientHandshakeContext) {
// For initial handshaking, including session resumption,
// ClientHello message is used as the kickstart message.
//
// TLS 1.2 and older protocols support renegotiation on existing
// connections. A ClientHello messages is used to kickstart the
// renegotiation.
//
// TLS 1.3 forbids renegotiation. The post-handshake KeyUpdate
// message is used to update the sending cryptographic keys.
if (context.conContext.isNegotiated &&
context.conContext.protocolVersion.useTLS13PlusSpec()) {
// Use KeyUpdate message for renegotiation.
KeyUpdate.kickstartProducer.produce(context);
} else {
// Using ClientHello message for the initial handshaking
// (including session resumption) or renegotiation.
// SSLHandshake.CLIENT_HELLO.produce(context);
ClientHello.kickstartProducer.produce(context);
}
} else {
// The server side can delivering kickstart message after the
// connection has established.
//
// TLS 1.2 and older protocols use HelloRequest to begin a
// negotiation process anew.
//
// While TLS 1.3 uses the post-handshake KeyUpdate message
// to update the sending cryptographic keys.
if (context.conContext.protocolVersion.useTLS13PlusSpec()) {
// Use KeyUpdate message for renegotiation.
KeyUpdate.kickstartProducer.produce(context);
} else {
// SSLHandshake.HELLO_REQUEST.produce(context);
HelloRequest.kickstartProducer.produce(context);
}
}
}
/**
* A (transparent) specification of handshake message.
*/
static abstract class HandshakeMessage {
final HandshakeContext handshakeContext;
HandshakeMessage(HandshakeContext handshakeContext) {
this.handshakeContext = handshakeContext;
}
abstract SSLHandshake handshakeType();
abstract int messageLength();
abstract void send(HandshakeOutStream hos) throws IOException;
void write(HandshakeOutStream hos) throws IOException {
int len = messageLength();
if (len >= Record.OVERFLOW_OF_INT24) {
throw new SSLException("Handshake message is overflow"
+ ", type = " + handshakeType() + ", len = " + len);
}
hos.write(handshakeType().id);
hos.putInt24(len);
send(hos);
hos.complete();
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018, 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.util.Map;
interface SSLHandshakeBinding {
default SSLHandshake[] getRelatedHandshakers(
HandshakeContext handshakeContext) {
return new SSLHandshake[0];
}
@SuppressWarnings({"unchecked", "rawtypes"})
default Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
HandshakeContext handshakeContext) {
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
}
@SuppressWarnings({"unchecked", "rawtypes"})
default Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
HandshakeContext handshakeContext) {
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, 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;
interface SSLKeyAgreement
extends SSLPossessionGenerator, SSLKeyAgreementGenerator,
SSLHandshakeBinding {
// blank
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018, 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;
interface SSLKeyAgreementGenerator {
SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018, 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.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
interface SSLKeyDerivation {
SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018, 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 javax.crypto.SecretKey;
interface SSLKeyDerivationGenerator {
SSLKeyDerivation createKeyDerivation(HandshakeContext context,
SecretKey secretKey) throws IOException;
}

View File

@@ -0,0 +1,633 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, Azul Systems, Inc. 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.util.AbstractMap.SimpleImmutableEntry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import sun.security.ssl.DHKeyExchange.DHEPossession;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.X509Authentication.X509Possession;
final class SSLKeyExchange implements SSLKeyAgreementGenerator,
SSLHandshakeBinding {
private final SSLAuthentication authentication;
private final SSLKeyAgreement keyAgreement;
SSLKeyExchange(X509Authentication authentication,
SSLKeyAgreement keyAgreement) {
this.authentication = authentication;
this.keyAgreement = keyAgreement;
}
SSLPossession[] createPossessions(HandshakeContext context) {
// authentication
SSLPossession authPossession = null;
if (authentication != null) {
authPossession = authentication.createPossession(context);
if (authPossession == null) {
return new SSLPossession[0];
} else if (context instanceof ServerHandshakeContext) {
// The authentication information may be used further for
// key agreement parameters negotiation.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
shc.interimAuthn = authPossession;
}
}
// key agreement
SSLPossession kaPossession;
if (keyAgreement == T12KeyAgreement.RSA_EXPORT) {
// a special case
X509Possession x509Possession = (X509Possession)authPossession;
if (JsseJce.getRSAKeyLength(
x509Possession.popCerts[0].getPublicKey()) > 512) {
kaPossession = keyAgreement.createPossession(context);
if (kaPossession == null) {
return new SSLPossession[0];
} else {
return authentication != null ?
new SSLPossession[] {authPossession, kaPossession} :
new SSLPossession[] {kaPossession};
}
} else {
return authentication != null ?
new SSLPossession[] {authPossession} :
new SSLPossession[0];
}
} else {
kaPossession = keyAgreement.createPossession(context);
if (kaPossession == null) {
// special cases
if (keyAgreement == T12KeyAgreement.RSA ||
keyAgreement == T12KeyAgreement.ECDH) {
return authentication != null ?
new SSLPossession[] {authPossession} :
new SSLPossession[0];
} else {
return new SSLPossession[0];
}
} else {
return authentication != null ?
new SSLPossession[] {authPossession, kaPossession} :
new SSLPossession[] {kaPossession};
}
}
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext handshakeContext) throws IOException {
return keyAgreement.createKeyDerivation(handshakeContext);
}
@Override
public SSLHandshake[] getRelatedHandshakers(
HandshakeContext handshakeContext) {
SSLHandshake[] auHandshakes;
if (authentication != null) {
auHandshakes =
authentication.getRelatedHandshakers(handshakeContext);
} else {
auHandshakes = null;
}
SSLHandshake[] kaHandshakes =
keyAgreement.getRelatedHandshakers(handshakeContext);
if (auHandshakes == null || auHandshakes.length == 0) {
return kaHandshakes;
} else if (kaHandshakes == null || kaHandshakes.length == 0) {
return auHandshakes;
} else {
SSLHandshake[] producers = Arrays.copyOf(
auHandshakes, auHandshakes.length + kaHandshakes.length);
System.arraycopy(kaHandshakes, 0,
producers, auHandshakes.length, kaHandshakes.length);
return producers;
}
}
@Override
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
HandshakeContext handshakeContext) {
Map.Entry<Byte, HandshakeProducer>[] auProducers;
if (authentication != null) {
auProducers =
authentication.getHandshakeProducers(handshakeContext);
} else {
auProducers = null;
}
Map.Entry<Byte, HandshakeProducer>[] kaProducers =
keyAgreement.getHandshakeProducers(handshakeContext);
if (auProducers == null || auProducers.length == 0) {
return kaProducers;
} else if (kaProducers == null || kaProducers.length == 0) {
return auProducers;
} else {
Map.Entry<Byte, HandshakeProducer>[] producers = Arrays.copyOf(
auProducers, auProducers.length + kaProducers.length);
System.arraycopy(kaProducers, 0,
producers, auProducers.length, kaProducers.length);
return producers;
}
}
@Override
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
HandshakeContext handshakeContext) {
Map.Entry<Byte, SSLConsumer>[] auConsumers;
if (authentication != null) {
auConsumers =
authentication.getHandshakeConsumers(handshakeContext);
} else {
auConsumers = null;
}
Map.Entry<Byte, SSLConsumer>[] kaConsumers =
keyAgreement.getHandshakeConsumers(handshakeContext);
if (auConsumers == null || auConsumers.length == 0) {
return kaConsumers;
} else if (kaConsumers == null || kaConsumers.length == 0) {
return auConsumers;
} else {
Map.Entry<Byte, SSLConsumer>[] producers = Arrays.copyOf(
auConsumers, auConsumers.length + kaConsumers.length);
System.arraycopy(kaConsumers, 0,
producers, auConsumers.length, kaConsumers.length);
return producers;
}
}
// SSL 3.0 - TLS 1.2
static SSLKeyExchange valueOf(
CipherSuite.KeyExchange keyExchange,
ProtocolVersion protocolVersion) {
if (keyExchange == null || protocolVersion == null) {
return null;
}
switch (keyExchange) {
case K_RSA:
return SSLKeyExRSA.KE;
case K_RSA_EXPORT:
return SSLKeyExRSAExport.KE;
case K_DHE_DSS:
return SSLKeyExDHEDSS.KE;
case K_DHE_DSS_EXPORT:
return SSLKeyExDHEDSSExport.KE;
case K_DHE_RSA:
if (protocolVersion.useTLS12PlusSpec()) { // TLS 1.2
return SSLKeyExDHERSAOrPSS.KE;
} else { // SSL 3.0, TLS 1.0/1.1
return SSLKeyExDHERSA.KE;
}
case K_DHE_RSA_EXPORT:
return SSLKeyExDHERSAExport.KE;
case K_DH_ANON:
return SSLKeyExDHANON.KE;
case K_DH_ANON_EXPORT:
return SSLKeyExDHANONExport.KE;
case K_ECDH_ECDSA:
return SSLKeyExECDHECDSA.KE;
case K_ECDH_RSA:
return SSLKeyExECDHRSA.KE;
case K_ECDHE_ECDSA:
return SSLKeyExECDHEECDSA.KE;
case K_ECDHE_RSA:
if (protocolVersion.useTLS12PlusSpec()) { // TLS 1.2
return SSLKeyExECDHERSAOrPSS.KE;
} else { // SSL 3.0, TLS 1.0/1.1
return SSLKeyExECDHERSA.KE;
}
case K_ECDH_ANON:
return SSLKeyExECDHANON.KE;
case K_KRB5:
return SSLKeyExKRB5.KE;
case K_KRB5_EXPORT:
return SSLKeyExKRB5EXPORT.KE;
}
return null;
}
// TLS 1.3
static SSLKeyExchange valueOf(NamedGroup namedGroup) {
SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
if (ka != null) {
return new SSLKeyExchange(
null, T13KeyAgreement.valueOf(namedGroup));
}
return null;
}
private static class SSLKeyExRSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA, T12KeyAgreement.RSA);
}
private static class SSLKeyExRSAExport {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT);
}
private static class SSLKeyExDHEDSS {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.DSA, T12KeyAgreement.DHE);
}
private static class SSLKeyExDHEDSSExport {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT);
}
private static class SSLKeyExDHERSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA, T12KeyAgreement.DHE);
}
private static class SSLKeyExDHERSAOrPSS {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE);
}
private static class SSLKeyExDHERSAExport {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT);
}
private static class SSLKeyExDHANON {
private static SSLKeyExchange KE = new SSLKeyExchange(
null, T12KeyAgreement.DHE);
}
private static class SSLKeyExDHANONExport {
private static SSLKeyExchange KE = new SSLKeyExchange(
null, T12KeyAgreement.DHE_EXPORT);
}
private static class SSLKeyExECDHECDSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.EC, T12KeyAgreement.ECDH);
}
private static class SSLKeyExECDHRSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.EC, T12KeyAgreement.ECDH);
}
private static class SSLKeyExECDHEECDSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.EC, T12KeyAgreement.ECDHE);
}
private static class SSLKeyExECDHERSA {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA, T12KeyAgreement.ECDHE);
}
private static class SSLKeyExECDHERSAOrPSS {
private static SSLKeyExchange KE = new SSLKeyExchange(
X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE);
}
private static class SSLKeyExECDHANON {
private static SSLKeyExchange KE = new SSLKeyExchange(
null, T12KeyAgreement.ECDHE);
}
private static class SSLKeyExKRB5 {
private static SSLKeyExchange KE = new SSLKeyExchange(
null, T12KeyAgreement.KRB5);
}
private static class SSLKeyExKRB5EXPORT {
private static SSLKeyExchange KE = new SSLKeyExchange(
null, T12KeyAgreement.KRB5_EXPORT);
}
private enum T12KeyAgreement implements SSLKeyAgreement {
RSA ("rsa", null,
RSAKeyExchange.kaGenerator),
RSA_EXPORT ("rsa_export", RSAKeyExchange.poGenerator,
RSAKeyExchange.kaGenerator),
DHE ("dhe", DHKeyExchange.poGenerator,
DHKeyExchange.kaGenerator),
DHE_EXPORT ("dhe_export", DHKeyExchange.poExportableGenerator,
DHKeyExchange.kaGenerator),
ECDH ("ecdh", null,
ECDHKeyExchange.ecdhKAGenerator),
ECDHE ("ecdhe", ECDHKeyExchange.poGenerator,
ECDHKeyExchange.ecdheKAGenerator),
KRB5 ("krb5", KrbKeyExchange.poGenerator,
KrbKeyExchange.kaGenerator),
KRB5_EXPORT ("krb5_export", KrbKeyExchange.poGenerator,
KrbKeyExchange.kaGenerator);
final String name;
final SSLPossessionGenerator possessionGenerator;
final SSLKeyAgreementGenerator keyAgreementGenerator;
T12KeyAgreement(String name,
SSLPossessionGenerator possessionGenerator,
SSLKeyAgreementGenerator keyAgreementGenerator) {
this.name = name;
this.possessionGenerator = possessionGenerator;
this.keyAgreementGenerator = keyAgreementGenerator;
}
@Override
public SSLPossession createPossession(HandshakeContext context) {
if (possessionGenerator != null) {
return possessionGenerator.createPossession(context);
}
return null;
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext context) throws IOException {
return keyAgreementGenerator.createKeyDerivation(context);
}
@Override
public SSLHandshake[] getRelatedHandshakers(
HandshakeContext handshakeContext) {
if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
if (this.possessionGenerator != null) {
return new SSLHandshake[] {
SSLHandshake.SERVER_KEY_EXCHANGE
};
}
}
return new SSLHandshake[0];
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
HandshakeContext handshakeContext) {
if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
}
if (handshakeContext.sslConfig.isClientMode) {
switch (this) {
case RSA:
case RSA_EXPORT:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
RSAClientKeyExchange.rsaHandshakeProducer
)
});
case DHE:
case DHE_EXPORT:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<Byte, HandshakeProducer>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
DHClientKeyExchange.dhHandshakeProducer
)
});
case ECDH:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
ECDHClientKeyExchange.ecdhHandshakeProducer
)
});
case ECDHE:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
ECDHClientKeyExchange.ecdheHandshakeProducer
)
});
case KRB5:
case KRB5_EXPORT:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
KrbClientKeyExchange.krbHandshakeProducer
)
});
}
} else {
switch (this) {
case RSA_EXPORT:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
RSAServerKeyExchange.rsaHandshakeProducer
)
});
case DHE:
case DHE_EXPORT:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
DHServerKeyExchange.dhHandshakeProducer
)
});
case ECDHE:
return (Map.Entry<Byte,
HandshakeProducer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
ECDHServerKeyExchange.ecdheHandshakeProducer
)
});
}
}
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
HandshakeContext handshakeContext) {
if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
}
if (handshakeContext.sslConfig.isClientMode) {
switch (this) {
case RSA_EXPORT:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
RSAServerKeyExchange.rsaHandshakeConsumer
)
});
case DHE:
case DHE_EXPORT:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
DHServerKeyExchange.dhHandshakeConsumer
)
});
case ECDHE:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.SERVER_KEY_EXCHANGE.id,
ECDHServerKeyExchange.ecdheHandshakeConsumer
)
});
}
} else {
switch (this) {
case RSA:
case RSA_EXPORT:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
RSAClientKeyExchange.rsaHandshakeConsumer
)
});
case DHE:
case DHE_EXPORT:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
DHClientKeyExchange.dhHandshakeConsumer
)
});
case ECDH:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
ECDHClientKeyExchange.ecdhHandshakeConsumer
)
});
case ECDHE:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
ECDHClientKeyExchange.ecdheHandshakeConsumer
)
});
case KRB5:
case KRB5_EXPORT:
return (Map.Entry<Byte,
SSLConsumer>[])(new Map.Entry[] {
new SimpleImmutableEntry<>(
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
KrbClientKeyExchange.krbHandshakeConsumer
)
});
}
}
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
}
}
private static final class T13KeyAgreement implements SSLKeyAgreement {
private final NamedGroup namedGroup;
static final Map<NamedGroup, T13KeyAgreement>
supportedKeyShares = new HashMap<>();
static {
for (NamedGroup namedGroup :
SupportedGroups.supportedNamedGroups) {
supportedKeyShares.put(
namedGroup, new T13KeyAgreement(namedGroup));
}
}
private T13KeyAgreement(NamedGroup namedGroup) {
this.namedGroup = namedGroup;
}
static T13KeyAgreement valueOf(NamedGroup namedGroup) {
return supportedKeyShares.get(namedGroup);
}
@Override
public SSLPossession createPossession(HandshakeContext hc) {
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
return new ECDHEPossession(
namedGroup, hc.sslContext.getSecureRandom());
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
return new DHEPossession(
namedGroup, hc.sslContext.getSecureRandom());
}
return null;
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext hc) throws IOException {
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
}
return null;
}
}
}

View File

@@ -0,0 +1,594 @@
/*
* Copyright (c) 2018, 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.security.cert.Certificate;
import java.security.cert.Extension;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.Map;
import java.util.ResourceBundle;
import sun.security.action.GetPropertyAction;
import sun.misc.HexDumpEncoder;
import sun.security.x509.*;
/**
* Implementation of SSL logger.
*
* If the system property "javax.net.debug" is not defined, the debug logging
* is turned off. If the system property "javax.net.debug" is defined as
* empty, the debug logger is specified by System.getLogger("javax.net.ssl"),
* and applications can customize and configure the logger or use external
* logging mechanisms. If the system property "javax.net.debug" is defined
* and non-empty, a private debug logger implemented in this class is used.
*/
public final class SSLLogger {
private static final Logger logger;
private static final String property;
public static final boolean isOn;
static {
String p = GetPropertyAction.privilegedGetProperty("javax.net.debug");
if (p != null) {
if (p.isEmpty()) {
property = "";
logger = Logger.getLogger("javax.net.ssl");
} else {
property = p.toLowerCase(Locale.ENGLISH);
if (property.equals("help")) {
help();
}
logger = new SSLConsoleLogger("javax.net.ssl", p);
}
isOn = true;
} else {
property = null;
logger = null;
isOn = false;
}
}
private static void help() {
System.err.println();
System.err.println("help print the help messages");
System.err.println("expand expand debugging information");
System.err.println();
System.err.println("all turn on all debugging");
System.err.println("ssl turn on ssl debugging");
System.err.println();
System.err.println("The following can be used with ssl:");
System.err.println("\trecord enable per-record tracing");
System.err.println("\thandshake print each handshake message");
System.err.println("\tkeygen print key generation data");
System.err.println("\tsession print session activity");
System.err.println("\tdefaultctx print default SSL initialization");
System.err.println("\tsslctx print SSLContext tracing");
System.err.println("\tsessioncache print session cache tracing");
System.err.println("\tkeymanager print key manager tracing");
System.err.println("\ttrustmanager print trust manager tracing");
System.err.println("\tpluggability print pluggability tracing");
System.err.println();
System.err.println("\thandshake debugging can be widened with:");
System.err.println("\tdata hex dump of each handshake message");
System.err.println("\tverbose verbose handshake message printing");
System.err.println();
System.err.println("\trecord debugging can be widened with:");
System.err.println("\tplaintext hex dump of record plaintext");
System.err.println("\tpacket print raw SSL/TLS packets");
System.err.println();
System.exit(0);
}
/**
* Return true if the "javax.net.debug" property contains the
* debug check points, or System.Logger is used.
*/
public static boolean isOn(String checkPoints) {
if (property == null) { // debugging is turned off
return false;
} else if (property.isEmpty()) { // use System.Logger
return true;
} // use provider logger
String[] options = checkPoints.split(",");
for (String option : options) {
option = option.trim();
if (!SSLLogger.hasOption(option)) {
return false;
}
}
return true;
}
private static boolean hasOption(String option) {
option = option.toLowerCase(Locale.ENGLISH);
if (property.contains("all")) {
return true;
} else {
int offset = property.indexOf("ssl");
if (offset != -1 && property.indexOf("sslctx", offset) != -1) {
// don't enable data and plaintext options by default
if (!(option.equals("data")
|| option.equals("packet")
|| option.equals("plaintext"))) {
return true;
}
}
}
return property.contains(option);
}
public static void severe(String msg, Object... params) {
SSLLogger.log(Level.SEVERE, msg, params);
}
public static void warning(String msg, Object... params) {
SSLLogger.log(Level.WARNING, msg, params);
}
public static void info(String msg, Object... params) {
SSLLogger.log(Level.INFO, msg, params);
}
public static void fine(String msg, Object... params) {
SSLLogger.log(Level.FINE, msg, params);
}
public static void finer(String msg, Object... params) {
SSLLogger.log(Level.FINER, msg, params);
}
public static void finest(String msg, Object... params) {
SSLLogger.log(Level.ALL, msg, params);
}
private static void log(Level level, String msg, Object... params) {
if (logger != null && logger.isLoggable(level)) {
if (params == null || params.length == 0) {
logger.log(level, msg);
} else {
try {
String formatted =
SSLSimpleFormatter.formatParameters(params);
logger.log(level, msg, formatted);
} catch (Exception exp) {
// ignore it, just for debugging.
}
}
}
}
static String toString(Object... params) {
try {
return SSLSimpleFormatter.formatParameters(params);
} catch (Exception exp) {
return "unexpected exception thrown: " + exp.getMessage();
}
}
private static class SSLConsoleLogger extends Logger {
private final String loggerName;
private final boolean useCompactFormat;
SSLConsoleLogger(String loggerName, String options) {
super(loggerName, null);
this.loggerName = loggerName;
options = options.toLowerCase(Locale.ENGLISH);
this.useCompactFormat = !options.contains("expand");
}
public String getName() {
return loggerName;
}
public boolean isLoggable(Level level) {
return (level != Level.OFF);
}
@Override
public void log(LogRecord record) {
if (isLoggable(record.getLevel())) {
try {
String formatted = null;
if (record.getThrown() != null) {
formatted =
SSLSimpleFormatter.format(this, record.getLevel(),
record.getMessage(),
record.getThrown());
} else {
formatted =
SSLSimpleFormatter.format(this, record.getLevel(),
record.getMessage(),
record.getParameters());
}
System.err.write(formatted.getBytes("UTF-8"));
} catch (Exception exp) {
// ignore it, just for debugging.
}
}
};
}
private static class SSLSimpleFormatter {
private static final ThreadLocal<SimpleDateFormat> dateFormat =
new ThreadLocal<SimpleDateFormat>() {
@Override protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(
"yyyy-MM-dd kk:mm:ss.SSS z", Locale.ENGLISH);
}
};
private static final MessageFormat basicCertFormat = new MessageFormat(
"\"version\" : \"v{0}\",\n" +
"\"serial number\" : \"{1}\",\n" +
"\"signature algorithm\": \"{2}\",\n" +
"\"issuer\" : \"{3}\",\n" +
"\"not before\" : \"{4}\",\n" +
"\"not after\" : \"{5}\",\n" +
"\"subject\" : \"{6}\",\n" +
"\"subject public key\" : \"{7}\"\n",
Locale.ENGLISH);
private static final MessageFormat extendedCertFormart =
new MessageFormat(
"\"version\" : \"v{0}\",\n" +
"\"serial number\" : \"{1}\",\n" +
"\"signature algorithm\": \"{2}\",\n" +
"\"issuer\" : \"{3}\",\n" +
"\"not before\" : \"{4}\",\n" +
"\"not after\" : \"{5}\",\n" +
"\"subject\" : \"{6}\",\n" +
"\"subject public key\" : \"{7}\",\n" +
"\"extensions\" : [\n" +
"{8}\n" +
"]\n",
Locale.ENGLISH);
//
// private static MessageFormat certExtFormat = new MessageFormat(
// "{0} [{1}] '{'\n" +
// " critical: {2}\n" +
// " value: {3}\n" +
// "'}'",
// Locale.ENGLISH);
//
private static final MessageFormat messageFormatNoParas =
new MessageFormat(
"'{'\n" +
" \"logger\" : \"{0}\",\n" +
" \"level\" : \"{1}\",\n" +
" \"thread id\" : \"{2}\",\n" +
" \"thread name\" : \"{3}\",\n" +
" \"time\" : \"{4}\",\n" +
" \"caller\" : \"{5}\",\n" +
" \"message\" : \"{6}\"\n" +
"'}'\n",
Locale.ENGLISH);
private static final MessageFormat messageCompactFormatNoParas =
new MessageFormat(
"{0}|{1}|{2}|{3}|{4}|{5}|{6}\n",
Locale.ENGLISH);
private static final MessageFormat messageFormatWithParas =
new MessageFormat(
"'{'\n" +
" \"logger\" : \"{0}\",\n" +
" \"level\" : \"{1}\",\n" +
" \"thread id\" : \"{2}\",\n" +
" \"thread name\" : \"{3}\",\n" +
" \"time\" : \"{4}\",\n" +
" \"caller\" : \"{5}\",\n" +
" \"message\" : \"{6}\",\n" +
" \"specifics\" : [\n" +
"{7}\n" +
" ]\n" +
"'}'\n",
Locale.ENGLISH);
private static final MessageFormat messageCompactFormatWithParas =
new MessageFormat(
"{0}|{1}|{2}|{3}|{4}|{5}|{6} (\n" +
"{7}\n" +
")\n",
Locale.ENGLISH);
private static final MessageFormat keyObjectFormat = new MessageFormat(
"\"{0}\" : '{'\n" +
"{1}" +
"'}'\n",
Locale.ENGLISH);
// INFO: [TH: 123450] 2011-08-20 23:12:32.3225 PDT
// log message
// log message
// ...
private static String format(SSLConsoleLogger logger, Level level,
String message, Object ... parameters) {
if (parameters == null || parameters.length == 0) {
Object[] messageFields = {
logger.loggerName,
level.getName(),
Utilities.toHexString(Thread.currentThread().getId()),
Thread.currentThread().getName(),
dateFormat.get().format(new Date(System.currentTimeMillis())),
formatCaller(),
message
};
if (logger.useCompactFormat) {
return messageCompactFormatNoParas.format(messageFields);
} else {
return messageFormatNoParas.format(messageFields);
}
}
Object[] messageFields = {
logger.loggerName,
level.getName(),
Utilities.toHexString(Thread.currentThread().getId()),
Thread.currentThread().getName(),
dateFormat.get().format(new Date(System.currentTimeMillis())),
formatCaller(),
message,
(logger.useCompactFormat ?
formatParameters(parameters) :
Utilities.indent(formatParameters(parameters)))
};
if (logger.useCompactFormat) {
return messageCompactFormatWithParas.format(messageFields);
} else {
return messageFormatWithParas.format(messageFields);
}
}
private static String formatCaller() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().startsWith(SSLLogger.class.getName()) &&
!ste.getClassName().startsWith(Logger.class.getName())) {
return ste.getFileName() + ":" + ste.getLineNumber();
}
}
return "unknown caller";
}
private static String formatParameters(Object ... parameters) {
StringBuilder builder = new StringBuilder(512);
boolean isFirst = true;
for (Object parameter : parameters) {
if (isFirst) {
isFirst = false;
} else {
builder.append(",\n");
}
if (parameter instanceof Throwable) {
builder.append(formatThrowable((Throwable)parameter));
} else if (parameter instanceof Certificate) {
builder.append(formatCertificate((Certificate)parameter));
} else if (parameter instanceof ByteArrayInputStream) {
builder.append(formatByteArrayInputStream(
(ByteArrayInputStream)parameter));
} else if (parameter instanceof ByteBuffer) {
builder.append(formatByteBuffer((ByteBuffer)parameter));
} else if (parameter instanceof byte[]) {
builder.append(formatByteArrayInputStream(
new ByteArrayInputStream((byte[])parameter)));
} else if (parameter instanceof Map.Entry) {
@SuppressWarnings("unchecked")
Map.Entry<String, ?> mapParameter =
(Map.Entry<String, ?>)parameter;
builder.append(formatMapEntry(mapParameter));
} else {
builder.append(formatObject(parameter));
}
}
return builder.toString();
}
// "throwable": {
// ...
// }
private static String formatThrowable(Throwable throwable) {
StringBuilder builder = new StringBuilder(512);
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(bytesOut)) {
throwable.printStackTrace(out);
builder.append(Utilities.indent(bytesOut.toString()));
}
Object[] fields = {
"throwable",
builder.toString()
};
return keyObjectFormat.format(fields);
}
// "certificate": {
// ...
// }
private static String formatCertificate(Certificate certificate) {
if (!(certificate instanceof X509Certificate)) {
return Utilities.indent(certificate.toString());
}
StringBuilder builder = new StringBuilder(512);
try {
X509CertImpl x509 =
X509CertImpl.toImpl((X509Certificate)certificate);
X509CertInfo certInfo =
(X509CertInfo)x509.get(X509CertImpl.NAME + "." +
X509CertImpl.INFO);
CertificateExtensions certExts = (CertificateExtensions)
certInfo.get(X509CertInfo.EXTENSIONS);
if (certExts == null) {
Object[] certFields = {
x509.getVersion(),
Utilities.toHexString(
x509.getSerialNumber().toByteArray()),
x509.getSigAlgName(),
x509.getIssuerX500Principal().toString(),
dateFormat.get().format(x509.getNotBefore()),
dateFormat.get().format(x509.getNotAfter()),
x509.getSubjectX500Principal().toString(),
x509.getPublicKey().getAlgorithm()
};
builder.append(Utilities.indent(
basicCertFormat.format(certFields)));
} else {
StringBuilder extBuilder = new StringBuilder(512);
boolean isFirst = true;
for (Extension certExt : certExts.getAllExtensions()) {
if (isFirst) {
isFirst = false;
} else {
extBuilder.append(",\n");
}
extBuilder.append("{\n" +
Utilities.indent(certExt.toString()) + "\n}");
}
Object[] certFields = {
x509.getVersion(),
Utilities.toHexString(
x509.getSerialNumber().toByteArray()),
x509.getSigAlgName(),
x509.getIssuerX500Principal().toString(),
dateFormat.get().format(x509.getNotBefore()),
dateFormat.get().format(x509.getNotAfter()),
x509.getSubjectX500Principal().toString(),
x509.getPublicKey().getAlgorithm(),
Utilities.indent(extBuilder.toString())
};
builder.append(Utilities.indent(
extendedCertFormart.format(certFields)));
}
} catch (Exception ce) {
// ignore the exception
}
Object[] fields = {
"certificate",
builder.toString()
};
return Utilities.indent(keyObjectFormat.format(fields));
}
private static String formatByteArrayInputStream(
ByteArrayInputStream bytes) {
StringBuilder builder = new StringBuilder(512);
try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
HexDumpEncoder hexEncoder = new HexDumpEncoder();
hexEncoder.encodeBuffer(bytes, bytesOut);
builder.append(Utilities.indent(bytesOut.toString()));
} catch (IOException ioe) {
// ignore it, just for debugging.
}
return builder.toString();
}
private static String formatByteBuffer(ByteBuffer byteBuffer) {
StringBuilder builder = new StringBuilder(512);
try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
HexDumpEncoder hexEncoder = new HexDumpEncoder();
hexEncoder.encodeBuffer(byteBuffer.duplicate(), bytesOut);
builder.append(Utilities.indent(bytesOut.toString()));
} catch (IOException ioe) {
// ignore it, just for debugging.
}
return builder.toString();
}
private static String formatMapEntry(Map.Entry<String, ?> entry) {
String key = entry.getKey();
Object value = entry.getValue();
String formatted;
if (value instanceof String) {
// "key": "value"
formatted = "\"" + key + "\": \"" + (String)value + "\"";
} else if (value instanceof String[]) {
// "key": [ "string a",
// "string b",
// "string c"
// ]
StringBuilder builder = new StringBuilder(512);
String[] strings = (String[])value;
builder.append("\"" + key + "\": [\n");
for (String string : strings) {
builder.append(" \"" + string + "\"");
if (string != strings[strings.length - 1]) {
builder.append(",");
}
builder.append("\n");
}
builder.append(" ]");
formatted = builder.toString();
} else if (value instanceof byte[]) {
formatted = "\"" + key + "\": \"" +
Utilities.toHexString((byte[])value) + "\"";
} else if (value instanceof Byte) {
formatted = "\"" + key + "\": \"" +
Utilities.toHexString((byte)value) + "\"";
} else {
formatted = "\"" + key + "\": " +
"\"" + value.toString() + "\"";
}
return Utilities.indent(formatted);
}
private static String formatObject(Object obj) {
return obj.toString();
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2018, 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.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import sun.security.internal.spec.TlsMasterSecretParameterSpec;
import sun.security.ssl.CipherSuite.HashAlg;
import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator {
SSL30 ("kdf_ssl30"),
TLS10 ("kdf_tls10"),
TLS12 ("kdf_tls12");
final String name;
private SSLMasterKeyDerivation(String name) {
this.name = name;
}
static SSLMasterKeyDerivation valueOf(ProtocolVersion protocolVersion) {
switch (protocolVersion) {
case SSL30:
return SSLMasterKeyDerivation.SSL30;
case TLS10:
case TLS11:
return SSLMasterKeyDerivation.TLS10;
case TLS12:
return SSLMasterKeyDerivation.TLS12;
default:
return null;
}
}
@Override
public SSLKeyDerivation createKeyDerivation(HandshakeContext context,
SecretKey secretKey) throws IOException {
return new LegacyMasterKeyDerivation(context, secretKey);
}
// Note, we may use different key derivation implementation in the future.
private static final
class LegacyMasterKeyDerivation implements SSLKeyDerivation {
final HandshakeContext context;
final SecretKey preMasterSecret;
LegacyMasterKeyDerivation(
HandshakeContext context, SecretKey preMasterSecret) {
this.context = context;
this.preMasterSecret = preMasterSecret;
}
@Override
@SuppressWarnings("deprecation")
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
CipherSuite cipherSuite = context.negotiatedCipherSuite;
ProtocolVersion protocolVersion = context.negotiatedProtocol;
// What algs/params do we need to use?
String masterAlg;
HashAlg hashAlg;
byte majorVersion = protocolVersion.major;
byte minorVersion = protocolVersion.minor;
if (protocolVersion.id >= ProtocolVersion.TLS12.id) {
masterAlg = "SunTls12MasterSecret";
hashAlg = cipherSuite.hashAlg;
} else {
masterAlg = "SunTlsMasterSecret";
hashAlg = H_NONE;
}
TlsMasterSecretParameterSpec spec;
if (context.handshakeSession.useExtendedMasterSecret) {
// reset to use the extended master secret algorithm
masterAlg = "SunTlsExtendedMasterSecret";
// For the session hash, use the handshake messages up to and
// including the ClientKeyExchange message.
context.handshakeHash.utilize();
byte[] sessionHash = context.handshakeHash.digest();
spec = new TlsMasterSecretParameterSpec(
preMasterSecret,
(majorVersion & 0xFF), (minorVersion & 0xFF),
sessionHash,
hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
} else {
spec = new TlsMasterSecretParameterSpec(
preMasterSecret,
(majorVersion & 0xFF), (minorVersion & 0xFF),
context.clientHelloRandom.randomBytes,
context.serverHelloRandom.randomBytes,
hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
}
try {
KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
kg.init(spec);
return kg.generateKey();
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException iae) {
// unlikely to happen, otherwise, must be a provider exception
//
// For RSA premaster secrets, do not signal a protocol error
// due to the Bleichenbacher attack. See comments further down.
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
SSLLogger.fine("RSA master secret generation error.", iae);
}
throw new ProviderException(iae);
}
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018, 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;
interface SSLPossession {
default byte[] encode() {
return new byte[0];
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018, 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;
interface SSLPossessionGenerator {
SSLPossession createPossession(HandshakeContext handshakeContext);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018, 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;
interface SSLProducer {
// return the encoded producing if it has not been dumped to the context
byte[] produce(ConnectionContext context) throws IOException;
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 1996, 2018, 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;
/**
* SSL/TLS record
*
* @author David Brownell
*/
interface SSLRecord extends Record {
static final int headerSize = 5; // SSLv3 record header
static final int handshakeHeaderSize = 4; // SSLv3 handshake header
/*
* The size of the header plus the max IV length
*/
static final int headerPlusMaxIVSize =
headerSize // header
+ maxIVLength; // iv
/*
* The maximum size that may be increased when translating plaintext to
* ciphertext fragment.
*/
static final int maxPlaintextPlusSize =
headerSize // header
+ maxIVLength // iv
+ maxMacSize // MAC or AEAD tag
+ maxPadding; // block cipher padding
/*
* SSL has a maximum record size. It's header, (compressed) data,
* padding, and a trailer for the message authentication information (MAC
* for block and stream ciphers, and message authentication tag for AEAD
* ciphers).
*
* Some compression algorithms have rare cases where they expand the data.
* As we don't support compression at this time, leave that out.
*/
static final int maxRecordSize =
headerPlusMaxIVSize // header + iv
+ maxDataSize // data
+ maxPadding // padding
+ maxMacSize; // MAC or AEAD tag
/*
* The maximum large record size.
*
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
* such as Microsoft. We support large incoming fragments.
*
* The maximum large record size is defined as maxRecordSize plus 2^14,
* this is the amount OpenSSL is using.
*/
static final int maxLargeRecordSize =
maxRecordSize // Max size with a conforming implementation
+ maxDataSize; // extra 2^14 bytes for large data packets.
/*
* We may need to send this SSL v2 "No Cipher" message back, if we
* are faced with an SSLv2 "hello" that's not saying "I talk v3".
* It's the only one documented in the V2 spec as a fatal error.
*/
static final byte[] v2NoCipher = {
(byte)0x80, (byte)0x03, // unpadded 3 byte record
(byte)0x00, // ... error message
(byte)0x00, (byte)0x01 // ... NO_CIPHER error
};
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2018, 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.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.CipherSuite.HashAlg;
final class SSLSecretDerivation implements SSLKeyDerivation {
private static final byte[] sha256EmptyDigest = new byte[] {
(byte)0xE3, (byte)0xB0, (byte)0xC4, (byte)0x42,
(byte)0x98, (byte)0xFC, (byte)0x1C, (byte)0x14,
(byte)0x9A, (byte)0xFB, (byte)0xF4, (byte)0xC8,
(byte)0x99, (byte)0x6F, (byte)0xB9, (byte)0x24,
(byte)0x27, (byte)0xAE, (byte)0x41, (byte)0xE4,
(byte)0x64, (byte)0x9B, (byte)0x93, (byte)0x4C,
(byte)0xA4, (byte)0x95, (byte)0x99, (byte)0x1B,
(byte)0x78, (byte)0x52, (byte)0xB8, (byte)0x55
};
private static final byte[] sha384EmptyDigest = new byte[] {
(byte)0x38, (byte)0xB0, (byte)0x60, (byte)0xA7,
(byte)0x51, (byte)0xAC, (byte)0x96, (byte)0x38,
(byte)0x4C, (byte)0xD9, (byte)0x32, (byte)0x7E,
(byte)0xB1, (byte)0xB1, (byte)0xE3, (byte)0x6A,
(byte)0x21, (byte)0xFD, (byte)0xB7, (byte)0x11,
(byte)0x14, (byte)0xBE, (byte)0x07, (byte)0x43,
(byte)0x4C, (byte)0x0C, (byte)0xC7, (byte)0xBF,
(byte)0x63, (byte)0xF6, (byte)0xE1, (byte)0xDA,
(byte)0x27, (byte)0x4E, (byte)0xDE, (byte)0xBF,
(byte)0xE7, (byte)0x6F, (byte)0x65, (byte)0xFB,
(byte)0xD5, (byte)0x1A, (byte)0xD2, (byte)0xF1,
(byte)0x48, (byte)0x98, (byte)0xB9, (byte)0x5B
};
private final HandshakeContext context;
private final String hkdfAlg;
private final HashAlg hashAlg;
private final SecretKey secret;
private final byte[] transcriptHash; // handshake messages transcript hash
SSLSecretDerivation(
HandshakeContext context, SecretKey secret) {
this.context = context;
this.secret = secret;
this.hashAlg = context.negotiatedCipherSuite.hashAlg;
this.hkdfAlg =
"HKDF-Expand/Hmac" + hashAlg.name.replace("-", "");
context.handshakeHash.update();
this.transcriptHash = context.handshakeHash.digest();
}
SSLSecretDerivation forContext(HandshakeContext context) {
return new SSLSecretDerivation(context, secret);
}
@Override
public SecretKey deriveKey(String algorithm,
AlgorithmParameterSpec params) throws IOException {
SecretSchedule ks = SecretSchedule.valueOf(algorithm);
try {
byte[] expandContext;
if (ks == SecretSchedule.TlsSaltSecret) {
if (hashAlg == HashAlg.H_SHA256) {
expandContext = sha256EmptyDigest;
} else if (hashAlg == HashAlg.H_SHA384) {
expandContext = sha384EmptyDigest;
} else {
// unlikely, but please update if more hash algorithm
// get supported in the future.
throw new SSLHandshakeException(
"Unexpected unsupported hash algorithm: " +
algorithm);
}
} else {
expandContext = transcriptHash;
}
byte[] hkdfInfo = createHkdfInfo(ks.label,
expandContext, hashAlg.hashLength);
HKDF hkdf = new HKDF(hashAlg.name);
return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm);
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(gse);
}
}
public static byte[] createHkdfInfo(
byte[] label, byte[] context, int length) {
byte[] info = new byte[4 + label.length + context.length];
ByteBuffer m = ByteBuffer.wrap(info);
try {
Record.putInt16(m, length);
Record.putBytes8(m, label);
Record.putBytes8(m, context);
} catch (IOException ioe) {
// unlikely
throw new RuntimeException("Unexpected exception", ioe);
}
return info;
}
private enum SecretSchedule {
// Note that we use enum name as the key/secret name.
TlsSaltSecret ("derived"),
TlsExtBinderKey ("ext binder"),
TlsResBinderKey ("res binder"),
TlsClientEarlyTrafficSecret ("c e traffic"),
TlsEarlyExporterMasterSecret ("e exp master"),
TlsClientHandshakeTrafficSecret ("c hs traffic"),
TlsServerHandshakeTrafficSecret ("s hs traffic"),
TlsClientAppTrafficSecret ("c ap traffic"),
TlsServerAppTrafficSecret ("s ap traffic"),
TlsExporterMasterSecret ("exp master"),
TlsResumptionMasterSecret ("res master");
private final byte[] label;
private SecretSchedule(String label) {
this.label = ("tls13 " + label).getBytes();
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 1997, 2018, 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.net.InetAddress;
import java.net.ServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
/**
* This class creates SSL server sockets.
*
* @author David Brownell
*/
public final class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
private static final int DEFAULT_BACKLOG = 50;
private final SSLContextImpl context;
/**
* Constructor used to instantiate the default factory. This method is
* only called if the old "ssl.ServerSocketFactory.provider" property in the
* java.security file is set.
*/
public SSLServerSocketFactoryImpl() throws Exception {
this.context = SSLContextImpl.DefaultSSLContext.getDefaultImpl();
}
/**
* Called from SSLContextImpl's getSSLServerSocketFactory().
*/
SSLServerSocketFactoryImpl(SSLContextImpl context) {
this.context = context;
}
/**
* Returns an unbound server socket.
*
* @return the unbound socket
* @throws IOException if the socket cannot be created
* @see java.net.Socket#bind(java.net.SocketAddress)
*/
@Override
public ServerSocket createServerSocket() throws IOException {
return new SSLServerSocketImpl(context);
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
return new SSLServerSocketImpl(context, port, DEFAULT_BACKLOG);
}
@Override
public ServerSocket createServerSocket (
int port, int backlog) throws IOException {
return new SSLServerSocketImpl(context, port, backlog);
}
@Override
public ServerSocket
createServerSocket (int port,
int backlog, InetAddress ifAddress) throws IOException {
return new SSLServerSocketImpl(context, port, backlog, ifAddress);
}
/**
* Returns the subset of the supported cipher suites which are
* enabled by default. These cipher suites all provide a minimum
* quality of service whereby the server authenticates itself
* (preventing person-in-the-middle attacks) and where traffic
* is encrypted to provide confidentiality.
*/
@Override
public String[] getDefaultCipherSuites() {
return CipherSuite.namesOf(context.getDefaultCipherSuites(true));
}
/**
* Returns the names of the cipher suites which could be enabled for use
* on an SSL connection. Normally, only a subset of these will actually
* be enabled by default, since this list may include cipher suites which
* do not support the mutual authentication of servers and clients, or
* which do not protect data confidentiality. Servers may also need
* certain kinds of certificates to use certain cipher suites.
*
* @return an array of cipher suite names
*/
@Override
public String[] getSupportedCipherSuites() {
return CipherSuite.namesOf(context.getSupportedCipherSuites());
}
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (c) 1996, 2018, 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.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLServerSocket;
/**
* This class provides a simple way for servers to support conventional
* use of the Secure Sockets Layer (SSL). Application code uses an
* SSLServerSocketImpl exactly like it uses a regular TCP ServerSocket; the
* difference is that the connections established are secured using SSL.
*
* <P> Also, the constructors take an explicit authentication context
* parameter, giving flexibility with respect to how the server socket
* authenticates itself. That policy flexibility is not exposed through
* the standard SSLServerSocketFactory API.
*
* <P> System security defaults prevent server sockets from accepting
* connections if they the authentication context has not been given
* a certificate chain and its matching private key. If the clients
* of your application support "anonymous" cipher suites, you may be
* able to configure a server socket to accept those suites.
*
* @see SSLSocketImpl
* @see SSLServerSocketFactoryImpl
*
* @author David Brownell
*/
final class SSLServerSocketImpl extends SSLServerSocket {
private final SSLContextImpl sslContext;
private final SSLConfiguration sslConfig;
SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException {
super();
this.sslContext = sslContext;
this.sslConfig = new SSLConfiguration(sslContext, false);
}
SSLServerSocketImpl(SSLContextImpl sslContext,
int port, int backlog) throws IOException {
super(port, backlog);
this.sslContext = sslContext;
this.sslConfig = new SSLConfiguration(sslContext, false);
}
SSLServerSocketImpl(SSLContextImpl sslContext,
int port, int backlog, InetAddress address) throws IOException {
super(port, backlog, address);
this.sslContext = sslContext;
this.sslConfig = new SSLConfiguration(sslContext, false);
}
@Override
public synchronized String[] getEnabledCipherSuites() {
return CipherSuite.namesOf(sslConfig.enabledCipherSuites);
}
@Override
public synchronized void setEnabledCipherSuites(String[] suites) {
sslConfig.enabledCipherSuites =
CipherSuite.validValuesOf(suites);
}
@Override
public String[] getSupportedCipherSuites() {
return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
}
@Override
public String[] getSupportedProtocols() {
return ProtocolVersion.toStringArray(
sslContext.getSupportedProtocolVersions());
}
@Override
public synchronized String[] getEnabledProtocols() {
return ProtocolVersion.toStringArray(sslConfig.enabledProtocols);
}
@Override
public synchronized void setEnabledProtocols(String[] protocols) {
if (protocols == null) {
throw new IllegalArgumentException("Protocols cannot be null");
}
sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols);
}
@Override
public synchronized void setNeedClientAuth(boolean need) {
sslConfig.clientAuthType =
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getNeedClientAuth() {
return (sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUIRED);
}
@Override
public synchronized void setWantClientAuth(boolean want) {
sslConfig.clientAuthType =
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
ClientAuthType.CLIENT_AUTH_NONE);
}
@Override
public synchronized boolean getWantClientAuth() {
return (sslConfig.clientAuthType ==
ClientAuthType.CLIENT_AUTH_REQUESTED);
}
@Override
public synchronized void setUseClientMode(boolean useClientMode) {
/*
* If we need to change the client mode and the enabled
* protocols and cipher suites haven't specifically been
* set by the user, change them to the corresponding
* default ones.
*/
if (sslConfig.isClientMode != useClientMode) {
if (sslContext.isDefaultProtocolVesions(
sslConfig.enabledProtocols)) {
sslConfig.enabledProtocols =
sslContext.getDefaultProtocolVersions(!useClientMode);
}
if (sslContext.isDefaultCipherSuiteList(
sslConfig.enabledCipherSuites)) {
sslConfig.enabledCipherSuites =
sslContext.getDefaultCipherSuites(!useClientMode);
}
sslConfig.toggleClientMode();
}
}
@Override
public synchronized boolean getUseClientMode() {
return sslConfig.isClientMode;
}
@Override
public synchronized void setEnableSessionCreation(boolean flag) {
sslConfig.enableSessionCreation = flag;
}
@Override
public synchronized boolean getEnableSessionCreation() {
return sslConfig.enableSessionCreation;
}
@Override
public synchronized SSLParameters getSSLParameters() {
return sslConfig.getSSLParameters();
}
@Override
public synchronized void setSSLParameters(SSLParameters params) {
sslConfig.setSSLParameters(params);
}
@Override
public Socket accept() throws IOException {
SSLSocketImpl s = new SSLSocketImpl(sslContext, sslConfig);
implAccept(s);
s.doneConnect();
return s;
}
@Override
public String toString() {
return "[SSL: "+ super.toString() + "]";
}
}

View File

@@ -0,0 +1,272 @@
/*
* Copyright (c) 1999, 2018, 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.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import sun.security.action.GetIntegerAction;
import sun.security.util.Cache;
final class SSLSessionContextImpl implements SSLSessionContext {
private final static int DEFAULT_MAX_CACHE_SIZE = 20480;
private final Cache<SessionId, SSLSessionImpl> sessionCache;
// session cache, session id as key
private final Cache<String, SSLSessionImpl> sessionHostPortCache;
// session cache, "host:port" as key
private int cacheLimit; // the max cache size
private int timeout; // timeout in seconds
// package private
SSLSessionContextImpl() {
cacheLimit = getDefaultCacheLimit(); // default cache size
timeout = 86400; // default, 24 hours
// use soft reference
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
}
/**
* Returns the <code>SSLSession</code> bound to the specified session id.
*/
@Override
public SSLSession getSession(byte[] sessionId) {
if (sessionId == null) {
throw new NullPointerException("session id cannot be null");
}
SSLSessionImpl sess = sessionCache.get(new SessionId(sessionId));
if (!isTimedout(sess)) {
return sess;
}
return null;
}
/**
* Returns an enumeration of the active SSL sessions.
*/
@Override
public Enumeration<byte[]> getIds() {
SessionCacheVisitor scVisitor = new SessionCacheVisitor();
sessionCache.accept(scVisitor);
return scVisitor.getSessionIds();
}
/**
* Sets the timeout limit for cached <code>SSLSession</code> objects
*
* Note that after reset the timeout, the cached session before
* should be timed within the shorter one of the old timeout and the
* new timeout.
*/
@Override
public void setSessionTimeout(int seconds)
throws IllegalArgumentException {
if (seconds < 0) {
throw new IllegalArgumentException();
}
if (timeout != seconds) {
sessionCache.setTimeout(seconds);
sessionHostPortCache.setTimeout(seconds);
timeout = seconds;
}
}
/**
* Gets the timeout limit for cached <code>SSLSession</code> objects
*/
@Override
public int getSessionTimeout() {
return timeout;
}
/**
* Sets the size of the cache used for storing
* <code>SSLSession</code> objects.
*/
@Override
public void setSessionCacheSize(int size)
throws IllegalArgumentException {
if (size < 0)
throw new IllegalArgumentException();
if (cacheLimit != size) {
sessionCache.setCapacity(size);
sessionHostPortCache.setCapacity(size);
cacheLimit = size;
}
}
/**
* Gets the size of the cache used for storing
* <code>SSLSession</code> objects.
*/
@Override
public int getSessionCacheSize() {
return cacheLimit;
}
// package-private method, used ONLY by ServerHandshaker
SSLSessionImpl get(byte[] id) {
return (SSLSessionImpl)getSession(id);
}
// package-private method, find and remove session from cache
// return found session
SSLSessionImpl pull(byte[] id) {
if (id != null) {
return sessionCache.pull(new SessionId(id));
}
return null;
}
// package-private method, used ONLY by ClientHandshaker
SSLSessionImpl get(String hostname, int port) {
/*
* If no session caching info is available, we won't
* get one, so exit before doing a lookup.
*/
if (hostname == null && port == -1) {
return null;
}
SSLSessionImpl sess = sessionHostPortCache.get(getKey(hostname, port));
if (!isTimedout(sess)) {
return sess;
}
return null;
}
private static String getKey(String hostname, int port) {
return (hostname + ":" +
String.valueOf(port)).toLowerCase(Locale.ENGLISH);
}
// cache a SSLSession
//
// In SunJSSE implementation, a session is created while getting a
// client hello or a server hello message, and cached while the
// handshaking finished.
// Here we time the session from the time it cached instead of the
// time it created, which is a little longer than the expected. So
// please do check isTimedout() while getting entry from the cache.
void put(SSLSessionImpl s) {
sessionCache.put(s.getSessionId(), s);
// If no hostname/port info is available, don't add this one.
if ((s.getPeerHost() != null) && (s.getPeerPort() != -1)) {
sessionHostPortCache.put(
getKey(s.getPeerHost(), s.getPeerPort()), s);
}
s.setContext(this);
}
// package-private method, remove a cached SSLSession
void remove(SessionId key) {
SSLSessionImpl s = sessionCache.get(key);
if (s != null) {
sessionCache.remove(key);
sessionHostPortCache.remove(
getKey(s.getPeerHost(), s.getPeerPort()));
}
}
private static int getDefaultCacheLimit() {
try {
int defaultCacheLimit = AccessController.doPrivileged(
new GetIntegerAction("javax.net.ssl.sessionCacheSize",
DEFAULT_MAX_CACHE_SIZE));
if (defaultCacheLimit >= 0) {
return defaultCacheLimit;
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning(
"invalid System Property javax.net.ssl.sessionCacheSize, " +
"use the default session cache size (" +
DEFAULT_MAX_CACHE_SIZE + ") instead");
}
} catch (Exception e) {
// unlikely, log it for safe
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning(
"the System Property javax.net.ssl.sessionCacheSize is " +
"not available, use the default value (" +
DEFAULT_MAX_CACHE_SIZE + ") instead");
}
}
return DEFAULT_MAX_CACHE_SIZE;
}
private boolean isTimedout(SSLSession sess) {
if (timeout == 0) {
return false;
}
if ((sess != null) && ((sess.getCreationTime() + timeout * 1000L)
<= (System.currentTimeMillis()))) {
sess.invalidate();
return true;
}
return false;
}
private final class SessionCacheVisitor
implements Cache.CacheVisitor<SessionId, SSLSessionImpl> {
ArrayList<byte[]> ids = null;
// public void visit(java.util.Map<K,V> map) {}
@Override
public void visit(java.util.Map<SessionId, SSLSessionImpl> map) {
ids = new ArrayList<>(map.size());
for (SessionId key : map.keySet()) {
SSLSessionImpl value = map.get(key);
if (!isTimedout(value)) {
ids.add(key.getId());
}
}
}
Enumeration<byte[]> getSessionIds() {
return ids != null ? Collections.enumeration(ids) :
Collections.emptyEnumeration();
}
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More