8196584: TLS 1.3 Implementation

Co-authored-by: Adam Petcher <adam.petcher@oracle.com>
Co-authored-by: Amanda Jiang <amanda.jiang@oracle.com>
Co-authored-by: Anthony Scarpino <anthony.scarpino@oracle.com>
Co-authored-by: Bradford Wetmore <bradford.wetmore@oracle.com>
Co-authored-by: Jamil Nimeh <jamil.j.nimeh@oracle.com>
Co-authored-by: John Jiang <sha.jiang@oracle.com>
Co-authored-by: Rajan Halade <rajan.halade@oracle.com>
Co-authored-by: Sibabrata Sahoo <sibabrata.sahoo@oracle.com>
Co-authored-by: Valerie Peng <valerie.peng@oracle.com>
Co-authored-by: Weijun Wang <weijun.wang@oracle.com>
Reviewed-by: ascarpino, coffeys, dfuchs, jjiang, jnimeh, mullan, rhalade, ssahoo, valeriep, weijun, wetmore, xuelei
This commit is contained in:
Xue-Lei Andrew Fan 2018-06-25 13:41:39 -07:00
parent c7c819cd8b
commit 87c6761704
262 changed files with 44368 additions and 32552 deletions

View file

@ -95,7 +95,7 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
premasterMajor = premaster[0] & 0xff;
premasterMinor = premaster[1] & 0xff;
} else {
// DH, KRB5, others
// DH, others
premasterMajor = -1;
premasterMinor = -1;
}

View file

@ -113,27 +113,19 @@ class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
* In com.sun.net.ssl.HostnameVerifier the method is defined
* as verify(String urlHostname, String certHostname).
* This means we need to extract the hostname from the X.509 certificate
* or from the Kerberos principal name, in this wrapper.
* in this wrapper.
*/
public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
try {
String serverName;
// Use ciphersuite to determine whether Kerberos is active.
if (session.getCipherSuite().startsWith("TLS_KRB5")) {
serverName =
HostnameChecker.getServerName(getPeerPrincipal(session));
} else { // X.509
Certificate[] serverChain = session.getPeerCertificates();
if ((serverChain == null) || (serverChain.length == 0)) {
return false;
}
if (serverChain[0] instanceof X509Certificate == false) {
return false;
}
X509Certificate serverCert = (X509Certificate)serverChain[0];
serverName = getServername(serverCert);
Certificate[] serverChain = session.getPeerCertificates();
if ((serverChain == null) || (serverChain.length == 0)) {
return false;
}
if (serverChain[0] instanceof X509Certificate == false) {
return false;
}
X509Certificate serverCert = (X509Certificate)serverChain[0];
String serverName = getServername(serverCert);
if (serverName == null) {
return false;
}
@ -143,23 +135,6 @@ class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
}
}
/*
* Get the peer principal from the session
*/
private Principal getPeerPrincipal(javax.net.ssl.SSLSession session)
throws javax.net.ssl.SSLPeerUnverifiedException
{
Principal principal;
try {
principal = session.getPeerPrincipal();
} catch (AbstractMethodError e) {
// if the provider does not support it, return null, since
// we need it only for Kerberos.
principal = null;
}
return principal;
}
/*
* Extract the name of the SSL server from the certificate.
*

View file

@ -360,7 +360,6 @@ module java.base {
// JDK-internal service types
uses jdk.internal.logger.DefaultLoggerFinder;
uses sun.security.ssl.ClientKeyExchangeService;
uses sun.text.spi.JavaTimeDateTimePatternProvider;
uses sun.util.spi.CalendarProvider;
uses sun.util.locale.provider.LocaleDataMetaInfo;

View file

@ -608,26 +608,17 @@ final class HttpsClient extends HttpClient
HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_TLS);
// Use ciphersuite to determine whether Kerberos is present.
if (cipher.startsWith("TLS_KRB5")) {
if (!HostnameChecker.match(host, getPeerPrincipal())) {
throw new SSLPeerUnverifiedException("Hostname checker" +
" failed for Kerberos");
}
} else { // X.509
// get the subject's certificate
peerCerts = session.getPeerCertificates();
// get the subject's certificate
peerCerts = session.getPeerCertificates();
X509Certificate peerCert;
if (peerCerts[0] instanceof
java.security.cert.X509Certificate) {
peerCert = (java.security.cert.X509Certificate)peerCerts[0];
} else {
throw new SSLPeerUnverifiedException("");
}
checker.match(host, peerCert);
X509Certificate peerCert;
if (peerCerts[0] instanceof
java.security.cert.X509Certificate) {
peerCert = (java.security.cert.X509Certificate)peerCerts[0];
} else {
throw new SSLPeerUnverifiedException("");
}
checker.match(host, peerCert);
// if it doesn't throw an exception, we passed. Return.
return;

View file

@ -1,168 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.nio.charset.*;
import java.util.*;
import javax.net.ssl.*;
/*
* [RFC 7301]
* This TLS extension facilitates the negotiation of application-layer protocols
* within the TLS handshake. Clients MAY include an extension of type
* "application_layer_protocol_negotiation" in the (extended) ClientHello
* message. The "extension_data" field of this extension SHALL contain a
* "ProtocolNameList" value:
*
* enum {
* application_layer_protocol_negotiation(16), (65535)
* } ExtensionType;
*
* opaque ProtocolName<1..2^8-1>;
*
* struct {
* ProtocolName protocol_name_list<2..2^16-1>
* } ProtocolNameList;
*/
final class ALPNExtension extends HelloExtension {
final static int ALPN_HEADER_LENGTH = 1;
final static int MAX_APPLICATION_PROTOCOL_LENGTH = 255;
final static int MAX_APPLICATION_PROTOCOL_LIST_LENGTH = 65535;
private int listLength = 0; // ProtocolNameList length
private List<String> protocolNames = null;
// constructor for ServerHello
ALPNExtension(String protocolName) throws SSLException {
this(new String[]{ protocolName });
}
// constructor for ClientHello
ALPNExtension(String[] protocolNames) throws SSLException {
super(ExtensionType.EXT_ALPN);
if (protocolNames.length == 0) { // never null, never empty
throw new IllegalArgumentException(
"The list of application protocols cannot be empty");
}
this.protocolNames = Arrays.asList(protocolNames);
for (String p : protocolNames) {
int length = p.getBytes(StandardCharsets.UTF_8).length;
if (length == 0) {
throw new SSLProtocolException(
"Application protocol name is empty");
}
if (length <= MAX_APPLICATION_PROTOCOL_LENGTH) {
listLength += length + ALPN_HEADER_LENGTH;
} else {
throw new SSLProtocolException(
"Application protocol name is too long: " + p);
}
if (listLength > MAX_APPLICATION_PROTOCOL_LIST_LENGTH) {
throw new SSLProtocolException(
"Application protocol name list is too long");
}
}
}
// constructor for ServerHello for parsing ALPN extension
ALPNExtension(HandshakeInStream s, int len) throws IOException {
super(ExtensionType.EXT_ALPN);
if (len >= 2) {
listLength = s.getInt16(); // list length
if (listLength < 2 || listLength + 2 != len) {
throw new SSLProtocolException(
"Invalid " + type + " extension: incorrect list length " +
"(length=" + listLength + ")");
}
} else {
throw new SSLProtocolException(
"Invalid " + type + " extension: insufficient data " +
"(length=" + len + ")");
}
int remaining = listLength;
this.protocolNames = new ArrayList<>();
while (remaining > 0) {
// opaque ProtocolName<1..2^8-1>; // RFC 7301
byte[] bytes = s.getBytes8();
if (bytes.length == 0) {
throw new SSLProtocolException("Invalid " + type +
" extension: empty application protocol name");
}
String p =
new String(bytes, StandardCharsets.UTF_8); // app protocol
protocolNames.add(p);
remaining -= bytes.length + ALPN_HEADER_LENGTH;
}
if (remaining != 0) {
throw new SSLProtocolException(
"Invalid " + type + " extension: extra data " +
"(length=" + remaining + ")");
}
}
List<String> getPeerAPs() {
return protocolNames;
}
/*
* Return the length in bytes, including extension type and length fields.
*/
@Override
int length() {
return 6 + listLength;
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(listLength + 2); // length of extension_data
s.putInt16(listLength); // length of ProtocolNameList
for (String p : protocolNames) {
s.putBytes8(p.getBytes(StandardCharsets.UTF_8));
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (protocolNames == null || protocolNames.isEmpty()) {
sb.append("<empty>");
} else {
for (String protocolName : protocolNames) {
sb.append("[" + protocolName + "]");
}
}
return "Extension " + type +
", protocol names: " + sb;
}
}

View file

@ -0,0 +1,270 @@
/*
* 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.text.MessageFormat;
import java.util.Locale;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
/**
* SSL/(D)TLS Alter description
*/
enum Alert {
// Please refer to TLS Alert Registry for the latest (D)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_CERTIFCATE ((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 = handshakeOnly ?
new SSLHandshakeException(reason) : new SSLException(reason);
if (cause != null) {
ssle.initCause(cause);
}
return ssle;
}
/**
* SSL/(D)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) {
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) {
if (tc.handshakeContext != null) {
tc.fatal(Alert.UNEXPECTED_MESSAGE,
"Received close_notify during handshake");
}
tc.isInputCloseNotified = true;
tc.closeInbound();
} 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)) {
tc.fatal(Alert.HANDSHAKE_FAILURE,
"received handshake warning: " + alert.description);
} // Otherwise, ignore the warning
} // 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;
}
tc.fatal(alert, diagnostic, true, null);
}
}
}
}

View file

@ -1,223 +0,0 @@
/*
* Copyright (c) 2003, 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;
import javax.net.ssl.*;
/*
* A simple class to congregate alerts, their definitions, and common
* support methods.
*/
final class Alerts {
/*
* Alerts are always a fixed two byte format (level/description).
*/
// warnings and fatal errors are package private facilities/constants
// Alert levels (enum AlertLevel)
static final byte alert_warning = 1;
static final byte alert_fatal = 2;
/*
* Alert descriptions (enum AlertDescription)
*
* We may not use them all in our processing, but if someone
* sends us one, we can at least convert it to a string for the
* user.
*/
static final byte alert_close_notify = 0;
static final byte alert_unexpected_message = 10;
static final byte alert_bad_record_mac = 20;
static final byte alert_decryption_failed = 21;
static final byte alert_record_overflow = 22;
static final byte alert_decompression_failure = 30;
static final byte alert_handshake_failure = 40;
static final byte alert_no_certificate = 41;
static final byte alert_bad_certificate = 42;
static final byte alert_unsupported_certificate = 43;
static final byte alert_certificate_revoked = 44;
static final byte alert_certificate_expired = 45;
static final byte alert_certificate_unknown = 46;
static final byte alert_illegal_parameter = 47;
static final byte alert_unknown_ca = 48;
static final byte alert_access_denied = 49;
static final byte alert_decode_error = 50;
static final byte alert_decrypt_error = 51;
static final byte alert_export_restriction = 60;
static final byte alert_protocol_version = 70;
static final byte alert_insufficient_security = 71;
static final byte alert_internal_error = 80;
static final byte alert_user_canceled = 90;
static final byte alert_no_renegotiation = 100;
// from RFC 3546 (TLS Extensions)
static final byte alert_unsupported_extension = 110;
static final byte alert_certificate_unobtainable = 111;
static final byte alert_unrecognized_name = 112;
static final byte alert_bad_certificate_status_response = 113;
static final byte alert_bad_certificate_hash_value = 114;
// from RFC 7301 (TLS ALPN Extension)
static final byte alert_no_application_protocol = 120;
static String alertDescription(byte code) {
switch (code) {
case alert_close_notify:
return "close_notify";
case alert_unexpected_message:
return "unexpected_message";
case alert_bad_record_mac:
return "bad_record_mac";
case alert_decryption_failed:
return "decryption_failed";
case alert_record_overflow:
return "record_overflow";
case alert_decompression_failure:
return "decompression_failure";
case alert_handshake_failure:
return "handshake_failure";
case alert_no_certificate:
return "no_certificate";
case alert_bad_certificate:
return "bad_certificate";
case alert_unsupported_certificate:
return "unsupported_certificate";
case alert_certificate_revoked:
return "certificate_revoked";
case alert_certificate_expired:
return "certificate_expired";
case alert_certificate_unknown:
return "certificate_unknown";
case alert_illegal_parameter:
return "illegal_parameter";
case alert_unknown_ca:
return "unknown_ca";
case alert_access_denied:
return "access_denied";
case alert_decode_error:
return "decode_error";
case alert_decrypt_error:
return "decrypt_error";
case alert_export_restriction:
return "export_restriction";
case alert_protocol_version:
return "protocol_version";
case alert_insufficient_security:
return "insufficient_security";
case alert_internal_error:
return "internal_error";
case alert_user_canceled:
return "user_canceled";
case alert_no_renegotiation:
return "no_renegotiation";
case alert_unsupported_extension:
return "unsupported_extension";
case alert_certificate_unobtainable:
return "certificate_unobtainable";
case alert_unrecognized_name:
return "unrecognized_name";
case alert_bad_certificate_status_response:
return "bad_certificate_status_response";
case alert_bad_certificate_hash_value:
return "bad_certificate_hash_value";
case alert_no_application_protocol:
return "no_application_protocol";
default:
return "<UNKNOWN ALERT: " + (code & 0x0ff) + ">";
}
}
static SSLException getSSLException(byte description, String reason) {
return getSSLException(description, null, reason);
}
/*
* Try to be a little more specific in our choice of
* exceptions to throw.
*/
static SSLException getSSLException(byte description, Throwable cause,
String reason) {
SSLException e;
// the SSLException classes do not have a no-args constructor
// make up a message if there is none
if (reason == null) {
if (cause != null) {
reason = cause.toString();
} else {
reason = "";
}
}
switch (description) {
case alert_handshake_failure:
case alert_no_certificate:
case alert_bad_certificate:
case alert_unsupported_certificate:
case alert_certificate_revoked:
case alert_certificate_expired:
case alert_certificate_unknown:
case alert_unknown_ca:
case alert_access_denied:
case alert_decrypt_error:
case alert_export_restriction:
case alert_insufficient_security:
case alert_unsupported_extension:
case alert_certificate_unobtainable:
case alert_unrecognized_name:
case alert_bad_certificate_status_response:
case alert_bad_certificate_hash_value:
case alert_no_application_protocol:
e = new SSLHandshakeException(reason);
break;
case alert_close_notify:
case alert_unexpected_message:
case alert_bad_record_mac:
case alert_decryption_failed:
case alert_record_overflow:
case alert_decompression_failure:
case alert_illegal_parameter:
case alert_decode_error:
case alert_protocol_version:
case alert_internal_error:
case alert_user_canceled:
case alert_no_renegotiation:
default:
e = new SSLException(reason);
break;
}
if (cause != null) {
e.initCause(cause);
}
return e;
}
}

View file

@ -0,0 +1,514 @@
/*
* 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.StandardCharsets;
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();
/**
* 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, StandardCharsets.UTF_8);
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.
int listLength = 0; // ProtocolNameList length
for (String ap : laps) {
int length = ap.getBytes(StandardCharsets.UTF_8).length;
if (length == 0) {
// log the configuration problem
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.severe(
"Application protocol name cannot be empty");
}
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)");
}
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)");
}
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);
for (String ap : laps) {
Record.putBytes8(m, ap.getBytes(StandardCharsets.UTF_8));
}
// 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// 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) {
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))) {
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))) {
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.
int listLen = shc.applicationProtocol.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,
shc.applicationProtocol.getBytes(StandardCharsets.UTF_8));
// 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()) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// Only one application protocol is allowed.
if (spec.applicationProtocols.size() != 1) {
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)) {
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

@ -1,212 +0,0 @@
/*
* Copyright (c) 1996, 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;
import java.io.InputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLProtocolException;
/**
* InputStream for application data as returned by SSLSocket.getInputStream().
*
* @author David Brownell
*/
final class AppInputStream extends InputStream {
// the buffer size for each read of network data
private static final int READ_BUFFER_SIZE = 4096;
// static dummy array we use to implement skip()
private static final byte[] SKIP_ARRAY = new byte[256];
// the related socket of the input stream
private final SSLSocketImpl socket;
// the temporary buffer used to read network
private ByteBuffer buffer;
// Is application data available in the stream?
private boolean appDataIsAvailable;
// One element array used to implement the single byte read() method
private final byte[] oneByte = new byte[1];
AppInputStream(SSLSocketImpl conn) {
this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
this.socket = conn;
this.appDataIsAvailable = false;
}
/**
* Return the minimum number of bytes that can be read without blocking.
*
* Currently not synchronized.
*/
@Override
public int available() throws IOException {
if ((!appDataIsAvailable) || socket.checkEOF()) {
return 0;
}
return buffer.remaining();
}
/**
* Read a single byte, returning -1 on non-fault EOF status.
*/
@Override
public synchronized int read() throws IOException {
int n = read(oneByte, 0, 1);
if (n <= 0) { // EOF
return -1;
}
return oneByte[0] & 0xFF;
}
/**
* Reads up to {@code len} bytes of data from the input stream into an
* array of bytes. An attempt is made to read as many as {@code len} bytes,
* but a smaller number may be read. The number of bytes actually read
* is returned as an integer.
*
* If the layer above needs more data, it asks for more, so we
* are responsible only for blocking to fill at most one buffer,
* and returning "-1" on non-fault EOF status.
*/
@Override
public synchronized int read(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (socket.checkEOF()) {
return -1;
}
// Read the available bytes at first.
int remains = available();
if (remains > 0) {
int howmany = Math.min(remains, len);
buffer.get(b, off, howmany);
return howmany;
}
appDataIsAvailable = false;
int volume = 0;
try {
/*
* Read data if needed ... notice that the connection guarantees
* that handshake, alert, and change cipher spec data streams are
* handled as they arrive, so we never see them here.
*/
while(volume == 0) {
// Clear the buffer for a new record reading.
buffer.clear();
//
// grow the buffer if needed
//
// Read the header of a record into the buffer, and return
// the packet size.
int packetLen = socket.bytesInCompletePacket();
if (packetLen < 0) { // EOF
return -1;
}
// Is this packet bigger than SSL/TLS normally allows?
if (packetLen > SSLRecord.maxLargeRecordSize) {
throw new SSLProtocolException(
"Illegal packet size: " + packetLen);
}
if (packetLen > buffer.remaining()) {
buffer = ByteBuffer.allocate(packetLen);
}
volume = socket.readRecord(buffer);
if (volume < 0) { // EOF
return -1;
} else if (volume > 0) {
appDataIsAvailable = true;
break;
}
}
int howmany = Math.min(len, volume);
buffer.get(b, off, howmany);
return howmany;
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
socket.handleException(e);
// dummy for compiler
return -1;
}
}
/**
* Skip n bytes. This implementation is somewhat less efficient
* than possible, but not badly so (redundant copy). We reuse
* the read() code to keep things simpler. Note that SKIP_ARRAY
* is static and may garbled by concurrent use, but we are not interested
* in the data anyway.
*/
@Override
public synchronized long skip(long n) throws IOException {
long skipped = 0;
while (n > 0) {
int len = (int)Math.min(n, SKIP_ARRAY.length);
int r = read(SKIP_ARRAY, 0, len);
if (r <= 0) {
break;
}
n -= r;
skipped += r;
}
return skipped;
}
/*
* Socket close is already synchronized, no need to block here.
*/
@Override
public void close() throws IOException {
socket.close();
}
// inherit default mark/reset behavior (throw Exceptions) from InputStream
}

View file

@ -1,93 +0,0 @@
/*
* Copyright (c) 1996, 2012, 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.OutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
/*
* OutputStream for application data as returned by SSLSocket.getOutputStream().
*
* @author David Brownell
*/
class AppOutputStream extends OutputStream {
private SSLSocketImpl socket;
// One element array used to implement the write(byte) method
private final byte[] oneByte = new byte[1];
AppOutputStream(SSLSocketImpl conn) {
this.socket = conn;
}
/**
* Write the data out, NOW.
*/
@Override
public synchronized void write(byte[] b, int off, int len)
throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
// check if the Socket is invalid (error or closed)
socket.checkWrite();
// Delegate the writing to the underlying socket.
try {
socket.writeRecord(b, off, len);
socket.checkWrite();
} catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate
socket.handleException(e);
}
}
/**
* Write one byte now.
*/
@Override
public synchronized void write(int i) throws IOException {
oneByte[0] = (byte)i;
write(oneByte, 0, 1);
}
/*
* Socket close is already synchronized, no need to block here.
*/
@Override
public void close() throws IOException {
socket.close();
}
// inherit no-op flush()
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,94 +25,80 @@
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/DTLS message authentication token,
* which encapsulates a sequence number and ensures that attempts to
* delete or reorder messages can be detected.
*
* Each connection state contains a sequence number, which is maintained
* separately for read and write states.
*
* For SSL/TLS protocols, the sequence number MUST be set to zero
* whenever a connection state is made the active state.
*
* DTLS uses an explicit sequence number, rather than an implicit one.
* Sequence numbers are maintained separately for each epoch, with
* each sequence number initially being 0 for each epoch. The sequence
* number used to compute the DTLS MAC is the 64-bit value formed by
* concatenating the epoch and the sequence number.
*
* Sequence numbers do not wrap. If an implementation would need to wrap
* a sequence number, it must renegotiate instead. A sequence number is
* incremented after each record: specifically, the first record transmitted
* under a particular connection state MUST use sequence number 0.
*/
class Authenticator {
abstract class Authenticator {
// byte array containing the additional authentication information for
// each record
private final byte[] block;
protected final byte[] block; // at least 8 bytes for sequence number
// the block size of SSL v3.0:
// sequence number + record type + + record length
private static final int BLOCK_SIZE_SSL = 8 + 1 + 2;
// the block size of TLS v1.0 and later:
// sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2;
// the block size of DTLS v1.0 and later:
// epoch + sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2;
private final boolean isDTLS;
/**
* Default construct, no message authentication token is initialized.
*
* Note that this construct can only be called for null MAC
*/
protected Authenticator(boolean isDTLS) {
if (isDTLS) {
// For DTLS protocols, plaintexts use explicit epoch and
// sequence number in each record. The first 8 byte of
// the block is initialized for null MAC so that the
// epoch and sequence number can be acquired to generate
// plaintext records.
block = new byte[8];
} else {
block = new byte[0];
}
this.isDTLS = isDTLS;
private Authenticator(byte[] block) {
this.block = block;
}
/**
* Constructs the message authentication token for the specified
* SSL/TLS protocol.
*/
Authenticator(ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLSProtocol()) {
block = new byte[BLOCK_SIZE_DTLS];
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
this.isDTLS = true;
} else if (protocolVersion.v >= ProtocolVersion.TLS10.v) {
block = new byte[BLOCK_SIZE_TLS];
block[9] = protocolVersion.major;
block[10] = protocolVersion.minor;
this.isDTLS = false;
static Authenticator valueOf(ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLS) {
if (protocolVersion.useTLS13PlusSpec()) {
return new DTLS13Authenticator(protocolVersion);
} else {
return new DTLS10Authenticator(protocolVersion);
}
} else {
block = new byte[BLOCK_SIZE_SSL];
this.isDTLS = false;
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.isDTLS) {
if (protocolVersion.useTLS13PlusSpec()) {
throw new RuntimeException("No MacAlg used in DTLS 1.3");
} else {
return (T)(new DTLS10Mac(protocolVersion, macAlg, key));
}
} else {
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();
}
static Authenticator nullDtlsMac() {
return new DTLSNullMac();
}
/**
* Checks whether the sequence number is close to wrap.
*
@ -122,25 +108,7 @@ class Authenticator {
*
* @return true if the sequence number is close to wrap
*/
final boolean seqNumOverflow() {
/*
* Conservatively, we don't allow more records to be generated
* when there are only 2^8 sequence numbers left.
*/
if (isDTLS) {
return (block.length != 0 &&
// no epoch bytes, block[0] and block[1]
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF);
} else {
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);
}
}
abstract boolean seqNumOverflow();
/**
* Checks whether the sequence number close to renew.
@ -152,21 +120,7 @@ class Authenticator {
*
* @return true if the sequence number is huge enough to renew
*/
final boolean seqNumIsHuge() {
/*
* Conservatively, we should ask for renegotiation when there are
* only 2^32 sequence numbers left.
*/
if (isDTLS) {
return (block.length != 0 &&
// no epoch bytes, block[0] and block[1]
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
} else {
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
}
}
abstract boolean seqNumIsHuge();
/**
* Gets the current sequence number, including the epoch number for
@ -181,14 +135,9 @@ class Authenticator {
/**
* Sets the epoch number (only apply to DTLS protocols).
*/
final void setEpochNumber(int epoch) {
if (!isDTLS) {
throw new RuntimeException(
void setEpochNumber(int epoch) {
throw new UnsupportedOperationException(
"Epoch numbers apply to DTLS protocols only");
}
block[0] = (byte)((epoch >> 8) & 0xFF);
block[1] = (byte)(epoch & 0xFF);
}
/**
@ -208,7 +157,7 @@ class Authenticator {
/**
* Acquires the current message authentication information with the
* specified record type and fragment length, and then increases the
* sequence number.
* sequence number if using implicit sequence number.
*
* @param type the record type
* @param length the fragment of the record
@ -216,32 +165,465 @@ class Authenticator {
*
* @return the byte array of the current message authentication information
*/
final byte[] acquireAuthenticationBytes(
byte[] acquireAuthenticationBytes(
byte type, int length, byte[] sequence) {
throw new UnsupportedOperationException("Used by AEAD algorithms only");
}
byte[] copy = block.clone();
if (sequence != null) {
if (sequence.length != 8) {
throw new RuntimeException(
"Insufficient explicit sequence number bytes");
}
private static class SSLAuthenticator extends Authenticator {
private SSLAuthenticator(byte[] block) {
super(block);
}
System.arraycopy(sequence, 0, copy, 0, sequence.length);
} // Otherwise, use the implicit sequence number.
@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);
}
if (block.length != 0) {
copy[8] = type;
@Override
boolean seqNumIsHuge() {
return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
}
}
copy[copy.length - 2] = (byte)(length >> 8);
copy[copy.length - 1] = (byte)(length);
// For null MAC only.
private static class SSLNullAuthenticator extends SSLAuthenticator {
private SSLNullAuthenticator() {
super(new byte[8]);
}
}
if (sequence == null || sequence.length != 0) {
// 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;
}
return copy;
@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;
}
}
private static class DTLSAuthenticator extends Authenticator {
private DTLSAuthenticator(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 &&
// no epoch bytes, block[0] and block[1]
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 &&
// no epoch bytes, block[0] and block[1]
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
}
@Override
void setEpochNumber(int epoch) {
block[0] = (byte)((epoch >> 8) & 0xFF);
block[1] = (byte)(epoch & 0xFF);
}
}
// For null MAC only.
private static class DTLSNullAuthenticator extends DTLSAuthenticator {
private DTLSNullAuthenticator() {
// For DTLS protocols, plaintexts use explicit epoch and
// sequence number in each record. The first 8 byte of
// the block is initialized for null MAC so that the
// epoch and sequence number can be acquired to generate
// plaintext records.
super(new byte[8]);
}
}
// DTLS 1.0/1.2
private static class DTLS10Authenticator extends DTLSAuthenticator {
// Block size of DTLS v1.0 and later:
// epoch + sequence number +
// record type + protocol version + record length
private static final int BLOCK_SIZE = 13; // 2 + 6 + 1 + 2 + 2;
private DTLS10Authenticator(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;
}
}
// DTLS 1.3
private static final class DTLS13Authenticator extends DTLSAuthenticator {
// Block size of DTLS v1.0 and later:
// epoch + sequence number +
// record type + protocol version + record length
private static final int BLOCK_SIZE = 13; // 2 + 6 + 1 + 2 + 2;
private DTLS13Authenticator(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);
}
}
// NULL DTLS MAC
private static final
class DTLSNullMac extends DTLSNullAuthenticator implements MAC {
private final MacImpl macImpl;
public DTLSNullMac() {
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);
}
}
// DTLS 1.0/1.2
private static final class DTLS10Mac
extends DTLS10Authenticator implements MAC {
private final MacImpl macImpl;
public DTLS10Mac(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) {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,24 +26,23 @@
package sun.security.ssl;
import java.io.*;
import java.nio.channels.SocketChannel;
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.
* 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 {
@ -92,7 +91,7 @@ abstract class BaseSSLSocketImpl extends SSLSocket {
"com.sun.net.ssl.requireCloseNotify";
static final boolean requireCloseNotify =
Debug.getBooleanProperty(PROP_NAME, false);
Utilities.getBooleanProperty(PROP_NAME, false);
//
// MISC SOCKET METHODS

View file

@ -1,187 +0,0 @@
/*
* Copyright (c) 2003, 2014, 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.nio.*;
/**
* A simple InputStream which uses ByteBuffers as it's backing store.
* <P>
* The only IOException should come if the InputStream has been closed.
* All other IOException should not occur because all the data is local.
* Data reads on an exhausted ByteBuffer returns a -1.
*
* @author Brad Wetmore
*/
class ByteBufferInputStream extends InputStream {
ByteBuffer bb;
ByteBufferInputStream(ByteBuffer bb) {
this.bb = bb;
}
/**
* Returns a byte from the ByteBuffer.
*
* Increments position().
*/
@Override
public int read() throws IOException {
if (bb == null) {
throw new IOException("read on a closed InputStream");
}
if (bb.remaining() == 0) {
return -1;
}
return (bb.get() & 0xFF); // need to be in the range 0 to 255
}
/**
* Returns a byte array from the ByteBuffer.
*
* Increments position().
*/
@Override
public int read(byte[] b) throws IOException {
if (bb == null) {
throw new IOException("read on a closed InputStream");
}
return read(b, 0, b.length);
}
/**
* Returns a byte array from the ByteBuffer.
*
* Increments position().
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (bb == null) {
throw new IOException("read on a closed InputStream");
}
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int length = Math.min(bb.remaining(), len);
if (length == 0) {
return -1;
}
bb.get(b, off, length);
return length;
}
/**
* Skips over and discards <code>n</code> bytes of data from this input
* stream.
*/
@Override
public long skip(long n) throws IOException {
if (bb == null) {
throw new IOException("skip on a closed InputStream");
}
if (n <= 0) {
return 0;
}
/*
* ByteBuffers have at most an int, so lose the upper bits.
* The contract allows this.
*/
int nInt = (int) n;
int skip = Math.min(bb.remaining(), nInt);
bb.position(bb.position() + skip);
return nInt;
}
/**
* Returns the number of bytes that can be read (or skipped over)
* from this input stream without blocking by the next caller of a
* method for this input stream.
*/
@Override
public int available() throws IOException {
if (bb == null) {
throw new IOException("available on a closed InputStream");
}
return bb.remaining();
}
/**
* Closes this input stream and releases any system resources associated
* with the stream.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
bb = null;
}
/**
* Marks the current position in this input stream.
*/
@Override
public synchronized void mark(int readlimit) {}
/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
*/
@Override
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
/**
* Tests if this input stream supports the <code>mark</code> and
* <code>reset</code> methods.
*/
@Override
public boolean markSupported() {
return false;
}
}

View file

@ -0,0 +1,348 @@
/*
* 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.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.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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// 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> shemes =
SignatureScheme.getSupportedAlgorithms(
shc.algorithmConstraints, shc.negotiatedProtocol,
spec.signatureSchemes);
shc.peerRequestedCertSignSchemes = shemes;
shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
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.
if (shc.localSupportedSignAlgs == null) {
shc.localSupportedSignAlgs =
SignatureScheme.getSupportedAlgorithms(
shc.algorithmConstraints, shc.activeProtocols);
}
int vectorLen = SignatureScheme.sizeInRecord() *
shc.localSupportedSignAlgs.size();
byte[] extData = new byte[vectorLen + 2];
ByteBuffer m = ByteBuffer.wrap(extData);
Record.putInt16(m, vectorLen);
for (SignatureScheme ss : shc.localSupportedSignAlgs) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// 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> shemes =
SignatureScheme.getSupportedAlgorithms(
chc.algorithmConstraints, chc.negotiatedProtocol,
spec.signatureSchemes);
chc.peerRequestedCertSignSchemes = shemes;
chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,205 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.util.Objects;
/*
* RFC6066 defines the TLS extension,"status_request" (type 0x5),
* which allows the client to request that the server perform OCSP
* on the client's behalf.
* The "extension data" field of this extension contains a
* "CertificateStatusRequest" structure:
*
* struct {
* CertificateStatusType status_type;
* select (status_type) {
* case ocsp: OCSPStatusRequest;
* } request;
* } CertificateStatusRequest;
*
* enum { ocsp(1), (255) } CertificateStatusType;
*
* struct {
* ResponderID responder_id_list<0..2^16-1>;
* Extensions request_extensions;
* } OCSPStatusRequest;
*
* opaque ResponderID<1..2^16-1>;
* opaque Extensions<0..2^16-1>;
*/
final class CertStatusReqExtension extends HelloExtension {
private final StatusRequestType statReqType;
private final StatusRequest request;
/**
* Construct the default status request extension object. The default
* object results in a status_request extension where the extension
* data segment is zero-length. This is used primarily in ServerHello
* messages where the server asserts it can do RFC 6066 status stapling.
*/
CertStatusReqExtension() {
super(ExtensionType.EXT_STATUS_REQUEST);
statReqType = null;
request = null;
}
/**
* Construct the status request extension object given a request type
* and {@code StatusRequest} object.
*
* @param reqType a {@code StatusRequestExtType object correspoding
* to the underlying {@code StatusRequest} object. A value of
* {@code null} is not allowed.
* @param statReq the {@code StatusRequest} object used to provide the
* encoding for the TLS extension. A value of {@code null} is not
* allowed.
*
* @throws IllegalArgumentException if the provided {@code StatusRequest}
* does not match the type.
* @throws NullPointerException if either the {@code reqType} or
* {@code statReq} arguments are {@code null}.
*/
CertStatusReqExtension(StatusRequestType reqType, StatusRequest statReq) {
super(ExtensionType.EXT_STATUS_REQUEST);
statReqType = Objects.requireNonNull(reqType,
"Unallowed null value for status_type");
request = Objects.requireNonNull(statReq,
"Unallowed null value for request");
// There is currently only one known status type (OCSP)
// We can add more clauses to cover other types in the future
if (statReqType == StatusRequestType.OCSP) {
if (!(statReq instanceof OCSPStatusRequest)) {
throw new IllegalArgumentException("StatusRequest not " +
"of type OCSPStatusRequest");
}
}
}
/**
* Construct the {@code CertStatusReqExtension} object from data read from
* a {@code HandshakeInputStream}
*
* @param s the {@code HandshakeInputStream} providing the encoded data
* @param len the length of the extension data
*
* @throws IOException if any decoding errors happen during object
* construction.
*/
CertStatusReqExtension(HandshakeInStream s, int len) throws IOException {
super(ExtensionType.EXT_STATUS_REQUEST);
if (len > 0) {
// Obtain the status type (first byte)
statReqType = StatusRequestType.get(s.getInt8());
if (statReqType == StatusRequestType.OCSP) {
request = new OCSPStatusRequest(s);
} else {
// This is a status_type we don't understand. Create
// an UnknownStatusRequest in order to preserve the data
request = new UnknownStatusRequest(s, len - 1);
}
} else {
// Treat this as a zero-length extension (i.e. from a ServerHello
statReqType = null;
request = null;
}
}
/**
* Return the length of the encoded extension, including extension type,
* extension length and status_type fields.
*
* @return the length in bytes, including the extension type and
* length fields.
*/
@Override
int length() {
return (statReqType != null ? 5 + request.length() : 4);
}
/**
* Send the encoded TLS extension through a {@code HandshakeOutputStream}
*
* @param s the {@code HandshakeOutputStream} used to send the encoded data
*
* @throws IOException tf any errors occur during the encoding process
*/
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(this.length() - 4);
if (statReqType != null) {
s.putInt8(statReqType.id);
request.send(s);
}
}
/**
* Create a string representation of this {@code CertStatusReqExtension}
*
* @return the string representation of this {@code CertStatusReqExtension}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Extension ").append(type);
if (statReqType != null) {
sb.append(": ").append(statReqType).append(", ").append(request);
}
return sb.toString();
}
/**
* Return the type field for this {@code CertStatusReqExtension}
*
* @return the {@code StatusRequestType} for this extension. {@code null}
* will be returned if the default constructor is used to create
* a zero length status_request extension (found in ServerHello
* messages)
*/
StatusRequestType getType() {
return statReqType;
}
/**
* Get the underlying {@code StatusRequest} for this
* {@code CertStatusReqExtension}
*
* @return the {@code StatusRequest} or {@code null} if the default
* constructor was used to create this extension.
*/
StatusRequest getRequest() {
return request;
}
}

View file

@ -1,199 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Objects;
import javax.net.ssl.SSLException;
/*
* RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),
* which allows the client to request that the server perform OCSP
* on the client's behalf.
*
* The RFC defines an CertStatusReqItemV2 structure:
*
* struct {
* CertificateStatusType status_type;
* uint16 request_length;
* select (status_type) {
* case ocsp: OCSPStatusRequest;
* case ocsp_multi: OCSPStatusRequest;
* } request;
* } CertificateStatusRequestItemV2;
*
* enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
*/
final class CertStatusReqItemV2 {
private final StatusRequestType statReqType;
private final StatusRequest request;
/**
* Construct a {@code CertStatusReqItemV2} object using a type value
* and empty ResponderId and Extension lists.
*
* @param reqType the type of request (e.g. ocsp). A {@code null} value
* is not allowed.
* @param statReq the {@code StatusRequest} object used to provide the
* encoding for this {@code CertStatusReqItemV2}. A {@code null}
* value is not allowed.
*
* @throws IllegalArgumentException if the provided {@code StatusRequest}
* does not match the type.
* @throws NullPointerException if either the reqType or statReq arguments
* are {@code null}.
*/
CertStatusReqItemV2(StatusRequestType reqType, StatusRequest statReq) {
statReqType = Objects.requireNonNull(reqType,
"Unallowed null value for status_type");
request = Objects.requireNonNull(statReq,
"Unallowed null value for request");
// There is currently only one known status type (OCSP)
// We can add more clauses to cover other types in the future
if (statReqType.equals(StatusRequestType.OCSP) ||
statReqType.equals(StatusRequestType.OCSP_MULTI)) {
if (!(statReq instanceof OCSPStatusRequest)) {
throw new IllegalArgumentException("StatusRequest not " +
"of type OCSPStatusRequest");
}
}
}
/**
* Construct a {@code CertStatusReqItemV2} object from encoded bytes
*
* @param requestBytes the encoded bytes for the {@code CertStatusReqItemV2}
*
* @throws IOException if any decoding errors take place
* @throws IllegalArgumentException if the parsed reqType value is not a
* supported status request type.
*/
CertStatusReqItemV2(byte[] reqItemBytes) throws IOException {
ByteBuffer reqBuf = ByteBuffer.wrap(reqItemBytes);
statReqType = StatusRequestType.get(reqBuf.get());
int requestLength = Short.toUnsignedInt(reqBuf.getShort());
if (requestLength == reqBuf.remaining()) {
byte[] statReqBytes = new byte[requestLength];
reqBuf.get(statReqBytes);
if (statReqType == StatusRequestType.OCSP ||
statReqType == StatusRequestType.OCSP_MULTI) {
request = new OCSPStatusRequest(statReqBytes);
} else {
request = new UnknownStatusRequest(statReqBytes);
}
} else {
throw new SSLException("Incorrect request_length: " +
"Expected " + reqBuf.remaining() + ", got " +
requestLength);
}
}
/**
* Construct an {@code CertStatusReqItemV2} object from data read from
* a {@code HandshakeInputStream}
*
* @param s the {@code HandshakeInputStream} providing the encoded data
*
* @throws IOException if any decoding errors happen during object
* construction.
* @throws IllegalArgumentException if the parsed reqType value is not a
* supported status request type.
*/
CertStatusReqItemV2(HandshakeInStream in) throws IOException {
statReqType = StatusRequestType.get(in.getInt8());
int requestLength = in.getInt16();
if (statReqType == StatusRequestType.OCSP ||
statReqType == StatusRequestType.OCSP_MULTI) {
request = new OCSPStatusRequest(in);
} else {
request = new UnknownStatusRequest(in, requestLength);
}
}
/**
* Return the length of this {@code CertStatusReqItemV2} in its encoded form
*
* @return the encoded length of this {@code CertStatusReqItemV2}
*/
int length() {
// The length is the status type (1 byte) + the request length
// field (2 bytes) + the StatusRequest data length.
return request.length() + 3;
}
/**
* Send the encoded {@code CertStatusReqItemV2} through a
* {@code HandshakeOutputStream}
*
* @param s the {@code HandshakeOutputStream} used to send the encoded data
*
* @throws IOException if any errors occur during the encoding process
*/
void send(HandshakeOutStream s) throws IOException {
s.putInt8(statReqType.id);
s.putInt16(request.length());
request.send(s);
}
/**
* Create a string representation of this {@code CertStatusReqItemV2}
*
* @return the string representation of this {@code CertStatusReqItemV2}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("CertStatusReqItemV2: ").append(statReqType).append(", ");
sb.append(request.toString());
return sb.toString();
}
/**
* Return the type field for this {@code CertStatusReqItemV2}
*
* @return the {@code StatusRequestType} for this extension.
*/
StatusRequestType getType() {
return statReqType;
}
/**
* Get the underlying {@code StatusRequest} for this
* {@code CertStatusReqItemV2}
*
* @return the {@code StatusRequest}
*/
StatusRequest getRequest() {
return request;
}
}

View file

@ -1,220 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Objects;
import javax.net.ssl.SSLException;
/*
* RFC6066 defines the TLS extension,"status_request" (type 0x5),
* which allows the client to request that the server perform OCSP
* on the client's behalf.
* The "extension data" field of this extension contains a
* "CertificateStatusRequest" structure:
*
* struct {
* CertificateStatusType status_type;
* select (status_type) {
* case ocsp: OCSPStatusRequest;
* } request;
* } CertificateStatusRequest;
*
* enum { ocsp(1), (255) } CertificateStatusType;
*
* struct {
* ResponderID responder_id_list<0..2^16-1>;
* Extensions request_extensions;
* } OCSPStatusRequest;
*
* opaque ResponderID<1..2^16-1>;
* opaque Extensions<0..2^16-1>;
*/
final class CertStatusReqListV2Extension extends HelloExtension {
private final List<CertStatusReqItemV2> itemList;
private final int itemListLength;
/**
* Construct a default {@code CertStatusReqListV2Extension}. The default
* object results in a status_request_v2 extension where the extension
* data segment is zero-length. This is used primarily in ServerHello
* messages where the server asserts it can do RFC 6961 status stapling.
*/
CertStatusReqListV2Extension() {
super(ExtensionType.EXT_STATUS_REQUEST_V2);
itemList = Collections.emptyList();
itemListLength = 0;
}
/**
* Construct a {@code CertStatusReqListV2Extension} from a provided list
* of {@code CertStatusReqItemV2} objects.
*
* @param reqList a {@code List} containing one or more
* {@code CertStatusReqItemV2} objects to be included in this TLS
* Hello extension. Passing an empty list will result in the encoded
* extension having a zero-length extension_data segment, and is
* the same as using the default constructor.
*
* @throws NullPointerException if reqList is {@code null}
*/
CertStatusReqListV2Extension(List<CertStatusReqItemV2> reqList) {
super(ExtensionType.EXT_STATUS_REQUEST_V2);
Objects.requireNonNull(reqList,
"Unallowed null value for certificate_status_req_list");
itemList = Collections.unmodifiableList(new ArrayList<>(reqList));
itemListLength = calculateListLength();
}
/**
* Construct the {@code CertStatusReqListV2Extension} object from data
* read from a {@code HandshakeInputStream}
*
* @param s the {@code HandshakeInputStream} providing the encoded data
* @param len the length of the extension data
*
* @throws IOException if any decoding errors happen during object
* construction.
*/
CertStatusReqListV2Extension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_STATUS_REQUEST_V2);
if (len <= 0) {
// Handle the empty extension data case (from a ServerHello)
itemList = Collections.emptyList();
itemListLength = 0;
} else {
List<CertStatusReqItemV2> workingList = new ArrayList<>();
itemListLength = s.getInt16();
if (itemListLength <= 0) {
throw new SSLException("certificate_status_req_list length " +
"must be greater than zero (received length: " +
itemListLength + ")");
}
int totalRead = 0;
CertStatusReqItemV2 reqItem;
do {
reqItem = new CertStatusReqItemV2(s);
totalRead += reqItem.length();
} while (workingList.add(reqItem) && totalRead < itemListLength);
// If for some reason the add returns false, we may not have read
// all the necessary bytes from the stream. Check this and throw
// an exception if we terminated the loop early.
if (totalRead != itemListLength) {
throw new SSLException("Not all certificate_status_req_list " +
"bytes were read: expected " + itemListLength +
", read " + totalRead);
}
itemList = Collections.unmodifiableList(workingList);
}
}
/**
* Get the list of {@code CertStatusReqItemV2} objects for this extension
*
* @return an unmodifiable list of {@code CertStatusReqItemV2} objects
*/
List<CertStatusReqItemV2> getRequestItems() {
return itemList;
}
/**
* Return the length of the encoded extension, including extension type
* and extension length fields.
*
* @return the length in bytes, including the extension type and
* extension_data length.
*/
@Override
int length() {
return (itemList.isEmpty() ? 4 : itemListLength + 6);
}
/**
* Send the encoded {@code CertStatusReqListV2Extension} through a
* {@code HandshakeOutputStream}
*
* @param s the {@code HandshakeOutputStream} used to send the encoded data
*
* @throws IOException if any errors occur during the encoding process
*/
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(this.length() - 4);
if (itemListLength > 0) {
s.putInt16(itemListLength);
for (CertStatusReqItemV2 item : itemList) {
item.send(s);
}
}
}
/**
* Create a string representation of this
* {@code CertStatusReqListV2Extension}
*
* @return the string representation of this
* {@code CertStatusReqListV2Extension}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Extension ").append(type);
for (CertStatusReqItemV2 item : itemList) {
sb.append("\n").append(item);
}
return sb.toString();
}
/**
* Determine the length of the certificate_status_req_list field in
* the status_request_v2 extension.
*
* @return the total encoded length of all items in the list, or 0 if the
* encapsulating extension_data is zero-length (from a ServerHello)
*/
private int calculateListLength() {
int listLen = 0;
for (CertStatusReqItemV2 item : itemList) {
listLen += item.length();
}
return listLen;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,890 @@
/*
* 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.PrivateKey;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.security.auth.x500.X500Principal;
import sun.security.ssl.CipherSuite.KeyExchange;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.X509Authentication.X509Possession;
/**
* Pack of the CertificateRequest handshake message.
*/
final class CertificateRequest {
static final SSLConsumer t10HandshakeConsumer =
new T10CertificateRequestConsumer();
static final HandshakeProducer t10HandshakeProducer =
new T10CertificateRequestProducer();
static final SSLConsumer t12HandshakeConsumer =
new T12CertificateRequestConsumer();
static final HandshakeProducer t12HandshakeProducer =
new T12CertificateRequestProducer();
static final SSLConsumer t13HandshakeConsumer =
new T13CertificateRequestConsumer();
static final HandshakeProducer t13HandshakeProducer =
new T13CertificateRequestProducer();
// TLS 1.2 and prior versions
private static enum ClientCertificateType {
// RFC 2246
RSA_SIGN ((byte)0x01, "rsa_sign", "RSA", true),
DSS_SIGN ((byte)0x02, "dss_sign", "DSA", true),
RSA_FIXED_DH ((byte)0x03, "rsa_fixed_dh"),
DSS_FIXED_DH ((byte)0x04, "dss_fixed_dh"),
// RFC 4346
RSA_EPHEMERAL_DH ((byte)0x05, "rsa_ephemeral_dh"),
DSS_EPHEMERAL_DH ((byte)0x06, "dss_ephemeral_dh"),
FORTEZZA_DMS ((byte)0x14, "fortezza_dms"),
// RFC 4492
ECDSA_SIGN ((byte)0x40, "ecdsa_sign",
"EC", JsseJce.isEcAvailable()),
RSA_FIXED_ECDH ((byte)0x41, "rsa_fixed_ecdh"),
ECDSA_FIXED_ECDH ((byte)0x42, "ecdsa_fixed_ecdh");
private static final byte[] CERT_TYPES =
JsseJce.isEcAvailable() ? new byte[] {
ECDSA_SIGN.id,
RSA_SIGN.id,
DSS_SIGN.id
} : new byte[] {
RSA_SIGN.id,
DSS_SIGN.id
};
final byte id;
final String name;
final String keyAlgorithm;
final boolean isAvailable;
private ClientCertificateType(byte id, String name) {
this(id, name, null, false);
}
private ClientCertificateType(byte id, String name,
String keyAlgorithm, boolean isAvailable) {
this.id = id;
this.name = name;
this.keyAlgorithm = keyAlgorithm;
this.isAvailable = isAvailable;
}
private static String nameOf(byte id) {
for (ClientCertificateType cct : ClientCertificateType.values()) {
if (cct.id == id) {
return cct.name;
}
}
return "UNDEFINED-CLIENT-CERTIFICATE-TYPE(" + (int)id + ")";
}
private static ClientCertificateType valueOf(byte id) {
for (ClientCertificateType cct : ClientCertificateType.values()) {
if (cct.id == id) {
return cct;
}
}
return null;
}
private static String[] getKeyTypes(byte[] ids) {
ArrayList<String> keyTypes = new ArrayList<>(3);
for (byte id : ids) {
ClientCertificateType cct = ClientCertificateType.valueOf(id);
if (cct.isAvailable) {
keyTypes.add(cct.keyAlgorithm);
}
}
return keyTypes.toArray(new String[0]);
}
}
/**
* The "CertificateRequest" handshake message for SSL 3.0 and TLS 1.0/1.1.
*/
static final class T10CertificateRequestMessage extends HandshakeMessage {
final byte[] types; // certificate types
final List<byte[]> authorities; // certificate authorities
T10CertificateRequestMessage(HandshakeContext handshakeContext,
X509Certificate[] trustedCerts, KeyExchange keyExchange) {
super(handshakeContext);
this.authorities = new ArrayList<>(trustedCerts.length);
for (X509Certificate cert : trustedCerts) {
X500Principal x500Principal = cert.getSubjectX500Principal();
authorities.add(x500Principal.getEncoded());
}
this.types = ClientCertificateType.CERT_TYPES;
}
T10CertificateRequestMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// struct {
// ClientCertificateType certificate_types<1..2^8-1>;
// DistinguishedName certificate_authorities<0..2^16-1>;
// } CertificateRequest;
if (m.remaining() < 4) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Incorrect CertificateRequest message: no sufficient data");
}
this.types = Record.getBytes8(m);
int listLen = Record.getInt16(m);
if (listLen > m.remaining()) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Incorrect CertificateRequest message:no sufficient data");
}
if (listLen > 0) {
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);
}
} else {
this.authorities = Collections.emptyList();
}
}
String[] getKeyTypes() {
return ClientCertificateType.getKeyTypes(types);
}
X500Principal[] getAuthorities() {
List<X500Principal> principals =
new ArrayList<>(authorities.size());
for (byte[] encoded : authorities) {
X500Principal principal = new X500Principal(encoded);
principals.add(principal);
}
return principals.toArray(new X500Principal[0]);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE_REQUEST;
}
@Override
public int messageLength() {
int len = 1 + types.length + 2;
for (byte[] encoded : authorities) {
len += encoded.length + 2;
}
return len;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putBytes8(types);
int listLen = 0;
for (byte[] encoded : authorities) {
listLen += encoded.length + 2;
}
hos.putInt16(listLen);
for (byte[] encoded : authorities) {
hos.putBytes16(encoded);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"CertificateRequest\": '{'\n" +
" \"certificate types\": {0}\n" +
" \"certificate authorities\": {1}\n" +
"'}'",
Locale.ENGLISH);
List<String> typeNames = new ArrayList<>(types.length);
for (byte type : types) {
typeNames.add(ClientCertificateType.nameOf(type));
}
List<String> authorityNames = new ArrayList<>(authorities.size());
for (byte[] encoded : authorities) {
X500Principal principal = new X500Principal(encoded);
authorityNames.add(principal.toString());
}
Object[] messageFields = {
typeNames,
authorityNames
};
return messageFormat.format(messageFields);
}
}
/**
* The "CertificateRequest" handshake message producer for SSL 3.0 and
* TLS 1.0/1.1.
*/
private static final
class T10CertificateRequestProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private T10CertificateRequestProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
X509Certificate[] caCerts =
shc.sslContext.getX509TrustManager().getAcceptedIssuers();
T10CertificateRequestMessage crm = new T10CertificateRequestMessage(
shc, caCerts, shc.negotiatedCipherSuite.keyExchange);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced CertificateRequest handshake message", crm);
}
// Output the handshake message.
crm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
//
// update
//
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
// The handshake message has been delivered.
return null;
}
}
/**
* The "CertificateRequest" handshake message consumer for SSL 3.0 and
* TLS 1.0/1.1.
*/
private static final
class T10CertificateRequestConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T10CertificateRequestConsumer() {
// 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.CERTIFICATE_REQUEST.id);
T10CertificateRequestMessage crm =
new T10CertificateRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming CertificateRequest handshake message", crm);
}
//
// validate
//
// blank
//
// update
//
// An empty client Certificate handshake message may be allow.
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
String clientAlias = null;
if (chc.conContext.transport instanceof SSLSocketImpl) {
clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
} else if (chc.conContext.transport instanceof SSLEngineImpl) {
clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
}
if (clientAlias == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client authentication");
}
return;
}
PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
if (clientPrivateKey == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client private key");
}
return;
}
X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
if ((clientCerts == null) || (clientCerts.length == 0)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client certificate");
}
return;
}
chc.handshakePossessions.add(
new X509Possession(clientPrivateKey, clientCerts));
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
}
}
/**
* The CertificateRequest handshake message for TLS 1.2.
*/
static final class T12CertificateRequestMessage extends HandshakeMessage {
final byte[] types; // certificate types
final int[] algorithmIds; // supported signature algorithms
final List<byte[]> authorities; // certificate authorities
T12CertificateRequestMessage(HandshakeContext handshakeContext,
X509Certificate[] trustedCerts, KeyExchange keyExchange,
List<SignatureScheme> signatureSchemes) throws IOException {
super(handshakeContext);
this.types = ClientCertificateType.CERT_TYPES;
if (signatureSchemes == null || signatureSchemes.isEmpty()) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No signature algorithms specified for " +
"CertificateRequest hanshake message");
}
this.algorithmIds = new int[signatureSchemes.size()];
int i = 0;
for (SignatureScheme scheme : signatureSchemes) {
algorithmIds[i++] = scheme.id;
}
this.authorities = new ArrayList<>(trustedCerts.length);
for (X509Certificate cert : trustedCerts) {
X500Principal x500Principal = cert.getSubjectX500Principal();
authorities.add(x500Principal.getEncoded());
}
}
T12CertificateRequestMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// struct {
// ClientCertificateType certificate_types<1..2^8-1>;
// SignatureAndHashAlgorithm
// supported_signature_algorithms<2..2^16-2>;
// DistinguishedName certificate_authorities<0..2^16-1>;
// } CertificateRequest;
// certificate_authorities
if (m.remaining() < 8) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"no sufficient data");
}
this.types = Record.getBytes8(m);
// supported_signature_algorithms
if (m.remaining() < 6) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"no sufficient data");
}
byte[] algs = Record.getBytes16(m);
if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"incomplete signature algorithms");
}
this.algorithmIds = new int[(algs.length >> 1)];
for (int i = 0, j = 0; i < algs.length;) {
byte hash = algs[i++];
byte sign = algs[i++];
algorithmIds[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
}
// certificate_authorities
if (m.remaining() < 2) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"no sufficient data");
}
int listLen = Record.getInt16(m);
if (listLen > m.remaining()) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest message: no sufficient data");
}
if (listLen > 0) {
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);
}
} else {
this.authorities = Collections.emptyList();
}
}
String[] getKeyTypes() {
return ClientCertificateType.getKeyTypes(types);
}
X500Principal[] getAuthorities() {
List<X500Principal> principals =
new ArrayList<>(authorities.size());
for (byte[] encoded : authorities) {
X500Principal principal = new X500Principal(encoded);
principals.add(principal);
}
return principals.toArray(new X500Principal[0]);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE_REQUEST;
}
@Override
public int messageLength() {
int len = 1 + types.length + 2 + (algorithmIds.length << 1) + 2;
for (byte[] encoded : authorities) {
len += encoded.length + 2;
}
return len;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putBytes8(types);
int listLen = 0;
for (byte[] encoded : authorities) {
listLen += encoded.length + 2;
}
hos.putInt16(algorithmIds.length << 1);
for (int algorithmId : algorithmIds) {
hos.putInt16(algorithmId);
}
hos.putInt16(listLen);
for (byte[] encoded : authorities) {
hos.putBytes16(encoded);
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"CertificateRequest\": '{'\n" +
" \"certificate types\": {0}\n" +
" \"supported signature algorithms\": {1}\n" +
" \"certificate authorities\": {2}\n" +
"'}'",
Locale.ENGLISH);
List<String> typeNames = new ArrayList<>(types.length);
for (byte type : types) {
typeNames.add(ClientCertificateType.nameOf(type));
}
List<String> algorithmNames = new ArrayList<>(algorithmIds.length);
for (int algorithmId : algorithmIds) {
algorithmNames.add(SignatureScheme.nameOf(algorithmId));
}
List<String> authorityNames = new ArrayList<>(authorities.size());
for (byte[] encoded : authorities) {
X500Principal principal = new X500Principal(encoded);
authorityNames.add(principal.toString());
}
Object[] messageFields = {
typeNames,
algorithmNames,
authorityNames
};
return messageFormat.format(messageFields);
}
}
/**
* The "CertificateRequest" handshake message producer for TLS 1.2.
*/
private static final
class T12CertificateRequestProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private T12CertificateRequestProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (shc.localSupportedSignAlgs == null) {
shc.localSupportedSignAlgs =
SignatureScheme.getSupportedAlgorithms(
shc.algorithmConstraints, shc.activeProtocols);
}
if (shc.localSupportedSignAlgs == null ||
shc.localSupportedSignAlgs.isEmpty()) {
shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No supported signature algorithm");
}
X509Certificate[] caCerts =
shc.sslContext.getX509TrustManager().getAcceptedIssuers();
T12CertificateRequestMessage crm = new T12CertificateRequestMessage(
shc, caCerts, shc.negotiatedCipherSuite.keyExchange,
shc.localSupportedSignAlgs);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced CertificateRequest handshake message", crm);
}
// Output the handshake message.
crm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
//
// update
//
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
// The handshake message has been delivered.
return null;
}
}
/**
* The "CertificateRequest" handshake message consumer for TLS 1.2.
*/
private static final
class T12CertificateRequestConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T12CertificateRequestConsumer() {
// 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.CERTIFICATE_REQUEST.id);
T12CertificateRequestMessage crm =
new T12CertificateRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming CertificateRequest handshake message", crm);
}
//
// validate
//
// blank
//
// update
//
// An empty client Certificate handshake message may be allow.
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
List<SignatureScheme> sss = new LinkedList<>();
for (int id : crm.algorithmIds) {
SignatureScheme ss = SignatureScheme.valueOf(id);
if (ss != null) {
sss.add(ss);
}
}
chc.peerRequestedSignatureSchemes = sss;
chc.peerRequestedCertSignSchemes = sss; // use the same schemes
chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss);
X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager();
String clientAlias = null;
if (chc.conContext.transport instanceof SSLSocketImpl) {
clientAlias = km.chooseClientAlias(crm.getKeyTypes(),
crm.getAuthorities(), (SSLSocket)chc.conContext.transport);
} else if (chc.conContext.transport instanceof SSLEngineImpl) {
clientAlias = km.chooseEngineClientAlias(crm.getKeyTypes(),
crm.getAuthorities(), (SSLEngine)chc.conContext.transport);
}
if (clientAlias == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client authentication");
}
return;
}
PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias);
if (clientPrivateKey == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client private key");
}
return;
}
X509Certificate[] clientCerts = km.getCertificateChain(clientAlias);
if ((clientCerts == null) || (clientCerts.length == 0)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available client certificate");
}
return;
}
chc.handshakePossessions.add(
new X509Possession(clientPrivateKey, clientCerts));
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
}
}
/**
* The CertificateRequest handshake message for TLS 1.3.
*/
static final class T13CertificateRequestMessage extends HandshakeMessage {
private final byte[] requestContext;
private final SSLExtensions extensions;
T13CertificateRequestMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
this.requestContext = new byte[0];
this.extensions = new SSLExtensions(this);
}
T13CertificateRequestMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
// struct {
// opaque certificate_request_context<0..2^8-1>;
// Extension extensions<2..2^16-1>;
// } CertificateRequest;
if (m.remaining() < 5) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"no sufficient data");
}
this.requestContext = Record.getBytes8(m);
if (m.remaining() < 4) {
handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid CertificateRequest handshake message: " +
"no sufficient extensions data");
}
SSLExtension[] enabledExtensions =
handshakeContext.sslConfig.getEnabledExtensions(
SSLHandshake.CERTIFICATE_REQUEST);
this.extensions = new SSLExtensions(this, m, enabledExtensions);
}
@Override
SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE_REQUEST;
}
@Override
int messageLength() {
// In TLS 1.3, use of certain extensions is mandatory.
return 1 + requestContext.length + extensions.length();
}
@Override
void send(HandshakeOutStream hos) throws IOException {
hos.putBytes8(requestContext);
// In TLS 1.3, use of certain extensions is mandatory.
extensions.send(hos);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"CertificateRequest\": '{'\n" +
" \"certificate_request_context\": \"{0}\",\n" +
" \"extensions\": [\n" +
"{1}\n" +
" ]\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
Utilities.toHexString(requestContext),
Utilities.indent(Utilities.indent(extensions.toString()))
};
return messageFormat.format(messageFields);
}
}
/**
* The "CertificateRequest" handshake message producer for TLS 1.3.
*/
private static final
class T13CertificateRequestProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private T13CertificateRequestProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
T13CertificateRequestMessage crm =
new T13CertificateRequestMessage(shc);
// Produce extensions for CertificateRequest handshake message.
SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
SSLHandshake.CERTIFICATE_REQUEST, shc.negotiatedProtocol);
crm.extensions.produce(shc, extTypes);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced CertificateRequest message", crm);
}
// Output the handshake message.
crm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
//
// update
//
shc.certRequestContext = crm.requestContext.clone();
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
// The handshake message has been delivered.
return null;
}
}
/**
* The "CertificateRequest" handshake message consumer for TLS 1.3.
*/
private static final
class T13CertificateRequestConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T13CertificateRequestConsumer() {
// 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.CERTIFICATE_REQUEST.id);
T13CertificateRequestMessage crm =
new T13CertificateRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming CertificateRequest handshake message", crm);
}
//
// validate
//
SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
SSLHandshake.CERTIFICATE_REQUEST);
crm.extensions.consumeOnLoad(chc, extTypes);
//
// update
//
crm.extensions.consumeOnTrade(chc, extTypes);
//
// produce
//
chc.certRequestContext = crm.requestContext.clone();
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
SSLHandshake.CERTIFICATE);
chc.handshakeProducers.put(SSLHandshake.CERTIFICATE_VERIFY.id,
SSLHandshake.CERTIFICATE_VERIFY);
}
}
}

View file

@ -0,0 +1,362 @@
/*
* 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.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 {
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) {
handshakeContext.conContext.fatal(Alert.INTERNAL_ERROR,
"Bad OCSP response list length");
}
} else {
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 certficicate
// checking phase.
chc.handshakeSession.setStatusResponses(cst.encodedResponses);
// Now perform the check
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
}
}
/**
* 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,238 @@
/*
* 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 (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) {
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) {
tc.fatal(Alert.HANDSHAKE_FAILURE,
"Unexpected ChangeCipherSpec message");
}
HandshakeContext hc = tc.handshakeContext;
if (hc.handshakeKeyDerivation == null) {
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);
}
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) {
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

File diff suppressed because it is too large Load diff

View file

@ -1,187 +0,0 @@
/*
* Copyright (c) 2002, 2017, 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.util.*;
import javax.net.ssl.SSLException;
import static sun.security.ssl.NamedGroupType.*;
/**
* A list of CipherSuites. Also maintains the lists of supported and
* default ciphersuites and supports I/O from handshake streams.
*
* Instances of this class are immutable.
*
*/
final class CipherSuiteList {
private final Collection<CipherSuite> cipherSuites;
private String[] suiteNames;
private final EnumSet<NamedGroupType> groupsTypes =
EnumSet.noneOf(NamedGroupType.class);
// for use by buildAvailableCache() and
// Handshaker.getKickstartMessage() only
CipherSuiteList(Collection<CipherSuite> cipherSuites) {
this.cipherSuites = cipherSuites;
for (CipherSuite suite : cipherSuites) {
updateGroupTypes(suite);
}
}
/**
* Create a CipherSuiteList with a single element.
*/
CipherSuiteList(CipherSuite suite) {
cipherSuites = new ArrayList<CipherSuite>(1);
cipherSuites.add(suite);
updateGroupTypes(suite);
}
/**
* Construct a CipherSuiteList from a array of names. We don't bother
* to eliminate duplicates.
*
* @exception IllegalArgumentException if the array or any of its elements
* is null or if the ciphersuite name is unrecognized or unsupported
* using currently installed providers.
*/
CipherSuiteList(String[] names) {
if (names == null) {
throw new IllegalArgumentException("CipherSuites may not be null");
}
cipherSuites = new ArrayList<CipherSuite>(names.length);
for (int i = 0; i < names.length; i++) {
String suiteName = names[i];
CipherSuite suite = CipherSuite.valueOf(suiteName);
if (suite.isAvailable() == false) {
throw new IllegalArgumentException("Cannot support "
+ suiteName + " with currently installed providers");
}
cipherSuites.add(suite);
updateGroupTypes(suite);
}
}
/**
* Read a CipherSuiteList from a HandshakeInStream in V3 ClientHello
* format. Does not check if the listed ciphersuites are known or
* supported.
*/
CipherSuiteList(HandshakeInStream in) throws IOException {
byte[] bytes = in.getBytes16();
if ((bytes.length & 1) != 0) {
throw new SSLException("Invalid ClientHello message");
}
cipherSuites = new ArrayList<CipherSuite>(bytes.length >> 1);
for (int i = 0; i < bytes.length; i += 2) {
CipherSuite suite = CipherSuite.valueOf(bytes[i], bytes[i+1]);
cipherSuites.add(suite);
updateGroupTypes(suite);
}
}
// Please don't use this method except constructors.
private void updateGroupTypes(CipherSuite cipherSuite) {
if (cipherSuite.keyExchange != null && (!cipherSuite.exportable)) {
NamedGroupType groupType = cipherSuite.keyExchange.groupType;
if ((groupType != NAMED_GROUP_NONE) &&
(!groupsTypes.contains(groupType))) {
groupsTypes.add(groupType);
}
}
}
/**
* Return whether this list contains the given CipherSuite.
*/
boolean contains(CipherSuite suite) {
return cipherSuites.contains(suite);
}
// Return whether this list contains cipher suites of a named group type.
boolean contains(NamedGroupType groupType) {
return groupsTypes.contains(groupType);
}
/**
* Return an Iterator for the CipherSuites in this list.
*/
Iterator<CipherSuite> iterator() {
return cipherSuites.iterator();
}
/**
* Return a reference to the internal Collection of CipherSuites.
* The Collection MUST NOT be modified.
*/
Collection<CipherSuite> collection() {
return cipherSuites;
}
/**
* Return the number of CipherSuites in this list.
*/
int size() {
return cipherSuites.size();
}
/**
* Return an array with the names of the CipherSuites in this list.
*/
synchronized String[] toStringArray() {
if (suiteNames == null) {
suiteNames = new String[cipherSuites.size()];
int i = 0;
for (CipherSuite c : cipherSuites) {
suiteNames[i++] = c.name;
}
}
return suiteNames.clone();
}
@Override
public String toString() {
return cipherSuites.toString();
}
/**
* Write this list to an HandshakeOutStream in V3 ClientHello format.
*/
void send(HandshakeOutStream s) throws IOException {
byte[] suiteBytes = new byte[cipherSuites.size() * 2];
int i = 0;
for (CipherSuite c : cipherSuites) {
suiteBytes[i] = (byte)(c.id >> 8);
suiteBytes[i+1] = (byte)c.id;
i += 2;
}
s.putBytes16(suiteBytes);
}
}

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/(D)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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,120 +26,30 @@
package sun.security.ssl;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import static sun.security.ssl.HandshakeMessage.*;
/*
* enumeration of record type
* Ciphertext
*/
final class Ciphertext {
static final Ciphertext CIPHERTEXT_NULL = new Ciphertext();
RecordType recordType;
long recordSN;
final byte contentType;
final byte handshakeType;
final long recordSN;
HandshakeStatus handshakeStatus; // null if not used or not handshaking
Ciphertext() {
this.recordType = null;
private Ciphertext() {
this.contentType = 0;
this.handshakeType = -1;
this.recordSN = -1L;
this.handshakeStatus = null;
}
Ciphertext(RecordType recordType, long recordSN) {
this.recordType = recordType;
Ciphertext(byte contentType, byte handshakeType, long recordSN) {
this.contentType = contentType;
this.handshakeType = handshakeType;
this.recordSN = recordSN;
this.handshakeStatus = null;
}
static enum RecordType {
RECORD_CHANGE_CIPHER_SPEC (
Record.ct_change_cipher_spec, ht_not_applicable),
RECORD_ALERT (
Record.ct_alert, ht_not_applicable),
RECORD_HELLO_REQUEST (
Record.ct_handshake, ht_hello_request),
RECORD_CLIENT_HELLO (
Record.ct_handshake, ht_client_hello),
RECORD_SERVER_HELLO (
Record.ct_handshake, ht_server_hello),
RECORD_HELLO_VERIFY_REQUEST (
Record.ct_handshake, ht_hello_verify_request),
RECORD_NEW_SESSION_TICKET (
Record.ct_handshake, ht_new_session_ticket),
RECORD_CERTIFICATE (
Record.ct_handshake, ht_certificate),
RECORD_SERVER_KEY_EXCHANGE (
Record.ct_handshake, ht_server_key_exchange),
RECORD_CERTIFICATE_REQUEST (
Record.ct_handshake, ht_certificate_request),
RECORD_SERVER_HELLO_DONE (
Record.ct_handshake, ht_server_hello_done),
RECORD_CERTIFICATE_VERIFY (
Record.ct_handshake, ht_certificate_verify),
RECORD_CLIENT_KEY_EXCHANGE (
Record.ct_handshake, ht_client_key_exchange),
RECORD_FINISHED (
Record.ct_handshake, ht_finished),
RECORD_CERTIFICATE_URL (
Record.ct_handshake, ht_certificate_url),
RECORD_CERTIFICATE_STATUS (
Record.ct_handshake, ht_certificate_status),
RECORD_SUPPLIEMENTAL_DATA (
Record.ct_handshake, ht_supplemental_data),
RECORD_APPLICATION_DATA (
Record.ct_application_data, ht_not_applicable);
byte contentType;
byte handshakeType;
private RecordType(byte contentType, byte handshakeType) {
this.contentType = contentType;
this.handshakeType = handshakeType;
}
static RecordType valueOf(byte contentType, byte handshakeType) {
if (contentType == Record.ct_change_cipher_spec) {
return RECORD_CHANGE_CIPHER_SPEC;
} else if (contentType == Record.ct_alert) {
return RECORD_ALERT;
} else if (contentType == Record.ct_application_data) {
return RECORD_APPLICATION_DATA;
} else if (handshakeType == ht_hello_request) {
return RECORD_HELLO_REQUEST;
} else if (handshakeType == ht_client_hello) {
return RECORD_CLIENT_HELLO;
} else if (handshakeType == ht_server_hello) {
return RECORD_SERVER_HELLO;
} else if (handshakeType == ht_hello_verify_request) {
return RECORD_HELLO_VERIFY_REQUEST;
} else if (handshakeType == ht_new_session_ticket) {
return RECORD_NEW_SESSION_TICKET;
} else if (handshakeType == ht_certificate) {
return RECORD_CERTIFICATE;
} else if (handshakeType == ht_server_key_exchange) {
return RECORD_SERVER_KEY_EXCHANGE;
} else if (handshakeType == ht_certificate_request) {
return RECORD_CERTIFICATE_REQUEST;
} else if (handshakeType == ht_server_hello_done) {
return RECORD_SERVER_HELLO_DONE;
} else if (handshakeType == ht_certificate_verify) {
return RECORD_CERTIFICATE_VERIFY;
} else if (handshakeType == ht_client_key_exchange) {
return RECORD_CLIENT_KEY_EXCHANGE;
} else if (handshakeType == ht_finished) {
return RECORD_FINISHED;
} else if (handshakeType == ht_certificate_url) {
return RECORD_CERTIFICATE_URL;
} else if (handshakeType == ht_certificate_status) {
return RECORD_CERTIFICATE_STATUS;
} else if (handshakeType == ht_supplemental_data) {
return RECORD_SUPPLIEMENTAL_DATA;
}
// otherwise, invalid record type
throw new IllegalArgumentException(
"Invalid record type (ContentType:" + contentType +
", HandshakeType:" + handshakeType + ")");
}
}
}

View file

@ -0,0 +1,107 @@
/*
* 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.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;
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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,36 +25,89 @@
package sun.security.ssl;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.PrintStream;
import java.security.Principal;
import java.nio.ByteBuffer;
import java.util.Map;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Models a non-certificate based ClientKeyExchange
* Pack of the "ClientKeyExchange" handshake message.
*/
public abstract class ClientKeyExchange extends HandshakeMessage {
final class ClientKeyExchange {
static final SSLConsumer handshakeConsumer =
new ClientKeyExchangeConsumer();
static final HandshakeProducer handshakeProducer =
new ClientKeyExchangeProducer();
public ClientKeyExchange() {
/**
* 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.
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ClientKeyExchange handshake message.");
return null; // make the compiler happe
}
}
@Override
int messageType() {
return ht_client_key_exchange;
/**
* 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);
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.
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected ClientKeyExchange handshake message.");
}
}
@Override
public abstract int messageLength();
@Override
public abstract void send(HandshakeOutStream s) throws IOException;
@Override
public abstract void print(PrintStream s) throws IOException;
public abstract SecretKey clientKeyExchange();
public abstract Principal getPeerPrincipal();
public abstract Principal getLocalPrincipal();
}

View file

@ -1,138 +0,0 @@
/*
* 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;
import sun.security.action.GetPropertyAction;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.SecureRandom;
import java.util.*;
/**
* Models a service that provides support for a particular client key exchange
* mode. Currently used to implement Kerberos-related cipher suites.
*
* @since 9
*/
public interface ClientKeyExchangeService {
static class Loader {
private static final Map<String,ClientKeyExchangeService>
providers = new HashMap<>();
static {
String path = GetPropertyAction.privilegedGetProperty("java.home");
ServiceLoader<ClientKeyExchangeService> sc =
AccessController.doPrivileged(
(PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
() -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
null,
new FilePermission(new File(path, "-").toString(), "read"));
Iterator<ClientKeyExchangeService> iter = sc.iterator();
while (iter.hasNext()) {
ClientKeyExchangeService cs = iter.next();
for (String ex: cs.supported()) {
providers.put(ex, cs);
}
}
}
}
public static ClientKeyExchangeService find(String ex) {
return Loader.providers.get(ex);
}
/**
* Returns the supported key exchange modes by this provider.
* @return the supported key exchange modes
*/
String[] supported();
/**
* Returns a generalized credential object on the server side. The server
* side can use the info to determine if a cipher suite can be enabled.
* @param acc the AccessControlContext of the SSL session
* @return the credential object
*/
Object getServiceCreds(AccessControlContext acc);
/**
* Returns the host name for a service principal. The info can be used in
* SNI or host name verifier.
* @param principal the principal of a service
* @return the string formed host name
*/
String getServiceHostName(Principal principal);
/**
* Returns whether the specified principal is related to the current
* SSLSession. The info can be used to verify a SSL resume.
* @param isClient if true called from client side, otherwise from server
* @param acc the AccessControlContext of the SSL session
* @param p the specified principal
* @return true if related
*/
boolean isRelated(boolean isClient, AccessControlContext acc, Principal p);
/**
* Creates the ClientKeyExchange object on the client side.
* @param serverName the intented peer name
* @param acc the AccessControlContext of the SSL session
* @param protocolVersion the TLS protocol version
* @param rand the SecureRandom that will used to generate the premaster
* @return the new Exchanger object
* @throws IOException if there is an error
*/
ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc,
ProtocolVersion protocolVersion, SecureRandom rand) throws IOException;
/**
* Create the ClientKeyExchange on the server side.
* @param protocolVersion the protocol version
* @param clientVersion the input protocol version
* @param rand a SecureRandom object used to generate premaster
* (if the server has to create one)
* @param encodedTicket the ticket from client
* @param encrypted the encrypted premaster secret from client
* @param acc the AccessControlContext of the SSL session
* @param ServiceCreds the service side credentials object as retrived from
* {@link #getServiceCreds}
* @return the new Exchanger object
* @throws IOException if there is an error
*/
ClientKeyExchange createServerExchange(
ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
AccessControlContext acc, Object ServiceCreds) throws IOException;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,8 +25,10 @@
package sun.security.ssl;
enum NamedGroupType {
NAMED_GROUP_ECDHE, // Elliptic Curve Groups (ECDHE)
NAMED_GROUP_FFDHE, // Finite Field Groups (DHE)
NAMED_GROUP_NONE // No predefined named group
/**
* SSL/(D)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/(D)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,318 @@
/*
* 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.security.util.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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
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)) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"unrecognized cookie");
return; // fatal() always throws, make the compiler happy.
}
}
}
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
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

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* 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
@ -23,88 +23,299 @@
* questions.
*/
package sun.security.ssl;
import java.io.IOException;
import java.io.PrintStream;
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.security.util.HexDumpEncoder;
/*
* Message used by clients to send their Diffie-Hellman public
* keys to servers.
*
* @author David Brownell
/**
* Pack of the "ClientKeyExchange" handshake message.
*/
final class DHClientKeyExchange extends HandshakeMessage {
final class DHClientKeyExchange {
static final DHClientKeyExchangeConsumer dhHandshakeConsumer =
new DHClientKeyExchangeConsumer();
static final DHClientKeyExchangeProducer dhHandshakeProducer =
new DHClientKeyExchangeProducer();
@Override
int messageType() {
return ht_client_key_exchange;
}
/*
* This value may be empty if it was included in the
* client's certificate ...
/**
* 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 byte[] dh_Yc; // 1 to 2^16 -1 bytes
private static final
class DHClientKeyExchangeMessage extends HandshakeMessage {
private byte[] y; // 1 to 2^16 - 1 bytes
BigInteger getClientPublicKey() {
return dh_Yc == null ? null : new BigInteger(1, dh_Yc);
}
DHClientKeyExchangeMessage(
HandshakeContext handshakeContext) throws IOException {
super(handshakeContext);
// This happens in client side only.
ClientHandshakeContext chc =
(ClientHandshakeContext)handshakeContext;
/*
* Either pass the client's public key explicitly (because it's
* using DHE or DH_anon), or implicitly (the public key was in the
* certificate).
*/
DHClientKeyExchange(BigInteger publicKey) {
dh_Yc = toByteArray(publicKey);
}
DHEPossession dhePossession = null;
for (SSLPossession possession : chc.handshakePossessions) {
if (possession instanceof DHEPossession) {
dhePossession = (DHEPossession)possession;
break;
}
}
DHClientKeyExchange() {
dh_Yc = null;
}
if (dhePossession == null) {
// unlikely
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No DHE credentials negotiated for client key exchange");
}
/*
* Get the client's public key either explicitly or implicitly.
* (It's ugly to have an empty record be sent in the latter case,
* but that's what the protocol spec requires.)
*/
DHClientKeyExchange(HandshakeInStream input) throws IOException {
if (input.available() >= 2) {
dh_Yc = input.getBytes16();
} else {
// currently, we don't support cipher suites that requires
// implicit public key of client.
throw new SSLHandshakeException(
"Unsupported implicit client DiffieHellman public key");
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) {
shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid DH ClientKeyExchange message: insufficient data");
}
this.y = Record.getBytes16(m);
if (m.hasRemaining()) {
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);
}
}
@Override
int messageLength() {
if (dh_Yc == null) {
return 0;
} else {
return dh_Yc.length + 2;
/**
* 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) {
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
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
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;
}
}
@Override
void send(HandshakeOutStream s) throws IOException {
if (dh_Yc != null && dh_Yc.length != 0) {
s.putBytes16(dh_Yc);
/**
* The DiffieHellman "ClientKeyExchange" handshake message consumer.
*/
private static final
class DHClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private DHClientKeyExchangeConsumer() {
// blank
}
}
@Override
void print(PrintStream s) throws IOException {
s.println("*** ClientKeyExchange, DH");
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (debug != null && Debug.isOn("verbose")) {
Debug.println(s, "DH Public key", dh_Yc);
DHEPossession dhePossession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof DHEPossession) {
dhePossession = (DHEPossession)possession;
break;
}
}
if (dhePossession == null) {
// unlikely
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
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
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}

View file

@ -1,278 +0,0 @@
/*
* Copyright (c) 1996, 2017, 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 javax.net.ssl.SSLHandshakeException;
import javax.crypto.SecretKey;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.*;
import java.util.EnumSet;
import sun.security.util.KeyUtil;
/**
* This class implements the Diffie-Hellman key exchange algorithm.
* D-H means combining your private key with your partners public key to
* generate a number. The peer does the same with its private key and our
* public key. Through the magic of Diffie-Hellman we both come up with the
* same number. This number is secret (discounting MITM attacks) and hence
* called the shared secret. It has the same length as the modulus, e.g. 512
* or 1024 bit. Man-in-the-middle attacks are typically countered by an
* independent authentication step using certificates (RSA, DSA, etc.).
*
* The thing to note is that the shared secret is constant for two partners
* with constant private keys. This is often not what we want, which is why
* it is generally a good idea to create a new private key for each session.
* Generating a private key involves one modular exponentiation assuming
* suitable D-H parameters are available.
*
* General usage of this class (TLS DHE case):
* . if we are server, call DHCrypt(keyLength,random). This generates
* an ephemeral keypair of the request length.
* . if we are client, call DHCrypt(modulus, base, random). This
* generates an ephemeral keypair using the parameters specified by
* the server.
* . send parameters and public value to remote peer
* . receive peers ephemeral public key
* . call getAgreedSecret() to calculate the shared secret
*
* In TLS the server chooses the parameter values itself, the client must use
* those sent to it by the server.
*
* The use of ephemeral keys as described above also achieves what is called
* "forward secrecy". This means that even if the authentication keys are
* broken at a later date, the shared secret remains secure. The session is
* compromised only if the authentication keys are already broken at the
* time the key exchange takes place and an active MITM attack is used.
* This is in contrast to straightforward encrypting RSA key exchanges.
*
* @author David Brownell
*/
final class DHCrypt {
// group parameters (prime modulus and generator)
private BigInteger modulus; // P (aka N)
private BigInteger base; // G (aka alpha)
// our private key (including private component x)
private PrivateKey privateKey;
// public component of our key, X = (g ^ x) mod p
private BigInteger publicValue; // X (aka y)
// the times to recove from failure if public key validation
private static int MAX_FAILOVER_TIMES = 2;
/**
* Generate a Diffie-Hellman keypair of the specified size.
*/
DHCrypt(int keyLength, SecureRandom random) {
this(keyLength,
PredefinedDHParameterSpecs.definedParams.get(keyLength), random);
}
/**
* Generate a Diffie-Hellman keypair using the specified parameters.
*
* @param modulus the Diffie-Hellman modulus P
* @param base the Diffie-Hellman base G
*/
DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) {
this(modulus.bitLength(),
new DHParameterSpec(modulus, base), random);
}
/**
* Generate a Diffie-Hellman keypair using the named group.
*/
DHCrypt(NamedGroup namedGroup, SecureRandom random) {
this(-1, // The length (-1) is not used in the implementation.
SupportedGroupsExtension.getDHParameterSpec(namedGroup), random);
}
/**
* Generate a Diffie-Hellman keypair using the specified size and
* parameters.
*/
private DHCrypt(int keyLength,
DHParameterSpec params, SecureRandom random) {
try {
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman");
if (params != null) {
kpg.initialize(params, random);
} else {
kpg.initialize(keyLength, random);
}
DHPublicKeySpec spec = generateDHPublicKeySpec(kpg);
if (spec == null) {
throw new RuntimeException("Could not generate DH keypair");
}
publicValue = spec.getY();
modulus = spec.getP();
base = spec.getG();
} catch (GeneralSecurityException e) {
throw new RuntimeException("Could not generate DH keypair", e);
}
}
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 (Exception e) {
throw new RuntimeException(e);
}
}
/** Returns the Diffie-Hellman modulus. */
BigInteger getModulus() {
return modulus;
}
/** Returns the Diffie-Hellman base (generator). */
BigInteger getBase() {
return base;
}
/**
* Gets the public key of this end of the key exchange.
*/
BigInteger getPublicKey() {
return publicValue;
}
/**
* Get the secret data that has been agreed on through Diffie-Hellman
* key agreement protocol. Note that in the two party protocol, if
* the peer keys are already known, no other data needs to be sent in
* order to agree on a secret. That is, a secured message may be
* sent without any mandatory round-trip overheads.
*
* <P>It is illegal to call this member function if the private key
* has not been set (or generated).
*
* @param peerPublicKey the peer's public key.
* @param keyIsValidated whether the {@code peerPublicKey} has beed
* validated
* @return the secret, which is an unsigned big-endian integer
* the same size as the Diffie-Hellman modulus.
*/
SecretKey getAgreedSecret(BigInteger peerPublicValue,
boolean keyIsValidated) throws SSLHandshakeException {
try {
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
DHPublicKeySpec spec =
new DHPublicKeySpec(peerPublicValue, modulus, base);
PublicKey publicKey = kf.generatePublic(spec);
KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
// validate the Diffie-Hellman public key
if (!keyIsValidated &&
!KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) {
try {
KeyUtil.validate(spec);
} catch (InvalidKeyException ike) {
// prefer handshake_failure alert to internal_error alert
throw new SSLHandshakeException(ike.getMessage());
}
}
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret("TlsPremasterSecret");
} catch (GeneralSecurityException e) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate secret").initCause(e);
}
}
// Check constraints of the specified DH public key.
void checkConstraints(AlgorithmConstraints constraints,
BigInteger peerPublicValue) throws SSLHandshakeException {
try {
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
DHPublicKeySpec spec =
new DHPublicKeySpec(peerPublicValue, modulus, base);
DHPublicKey publicKey = (DHPublicKey)kf.generatePublic(spec);
// check constraints of DHPublicKey
if (!constraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
throw new SSLHandshakeException(
"DHPublicKey does not comply to algorithm constraints");
}
} catch (GeneralSecurityException gse) {
throw (SSLHandshakeException) new SSLHandshakeException(
"Could not generate DHPublicKey").initCause(gse);
}
}
// Generate and validate DHPublicKeySpec
private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg)
throws GeneralSecurityException {
boolean doExtraValiadtion =
(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) {
KeyPair kp = kpg.generateKeyPair();
privateKey = kp.getPrivate();
DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
// validate the Diffie-Hellman public key
if (doExtraValiadtion) {
try {
KeyUtil.validate(spec);
} catch (InvalidKeyException ivke) {
if (i == MAX_FAILOVER_TIMES) {
throw ivke;
}
// otherwise, ignore the exception and try the next one
continue;
}
}
return spec;
}
return null;
}
}

View file

@ -0,0 +1,534 @@
/*
* 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.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 = publicKey.getY().toByteArray();
int pSize = KeyUtil.getKeySize(publicKey);
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.length() == 0) {
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) {
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,565 @@
/*
* 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 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.security.util.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
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) {
signatureScheme = SignatureScheme.getPreferableAlgorithm(
shc.peerRequestedSignatureSchemes,
x509Possession.popPrivateKey,
shc.negotiatedProtocol);
if (signatureScheme == null) {
// Unlikely, the credentials generator should have
// selected the preferable signature algorithm properly.
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No preferred signature algorithm");
}
try {
signer = signatureScheme.getSignature(
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
}
} else {
signatureScheme = null;
try {
signer = getSignature(
x509Possession.popPrivateKey.getAlgorithm(),
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
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) {
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) {
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()) {
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) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature algorithm (" + ssid +
") used in DH ServerKeyExchange handshake message");
}
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
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.getSignature(
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
return; // make the compiler happe
}
} else {
try {
signer = getSignature(
x509Credentials.popPublicKey.getAlgorithm(),
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Credentials.popPublicKey.getAlgorithm(), e);
return; // make the compiler happe
}
}
try {
updateSignature(signer,
chc.clientHelloRandom.randomBytes,
chc.serverHelloRandom.randomBytes);
if (!signer.verify(paramsSignature)) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature on DH ServerKeyExchange message");
}
} catch (SignatureException ex) {
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) {
chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"Could not generate DHPublicKey", gse);
return; // make the compiler happy
}
if (!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -18,56 +18,41 @@
* 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
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 9406+5 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.security.util.HexDumpEncoder;
import static sun.security.ssl.HandshakeMessage.*;
import javax.net.ssl.SSLException;
import sun.security.ssl.SSLCipher.SSLReadCipher;
/**
* DTLS {@code InputRecord} implementation for {@code SSLEngine}.
*/
final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private DTLSReassembler reassembler = null;
private int readEpoch;
int readEpoch;
int prevReadEpoch;
Authenticator prevReadAuthenticator;
CipherBox prevReadCipher;
DTLSInputRecord() {
DTLSInputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
this.readEpoch = 0;
this.readAuthenticator = new MAC(true);
this.prevReadEpoch = 0;
this.prevReadCipher = CipherBox.NULL;
this.prevReadAuthenticator = new MAC(true);
}
@Override
void changeReadCiphers(Authenticator readAuthenticator,
CipherBox readCipher) {
prevReadCipher.dispose();
this.prevReadAuthenticator = this.readAuthenticator;
this.prevReadCipher = this.readCipher;
this.prevReadEpoch = this.readEpoch;
this.readAuthenticator = readAuthenticator;
void changeReadCiphers(SSLReadCipher readCipher) {
this.readCipher = readCipher;
this.readEpoch++;
}
@ -75,7 +60,6 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
@Override
public synchronized void close() throws IOException {
if (!isClosed) {
prevReadCipher.dispose();
super.close();
}
}
@ -87,14 +71,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
@Override
int estimateFragmentSize(int packetSize) {
int macLen = 0;
if (readAuthenticator instanceof MAC) {
macLen = ((MAC)readAuthenticator).MAClen();
}
if (packetSize > 0) {
return readCipher.estimateFragmentSize(
packetSize, macLen, headerSize);
return readCipher.estimateFragmentSize(packetSize, headerSize);
} else {
return Record.maxDataSize;
}
@ -107,6 +85,11 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
@Override
void finishHandshake() {
reassembler = null;
}
@Override
Plaintext acquirePlaintext() {
if (reassembler != null) {
@ -116,16 +99,28 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
return null;
}
@Override
Plaintext decode(ByteBuffer packet) {
@Override
Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
int srcsLength) throws IOException, BadPaddingException {
if (srcs == null || srcs.length == 0 || srcsLength == 0) {
Plaintext pt = acquirePlaintext();
return pt == null ? new Plaintext[0] : new Plaintext[] { pt };
} else if (srcsLength == 1) {
return decode(srcs[srcsOffset]);
} else {
ByteBuffer packet = extract(srcs,
srcsOffset, srcsLength, DTLSRecord.headerSize);
return decode(packet);
}
}
Plaintext[] decode(ByteBuffer packet) {
if (isClosed) {
return null;
}
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw read]: length = " + packet.remaining(), packet);
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
SSLLogger.fine("Raw read", packet);
}
// The caller should have validated the record.
@ -149,20 +144,20 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
int contentLen = ((packet.get() & 0xFF) << 8) |
(packet.get() & 0xFF); // pos: 11, 12
if (debug != null && Debug.isOn("record")) {
Debug.log("READ: " +
ProtocolVersion.valueOf(majorVersion, minorVersion) +
" " + Record.contentName(contentType) + ", length = " +
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine("READ: " +
ProtocolVersion.nameOf(majorVersion, minorVersion) +
" " + ContentType.nameOf(contentType) + ", length = " +
contentLen);
}
int recLim = srcPos + DTLSRecord.headerSize + contentLen;
int recLim = Math.addExact(srcPos, DTLSRecord.headerSize + contentLen);
if (this.prevReadEpoch > recordEpoch) {
if (this.readEpoch > recordEpoch) {
// Reset the position of the packet buffer.
packet.position(recLim);
if (debug != null && Debug.isOn("record")) {
Debug.printHex("READ: discard this old record", recordEnS);
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine("READ: discard this old record", recordEnS);
}
return null;
}
@ -171,20 +166,23 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (this.readEpoch < recordEpoch) {
// Discard the record younger than the current epcoh if:
// 1. it is not a handshake message, or
// 2. it is not of next epoch.
if (((contentType != Record.ct_handshake) &&
(contentType != Record.ct_change_cipher_spec)) ||
// 3. it is not of next epoch.
if ((contentType != ContentType.HANDSHAKE.id &&
contentType != ContentType.CHANGE_CIPHER_SPEC.id) ||
(reassembler == null &&
contentType != ContentType.HANDSHAKE.id) ||
(this.readEpoch < (recordEpoch - 1))) {
packet.position(recLim);
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Premature record (epoch), discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Premature record (epoch), discard it.");
}
return null;
}
// Not ready to decrypt this record, may be an encrypted Finished
// message, need to buffer it.
byte[] fragment = new byte[contentLen];
@ -193,80 +191,65 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
majorVersion, minorVersion,
recordEnS, recordEpoch, recordSeq, true);
if (reassembler == null) {
reassembler = new DTLSReassembler(recordEpoch);
}
reassembler.queueUpFragment(buffered);
// consume the full record in the packet buffer.
packet.position(recLim);
return reassembler.acquirePlaintext();
Plaintext pt = reassembler.acquirePlaintext();
return pt == null ? null : new Plaintext[] { pt };
}
//
// Now, the message is of this epoch or the previous epoch.
// Now, the message is of this epoch.
//
Authenticator decodeAuthenticator;
CipherBox decodeCipher;
if (this.readEpoch == recordEpoch) {
decodeAuthenticator = readAuthenticator;
decodeCipher = readCipher;
} else { // prevReadEpoch == recordEpoch
decodeAuthenticator = prevReadAuthenticator;
decodeCipher = prevReadCipher;
}
// decrypt the fragment
packet.limit(recLim);
packet.position(srcPos + DTLSRecord.headerSize);
ByteBuffer plaintextFragment;
try {
plaintextFragment = decrypt(decodeAuthenticator,
decodeCipher, contentType, packet, recordEnS);
} catch (BadPaddingException bpe) {
if (debug != null && Debug.isOn("ssl")) {
Debug.log("Discard invalid record: " + bpe);
Plaintext plaintext =
readCipher.decrypt(contentType, packet, recordEnS);
plaintextFragment = plaintext.fragment;
contentType = plaintext.contentType;
} catch (GeneralSecurityException gse) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("Discard invalid record: " + gse);
}
// invalid, discard this record [section 4.1.2.7, RFC 6347]
return null;
} finally {
// comsume a complete record
// consume a complete record
packet.limit(srcLim);
packet.position(recLim);
}
if (contentType != Record.ct_change_cipher_spec &&
contentType != Record.ct_handshake) { // app data or alert
if (contentType != ContentType.CHANGE_CIPHER_SPEC.id &&
contentType != ContentType.HANDSHAKE.id) { // app data or alert
// no retransmission
// Cleanup the handshake reassembler if necessary.
if ((reassembler != null) &&
(reassembler.handshakeEpoch < recordEpoch)) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Cleanup the handshake reassembler");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Cleanup the handshake reassembler");
}
reassembler = null;
}
return new Plaintext(contentType, majorVersion, minorVersion,
recordEpoch, Authenticator.toLong(recordEnS),
plaintextFragment);
return new Plaintext[] {
new Plaintext(contentType, majorVersion, minorVersion,
recordEpoch, Authenticator.toLong(recordEnS),
plaintextFragment)};
}
if (contentType == Record.ct_change_cipher_spec) {
if (contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
if (reassembler == null) {
if (this.readEpoch != recordEpoch) {
// handshake has not started, should be an
// old handshake message, discard it.
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
"Lagging behind ChangeCipherSpec, discard it.");
}
return null;
}
reassembler = new DTLSReassembler(recordEpoch);
}
@ -284,26 +267,15 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (hsFrag == null) {
// invalid, discard this record
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Invalid handshake message, discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Invalid handshake message, discard it.");
}
return null;
}
if (reassembler == null) {
if (this.readEpoch != recordEpoch) {
// handshake has not started, should be an
// old handshake message, discard it.
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
"Lagging behind handshake record, discard it.");
}
return null;
}
reassembler = new DTLSReassembler(recordEpoch);
}
@ -314,18 +286,25 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Completed the read of the full record. Acquire the reassembled
// messages.
if (reassembler != null) {
return reassembler.acquirePlaintext();
Plaintext pt = reassembler.acquirePlaintext();
return pt == null ? null : new Plaintext[] { pt };
}
if (debug != null && Debug.isOn("verbose")) {
Debug.log("The reassembler is not initialized yet.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("The reassembler is not initialized yet.");
}
return null;
}
@Override
int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
int bytesInCompletePacket(
ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
return bytesInCompletePacket(srcs[srcsOffset]);
}
private int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
// DTLS length field is in bytes 11/12
if (packet.remaining() < headerSize) {
@ -337,15 +316,20 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Check the content type of the record.
byte contentType = packet.get(pos);
if (!Record.isValidContentType(contentType)) {
if (ContentType.valueOf(contentType) == null) {
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
// Check the protocol version of the record.
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2));
checkRecordVersion(recordVersion, false);
byte majorVersion = packet.get(pos + 1);
byte minorVersion = packet.get(pos + 2);
if (!ProtocolVersion.isNegotiable(
majorVersion, minorVersion, true, false)) {
throw new SSLException("Unrecognized record version " +
ProtocolVersion.nameOf(majorVersion, minorVersion) +
" , plaintext connection?");
}
// Get the fragment length of the record.
int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) +
@ -359,17 +343,6 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
return fragLen;
}
@Override
void checkRecordVersion(ProtocolVersion recordVersion,
boolean allowSSL20Hello) throws SSLException {
if (!recordVersion.maybeDTLSProtocol()) {
throw new SSLException(
"Unrecognized record version " + recordVersion +
" , plaintext connection?");
}
}
private static HandshakeFragment parseHandshakeMessage(
byte contentType, byte majorVersion, byte minorVersion,
byte[] recordEnS, int recordEpoch, long recordSeq,
@ -377,8 +350,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
int remaining = plaintextFragment.remaining();
if (remaining < handshakeHeaderSize) {
if (debug != null && Debug.isOn("ssl")) {
Debug.log("Discard invalid record: " +
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("Discard invalid record: " +
"too small record to hold a handshake fragment");
}
@ -403,8 +376,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
((plaintextFragment.get() & 0xFF) << 8) |
(plaintextFragment.get() & 0xFF); // pos: 9-11
if ((remaining - handshakeHeaderSize) < fragmentLength) {
if (debug != null && Debug.isOn("ssl")) {
Debug.log("Discard invalid record: " +
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("Discard invalid record: " +
"not a complete handshake fragment in the record");
}
@ -461,20 +434,20 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
@Override
public int compareTo(RecordFragment o) {
if (this.contentType == Record.ct_change_cipher_spec) {
if (o.contentType == Record.ct_change_cipher_spec) {
if (this.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
if (o.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
// Only one incoming ChangeCipherSpec message for an epoch.
//
// Ignore duplicated ChangeCipherSpec messages.
return Integer.compare(this.recordEpoch, o.recordEpoch);
} else if ((this.recordEpoch == o.recordEpoch) &&
(o.contentType == Record.ct_handshake)) {
(o.contentType == ContentType.HANDSHAKE.id)) {
// ChangeCipherSpec is the latest message of an epoch.
return 1;
}
} else if (o.contentType == Record.ct_change_cipher_spec) {
} else if (o.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
if ((this.recordEpoch == o.recordEpoch) &&
(this.contentType == Record.ct_handshake)) {
(this.contentType == ContentType.HANDSHAKE.id)) {
// ChangeCipherSpec is the latest message of an epoch.
return -1;
} else {
@ -559,7 +532,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
private static final class HandshakeFlight implements Cloneable {
static final byte HF_UNKNOWN = HandshakeMessage.ht_not_applicable;
static final byte HF_UNKNOWN = SSLHandshake.NOT_APPLICABLE.id;
byte handshakeType; // handshake type
int flightEpoch; // the epoch of the first message
@ -665,7 +638,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
if (isMinimalFlightMessage && (hsf.fragmentOffset == 0) &&
(hsf.handshakeType != HandshakeMessage.ht_finished)) {
(hsf.handshakeType != SSLHandshake.FINISHED.id)) {
// reset the handshake flight
handshakeFlight.handshakeType = hsf.handshakeType;
@ -673,7 +646,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
handshakeFlight.minMessageSeq = hsf.messageSeq;
}
if (hsf.handshakeType == HandshakeMessage.ht_finished) {
if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
handshakeFlight.maxMessageSeq = hsf.messageSeq;
handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
handshakeFlight.maxRecordSeq = hsf.recordSeq;
@ -718,8 +691,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// It's OK to discard retransmission as the handshake hash
// is computed as if each handshake message had been sent
// as a single fragment.
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Have got the full message, discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Have got the full message, discard it.");
}
return;
@ -742,8 +715,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
((hole.limit > hsf.fragmentOffset) &&
(hole.limit < fragmentLimit))) {
if (debug != null && Debug.isOn("ssl")) {
Debug.log("Discard invalid record: " +
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("Discard invalid record: " +
"handshake fragment ranges are overlapping");
}
@ -773,7 +746,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
// buffer this fragment
if (hsf.handshakeType == HandshakeMessage.ht_finished) {
if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
// Need no status update.
bufferedFragments.add(hsf);
} else {
@ -849,7 +822,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (precedingFlight.maxMessageSeq < hsf.messageSeq) {
isNewFlight = true;
}
} else if (rf.contentType != Record.ct_change_cipher_spec) {
} else if (
rf.contentType != ContentType.CHANGE_CIPHER_SPEC.id) {
// ciphertext
if (precedingFlight.maxRecordEpoch < rf.recordEpoch) {
isNewFlight = true;
@ -904,8 +879,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
int previousEpoch = nextRecordEpoch - 1;
if (rf.recordEpoch < previousEpoch) {
// Too old to use, discard this record.
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Too old epoch to use this record, discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Too old epoch to use this record, discard it.");
}
return false;
@ -932,7 +908,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (precedingFlight.minMessageSeq > hsf.messageSeq) {
isDesired = false;
}
} else if (rf.contentType == Record.ct_change_cipher_spec) {
} else if (
rf.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
// ChangeCipherSpec
if (precedingFlight.flightEpoch != rf.recordEpoch) {
isDesired = false;
@ -948,8 +926,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (!isDesired) {
// Too old to use, discard this retransmitted record
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Too old retransmission to use, discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Too old retransmission to use, discard it.");
}
return false;
@ -960,8 +939,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Previously disordered record for the current epoch.
//
// Should has been retransmitted. Discard this record.
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Lagging behind record (sequence), discard it.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Lagging behind record (sequence), discard it.");
}
return false;
@ -978,8 +958,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
Plaintext acquirePlaintext() {
if (bufferedFragments.isEmpty()) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log("No received handshake messages");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("No received handshake messages");
}
return null;
}
@ -999,8 +979,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Reset the next handshake flight.
resetHandshakeFlight(precedingFlight);
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Received a retransmission flight.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Received a retransmission flight.");
}
return Plaintext.PLAINTEXT_NULL;
@ -1011,9 +991,10 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
if (!flightIsReady) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log("The handshake flight is not ready to use: " +
handshakeFlight.handshakeType);
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"The handshake flight is not ready to use: " +
handshakeFlight.handshakeType);
}
return null;
}
@ -1036,7 +1017,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
resetHandshakeFlight(precedingFlight);
if (expectCCSFlight &&
(precedingFlight.flightEpoch ==
(precedingFlight.handshakeType ==
HandshakeFlight.HF_UNKNOWN)) {
expectCCSFlight = false;
}
@ -1088,13 +1069,13 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
private Plaintext acquireCachedMessage() {
RecordFragment rFrag = bufferedFragments.first();
if (readEpoch != rFrag.recordEpoch) {
if (readEpoch > rFrag.recordEpoch) {
// discard old records
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Discard old buffered ciphertext fragments.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Discard old buffered ciphertext fragments.");
}
bufferedFragments.remove(rFrag); // popup the fragment
}
@ -1104,8 +1085,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
flightIsReady = false;
}
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Not yet ready to decrypt the cached fragments.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Not yet ready to decrypt the cached fragments.");
}
return null;
}
@ -1115,11 +1097,13 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment);
ByteBuffer plaintextFragment = null;
try {
plaintextFragment = decrypt(readAuthenticator, readCipher,
Plaintext plaintext = readCipher.decrypt(
rFrag.contentType, fragment, rFrag.recordEnS);
} catch (BadPaddingException bpe) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Discard invalid record: " + bpe);
plaintextFragment = plaintext.fragment;
rFrag.contentType = plaintext.contentType;
} catch (GeneralSecurityException gse) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Discard invalid record: ", gse);
}
// invalid, discard this record [section 4.1.2.7, RFC 6347]
@ -1130,7 +1114,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// end of this flight), ClinetHello or HelloRequest (the
// beginning of the next flight) message. Need not to check
// any ChangeCipherSpec message.
if (rFrag.contentType == Record.ct_handshake) {
if (rFrag.contentType == ContentType.HANDSHAKE.id) {
while (plaintextFragment.remaining() > 0) {
HandshakeFragment hsFrag = parseHandshakeMessage(
rFrag.contentType,
@ -1140,8 +1124,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (hsFrag == null) {
// invalid, discard this record
if (debug != null && Debug.isOn("verbose")) {
Debug.printHex(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Invalid handshake fragment, discard it",
plaintextFragment);
}
@ -1153,7 +1137,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// been checked and updated for the Finished handshake
// message before the decryption. Please don't update
// flightIsReady for Finished messages.
if (hsFrag.handshakeType != HandshakeMessage.ht_finished) {
if (hsFrag.handshakeType != SSLHandshake.FINISHED.id) {
flightIsReady = false;
needToCheckFlight = true;
}
@ -1172,7 +1156,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private Plaintext acquireHandshakeMessage() {
RecordFragment rFrag = bufferedFragments.first();
if (rFrag.contentType == Record.ct_change_cipher_spec) {
if (rFrag.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
this.nextRecordEpoch = rFrag.recordEpoch + 1;
// For retransmissions, the next record sequence number is a
@ -1183,16 +1167,12 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Popup the fragment.
bufferedFragments.remove(rFrag);
// Reload if this message has been reserved for handshake hash.
handshakeHash.reload();
return new Plaintext(rFrag.contentType,
rFrag.majorVersion, rFrag.minorVersion,
rFrag.recordEpoch,
Authenticator.toLong(rFrag.recordEnS),
ByteBuffer.wrap(rFrag.fragment));
} else { // rFrag.contentType == Record.ct_handshake
} else { // rFrag.contentType == ContentType.HANDSHAKE.id
HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
if ((hsFrag.messageLength == hsFrag.fragmentLength) &&
(hsFrag.fragmentOffset == 0)) { // no fragmentation
@ -1204,7 +1184,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Note: may try to avoid byte array copy in the future.
byte[] recordFrag = new byte[hsFrag.messageLength + 4];
Plaintext plaintext = new Plaintext(hsFrag.contentType,
Plaintext plaintext = new Plaintext(
hsFrag.contentType,
hsFrag.majorVersion, hsFrag.minorVersion,
hsFrag.recordEpoch,
Authenticator.toLong(hsFrag.recordEnS),
@ -1230,7 +1211,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
//
// Note: may try to avoid byte array copy in the future.
byte[] recordFrag = new byte[hsFrag.messageLength + 4];
Plaintext plaintext = new Plaintext(hsFrag.contentType,
Plaintext plaintext = new Plaintext(
hsFrag.contentType,
hsFrag.majorVersion, hsFrag.minorVersion,
hsFrag.recordEpoch,
Authenticator.toLong(hsFrag.recordEnS),
@ -1264,7 +1246,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// read the next buffered record
if (!bufferedFragments.isEmpty()) {
rFrag = bufferedFragments.first();
if (rFrag.contentType != Record.ct_handshake) {
if (rFrag.contentType != ContentType.HANDSHAKE.id) {
break;
} else {
hmFrag = (HandshakeFragment)rFrag;
@ -1293,29 +1275,30 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (expectCCSFlight) {
// Have the ChangeCipherSpec/Finished flight been received?
boolean isReady = hasFinishedMessage(bufferedFragments);
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Has the final flight been received? " + isReady);
}
return isReady;
}
if (debug != null && Debug.isOn("verbose")) {
Debug.log("No flight is received yet.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("No flight is received yet.");
}
return false;
}
if ((flightType == HandshakeMessage.ht_client_hello) ||
(flightType == HandshakeMessage.ht_hello_request) ||
(flightType == HandshakeMessage.ht_hello_verify_request)) {
if ((flightType == SSLHandshake.CLIENT_HELLO.id) ||
(flightType == SSLHandshake.HELLO_REQUEST.id) ||
(flightType == SSLHandshake.HELLO_VERIFY_REQUEST.id)) {
// single handshake message flight
boolean isReady = hasCompleted(flightType);
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Is the handshake message completed? " + isReady);
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Is the handshake message completed? " + isReady);
}
return isReady;
@ -1324,11 +1307,11 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
//
// the ServerHello flight
//
if (flightType == HandshakeMessage.ht_server_hello) {
if (flightType == SSLHandshake.SERVER_HELLO.id) {
// Firstly, check the first flight handshake message.
if (!hasCompleted(flightType)) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"The ServerHello message is not completed yet.");
}
@ -1339,8 +1322,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// an abbreviated handshake
//
if (hasFinishedMessage(bufferedFragments)) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log("It's an abbreviated handshake.");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("It's an abbreviated handshake.");
}
return true;
@ -1350,11 +1333,12 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// a full handshake
//
List<HoleDescriptor> holes = handshakeFlight.holesMap.get(
HandshakeMessage.ht_server_hello_done);
SSLHandshake.SERVER_HELLO_DONE.id);
if ((holes == null) || !holes.isEmpty()) {
// Not yet got the final message of the flight.
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Not yet got the ServerHelloDone message");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Not yet got the ServerHelloDone message");
}
return false;
@ -1364,8 +1348,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
boolean isReady = hasCompleted(bufferedFragments,
handshakeFlight.minMessageSeq,
handshakeFlight.maxMessageSeq);
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Is the ServerHello flight (message " +
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Is the ServerHello flight (message " +
handshakeFlight.minMessageSeq + "-" +
handshakeFlight.maxMessageSeq +
") completed? " + isReady);
@ -1381,13 +1366,13 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// ht_supplemental_data and ht_certificate_url are
// suppported in the future.
//
if ((flightType == HandshakeMessage.ht_certificate) ||
(flightType == HandshakeMessage.ht_client_key_exchange)) {
if ((flightType == SSLHandshake.CERTIFICATE.id) ||
(flightType == SSLHandshake.CLIENT_KEY_EXCHANGE.id)) {
// Firstly, check the first flight handshake message.
if (!hasCompleted(flightType)) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"The ClientKeyExchange or client Certificate " +
"message is not completed yet.");
}
@ -1396,12 +1381,12 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
// Is client CertificateVerify a mandatory message?
if (flightType == HandshakeMessage.ht_certificate) {
if (flightType == SSLHandshake.CERTIFICATE.id) {
if (needClientVerify(bufferedFragments) &&
!hasCompleted(ht_certificate_verify)) {
!hasCompleted(SSLHandshake.CERTIFICATE_VERIFY.id)) {
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Not yet have the CertificateVerify message");
}
@ -1411,8 +1396,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
if (!hasFinishedMessage(bufferedFragments)) {
// not yet have the ChangeCipherSpec/Finished messages
if (debug != null && Debug.isOn("verbose")) {
Debug.log(
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Not yet have the ChangeCipherSpec and " +
"Finished messages");
}
@ -1424,8 +1409,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
boolean isReady = hasCompleted(bufferedFragments,
handshakeFlight.minMessageSeq,
handshakeFlight.maxMessageSeq);
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Is the ClientKeyExchange flight (message " +
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine(
"Is the ClientKeyExchange flight (message " +
handshakeFlight.minMessageSeq + "-" +
handshakeFlight.maxMessageSeq +
") completed? " + isReady);
@ -1437,8 +1423,8 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
//
// Otherwise, need to receive more handshake messages.
//
if (debug != null && Debug.isOn("verbose")) {
Debug.log("Need to receive more handshake messages");
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Need to receive more handshake messages");
}
return false;
@ -1456,12 +1442,12 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
boolean hasCCS = false;
boolean hasFin = false;
for (RecordFragment fragment : fragments) {
if (fragment.contentType == Record.ct_change_cipher_spec) {
if (fragment.contentType == ContentType.CHANGE_CIPHER_SPEC.id) {
if (hasFin) {
return true;
}
hasCCS = true;
} else if (fragment.contentType == Record.ct_handshake) {
} else if (fragment.contentType == ContentType.HANDSHAKE.id) {
// Finished is the first expected message of a new epoch.
if (fragment.isCiphertext) {
if (hasCCS) {
@ -1484,13 +1470,13 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// The caller should have checked the completion of the first
// present handshake message. Need not to check it again.
for (RecordFragment rFrag : fragments) {
if ((rFrag.contentType != Record.ct_handshake) ||
if ((rFrag.contentType != ContentType.HANDSHAKE.id) ||
rFrag.isCiphertext) {
break;
}
HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
if (hsFrag.handshakeType != HandshakeMessage.ht_certificate) {
if (hsFrag.handshakeType != SSLHandshake.CERTIFICATE.id) {
continue;
}
@ -1519,7 +1505,7 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// The caller should have checked the completion of the first
// present handshake message. Need not to check it again.
for (RecordFragment rFrag : fragments) {
if ((rFrag.contentType != Record.ct_handshake) ||
if ((rFrag.contentType != ContentType.HANDSHAKE.id) ||
rFrag.isCiphertext) {
break;
}
@ -1546,32 +1532,16 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private void handshakeHashing(
HandshakeFragment hsFrag, Plaintext plaintext) {
byte hsType = hsFrag.handshakeType;
if ((hsType == HandshakeMessage.ht_hello_request) ||
(hsType == HandshakeMessage.ht_hello_verify_request)) {
if (!handshakeHash.isHashable(hsType)) {
// omitted from handshake hash computation
return;
}
if ((hsFrag.messageSeq == 0) &&
(hsType == HandshakeMessage.ht_client_hello)) {
// omit initial ClientHello message
//
// 4: handshake header
// 2: ClientHello.client_version
// 32: ClientHello.random
int sidLen = plaintext.fragment.get(38);
if (sidLen == 0) { // empty session_id, initial handshake
return;
}
}
// calculate the DTLS header
byte[] temporary = new byte[12]; // 12: handshake header size
// calculate the DTLS header and reserve the handshake message
plaintext.fragment.position(4); // ignore the TLS header
byte[] temporary = new byte[plaintext.fragment.remaining() + 12];
// 12: handshake header size
// Handshake.msg_type
temporary[0] = hsFrag.handshakeType;
@ -1595,25 +1565,9 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
temporary[10] = temporary[2];
temporary[11] = temporary[3];
plaintext.fragment.position(4); // ignore the TLS header
if ((hsType != HandshakeMessage.ht_finished) &&
(hsType != HandshakeMessage.ht_certificate_verify)) {
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(temporary, 0, 12);
handshakeHash.update(plaintext.fragment);
} else {
// Reserve until this handshake message has been processed.
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.reserve(temporary, 0, 12);
handshakeHash.reserve(plaintext.fragment);
}
plaintext.fragment.get(temporary,
12, plaintext.fragment.remaining());
handshakeHash.receive(temporary);
plaintext.fragment.position(0); // restore the position
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -28,13 +28,8 @@ package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.security.util.HexDumpEncoder;
import static sun.security.ssl.Ciphertext.RecordType;
import sun.security.ssl.SSLCipher.SSLWriteCipher;
/**
* DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
@ -47,54 +42,62 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
int prevWriteEpoch;
Authenticator prevWriteAuthenticator;
CipherBox prevWriteCipher;
SSLWriteCipher prevWriteCipher;
private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
private final LinkedList<RecordMemo> alertMemos = new LinkedList<>();
DTLSOutputRecord() {
this.writeAuthenticator = new MAC(true);
DTLSOutputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLWriteCipher.nullDTlsWriteCipher());
this.writeEpoch = 0;
this.prevWriteEpoch = 0;
this.prevWriteCipher = CipherBox.NULL;
this.prevWriteAuthenticator = new MAC(true);
this.prevWriteCipher = SSLWriteCipher.nullDTlsWriteCipher();
this.packetSize = DTLSRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
this.protocolVersion = ProtocolVersion.NONE;
}
@Override
void changeWriteCiphers(Authenticator writeAuthenticator,
CipherBox writeCipher) throws IOException {
void initHandshaker() {
// clean up
fragmenter = null;
}
encodeChangeCipherSpec();
@Override
void finishHandshake() {
// Nothing to do here currently.
}
@Override
void changeWriteCiphers(SSLWriteCipher writeCipher,
boolean useChangeCipherSpec) throws IOException {
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
prevWriteCipher.dispose();
this.prevWriteAuthenticator = this.writeAuthenticator;
this.prevWriteCipher = this.writeCipher;
this.prevWriteEpoch = this.writeEpoch;
this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.writeEpoch++;
this.isFirstAppOutputRecord = true;
// set the epoch number
this.writeAuthenticator.setEpochNumber(this.writeEpoch);
this.writeCipher.authenticator.setEpochNumber(this.writeEpoch);
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_alert;
memo.contentType = ContentType.ALERT.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[2];
memo.fragment[0] = level;
@ -114,7 +117,6 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (firstMessage) {
firstMessage = false;
}
@ -127,30 +129,53 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
}
@Override
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
return encode(srcs, srcsOffset, srcsLength, dsts[0]);
}
private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
if (writeAuthenticator.seqNumOverflow()) {
if (debug != null && Debug.isOn("ssl")) {
System.out.println(Thread.currentThread().getName() +
", sequence number extremely close to overflow " +
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");
}
// not apply to handshake message
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
// Don't process the incoming record until all of the buffered records
// get handled. May need retransmission if no sources specified.
if (!isEmpty() || sources == null || sources.length == 0) {
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;
}
// not apply to handshake message
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
fragLen, macLen, headerSize);
fragLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
@ -183,44 +208,38 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_application_data) +
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.
long recordSN = encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, destination,
long recordSN = encrypt(writeCipher,
ContentType.APPLICATION_DATA.id, destination,
dstPos, dstLim, headerSize,
protocolVersion, true);
protocolVersion);
if ((debug != null) && Debug.isOn("packet")) {
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
return new Ciphertext(ContentType.APPLICATION_DATA.id,
SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
@Override
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
private Ciphertext acquireCiphertext(
ByteBuffer destination) throws IOException {
if (alertMemos != null && !alertMemos.isEmpty()) {
RecordMemo memo = alertMemos.pop();
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
int dstPos = destination.position();
int dstLim = destination.limit();
int dstContent = dstPos + headerSize +
@ -232,32 +251,32 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
destination.limit(destination.position());
destination.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(Record.ct_alert) +
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
SSLLogger.fine(
"WRITE: " + protocolVersion + " " +
ContentType.ALERT.name +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
Record.ct_alert, destination, dstPos, dstLim, headerSize,
long recordSN = encrypt(memo.encodeCipher,
ContentType.ALERT.id,
destination, dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), true);
memo.minorVersion));
if ((debug != null) && Debug.isOn("packet")) {
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
return new Ciphertext(ContentType.ALERT.id,
SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
if (fragmenter != null) {
@ -273,12 +292,6 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
((alertMemos == null) || alertMemos.isEmpty());
}
@Override
void initHandshaker() {
// clean up
fragmenter = null;
}
@Override
void launchRetransmission() {
// Note: Please don't retransmit if there are handshake messages
@ -295,8 +308,7 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
byte majorVersion;
byte minorVersion;
int encodeEpoch;
CipherBox encodeCipher;
Authenticator encodeAuthenticator;
SSLWriteCipher encodeCipher;
byte[] fragment;
}
@ -308,7 +320,8 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
}
private final class DTLSFragmenter {
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
private final LinkedList<RecordMemo> handshakeMemos =
new LinkedList<>();
private int acquireIndex = 0;
private int messageSequence = 0;
private boolean flightIsReady = false;
@ -336,12 +349,11 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_change_cipher_spec;
memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
@ -361,12 +373,11 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
HandshakeMemo memo = new HandshakeMemo();
memo.contentType = Record.ct_handshake;
memo.contentType = ContentType.HANDSHAKE.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = buf[offset];
memo.messageSequence = messageSequence++;
@ -379,12 +390,12 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
handshakeHashing(memo, memo.fragment);
handshakeMemos.add(memo);
if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
(memo.handshakeType == HandshakeMessage.ht_hello_request) ||
if ((memo.handshakeType == SSLHandshake.CLIENT_HELLO.id) ||
(memo.handshakeType == SSLHandshake.HELLO_REQUEST.id) ||
(memo.handshakeType ==
HandshakeMessage.ht_hello_verify_request) ||
(memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
(memo.handshakeType == HandshakeMessage.ht_finished)) {
SSLHandshake.HELLO_VERIFY_REQUEST.id) ||
(memo.handshakeType == SSLHandshake.SERVER_HELLO_DONE.id) ||
(memo.handshakeType == SSLHandshake.FINISHED.id)) {
flightIsReady = true;
}
@ -401,22 +412,17 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
RecordMemo memo = handshakeMemos.get(acquireIndex);
HandshakeMemo hsMemo = null;
if (memo.contentType == Record.ct_handshake) {
if (memo.contentType == ContentType.HANDSHAKE.id) {
hsMemo = (HandshakeMemo)memo;
}
int macLen = 0;
if (memo.encodeAuthenticator instanceof MAC) {
macLen = ((MAC)memo.encodeAuthenticator).MAClen();
}
// 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, macLen, 25); // 25: header size
fragLen, 25); // 25: header size
// 13: DTLS record
// 12: DTLS handshake message
fragLen = Math.min(fragLen, Record.maxDataSize);
@ -459,27 +465,26 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
if ((debug != null) && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion + " " +
Record.contentName(memo.contentType) +
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.encodeAuthenticator, memo.encodeCipher,
long recordSN = encrypt(memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
memo.minorVersion), true);
memo.minorVersion));
if ((debug != null) && Debug.isOn("packet")) {
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
Debug.printHex(
"[Raw write]: length = " + temporary.remaining(),
temporary);
SSLLogger.fine(
"Raw write (" + temporary.remaining() + ")", temporary);
}
// remain the limit unchanged
@ -492,39 +497,23 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
acquireIndex++;
}
return new Ciphertext(RecordType.valueOf(
hsMemo.contentType, hsMemo.handshakeType), recordSN);
return new Ciphertext(hsMemo.contentType,
hsMemo.handshakeType, recordSN);
} else {
acquireIndex++;
return new Ciphertext(
RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
return new Ciphertext(ContentType.CHANGE_CIPHER_SPEC.id,
SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
}
private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
byte hsType = hsFrag.handshakeType;
if ((hsType == HandshakeMessage.ht_hello_request) ||
(hsType == HandshakeMessage.ht_hello_verify_request)) {
if (!handshakeHash.isHashable(hsType)) {
// omitted from handshake hash computation
return;
}
if ((hsFrag.messageSequence == 0) &&
(hsType == HandshakeMessage.ht_client_hello)) {
// omit initial ClientHello message
//
// 2: ClientHello.client_version
// 32: ClientHello.random
int sidLen = hsBody[34];
if (sidLen == 0) { // empty session_id, initial handshake
return;
}
}
// calculate the DTLS header
byte[] temporary = new byte[12]; // 12: handshake header size
@ -550,17 +539,8 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
temporary[10] = temporary[2];
temporary[11] = temporary[3];
if ((hsType != HandshakeMessage.ht_finished) &&
(hsType != HandshakeMessage.ht_certificate_verify)) {
handshakeHash.update(temporary, 0, 12);
handshakeHash.update(hsBody, 0, hsBody.length);
} else {
// Reserve until this handshake message has been processed.
handshakeHash.reserve(temporary, 0, 12);
handshakeHash.reserve(hsBody, 0, hsBody.length);
}
handshakeHash.deliver(temporary, 0, 12);
handshakeHash.deliver(hsBody, 0, hsBody.length);
}
boolean isEmpty() {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -60,30 +60,6 @@ interface DTLSRecord extends Record {
+ maxPadding // padding
+ maxMacSize; // MAC or AEAD tag
/*
* For CBC protection in SSL3/TLS1, we break some plaintext into two
* packets. Max application data size for the second packet.
*/
static final int maxDataSizeMinusOneByteRecord =
maxDataSize // max data size
- ( // max one byte record size
headerPlusMaxIVSize // header + iv
+ 1 // one byte data
+ maxPadding // padding
+ maxMacSize // MAC
);
/*
* Maximum record size for alert and change cipher spec records.
* They only contain 2 and 1 bytes of data, respectively.
* Allocate a smaller array.
*/
static final int maxAlertRecordSize =
headerPlusMaxIVSize // header + iv
+ 2 // alert
+ maxPadding // padding
+ maxMacSize; // MAC
/*
* Minimum record size of Certificate handshake message.
* Client sends a certificate message containing no certificates if no

View file

@ -1,250 +0,0 @@
/*
* Copyright (c) 1999, 2016, 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.PrintStream;
import java.util.Locale;
import sun.security.util.HexDumpEncoder;
import java.nio.ByteBuffer;
import sun.security.action.GetPropertyAction;
/**
* This class has be shamefully lifted from sun.security.util.Debug
*
* @author Gary Ellison
*/
public class Debug {
private String prefix;
private static String args;
static {
args = GetPropertyAction.privilegedGetProperty("javax.net.debug", "");
args = args.toLowerCase(Locale.ENGLISH);
if (args.equals("help")) {
Help();
}
}
public static void Help()
{
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);
}
/**
* Get a Debug object corresponding to whether or not the given
* option is set. Set the prefix to be the same as option.
*/
public static Debug getInstance(String option)
{
return getInstance(option, option);
}
/**
* Get a Debug object corresponding to whether or not the given
* option is set. Set the prefix to be prefix.
*/
public static Debug getInstance(String option, String prefix)
{
if (isOn(option)) {
Debug d = new Debug();
d.prefix = prefix;
return d;
} else {
return null;
}
}
/**
* True if the property "javax.net.debug" contains the
* string "option".
*/
public static boolean isOn(String option)
{
if (args == null) {
return false;
} else {
int n = 0;
option = option.toLowerCase(Locale.ENGLISH);
if (args.indexOf("all") != -1) {
return true;
} else if ((n = args.indexOf("ssl")) != -1) {
if (args.indexOf("sslctx", n) == -1) {
// don't enable data and plaintext options by default
if (!(option.equals("data")
|| option.equals("packet")
|| option.equals("plaintext"))) {
return true;
}
}
}
return (args.indexOf(option) != -1);
}
}
/**
* print a message to stderr that is prefixed with the prefix
* created from the call to getInstance.
*/
public void println(String message)
{
System.err.println(prefix + ": "+message);
}
/**
* Print a message to stdout.
*/
static void log(String message) {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
/**
* print a blank line to stderr that is prefixed with the prefix.
*/
public void println()
{
System.err.println(prefix + ":");
}
/**
* print a message to stderr that is prefixed with the prefix.
*/
public static void println(String prefix, String message)
{
System.err.println(prefix + ": "+message);
}
public static void println(PrintStream s, String name, byte[] data) {
s.print(name + ": { ");
if (data == null) {
s.print("null");
} else {
for (int i = 0; i < data.length; i++) {
if (i != 0) s.print(", ");
s.print(data[i] & 0x0ff);
}
}
s.println(" }");
}
/**
* Return the value of the boolean System property propName.
*
* Note use of privileged action. Do NOT make accessible to applications.
*/
static boolean getBooleanProperty(String propName, boolean defaultValue) {
// if set, require value of either true or false
String b = GetPropertyAction.privilegedGetProperty(propName);
if (b == null) {
return defaultValue;
} else if (b.equalsIgnoreCase("false")) {
return false;
} else if (b.equalsIgnoreCase("true")) {
return true;
} else {
throw new RuntimeException("Value of " + propName
+ " must either be 'true' or 'false'");
}
}
static String toString(byte[] b) {
return sun.security.util.Debug.toString(b);
}
static void printHex(String prefix, byte[] bytes) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
dump.encodeBuffer(bytes, System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
static void printHex(String prefix, ByteBuffer bb) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
dump.encodeBuffer(bb.slice(), System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
static void printHex(String prefix, byte[] bytes, int offset, int length) {
HexDumpEncoder dump = new HexDumpEncoder();
synchronized (System.out) {
System.out.println(prefix);
try {
ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
dump.encodeBuffer(bb, System.out);
} catch (Exception e) {
// ignore
}
System.out.flush();
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,60 +26,515 @@
package sun.security.ssl;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.*;
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.security.util.HexDumpEncoder;
/**
* ClientKeyExchange message for all ECDH based key exchange methods. It
* contains the client's ephemeral public value.
*
* @since 1.6
* @author Andreas Sterbenz
* Pack of the "ClientKeyExchange" handshake message.
*/
final class ECDHClientKeyExchange extends HandshakeMessage {
final class ECDHClientKeyExchange {
static final SSLConsumer ecdhHandshakeConsumer =
new ECDHClientKeyExchangeConsumer();
static final HandshakeProducer ecdhHandshakeProducer =
new ECDHClientKeyExchangeProducer();
@Override
int messageType() {
return ht_client_key_exchange;
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.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);
}
}
}
private byte[] encodedPoint;
/**
* The ECDH "ClientKeyExchange" handshake message producer.
*/
private static final
class ECDHClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private ECDHClientKeyExchangeProducer() {
// blank
}
byte[] getEncodedPoint() {
return encodedPoint;
@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) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"No server certificate for ECDH client key exchange");
}
PublicKey publicKey = x509Credentials.popPublicKey;
if (!publicKey.getAlgorithm().equals("EC")) {
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) {
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
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
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;
}
}
// Called by the client with its ephemeral public key.
ECDHClientKeyExchange(PublicKey publicKey) {
ECPublicKey ecKey = (ECPublicKey)publicKey;
ECPoint point = ecKey.getW();
ECParameterSpec params = ecKey.getParams();
encodedPoint = JsseJce.encodePoint(point, params.getCurve());
/**
* 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.
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No expected EC server cert for ECDH client key exchange");
return; // make the compiler happy
}
PrivateKey privateKey = x509Possession.popPrivateKey;
if (!privateKey.getAlgorithm().equals("EC")) {
// unlikely, have been checked during cipher suite negotiation.
shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Not EC server cert for ECDH client key exchange");
}
ECParameterSpec params = ((ECPrivateKey)privateKey).getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
// unlikely, have been checked during cipher suite negotiation.
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
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
return; // make the compiler happy
}
// 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.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
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
ECDHClientKeyExchange(HandshakeInStream input) throws IOException {
encodedPoint = input.getBytes8();
/**
* 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) {
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
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
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;
}
}
@Override
int messageLength() {
return encodedPoint.length + 1;
}
/**
* The ECDHE "ClientKeyExchange" handshake message consumer.
*/
private static final
class ECDHEClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private ECDHEClientKeyExchangeConsumer() {
// blank
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putBytes8(encodedPoint);
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
// The consuming happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
@Override
void print(PrintStream s) throws IOException {
s.println("*** ECDHClientKeyExchange");
ECDHEPossession ecdhePossession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof ECDHEPossession) {
ecdhePossession = (ECDHEPossession)possession;
break;
}
}
if (ecdhePossession == null) {
// unlikely
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No expected ECDHE possessions for client key exchange");
return; // make the compiler happy
}
if (debug != null && Debug.isOn("verbose")) {
Debug.println(s, "ECDH Public value", encodedPoint);
ECParameterSpec params = ecdhePossession.publicKey.getParams();
NamedGroup namedGroup = NamedGroup.valueOf(params);
if (namedGroup == null) {
// unlikely, have been checked during cipher suite negotiation.
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
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key exchange type");
return; // make the compiler happy
}
// 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.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
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " + shc.negotiatedProtocol);
} else {
shc.handshakeKeyDerivation =
kd.createKeyDerivation(shc, masterSecret);
}
}
}
}

View file

@ -1,153 +0,0 @@
/*
* Copyright (c) 2006, 2017, 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.interfaces.ECPublicKey;
import java.security.spec.*;
import java.util.EnumSet;
import javax.crypto.SecretKey;
import javax.crypto.KeyAgreement;
import javax.net.ssl.SSLHandshakeException;
/**
* Helper class for the ECDH key exchange. It generates the appropriate
* ephemeral keys as necessary and performs the actual shared secret derivation.
*
* @since 1.6
* @author Andreas Sterbenz
*/
final class ECDHCrypt {
// our private key
private PrivateKey privateKey;
// our public key
private ECPublicKey publicKey;
// Called by ServerHandshaker for static ECDH
ECDHCrypt(PrivateKey privateKey, PublicKey publicKey) {
this.privateKey = privateKey;
this.publicKey = (ECPublicKey)publicKey;
}
// Called by ServerHandshaker for ephemeral ECDH
ECDHCrypt(NamedGroup namedGroup, SecureRandom random) {
try {
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
ECGenParameterSpec params =
SupportedGroupsExtension.getECGenParamSpec(namedGroup);
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);
}
}
// Called by ClientHandshaker with params it received from the server
ECDHCrypt(ECParameterSpec params, SecureRandom random) {
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);
}
}
/**
* Gets the public key of this end of the key exchange.
*/
PublicKey getPublicKey() {
return publicKey;
}
// 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 publicKey = (ECPublicKey)kf.generatePublic(spec);
// check constraints of ECPublicKey
if (!constraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
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);
}
}
}

View file

@ -0,0 +1,485 @@
/*
* 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.ECPrivateKey;
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;
}
PrivateKey privateKey = ((X509Possession)poss).popPrivateKey;
if (!privateKey.getAlgorithm().equals("EC")) {
continue;
}
ECParameterSpec params = ((ECPrivateKey)privateKey).getParams();
NamedGroup ng = NamedGroup.valueOf(params);
if (ng == null) {
// unlikely, have been checked during cipher suite negotiation.
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) {
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
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) {
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) {
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,567 @@
/*
* 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 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.security.util.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
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
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) {
signatureScheme = SignatureScheme.getPreferableAlgorithm(
shc.peerRequestedSignatureSchemes,
x509Possession.popPrivateKey,
shc.negotiatedProtocol);
if (signatureScheme == null) {
// Unlikely, the credentials generator should have
// selected the preferable signature algorithm properly.
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No preferred signature algorithm for " +
x509Possession.popPrivateKey.getAlgorithm() +
" key");
}
try {
signer = signatureScheme.getSignature(
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
}
} else {
signatureScheme = null;
try {
signer = getSignature(
x509Possession.popPrivateKey.getAlgorithm(),
x509Possession.popPrivateKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
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) {
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.
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported ECCurveType: " + curveType);
}
int namedGroupId = Record.getInt16(m);
this.namedGroup = NamedGroup.valueOf(namedGroupId);
if (namedGroup == null) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unknown named group ID: " + namedGroupId);
}
if (!SupportedGroups.isSupported(namedGroup)) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unsupported named group: " + namedGroup);
}
if (namedGroup.oid == null) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Unknown named EC curve: " + namedGroup);
}
ECParameterSpec parameters =
JsseJce.getECParameterSpec(namedGroup.oid);
if (parameters == null) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No supported EC parameter: " + namedGroup);
}
publicPoint = Record.getBytes8(m);
if (publicPoint.length == 0) {
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) {
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()) {
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) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature algorithm (" + ssid +
") used in ECDH ServerKeyExchange handshake message");
}
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
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.getSignature(
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException |
InvalidAlgorithmParameterException nsae) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
signatureScheme.name, nsae);
return; // make the compiler happe
}
} else {
try {
signer = getSignature(
x509Credentials.popPublicKey.getAlgorithm(),
x509Credentials.popPublicKey);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Unsupported signature algorithm: " +
x509Credentials.popPublicKey.getAlgorithm(), e);
return; // make the compiler happe
}
}
try {
updateSignature(signer,
chc.clientHelloRandom.randomBytes,
chc.serverHelloRandom.randomBytes,
namedGroup.id, publicPoint);
if (!signer.verify(paramsSignature)) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid ECDH ServerKeyExchange signature");
}
} catch (SignatureException ex) {
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.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
skem.publicKey)) {
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,302 @@
/*
* 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// per RFC 4492, uncompressed points must always be supported.
if (!spec.hasUncompressedFormat()) {
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) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// per RFC 4492, uncompressed points must always be supported.
if (!spec.hasUncompressedFormat()) {
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

@ -1,103 +0,0 @@
/*
* Copyright (c) 2006, 2016, 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.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLProtocolException;
final class EllipticPointFormatsExtension extends HelloExtension {
static final int FMT_UNCOMPRESSED = 0;
static final int FMT_ANSIX962_COMPRESSED_PRIME = 1;
static final int FMT_ANSIX962_COMPRESSED_CHAR2 = 2;
static final HelloExtension DEFAULT =
new EllipticPointFormatsExtension(new byte[] {FMT_UNCOMPRESSED});
private final byte[] formats;
private EllipticPointFormatsExtension(byte[] formats) {
super(ExtensionType.EXT_EC_POINT_FORMATS);
this.formats = formats;
}
EllipticPointFormatsExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_EC_POINT_FORMATS);
formats = s.getBytes8();
// RFC 4492 says uncompressed points must always be supported.
// Check just to make sure.
boolean uncompressed = false;
for (int format : formats) {
if (format == FMT_UNCOMPRESSED) {
uncompressed = true;
break;
}
}
if (uncompressed == false) {
throw new SSLProtocolException
("Peer does not support uncompressed points");
}
}
@Override
int length() {
return 5 + formats.length;
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(formats.length + 1);
s.putBytes8(formats);
}
private static String toString(byte format) {
int f = format & 0xff;
switch (f) {
case FMT_UNCOMPRESSED:
return "uncompressed";
case FMT_ANSIX962_COMPRESSED_PRIME:
return "ansiX962_compressed_prime";
case FMT_ANSIX962_COMPRESSED_CHAR2:
return "ansiX962_compressed_char2";
default:
return "unknown-" + f;
}
}
@Override
public String toString() {
List<String> list = new ArrayList<String>();
for (byte format : formats) {
list.add(toString(format));
}
return "Extension " + type + ", formats: " + list;
}
}

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) {
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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
* 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

View file

@ -1,5 +1,6 @@
/*
* 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
@ -26,46 +27,365 @@
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;
/**
* Extended Master Secret TLS extension (TLS 1.0+). This extension
* defines how to calculate the TLS connection master secret and
* mitigates some types of man-in-the-middle attacks.
*
* See further information in
* <a href="https://tools.ietf.org/html/rfc7627">RFC 7627</a>.
*
* @author Martin Balao (mbalao@redhat.com)
* Pack of the "extended_master_secret" extensions [RFC 7627].
*/
final class ExtendedMasterSecretExtension extends HelloExtension {
ExtendedMasterSecretExtension() {
super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
}
final class ExtendedMasterSecretExtension {
static final HandshakeProducer chNetworkProducer =
new CHExtendedMasterSecretProducer();
static final ExtensionConsumer chOnLoadConsumer =
new CHExtendedMasterSecretConsumer();
static final HandshakeAbsence chOnLoadAbsence =
new CHExtendedMasterSecretAbsence();
ExtendedMasterSecretExtension(HandshakeInStream s,
int len) throws IOException {
super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
static final HandshakeProducer shNetworkProducer =
new SHExtendedMasterSecretProducer();
static final ExtensionConsumer shOnLoadConsumer =
new SHExtendedMasterSecretConsumer();
static final HandshakeAbsence shOnLoadAbsence =
new SHExtendedMasterSecretAbsence();
if (len != 0) {
throw new SSLProtocolException("Invalid " + type + " extension");
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>";
}
}
@Override
int length() {
return 4; // 4: extension type and length fields
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();
}
}
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id); // ExtensionType extension_type;
s.putInt16(0); // extension_data length
/**
* 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;
}
}
@Override
public String toString() {
return "Extension " + type;
/**
* 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
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.
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.
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) {
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) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
if (chc.isResumption && chc.resumingSession != null &&
!chc.resumingSession.useExtendedMasterSecret) {
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.
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.
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.
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Extended Master Secret extension is required");
}
}
}
}
}

View file

@ -1,115 +0,0 @@
/*
* Copyright (c) 2006, 2017, 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.ArrayList;
import java.util.List;
final class ExtensionType {
final int id;
final String name;
private ExtensionType(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name;
}
static List<ExtensionType> knownExtensions = new ArrayList<>(16);
static ExtensionType get(int id) {
for (ExtensionType ext : knownExtensions) {
if (ext.id == id) {
return ext;
}
}
return new ExtensionType(id, "type_" + id);
}
private static ExtensionType e(int id, String name) {
ExtensionType ext = new ExtensionType(id, name);
knownExtensions.add(ext);
return ext;
}
// extensions defined in RFC 3546
static final ExtensionType EXT_SERVER_NAME =
e(0x0000, "server_name"); // IANA registry value: 0
static final ExtensionType EXT_MAX_FRAGMENT_LENGTH =
e(0x0001, "max_fragment_length"); // IANA registry value: 1
static final ExtensionType EXT_CLIENT_CERTIFICATE_URL =
e(0x0002, "client_certificate_url"); // IANA registry value: 2
static final ExtensionType EXT_TRUSTED_CA_KEYS =
e(0x0003, "trusted_ca_keys"); // IANA registry value: 3
static final ExtensionType EXT_TRUNCATED_HMAC =
e(0x0004, "truncated_hmac"); // IANA registry value: 4
static final ExtensionType EXT_STATUS_REQUEST =
e(0x0005, "status_request"); // IANA registry value: 5
// extensions defined in RFC 4681
static final ExtensionType EXT_USER_MAPPING =
e(0x0006, "user_mapping"); // IANA registry value: 6
// extensions defined in RFC 5081
static final ExtensionType EXT_CERT_TYPE =
e(0x0009, "cert_type"); // IANA registry value: 9
// extensions defined in RFC 4492 (ECC) and RFC 7919 (FFDHE)
static final ExtensionType EXT_SUPPORTED_GROUPS =
e(0x000A, "supported_groups"); // IANA registry value: 10
static final ExtensionType EXT_EC_POINT_FORMATS =
e(0x000B, "ec_point_formats"); // IANA registry value: 11
// extensions defined in RFC 5054
static final ExtensionType EXT_SRP =
e(0x000C, "srp"); // IANA registry value: 12
// extensions defined in RFC 5246
static final ExtensionType EXT_SIGNATURE_ALGORITHMS =
e(0x000D, "signature_algorithms"); // IANA registry value: 13
// extension defined in RFC 7301 (ALPN)
static final ExtensionType EXT_ALPN =
e(0x0010, "application_layer_protocol_negotiation");
// IANA registry value: 16
// extensions defined in RFC 6961
static final ExtensionType EXT_STATUS_REQUEST_V2 =
e(0x0011, "status_request_v2"); // IANA registry value: 17
// extensions defined in RFC 7627
static final ExtensionType EXT_EXTENDED_MASTER_SECRET =
e(0x0017, "extended_master_secret"); // IANA registry value: 23
// extensions defined in RFC 5746
static final ExtensionType EXT_RENEGOTIATION_INFO =
e(0xff01, "renegotiation_info"); // IANA registry value: 65281
}

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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,36 +26,17 @@
package sun.security.ssl;
import java.io.IOException;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
final class UnknownStatusRequest implements StatusRequest {
private final byte[] data;
UnknownStatusRequest(HandshakeInStream s, int len) throws IOException {
data = new byte[len];
if (len > 0) {
s.read(data);
}
}
UnknownStatusRequest(byte[] requestBytes) {
data = requestBytes;
}
@Override
public int length() {
return data.length;
}
@Override
public void send(HandshakeOutStream s) throws IOException {
// A raw write of the data
s.write(data);
}
@Override
public String toString() {
return "Unsupported StatusRequest, data: " +
Debug.toString(data);
}
/**
* 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,557 @@
/*
* 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.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 sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeMode;
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;
// 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.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
if (activeProtocols.isEmpty()) {
throw new SSLHandshakeException(
"No appropriate protocol (protocol is disabled or " +
"cipher suites are inappropriate)");
}
ProtocolVersion maximumVersion = ProtocolVersion.NONE;
for (ProtocolVersion pv : this.activeProtocols) {
if (maximumVersion == ProtocolVersion.NONE ||
pv.compare(maximumVersion) > 0) {
maximumVersion = pv;
}
}
this.maximumActiveProtocol = maximumVersion;
this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
if (activeCipherSuites.isEmpty()) {
throw new SSLHandshakeException("No appropriate cipher suite");
}
this.algorithmConstraints =
new SSLAlgorithmConstraints(sslConfig.algorithmConstraints);
this.handshakeConsumers = new LinkedHashMap<>();
this.handshakeProducers = new HashMap<>();
this.handshakeHash = conContext.inputRecord.handshakeHash;
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
this.handshakeFinished = false;
this.kickstartMessageDelivered = false;
this.delegatedActions = new LinkedList<>();
this.handshakeExtensions = new HashMap<>();
this.handshakePossessions = new LinkedList<>();
this.handshakeCredentials = new LinkedList<>();
this.requestedServerNames = null;
this.negotiatedServerName = null;
this.negotiatedCipherSuite = conContext.cipherSuite;
initialize();
}
/**
* Constructor for PostHandshakeContext
*/
HandshakeContext(TransportContext conContext) {
this.sslContext = conContext.sslContext;
this.conContext = conContext;
this.sslConfig = conContext.sslConfig;
this.negotiatedProtocol = conContext.protocolVersion;
this.negotiatedCipherSuite = conContext.cipherSuite;
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
this.delegatedActions = new LinkedList<>();
this.handshakeProducers = null;
this.handshakeHash = null;
this.activeProtocols = null;
this.activeCipherSuites = null;
this.algorithmConstraints = null;
this.maximumActiveProtocol = null;
this.handshakeExtensions = Collections.emptyMap(); // Not in TLS13
this.handshakePossessions = null;
this.handshakeCredentials = null;
}
// Initialize the non-final class variables.
private void initialize() {
ProtocolVersion inputHelloVersion;
ProtocolVersion outputHelloVersion;
if (conContext.isNegotiated) {
inputHelloVersion = conContext.protocolVersion;
outputHelloVersion = conContext.protocolVersion;
} else {
if (activeProtocols.contains(ProtocolVersion.SSL20Hello)) {
inputHelloVersion = ProtocolVersion.SSL20Hello;
// Per TLS 1.3 protocol, implementation MUST NOT send an SSL
// version 2.0 compatible CLIENT-HELLO.
if (maximumActiveProtocol.useTLS13PlusSpec()) {
outputHelloVersion = maximumActiveProtocol;
} else {
outputHelloVersion = ProtocolVersion.SSL20Hello;
}
} else {
inputHelloVersion = maximumActiveProtocol;
outputHelloVersion = maximumActiveProtocol;
}
}
conContext.inputRecord.setHelloVersion(inputHelloVersion);
conContext.outputRecord.setHelloVersion(outputHelloVersion);
if (!conContext.isNegotiated) {
conContext.protocolVersion = maximumActiveProtocol;
}
conContext.outputRecord.setVersion(conContext.protocolVersion);
}
private static List<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) {
conContext.fatal(Alert.INTERNAL_ERROR,
"Unexpected operation for record: " + plaintext.contentType);
return 0;
}
if (plaintext.fragment == null || plaintext.fragment.remaining() < 4) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid handshake message: insufficient data");
return 0;
}
byte handshakeType = (byte)Record.getInt8(plaintext.fragment);
int handshakeLen = Record.getInt24(plaintext.fragment);
if (handshakeLen != plaintext.fragment.remaining()) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Invalid handshake message: insufficient handshake body");
return 0;
}
return handshakeType;
}
void dispatch(byte handshakeType, Plaintext plaintext) throws IOException {
if (conContext.transport.useDelegatedTask()) {
boolean hasDelegated = !delegatedActions.isEmpty();
if (hasDelegated ||
(handshakeType != SSLHandshake.FINISHED.id &&
handshakeType != SSLHandshake.KEY_UPDATE.id &&
handshakeType != SSLHandshake.NEW_SESSION_TICKET.id)) {
if (!hasDelegated) {
taskDelegated = false;
delegatedThrown = null;
}
// Clone the fragment for delegated actions.
//
// The plaintext may share the application buffers. It is
// fine to use shared buffers if no delegated actions.
// However, for delegated actions, the shared buffers may be
// polluted in application layer before the delegated actions
// executed.
ByteBuffer fragment = ByteBuffer.wrap(
new byte[plaintext.fragment.remaining()]);
fragment.put(plaintext.fragment);
fragment = fragment.rewind();
delegatedActions.add(new SimpleImmutableEntry<>(
handshakeType,
fragment
));
} else {
dispatch(handshakeType, plaintext.fragment);
}
} else {
dispatch(handshakeType, plaintext.fragment);
}
}
void dispatch(byte handshakeType,
ByteBuffer fragment) throws IOException {
SSLConsumer consumer;
if (handshakeType == SSLHandshake.HELLO_REQUEST.id) {
// For TLS 1.2 and prior versions, the HelloRequest message MAY
// be sent by the server at any time.
consumer = SSLHandshake.HELLO_REQUEST;
} else {
consumer = handshakeConsumers.get(handshakeType);
}
if (consumer == null) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected handshake message: " +
SSLHandshake.nameOf(handshakeType));
return;
}
try {
consumer.consume(this, fragment);
} catch (UnsupportedOperationException unsoe) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported handshake message: " +
SSLHandshake.nameOf(handshakeType), unsoe);
}
// 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);
}
/**
* Set the active protocol version and propagate it to the SSLSocket
* and our handshake streams. Called from ClientHandshaker
* and ServerHandshaker with the negotiated protocol version.
*/
void setVersion(ProtocolVersion protocolVersion) {
this.conContext.protocolVersion = protocolVersion;
}
private static boolean isActivatable(CipherSuite suite,
AlgorithmConstraints algorithmConstraints,
Map<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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,179 +0,0 @@
/*
* Copyright (c) 1996, 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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
/**
* InputStream for handshake data, used internally only. Contains the
* handshake message buffer and methods to parse them.
*
* Once a new handshake record arrives, it is buffered in this class until
* processed by the Handshaker. The buffer may also contain incomplete
* handshake messages in case the message is split across multiple records.
* Handshaker.processRecord deals with all that. It may also contain
* handshake messages larger than the default buffer size (e.g. large
* certificate messages). The buffer is grown dynamically to handle that.
*
* Note that this class only handles Handshake messages in TLS format.
* DTLS Handshake messages should be converted into TLS format before
* calling into this method.
*
* @author David Brownell
*/
// This class is used to handle plain text handshake messages.
//
public final class HandshakeInStream extends ByteArrayInputStream {
/*
* Construct the stream; we'll be accumulating hashes of the
* input records using two sets of digests.
*/
HandshakeInStream() {
super(new byte[0]); // lazy to alloacte the internal buffer
}
//
// overridden ByteArrayInputStream methods
//
@Override
public int read(byte[] b) throws IOException {
if (super.read(b) != b.length) {
throw new SSLException("Unexpected end of handshake data");
}
return b.length;
}
//
// handshake input stream management functions
//
/*
* Here's an incoming record with handshake data. Queue the contents;
* it might be one or more entire messages, complete a message that's
* partly queued, or both.
*/
void incomingRecord(ByteBuffer in) throws IOException {
int len;
// Move any unread data to the front of the buffer.
if (pos != 0) {
len = count - pos;
if (len != 0) {
System.arraycopy(buf, pos, buf, 0, len);
}
pos = 0;
count = len;
}
// Grow buffer if needed.
len = in.remaining() + count;
if (buf.length < len) {
byte[] newbuf = new byte[len];
if (count != 0) {
System.arraycopy(buf, 0, newbuf, 0, count);
}
buf = newbuf;
}
// Append the incoming record to the buffer
in.get(buf, count, in.remaining());
count = len;
}
//
// Message parsing methods
//
/*
* Read 8, 16, 24, and 32 bit SSL integer data types, encoded
* in standard big-endian form.
*/
int getInt8() throws IOException {
verifyLength(1);
return read();
}
int getInt16() throws IOException {
verifyLength(2);
return (getInt8() << 8) | getInt8();
}
int getInt24() throws IOException {
verifyLength(3);
return (getInt8() << 16) | (getInt8() << 8) | getInt8();
}
int getInt32() throws IOException {
verifyLength(4);
return (getInt8() << 24) | (getInt8() << 16)
| (getInt8() << 8) | getInt8();
}
/*
* Read byte vectors with 8, 16, and 24 bit length encodings.
*/
byte[] getBytes8() throws IOException {
int len = getInt8();
verifyLength(len);
byte[] b = new byte[len];
read(b);
return b;
}
public byte[] getBytes16() throws IOException {
int len = getInt16();
verifyLength(len);
byte[] b = new byte[len];
read(b);
return b;
}
byte[] getBytes24() throws IOException {
int len = getInt24();
verifyLength(len);
byte[] b = new byte[len];
read(b);
return b;
}
// Is a length greater than available bytes in the record?
private void verifyLength(int len) throws SSLException {
if (len > available()) {
throw new SSLException("Unexpected end of handshake data");
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* 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
@ -56,11 +56,12 @@ public class HandshakeOutStream extends ByteArrayOutputStream {
throw new RuntimeException("handshake message is not available");
}
// outputRecord cannot be null
outputRecord.encodeHandshake(buf, 0, count);
if (outputRecord != null) {
outputRecord.encodeHandshake(buf, 0, count);
// reset the byte array output stream
reset();
// reset the byte array output stream
reset();
} // otherwise, the handshake outstream is temporarily used only.
}
//
@ -76,7 +77,9 @@ public class HandshakeOutStream extends ByteArrayOutputStream {
@Override
public void flush() throws IOException {
outputRecord.flush();
if (outputRecord != null) {
outputRecord.flush();
}
}
//
@ -106,6 +109,13 @@ public class HandshakeOutStream extends ByteArrayOutputStream {
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.

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

@ -1,922 +0,0 @@
/*
* 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;
import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
import java.util.HashMap;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.CipherSuite.KeyExchange;
import static sun.security.ssl.CipherSuite.KeyExchange.*;
import static sun.security.ssl.HandshakeStateManager.HandshakeState.*;
import static sun.security.ssl.HandshakeMessage.*;
/*
* Handshake state manager.
*
* Messages flow for a full handshake:
*
* - -
* | HelloRequest (No.0, RFC 5246) [*] |
* | <-------------------------------------------- |
* | |
* | ClientHello (No.1, RFC 5246) |
* | --------------------------------------------> |
* | |
* | - HelloVerifyRequest (No.3, RFC 6347) - |
* | D | <-------------------------------------------- | D |
* | T | | T |
* | L | ClientHello (No.1, RFC 5246) | L |
* | S | --------------------------------------------> | S |
* | - - |
* | |
* C | ServerHello (No.2, RFC 5246) | S
* L | SupplementalData (No.23, RFC4680) [*] | E
* I | Certificate (No.11, RFC 5246) [*] | R
* E | CertificateStatus (No.22, RFC 6066) [*] | V
* N | ServerKeyExchange (No.12, RFC 5246) [*] | E
* T | CertificateRequest (No.13, RFC 5246) [*] | R
* | ServerHelloDone (No.14, RFC 5246) |
* | <-------------------------------------------- |
* | |
* | SupplementalData (No.23, RFC4680) [*] |
* | Certificate (No.11, RFC 5246) [*] Or |
* | CertificateURL (No.21, RFC6066) [*] |
* | ClientKeyExchange (No.16, RFC 5246) |
* | CertificateVerify (No.15, RFC 5246) [*] |
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | --------------------------------------------> |
* | |
* | NewSessionTicket (No.4, RFC4507) [*] |
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | <-------------------------------------------- |
* - -
* [*] Indicates optional or situation-dependent messages that are not
* always sent.
*
* Message flow for an abbreviated handshake:
* - -
* | ClientHello (No.1, RFC 5246) |
* | --------------------------------------------> |
* | |
* C | ServerHello (No.2, RFC 5246) | S
* L | NewSessionTicket (No.4, RFC4507) [*] | E
* I | [ChangeCipherSpec] (RFC 5246) | R
* E | Finished (No.20, RFC 5246) | V
* N | <-------------------------------------------- | E
* T | | R
* | [ChangeCipherSpec] (RFC 5246) |
* | Finished (No.20, RFC 5246) |
* | --------------------------------------------> |
* - -
*
*
* State machine of handshake states:
*
* +--------------+
* START -----> | HelloRequest |
* | +--------------+
* | |
* v v
* +---------------------+ --> +---------------------+
* | ClientHello | | HelloVerifyRequest |
* +---------------------+ <-- +---------------------+
* |
* |
* =========================================================================
* |
* v
* +---------------------+
* | ServerHello | ----------------------------------+------+
* +---------------------+ --> +-------------------------+ | |
* | | Server SupplementalData | | |
* | +-------------------------+ | |
* | | | |
* v v | |
* +---------------------+ | |
* +---- | Server Certificate | | |
* | +---------------------+ | |
* | | | |
* | | +--------------------+ | |
* | +-> | CertificateStatus | | |
* | | +--------------------+ v |
* | | | | +--------------------+ |
* | v v +--> | ServerKeyExchange | |
* | +---------------------+ | +--------------------+ |
* | | CertificateRequest | | | |
* | +---------------------+ <-+---------+ |
* | | | | |
* v v | | |
* +---------------------+ <-------+ | |
* | ServerHelloDone | <-----------------+ |
* +---------------------+ |
* | | |
* | | |
* | | |
* =========================================================================
* | | |
* | v |
* | +-------------------------+ |
* | | Client SupplementalData | --------------+ |
* | +-------------------------+ | |
* | | | |
* | v | |
* | +--------------------+ | |
* +-> | Client Certificate | ALT. | |
* | +--------------------+----------------+ | |
* | | CertificateURL | | |
* | +----------------+ | |
* v | |
* +-------------------+ <------------------------+ |
* | ClientKeyExchange | |
* +-------------------+ |
* | | |
* | v |
* | +-------------------+ |
* | | CertificateVerify | |
* | +-------------------+ |
* | | |
* v v |
* +-------------------------+ |
* | Client ChangeCipherSpec | <---------------+ |
* +-------------------------+ | |
* | | |
* v | |
* +-----------------+ (abbreviated) | |
* | Client Finished | -------------> END | |
* +-----------------+ (Abbreviated handshake) | |
* | | |
* | (full) | |
* | | |
* ================================ | |
* | | |
* | ================================
* | | |
* v | |
* +------------------+ | (abbreviated) |
* | NewSessionTicket | <--------------------------------+
* +------------------+ | |
* | | |
* v | |
* +-------------------------+ | (abbreviated) |
* | Server ChangeCipherSpec | <-------------------------------------+
* +-------------------------+ |
* | |
* v |
* +-----------------+ (abbreviated) |
* | Server Finished | -------------------------+
* +-----------------+
* | (full)
* v
* END (Full handshake)
*
*
* The scenarios of the use of this class:
* 1. Create an instance of HandshakeStateManager during the initializtion
* handshake.
* 2. If receiving a handshake message, call HandshakeStateManager.check()
* to make sure that the message is of the expected handshake type. And
* then call HandshakeStateManager.update() in case handshake states may
* be impacted by this new incoming handshake message.
* 3. On delivering a handshake message, call HandshakeStateManager.update()
* in case handshake states may by thie new outgoing handshake message.
* 4. On receiving and delivering ChangeCipherSpec message, call
* HandshakeStateManager.changeCipherSpec() to check the present sequence
* of this message, and update the states if necessary.
*/
final class HandshakeStateManager {
// upcoming handshake states.
private LinkedList<HandshakeState> upcomingStates;
private LinkedList<HandshakeState> alternatives;
private boolean isDTLS;
private static final boolean debugIsOn;
private static final HashMap<Byte, String> handshakeTypes;
static {
debugIsOn = (Handshaker.debug != null) &&
Debug.isOn("handshake") && Debug.isOn("verbose");
handshakeTypes = new HashMap<>(15);
handshakeTypes.put(ht_hello_request, "hello_request");
handshakeTypes.put(ht_client_hello, "client_hello");
handshakeTypes.put(ht_server_hello, "server_hello");
handshakeTypes.put(ht_hello_verify_request, "hello_verify_request");
handshakeTypes.put(ht_new_session_ticket, "session_ticket");
handshakeTypes.put(ht_certificate, "certificate");
handshakeTypes.put(ht_server_key_exchange, "server_key_exchange");
handshakeTypes.put(ht_certificate_request, "certificate_request");
handshakeTypes.put(ht_server_hello_done, "server_hello_done");
handshakeTypes.put(ht_certificate_verify, "certificate_verify");
handshakeTypes.put(ht_client_key_exchange, "client_key_exchange");
handshakeTypes.put(ht_finished, "finished");
handshakeTypes.put(ht_certificate_url, "certificate_url");
handshakeTypes.put(ht_certificate_status, "certificate_status");
handshakeTypes.put(ht_supplemental_data, "supplemental_data");
}
HandshakeStateManager(boolean isDTLS) {
this.upcomingStates = new LinkedList<>();
this.alternatives = new LinkedList<>();
this.isDTLS = isDTLS;
}
//
// enumation of handshake type
//
static enum HandshakeState {
HS_HELLO_REQUEST(
"hello_request",
HandshakeMessage.ht_hello_request),
HS_CLIENT_HELLO(
"client_hello",
HandshakeMessage.ht_client_hello),
HS_HELLO_VERIFY_REQUEST(
"hello_verify_request",
HandshakeMessage.ht_hello_verify_request),
HS_SERVER_HELLO(
"server_hello",
HandshakeMessage.ht_server_hello),
HS_SERVER_SUPPLEMENTAL_DATA(
"server supplemental_data",
HandshakeMessage.ht_supplemental_data, true),
HS_SERVER_CERTIFICATE(
"server certificate",
HandshakeMessage.ht_certificate),
HS_CERTIFICATE_STATUS(
"certificate_status",
HandshakeMessage.ht_certificate_status, true),
HS_SERVER_KEY_EXCHANGE(
"server_key_exchange",
HandshakeMessage.ht_server_key_exchange, true),
HS_CERTIFICATE_REQUEST(
"certificate_request",
HandshakeMessage.ht_certificate_request, true),
HS_SERVER_HELLO_DONE(
"server_hello_done",
HandshakeMessage.ht_server_hello_done),
HS_CLIENT_SUPPLEMENTAL_DATA(
"client supplemental_data",
HandshakeMessage.ht_supplemental_data, true),
HS_CLIENT_CERTIFICATE(
"client certificate",
HandshakeMessage.ht_certificate, true),
HS_CERTIFICATE_URL(
"certificate_url",
HandshakeMessage.ht_certificate_url, true),
HS_CLIENT_KEY_EXCHANGE(
"client_key_exchange",
HandshakeMessage.ht_client_key_exchange),
HS_CERTIFICATE_VERIFY(
"certificate_verify",
HandshakeMessage.ht_certificate_verify, true),
HS_CLIENT_CHANGE_CIPHER_SPEC(
"client change_cipher_spec",
HandshakeMessage.ht_not_applicable),
HS_CLEINT_FINISHED(
"client finished",
HandshakeMessage.ht_finished),
HS_NEW_SESSION_TICKET(
"session_ticket",
HandshakeMessage.ht_new_session_ticket),
HS_SERVER_CHANGE_CIPHER_SPEC(
"server change_cipher_spec",
HandshakeMessage.ht_not_applicable),
HS_SERVER_FINISHED(
"server finished",
HandshakeMessage.ht_finished);
final String description;
final byte handshakeType;
final boolean isOptional;
HandshakeState(String description, byte handshakeType) {
this.description = description;
this.handshakeType = handshakeType;
this.isOptional = false;
}
HandshakeState(String description,
byte handshakeType, boolean isOptional) {
this.description = description;
this.handshakeType = handshakeType;
this.isOptional = isOptional;
}
public String toString() {
return description + "[" + handshakeType + "]" +
(isOptional ? "(optional)" : "");
}
}
boolean isEmpty() {
return upcomingStates.isEmpty();
}
List<Byte> check(byte handshakeType) throws SSLProtocolException {
List<Byte> ignoredOptional = new LinkedList<>();
String exceptionMsg =
"Handshake message sequence violation, " + handshakeType;
if (debugIsOn) {
System.out.println(
"check handshake state: " + toString(handshakeType));
}
if (upcomingStates.isEmpty()) {
// Is it a kickstart message?
if ((handshakeType != HandshakeMessage.ht_hello_request) &&
(handshakeType != HandshakeMessage.ht_client_hello)) {
throw new SSLProtocolException(
"Handshake message sequence violation, " + handshakeType);
}
// It is a kickstart message.
return Collections.emptyList();
}
// Ignore the checking for HelloRequest messages as they
// may be sent by the server at any time.
if (handshakeType == HandshakeMessage.ht_hello_request) {
return Collections.emptyList();
}
for (HandshakeState handshakeState : upcomingStates) {
if (handshakeState.handshakeType == handshakeType) {
// It's the expected next handshake type.
return ignoredOptional;
}
if (handshakeState.isOptional) {
ignoredOptional.add(handshakeState.handshakeType);
continue;
} else {
for (HandshakeState alternative : alternatives) {
if (alternative.handshakeType == handshakeType) {
return ignoredOptional;
}
if (alternative.isOptional) {
continue;
} else {
throw new SSLProtocolException(exceptionMsg);
}
}
}
throw new SSLProtocolException(exceptionMsg);
}
// Not an expected Handshake message.
throw new SSLProtocolException(
"Handshake message sequence violation, " + handshakeType);
}
void update(HandshakeMessage handshakeMessage,
boolean isAbbreviated) throws SSLProtocolException {
byte handshakeType = (byte)handshakeMessage.messageType();
String exceptionMsg =
"Handshake message sequence violation, " + handshakeType;
if (debugIsOn) {
System.out.println(
"update handshake state: " + toString(handshakeType));
}
boolean hasPresentState = false;
switch (handshakeType) {
case HandshakeMessage.ht_hello_request:
//
// State machine:
// PRESENT: START
// TO : ClientHello
//
// No old state to update.
// Add the upcoming states.
if (!upcomingStates.isEmpty()) {
// A ClientHello message should be followed.
upcomingStates.add(HS_CLIENT_HELLO);
} // Otherwise, ignore this HelloRequest message.
break;
case HandshakeMessage.ht_client_hello:
//
// State machine:
// PRESENT: START
// HS_CLIENT_HELLO
// TO : HS_HELLO_VERIFY_REQUEST (DTLS)
// HS_SERVER_HELLO
//
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_CLIENT_HELLO.
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState != HS_CLIENT_HELLO) {
throw new SSLProtocolException(exceptionMsg);
}
}
// Add the upcoming states.
ClientHello clientHello = (ClientHello)handshakeMessage;
if (isDTLS) {
// Is it an initial ClientHello message?
if (clientHello.cookie == null ||
clientHello.cookie.length == 0) {
// Is it an abbreviated handshake?
if (clientHello.sessionId.length() != 0) {
// A HelloVerifyRequest message or a ServerHello
// message may follow the abbreviated session
// resuming handshake request.
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
alternatives.add(HS_SERVER_HELLO);
} else {
// A HelloVerifyRequest message should follow
// the initial ClientHello message.
upcomingStates.add(HS_HELLO_VERIFY_REQUEST);
}
} else {
// A HelloVerifyRequest may be followed if the cookie
// cannot be verified.
upcomingStates.add(HS_SERVER_HELLO);
alternatives.add(HS_HELLO_VERIFY_REQUEST);
}
} else {
upcomingStates.add(HS_SERVER_HELLO);
}
break;
case HandshakeMessage.ht_hello_verify_request:
//
// State machine:
// PRESENT: HS_HELLO_VERIFY_REQUEST
// TO : HS_CLIENT_HELLO
//
// Note that this state may have an alternative option.
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_HELLO_VERIFY_REQUEST.
HandshakeState handshakeState = upcomingStates.pop();
HandshakeState alternative = null;
if (!alternatives.isEmpty()) {
alternative = alternatives.pop();
}
if ((handshakeState != HS_HELLO_VERIFY_REQUEST) &&
(alternative != HS_HELLO_VERIFY_REQUEST)) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// Add the upcoming states.
upcomingStates.add(HS_CLIENT_HELLO);
break;
case HandshakeMessage.ht_server_hello:
//
// State machine:
// PRESENT: HS_SERVER_HELLO
// TO :
// Full handshake state stacks
// (ServerHello Flight)
// HS_SERVER_SUPPLEMENTAL_DATA [optional]
// --> HS_SERVER_CERTIFICATE [optional]
// --> HS_CERTIFICATE_STATUS [optional]
// --> HS_SERVER_KEY_EXCHANGE [optional]
// --> HS_CERTIFICATE_REQUEST [optional]
// --> HS_SERVER_HELLO_DONE
// (Client ClientKeyExchange Flight)
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
// --> HS_CLIENT_CERTIFICATE or
// HS_CERTIFICATE_URL
// --> HS_CLIENT_KEY_EXCHANGE
// --> HS_CERTIFICATE_VERIFY [optional]
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
// --> HS_CLEINT_FINISHED
// (Server Finished Flight)
// --> HS_CLIENT_SUPPLEMENTAL_DATA [optional]
//
// Abbreviated handshake state stacks
// (Server Finished Flight)
// HS_NEW_SESSION_TICKET
// --> HS_SERVER_CHANGE_CIPHER_SPEC
// --> HS_SERVER_FINISHED
// (Client Finished Flight)
// --> HS_CLIENT_CHANGE_CIPHER_SPEC
// --> HS_CLEINT_FINISHED
//
// Note that this state may have an alternative option.
// Check and update the present state.
if (!upcomingStates.isEmpty()) {
// The current state should be HS_SERVER_HELLO
HandshakeState handshakeState = upcomingStates.pop();
HandshakeState alternative = null;
if (!alternatives.isEmpty()) {
alternative = alternatives.pop();
}
if ((handshakeState != HS_SERVER_HELLO) &&
(alternative != HS_SERVER_HELLO)) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// Add the upcoming states.
ServerHello serverHello = (ServerHello)handshakeMessage;
HelloExtensions hes = serverHello.extensions;
// Not support SessionTicket extension yet.
//
// boolean hasSessionTicketExt =
// (hes.get(HandshakeMessage.ht_new_session_ticket) != null);
if (isAbbreviated) {
// Not support SessionTicket extension yet.
//
// // Mandatory NewSessionTicket message
// if (hasSessionTicketExt) {
// upcomingStates.add(HS_NEW_SESSION_TICKET);
// }
// Mandatory server ChangeCipherSpec and Finished messages
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_SERVER_FINISHED);
// Mandatory client ChangeCipherSpec and Finished messages
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_CLEINT_FINISHED);
} else {
// Not support SupplementalData extension yet.
//
// boolean hasSupplementalDataExt =
// (hes.get(HandshakeMessage.ht_supplemental_data) != null);
// Not support CertificateURL extension yet.
//
// boolean hasCertificateUrlExt =
// (hes.get(ExtensionType EXT_CLIENT_CERTIFICATE_URL)
// != null);
// Not support SupplementalData extension yet.
//
// // Optional SupplementalData message
// if (hasSupplementalDataExt) {
// upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA);
// }
// Need server Certificate message or not?
KeyExchange keyExchange = serverHello.cipherSuite.keyExchange;
if ((keyExchange != K_KRB5) &&
(keyExchange != K_KRB5_EXPORT) &&
(keyExchange != K_DH_ANON) &&
(keyExchange != K_ECDH_ANON)) {
// Mandatory Certificate message
upcomingStates.add(HS_SERVER_CERTIFICATE);
}
// Optional CertificateStatus message
if (hes.get(ExtensionType.EXT_STATUS_REQUEST) != null ||
hes.get(ExtensionType.EXT_STATUS_REQUEST_V2) != null) {
upcomingStates.add(HS_CERTIFICATE_STATUS);
}
// Need ServerKeyExchange message or not?
if ((keyExchange == K_RSA_EXPORT) ||
(keyExchange == K_DHE_RSA) ||
(keyExchange == K_DHE_DSS) ||
(keyExchange == K_DH_ANON) ||
(keyExchange == K_ECDHE_RSA) ||
(keyExchange == K_ECDHE_ECDSA) ||
(keyExchange == K_ECDH_ANON)) {
// Optional ServerKeyExchange message
upcomingStates.add(HS_SERVER_KEY_EXCHANGE);
}
// Optional CertificateRequest message
upcomingStates.add(HS_CERTIFICATE_REQUEST);
// Mandatory ServerHelloDone message
upcomingStates.add(HS_SERVER_HELLO_DONE);
// Not support SupplementalData extension yet.
//
// // Optional SupplementalData message
// if (hasSupplementalDataExt) {
// upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA);
// }
// Optional client Certificate message
upcomingStates.add(HS_CLIENT_CERTIFICATE);
// Not support CertificateURL extension yet.
//
// // Alternative CertificateURL message, optional too.
// //
// // Please put CertificateURL rather than Certificate
// // message in the alternatives list. So that we can
// // simplify the process of this alternative pair later.
// if (hasCertificateUrlExt) {
// alternatives.add(HS_CERTIFICATE_URL);
// }
// Mandatory ClientKeyExchange message
upcomingStates.add(HS_CLIENT_KEY_EXCHANGE);
// Optional CertificateVerify message
upcomingStates.add(HS_CERTIFICATE_VERIFY);
// Mandatory client ChangeCipherSpec and Finished messages
upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_CLEINT_FINISHED);
// Not support SessionTicket extension yet.
//
// // Mandatory NewSessionTicket message
// if (hasSessionTicketExt) {
// upcomingStates.add(HS_NEW_SESSION_TICKET);
// }
// Mandatory server ChangeCipherSpec and Finished messages
upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC);
upcomingStates.add(HS_SERVER_FINISHED);
}
break;
case HandshakeMessage.ht_certificate:
//
// State machine:
// PRESENT: HS_CERTIFICATE_URL or
// HS_CLIENT_CERTIFICATE
// TO : HS_CLIENT_KEY_EXCHANGE
//
// Or
//
// PRESENT: HS_SERVER_CERTIFICATE
// TO : HS_CERTIFICATE_STATUS [optional]
// HS_SERVER_KEY_EXCHANGE [optional]
// HS_CERTIFICATE_REQUEST [optional]
// HS_SERVER_HELLO_DONE
//
// Note that this state may have an alternative option.
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType == handshakeType) {
hasPresentState = true;
// The current state should be HS_CLIENT_CERTIFICATE or
// HS_SERVER_CERTIFICATE.
//
// Note that we won't put HS_CLIENT_CERTIFICATE into
// the alternative list.
if ((handshakeState != HS_CLIENT_CERTIFICATE) &&
(handshakeState != HS_SERVER_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
// Is it an expected client Certificate message?
boolean isClientMessage = false;
if (!upcomingStates.isEmpty()) {
// If the next expected message is ClientKeyExchange,
// this one should be an expected client Certificate
// message.
HandshakeState nextState = upcomingStates.getFirst();
if (nextState == HS_CLIENT_KEY_EXCHANGE) {
isClientMessage = true;
}
}
if (isClientMessage) {
if (handshakeState != HS_CLIENT_CERTIFICATE) {
throw new SSLProtocolException(exceptionMsg);
}
// Not support CertificateURL extension yet.
/*******************************************
// clear up the alternatives list
if (!alternatives.isEmpty()) {
HandshakeState alternative = alternatives.pop();
if (alternative != HS_CERTIFICATE_URL) {
throw new SSLProtocolException(exceptionMsg);
}
}
********************************************/
} else {
if ((handshakeState != HS_SERVER_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
}
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
break;
// Not support CertificateURL extension yet.
/*************************************************/
case HandshakeMessage.ht_certificate_url:
//
// State machine:
// PRESENT: HS_CERTIFICATE_URL or
// HS_CLIENT_CERTIFICATE
// TO : HS_CLIENT_KEY_EXCHANGE
//
// Note that this state may have an alternative option.
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
// The current state should be HS_CLIENT_CERTIFICATE.
//
// Note that we won't put HS_CLIENT_CERTIFICATE into
// the alternative list.
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType ==
HS_CLIENT_CERTIFICATE.handshakeType) {
hasPresentState = true;
// Look for HS_CERTIFICATE_URL state track.
if (!alternatives.isEmpty()) {
HandshakeState alternative = alternatives.pop();
if (alternative != HS_CERTIFICATE_URL) {
throw new SSLProtocolException(exceptionMsg);
}
} else {
// No alternative CertificateUR state track.
throw new SSLProtocolException(exceptionMsg);
}
if ((handshakeState != HS_CLIENT_CERTIFICATE)) {
throw new SSLProtocolException(exceptionMsg);
}
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
// No present state.
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
break;
/*************************************************/
default:
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState.handshakeType == handshakeType) {
hasPresentState = true;
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
}
if (debugIsOn) {
for (HandshakeState handshakeState : upcomingStates) {
System.out.println(
"upcoming handshake states: " + handshakeState);
}
for (HandshakeState handshakeState : alternatives) {
System.out.println(
"upcoming handshake alternative state: " + handshakeState);
}
}
}
void changeCipherSpec(boolean isInput,
boolean isClient) throws SSLProtocolException {
if (debugIsOn) {
System.out.println(
"update handshake state: change_cipher_spec");
}
String exceptionMsg = "ChangeCipherSpec message sequence violation";
HandshakeState expectedState;
if ((isClient && isInput) || (!isClient && !isInput)) {
expectedState = HS_SERVER_CHANGE_CIPHER_SPEC;
} else {
expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC;
}
boolean hasPresentState = false;
// Check and update the present state.
while (!upcomingStates.isEmpty()) {
HandshakeState handshakeState = upcomingStates.pop();
if (handshakeState == expectedState) {
hasPresentState = true;
break;
} else if (!handshakeState.isOptional) {
throw new SSLProtocolException(exceptionMsg);
} // Otherwise, looking for next state track.
}
// No present state.
if (!hasPresentState) {
throw new SSLProtocolException(exceptionMsg);
}
// no new upcoming states.
if (debugIsOn) {
for (HandshakeState handshakeState : upcomingStates) {
System.out.println(
"upcoming handshake states: " + handshakeState);
}
for (HandshakeState handshakeState : alternatives) {
System.out.println(
"upcoming handshake alternative state: " + handshakeState);
}
}
}
private static String toString(byte handshakeType) {
String s = handshakeTypes.get(handshakeType);
if (s == null) {
s = "unknown";
}
return (s + "[" + handshakeType + "]");
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,119 +26,315 @@
package sun.security.ssl;
import java.io.IOException;
import javax.net.ssl.SSLProtocolException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import static sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.HandshakeMessage.ClientHello;
/*
* HelloVerifyRequest cookie manager
/**
* (D)TLS handshake cookie manager
*/
final class HelloCookieManager {
// the cookie secret life time
private static long COOKIE_TIMING_WINDOW = 3600000; // in milliseconds
private static int COOKIE_MAX_LENGTH_DTLS10 = 32; // 32 bytes
private static int COOKIE_MAX_LENGTH_DTLS12 = 0xFF; // 2^8 -1 bytes
abstract class HelloCookieManager {
private final SecureRandom secureRandom;
private final MessageDigest cookieDigest;
static class Builder {
private int cookieVersion; // allow to wrap
private long secretLifetime;
private byte[] cookieSecret;
final SecureRandom secureRandom;
private int prevCookieVersion;
private byte[] prevCookieSecret;
private volatile D10HelloCookieManager d10HelloCookieManager;
private volatile D13HelloCookieManager d13HelloCookieManager;
private volatile T13HelloCookieManager t13HelloCookieManager;
HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
this.cookieDigest = JsseJce.getMessageDigest("SHA-256");
Builder(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
}
this.cookieVersion = secureRandom.nextInt();
this.secretLifetime = 0;
this.cookieSecret = null;
HelloCookieManager valueOf(ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLS) {
if (protocolVersion.useTLS13PlusSpec()) {
if (d13HelloCookieManager != null) {
return d13HelloCookieManager;
}
this.prevCookieVersion = 0;
this.prevCookieSecret = null;
}
synchronized (this) {
if (d13HelloCookieManager == null) {
d13HelloCookieManager =
new D13HelloCookieManager(secureRandom);
}
}
// Used by server side to generate cookies in HelloVerifyRequest message.
synchronized byte[] getCookie(ClientHello clientHelloMsg) {
if (secretLifetime < System.currentTimeMillis()) {
if (cookieSecret != null) {
prevCookieVersion = cookieVersion;
prevCookieSecret = cookieSecret.clone();
return d13HelloCookieManager;
} else {
if (d10HelloCookieManager != null) {
return d10HelloCookieManager;
}
synchronized (this) {
if (d10HelloCookieManager == null) {
d10HelloCookieManager =
new D10HelloCookieManager(secureRandom);
}
}
return d10HelloCookieManager;
}
} else {
cookieSecret = new byte[32];
if (protocolVersion.useTLS13PlusSpec()) {
if (t13HelloCookieManager != null) {
return t13HelloCookieManager;
}
synchronized (this) {
if (t13HelloCookieManager == null) {
t13HelloCookieManager =
new T13HelloCookieManager(secureRandom);
}
}
return t13HelloCookieManager;
}
}
cookieVersion++;
secureRandom.nextBytes(cookieSecret);
secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW;
return null;
}
clientHelloMsg.updateHelloCookie(cookieDigest);
byte[] cookie = cookieDigest.digest(cookieSecret); // 32 bytes
cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
cookie[3] = (byte)(cookieVersion & 0xFF);
return cookie;
}
// Used by server side to check the cookie in ClientHello message.
synchronized boolean isValid(ClientHello clientHelloMsg) {
byte[] cookie = clientHelloMsg.cookie;
abstract byte[] createCookie(ServerHandshakeContext context,
ClientHelloMessage clientHello) throws IOException;
// no cookie exchange or not a valid cookie length
if ((cookie == null) || (cookie.length != 32)) {
return false;
abstract boolean isCookieValid(ServerHandshakeContext context,
ClientHelloMessage clientHello, byte[] cookie) throws IOException;
// DTLS 1.0/1.2
private static final
class D10HelloCookieManager extends HelloCookieManager {
final SecureRandom secureRandom;
private int cookieVersion; // allow to wrap, version + sequence
private byte[] cookieSecret;
private byte[] legacySecret;
D10HelloCookieManager(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
this.cookieVersion = secureRandom.nextInt();
this.cookieSecret = new byte[32];
this.legacySecret = new byte[32];
secureRandom.nextBytes(cookieSecret);
System.arraycopy(cookieSecret, 0, legacySecret, 0, 32);
}
int version = ((cookie[0] & 0xFF) << 24) |
((cookie[1] & 0xFF) << 16) |
((cookie[2] & 0xFF) << 8) |
(cookie[3] & 0xFF);
@Override
byte[] createCookie(ServerHandshakeContext context,
ClientHelloMessage clientHello) throws IOException {
int version;
byte[] secret;
byte[] secret;
if (version == cookieVersion) {
secret = cookieSecret;
} else if (version == prevCookieVersion) {
secret = prevCookieSecret;
} else {
return false; // may be out of the timing window
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, 32);
secureRandom.nextBytes(cookieSecret);
}
cookieVersion++;
}
MessageDigest md = JsseJce.getMessageDigest("SHA-256");
byte[] helloBytes = clientHello.getHelloCookieBytes();
md.update(helloBytes);
byte[] cookie = md.digest(secret); // 32 bytes
cookie[0] = (byte)((version >> 24) & 0xFF);
return cookie;
}
clientHelloMsg.updateHelloCookie(cookieDigest);
byte[] target = cookieDigest.digest(secret); // 32 bytes
for (int i = 4; i < 32; i++) {
if (cookie[i] != target[i]) {
@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)) {
return false;
}
}
return true;
byte[] secret;
synchronized (this) {
if (((cookieVersion >> 24) & 0xFF) == cookie[0]) {
secret = cookieSecret;
} else {
secret = legacySecret; // including out of window cookies
}
}
MessageDigest md = JsseJce.getMessageDigest("SHA-256");
byte[] helloBytes = clientHello.getHelloCookieBytes();
md.update(helloBytes);
byte[] target = md.digest(secret); // 32 bytes
target[0] = cookie[0];
return Arrays.equals(target, cookie);
}
}
// Used by client side to check the cookie in HelloVerifyRequest message.
static void checkCookie(ProtocolVersion protocolVersion,
byte[] cookie) throws IOException {
if (cookie != null && cookie.length != 0) {
int limit = COOKIE_MAX_LENGTH_DTLS12;
if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
limit = COOKIE_MAX_LENGTH_DTLS10;
}
if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
throw new SSLProtocolException(
"Invalid HelloVerifyRequest.cookie (length = " +
cookie.length + " bytes)");
}
private static final
class D13HelloCookieManager extends HelloCookieManager {
D13HelloCookieManager(SecureRandom secureRandom) {
}
// Otherwise, no cookie exchange.
@Override
byte[] createCookie(ServerHandshakeContext context,
ClientHelloMessage clientHello) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
boolean isCookieValid(ServerHandshakeContext context,
ClientHelloMessage clientHello, byte[] cookie) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
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 (!Arrays.equals(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

@ -1,163 +0,0 @@
/*
* Copyright (c) 2006, 2017, 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.io.PrintStream;
import java.util.*;
import javax.net.ssl.*;
/**
* This file contains all the classes relevant to TLS Extensions for the
* ClientHello and ServerHello messages. The extension mechanism and
* several extensions are defined in RFC 6066. Additional extensions are
* defined in the ECC RFC 4492 and the ALPN extension is defined in RFC 7301.
*
* Currently, only the two ECC extensions are fully supported.
*
* The classes contained in this file are:
* . HelloExtensions: a List of extensions as used in the client hello
* and server hello messages.
* . ExtensionType: an enum style class for the extension type
* . HelloExtension: abstract base class for all extensions. All subclasses
* must be immutable.
*
* . UnknownExtension: used to represent all parsed extensions that we do not
* explicitly support.
* . ServerNameExtension: the server_name extension.
* . SignatureAlgorithmsExtension: the signature_algorithms extension.
* . SupportedGroupsExtension: the supported groups extension.
* . EllipticPointFormatsExtension: the ECC supported point formats
* (compressed/uncompressed) extension.
* . ALPNExtension: the application_layer_protocol_negotiation extension.
*
* @since 1.6
* @author Andreas Sterbenz
*/
final class HelloExtensions {
private List<HelloExtension> extensions;
private int encodedLength;
HelloExtensions() {
extensions = Collections.emptyList();
}
HelloExtensions(HandshakeInStream s) throws IOException {
int len = s.getInt16();
extensions = new ArrayList<HelloExtension>();
encodedLength = len + 2;
while (len > 0) {
int type = s.getInt16();
int extlen = s.getInt16();
ExtensionType extType = ExtensionType.get(type);
HelloExtension extension;
if (extType == ExtensionType.EXT_SERVER_NAME) {
extension = new ServerNameExtension(s, extlen);
} else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) {
extension = new SignatureAlgorithmsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_SUPPORTED_GROUPS) {
extension = new SupportedGroupsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) {
extension = new EllipticPointFormatsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
extension = new RenegotiationInfoExtension(s, extlen);
} else if (extType == ExtensionType.EXT_ALPN) {
extension = new ALPNExtension(s, extlen);
} else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
extension = new MaxFragmentLengthExtension(s, extlen);
} else if (extType == ExtensionType.EXT_STATUS_REQUEST) {
extension = new CertStatusReqExtension(s, extlen);
} else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) {
extension = new CertStatusReqListV2Extension(s, extlen);
} else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) {
extension = new ExtendedMasterSecretExtension(s, extlen);
} else {
extension = new UnknownExtension(s, extlen, extType);
}
extensions.add(extension);
len -= extlen + 4;
}
if (len != 0) {
throw new SSLProtocolException(
"Error parsing extensions: extra data");
}
}
// Return the List of extensions. Must not be modified by the caller.
List<HelloExtension> list() {
return extensions;
}
void add(HelloExtension ext) {
if (extensions.isEmpty()) {
extensions = new ArrayList<HelloExtension>();
}
extensions.add(ext);
encodedLength = -1;
}
HelloExtension get(ExtensionType type) {
for (HelloExtension ext : extensions) {
if (ext.type == type) {
return ext;
}
}
return null;
}
int length() {
if (encodedLength >= 0) {
return encodedLength;
}
if (extensions.isEmpty()) {
encodedLength = 0;
} else {
encodedLength = 2;
for (HelloExtension ext : extensions) {
encodedLength += ext.length();
}
}
return encodedLength;
}
void send(HandshakeOutStream s) throws IOException {
int length = length();
if (length == 0) {
return;
}
s.putInt16(length - 2);
for (HelloExtension ext : extensions) {
ext.send(s);
}
}
void print(PrintStream s) throws IOException {
for (HelloExtension ext : extensions) {
s.println(ext.toString());
}
}
}

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()) {
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) {
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,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 java.text.MessageFormat;
import java.util.Locale;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
/**
* Pack of the HelloVerifyRequest handshake message.
*/
final class HelloVerifyRequest {
static final SSLConsumer handshakeConsumer =
new HelloVerifyRequestConsumer();
static final HandshakeProducer handshakeProducer =
new HelloVerifyRequestProducer();
/**
* The HelloVerifyRequest handshake message [RFC 6347].
*/
static final class HelloVerifyRequestMessage extends HandshakeMessage {
final int serverVersion;
final byte[] cookie;
HelloVerifyRequestMessage(HandshakeContext context,
HandshakeMessage message) throws IOException {
super(context);
// This happens in server side only.
ServerHandshakeContext shc =
(ServerHandshakeContext)context;
ClientHelloMessage clientHello = (ClientHelloMessage)message;
HelloCookieManager hcMgr =
shc.sslContext.getHelloCookieManager(ProtocolVersion.DTLS10);
this.serverVersion = shc.clientHelloVersion;
this.cookie = hcMgr.createCookie(shc, clientHello);
}
HelloVerifyRequestMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
// This happens in client side only.
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// struct {
// ProtocolVersion server_version;
// opaque cookie<0..2^8-1>;
// } HelloVerifyRequest;
if (m.remaining() < 3) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid HelloVerifyRequest: no sufficient data");
}
byte major = m.get();
byte minor = m.get();
this.serverVersion = ((major & 0xFF) << 8) | (minor & 0xFF);
this.cookie = Record.getBytes8(m);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.HELLO_VERIFY_REQUEST;
}
@Override
public int messageLength() {
return 3 + cookie.length; // 2: the length of protocol version
// +1: the cookie length
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putInt8((byte)((serverVersion >>> 8) & 0xFF));
hos.putInt8((byte)(serverVersion & 0xFF));
hos.putBytes8(cookie);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"HelloVerifyRequest\": '{'\n" +
" \"server version\" : \"{0}\",\n" +
" \"cookie\" : \"{1}\",\n" +
"'}'",
Locale.ENGLISH);
Object[] messageFields = {
ProtocolVersion.nameOf(serverVersion),
Utilities.toHexString(cookie),
};
return messageFormat.format(messageFields);
}
}
/**
* The "HelloVerifyRequest" handshake message producer.
*/
private static final
class HelloVerifyRequestProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private HelloVerifyRequestProducer() {
// blank
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// clean up this producer
shc.handshakeProducers.remove(SSLHandshake.HELLO_VERIFY_REQUEST.id);
HelloVerifyRequestMessage hvrm =
new HelloVerifyRequestMessage(shc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced HelloVerifyRequest handshake message", hvrm);
}
// Output the handshake message.
hvrm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// update the context
// Stateless, clean up the handshake context as well?
shc.handshakeHash.finish(); // forgot about the handshake hash
shc.handshakeExtensions.clear();
// What's the expected response?
shc.handshakeConsumers.put(
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
// The handshake message has been delivered.
return null;
}
}
/**
* The "HelloVerifyRequest" handshake message consumer.
*/
private static final class HelloVerifyRequestConsumer
implements SSLConsumer {
// Prevent instantiation of this class.
private HelloVerifyRequestConsumer() {
// 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.HELLO_VERIFY_REQUEST.id);
if (!chc.handshakeConsumers.isEmpty()) {
chc.handshakeConsumers.remove(SSLHandshake.SERVER_HELLO.id);
}
if (!chc.handshakeConsumers.isEmpty()) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"No more message expected before " +
"HelloVerifyRequest is processed");
}
// Refresh handshake hash.
chc.handshakeHash.finish(); // forgot about the handshake hash
HelloVerifyRequestMessage hvrm =
new HelloVerifyRequestMessage(chc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming HelloVerifyRequest handshake message", hvrm);
}
// Note that HelloVerifyRequest.server_version is used solely to
// indicate packet formatting, and not as part of version
// negotiation. Need not to check version values match for
// HelloVerifyRequest message.
chc.initialClientHelloMsg.setHelloCookie(hvrm.cookie);
//
// produce response handshake message
//
SSLHandshake.CLIENT_HELLO.produce(context, hvrm);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,16 +25,14 @@
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
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 javax.net.ssl.*;
import sun.security.util.HexDumpEncoder;
import sun.security.ssl.SSLCipher.SSLReadCipher;
/**
* {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
@ -42,15 +40,12 @@ import sun.security.util.HexDumpEncoder;
*
* @author David Brownell
*/
class InputRecord implements Record, Closeable {
abstract class InputRecord implements Record, Closeable {
SSLReadCipher readCipher;
// Needed for KeyUpdate, used after Handshake.Finished
TransportContext tc;
/* Class and subclass dynamic debugging support */
static final Debug debug = Debug.getInstance("ssl");
Authenticator readAuthenticator;
CipherBox readCipher;
HandshakeHash handshakeHash;
final HandshakeHash handshakeHash;
boolean isClosed;
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
@ -61,10 +56,11 @@ class InputRecord implements Record, Closeable {
// fragment size
int fragmentSize;
InputRecord() {
this.readCipher = CipherBox.NULL;
this.readAuthenticator = null; // Please override this assignment.
this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
this.readCipher = readCipher;
this.helloVersion = ProtocolVersion.TLS10;
this.handshakeHash = handshakeHash;
this.isClosed = false;
this.fragmentSize = Record.maxDataSize;
}
@ -72,41 +68,9 @@ class InputRecord implements Record, Closeable {
this.helloVersion = helloVersion;
}
ProtocolVersion getHelloVersion() {
return helloVersion;
}
/*
* Set instance for the computation of handshake hashes.
*
* For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them ... especially in the
* case of hashing the initial V2 message!
*/
void setHandshakeHash(HandshakeHash handshakeHash) {
if (handshakeHash != null) {
byte[] reserved = null;
if (this.handshakeHash != null) {
reserved = this.handshakeHash.getAllHandshakeMessages();
}
if ((reserved != null) && (reserved.length != 0)) {
handshakeHash.update(reserved, 0, reserved.length);
if (debug != null && Debug.isOn("data")) {
Debug.printHex(
"[reserved] handshake hash: len = " + reserved.length,
reserved);
}
}
}
this.handshakeHash = handshakeHash;
}
boolean seqNumIsHuge() {
return (readAuthenticator != null) &&
readAuthenticator.seqNumIsHuge();
return (readCipher.authenticator != null) &&
readCipher.authenticator.seqNumIsHuge();
}
boolean isEmpty() {
@ -118,6 +82,11 @@ class InputRecord implements Record, Closeable {
// blank
}
// apply to DTLS SSLEngine
void finishHandshake() {
// blank
}
/**
* Prevent any more data from being read into this record,
* and flag the record as holding no data.
@ -130,9 +99,12 @@ class InputRecord implements Record, Closeable {
}
}
synchronized boolean isClosed() {
return isClosed;
}
// apply to SSLSocket and SSLEngine
void changeReadCiphers(
Authenticator readAuthenticator, CipherBox readCipher) {
void changeReadCiphers(SSLReadCipher readCipher) {
/*
* Dispose of any intermediate state in the underlying cipher.
@ -144,7 +116,6 @@ class InputRecord implements Record, Closeable {
*/
readCipher.dispose();
this.readAuthenticator = readAuthenticator;
this.readCipher = readCipher;
}
@ -160,24 +131,22 @@ class InputRecord implements Record, Closeable {
* @return -1 if there are not enough bytes to tell (small header),
*/
// apply to SSLEngine only
int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
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
int bytesInCompletePacket(InputStream is) throws IOException {
void setReceiverStream(InputStream inputStream) {
throw new UnsupportedOperationException();
}
/**
* Return true if the specified record protocol version is out of the
* range of the possible supported versions.
*/
void checkRecordVersion(ProtocolVersion version,
boolean allowSSL20Hello) throws SSLException {
// blank
}
// apply to DTLS SSLEngine only
Plaintext acquirePlaintext()
throws IOException, BadPaddingException {
@ -186,17 +155,8 @@ class InputRecord implements Record, Closeable {
// read, decrypt and decompress the network record.
//
// apply to SSLEngine only
Plaintext decode(ByteBuffer netData)
throws IOException, BadPaddingException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
Plaintext decode(InputStream is, ByteBuffer destination)
throws IOException, BadPaddingException {
throw new UnsupportedOperationException();
}
abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
int srcsLength) throws IOException, BadPaddingException;
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
@ -216,9 +176,7 @@ class InputRecord implements Record, Closeable {
// Not apply to DTLS
static ByteBuffer convertToClientHello(ByteBuffer packet) {
int srcPos = packet.position();
int srcLim = packet.limit();
byte firstByte = packet.get();
byte secondByte = packet.get();
@ -244,7 +202,7 @@ class InputRecord implements Record, Closeable {
// 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 );
int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
byte[] converted = new byte[requiredSize];
/*
@ -252,7 +210,7 @@ class InputRecord implements Record, Closeable {
* that's now buffered up. (Lengths are fixed up later).
*/
// Note: need not to set the header actually.
converted[0] = ct_handshake;
converted[0] = ContentType.HANDSHAKE.id;
converted[1] = majorVersion;
converted[2] = minorVersion;
// header [3..4] for handshake message length
@ -325,7 +283,7 @@ class InputRecord implements Record, Closeable {
j = pointer + 2;
for (int i = 0; i < cipherSpecLen; i += 3) {
if (packet.get() != 0) {
// Ignore version 2.0 specifix cipher suite. Clients
// 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
@ -372,237 +330,61 @@ class InputRecord implements Record, Closeable {
return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size
}
static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
byte contentType, ByteBuffer bb) throws BadPaddingException {
// Extract an SSL/(D)TLS record from the specified source buffers.
static ByteBuffer extract(
ByteBuffer[] buffers, int offset, int length, int headerSize) {
return decrypt(authenticator, box, contentType, bb, null);
}
static ByteBuffer decrypt(Authenticator authenticator,
CipherBox box, byte contentType, ByteBuffer bb,
byte[] sequence) throws BadPaddingException {
BadPaddingException reservedBPE = null;
int tagLen =
(authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
int cipheredLength = bb.remaining();
int srcPos = bb.position();
if (!box.isNullCipher()) {
try {
// apply explicit nonce for AEAD/CBC cipher suites if needed
int nonceSize = box.applyExplicitNonce(
authenticator, contentType, bb, sequence);
// decrypt the content
if (box.isAEADMode()) {
// DON'T decrypt the nonce_explicit for AEAD mode
bb.position(srcPos + nonceSize);
} // The explicit IV for CBC mode can be decrypted.
// Note that the CipherBox.decrypt() does not change
// the capacity of the buffer.
box.decrypt(bb, tagLen);
// We don't actually remove the nonce.
bb.position(srcPos + nonceSize);
} catch (BadPaddingException bpe) {
// RFC 2246 states that decryption_failed should be used
// for this purpose. However, that allows certain attacks,
// so we just send bad record MAC. We also need to make
// sure to always check the MAC to avoid a timing attack
// for the same issue. See paper by Vaudenay et al and the
// update in RFC 4346/5246.
//
// Failover to message authentication code checking.
reservedBPE = bpe;
}
}
// Requires message authentication code for null, stream and block
// cipher suites.
if ((authenticator instanceof MAC) && (tagLen != 0)) {
MAC signer = (MAC)authenticator;
int contentLen = bb.remaining() - tagLen;
// Note that although it is not necessary, we run the same MAC
// computation and comparison on the payload for both stream
// cipher and CBC block cipher.
if (contentLen < 0) {
// negative data length, something is wrong
if (reservedBPE == null) {
reservedBPE = new BadPaddingException("bad record");
}
// set offset of the dummy MAC
contentLen = cipheredLength - tagLen;
bb.limit(srcPos + cipheredLength);
}
// Run MAC computation and comparison on the payload.
//
// MAC data would be stripped off during the check.
if (checkMacTags(contentType, bb, signer, sequence, false)) {
if (reservedBPE == null) {
reservedBPE = new BadPaddingException("bad record MAC");
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;
}
}
}
// Run MAC computation and comparison on the remainder.
//
// It is only necessary for CBC block cipher. It is used to get a
// constant time of MAC computation and comparison on each record.
if (box.isCBCMode()) {
int remainingLen = calculateRemainingLen(
signer, cipheredLength, contentLen);
if (!hasFullHeader) {
throw new BufferUnderflowException();
}
// NOTE: remainingLen may be bigger (less than 1 block of the
// hash algorithm of the MAC) than the cipheredLength.
//
// Is it possible to use a static buffer, rather than allocate
// it dynamically?
remainingLen += signer.MAClen();
ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
// Won't need to worry about the result on the remainder. And
// then we won't need to worry about what's actual data to
// check MAC tag on. We start the check from the header of the
// buffer so that we don't need to construct a new byte buffer.
checkMacTags(contentType, temporary, signer, sequence, true);
int packetLen = headerSize + contentLen;
int remains = 0;
for (int i = offset; i < offset + length; i++) {
remains += buffers[i].remaining();
if (remains >= packetLen) {
break;
}
}
// Is it a failover?
if (reservedBPE != null) {
throw reservedBPE;
if (remains < packetLen) {
throw new BufferUnderflowException();
}
return bb.slice();
}
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;
}
/*
* Run MAC computation and comparison
*
*/
private static boolean checkMacTags(byte contentType, ByteBuffer bb,
MAC signer, byte[] sequence, boolean isSimulated) {
int tagLen = signer.MAClen();
int position = bb.position();
int lim = bb.limit();
int macOffset = lim - tagLen;
bb.limit(macOffset);
byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
if (hash == null || tagLen != hash.length) {
// Something is wrong with MAC implementation.
throw new RuntimeException("Internal MAC error");
}
bb.position(macOffset);
bb.limit(lim);
try {
int[] results = compareMacTags(bb, hash);
return (results[0] != 0);
} finally {
// reset to the data
bb.position(position);
bb.limit(macOffset);
}
}
/*
* A constant-time comparison of the MAC tags.
*
* Please DON'T change the content of the ByteBuffer parameter!
*/
private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
// An array of hits is used to prevent Hotspot optimization for
// the purpose of a constant-time check.
int[] results = {0, 0}; // {missed #, matched #}
// The caller ensures there are enough bytes available in the buffer.
// So we won't need to check the remaining of the buffer.
for (int i = 0; i < tag.length; i++) {
if (bb.get() != tag[i]) {
results[0]++; // mismatched bytes
} else {
results[1]++; // matched bytes
if (packetSpaces <= 0) {
break;
}
}
return results;
}
/*
* Run MAC computation and comparison
*
* Please DON'T change the content of the byte buffer parameter!
*/
private static boolean checkMacTags(byte contentType, byte[] buffer,
int offset, int contentLen, MAC signer, boolean isSimulated) {
int tagLen = signer.MAClen();
byte[] hash = signer.compute(
contentType, buffer, offset, contentLen, isSimulated);
if (hash == null || tagLen != hash.length) {
// Something is wrong with MAC implementation.
throw new RuntimeException("Internal MAC error");
}
int[] results = compareMacTags(buffer, offset + contentLen, hash);
return (results[0] != 0);
}
/*
* A constant-time comparison of the MAC tags.
*
* Please DON'T change the content of the byte buffer parameter!
*/
private static int[] compareMacTags(
byte[] buffer, int offset, byte[] tag) {
// An array of hits is used to prevent Hotspot optimization for
// the purpose of a constant-time check.
int[] results = {0, 0}; // {missed #, matched #}
// The caller ensures there are enough bytes available in the buffer.
// So we won't need to check the length of the buffer.
for (int i = 0; i < tag.length; i++) {
if (buffer[offset + i] != tag[i]) {
results[0]++; // mismatched bytes
} else {
results[1]++; // matched bytes
}
}
return results;
}
/*
* Calculate the length of a dummy buffer to run MAC computation
* and comparison on the remainder.
*
* The caller MUST ensure that the fullLen is not less than usedLen.
*/
private static int calculateRemainingLen(
MAC signer, int fullLen, int usedLen) {
int blockLen = signer.hashBlockLen();
int minimalPaddingLen = signer.minimalPaddingLen();
// (blockLen - minimalPaddingLen) is the maximum message size of
// the last block of hash function operation. See FIPS 180-4, or
// MD5 specification.
fullLen += 13 - (blockLen - minimalPaddingLen);
usedLen += 13 - (blockLen - minimalPaddingLen);
// Note: fullLen is always not less than usedLen, and blockLen
// is always bigger than minimalPaddingLen, so we don't worry
// about negative values. 0x01 is added to the result to ensure
// that the return value is positive. The extra one byte does
// not impact the overall MAC compression function evaluations.
return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
return ByteBuffer.wrap(packet);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,25 +25,16 @@
package sun.security.ssl;
import java.util.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.*;
import javax.crypto.*;
// explicit import to override the Provider class in this package
import java.security.Provider;
// need internal Sun classes for FIPS tricks
import sun.security.jca.Providers;
import sun.security.jca.ProviderList;
import sun.security.util.ECUtil;
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;
/**
@ -53,18 +44,11 @@ import static sun.security.util.SecurityConstants.PROVIDER_VER;
* @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 static final boolean kerberosAvailable;
static {
ClientKeyExchangeService p =
ClientKeyExchangeService.find("KRB5");
kerberosAvailable = (p != null);
}
static {
// force FIPS flag initialization
// Because isFIPS() is synchronized and cryptoProvider is not modified
@ -116,37 +100,45 @@ final class JsseJce {
* 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.
@ -154,17 +146,20 @@ final class JsseJce {
* 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
@ -180,10 +175,6 @@ final class JsseJce {
return EcAvailability.isAvailable;
}
static boolean isKerberosAvailable() {
return kerberosAvailable;
}
/**
* Return an JCE cipher implementation for the specified algorithm.
*/
@ -299,7 +290,8 @@ final class JsseJce {
for (Provider.Service s : cryptoProvider.getServices()) {
if (s.getType().equals("SecureRandom")) {
try {
return SecureRandom.getInstance(s.getAlgorithm(), cryptoProvider);
return SecureRandom.getInstance(
s.getAlgorithm(), cryptoProvider);
} catch (NoSuchAlgorithmException ee) {
// ignore
}
@ -394,7 +386,7 @@ final class JsseJce {
// See Effective Java Second Edition: Item 71.
private static class EcAvailability {
// Is EC crypto available?
private final static boolean isAvailable;
private static final boolean isAvailable;
static {
boolean mediator = true;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* 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
@ -91,9 +91,11 @@ abstract class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
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());
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,
@ -114,7 +116,6 @@ abstract class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
"Parameters must be instance of KeyStoreBuilderParameters");
}
if (SunJSSE.isFIPS()) {
// XXX should be fixed
throw new InvalidAlgorithmParameterException
("FIPS mode: KeyStoreBuilderParameters not supported");
}

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.security.util.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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
List<SSLCredentials> credentials = new LinkedList<>();
for (KeyShareEntry entry : spec.clientShares) {
NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
shc.sslConfig.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.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
ecdhec.popPublicKey)) {
SSLLogger.warning(
"ECDHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials.add(ecdhec);
}
}
} catch (IOException | GeneralSecurityException ex) {
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.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
dhec.popPublicKey)) {
SSLLogger.warning(
"DHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials.add(dhec);
}
}
} catch (IOException | GeneralSecurityException ex) {
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.
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in ServerHello");
return; // fatal() always throws, make the compiler happy.
}
// Is it a supported and enabled extension?
if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in ServerHello");
return; // fatal() always throws, make the compiler happy.
}
// Parse the extension
SHKeyShareSpec spec;
try {
spec = new SHKeyShareSpec(buffer);
} catch (IOException ioe) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
KeyShareEntry keyShare = spec.serverShare;
NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
if (ng == null || !SupportedGroups.isActivatable(
chc.sslConfig.algorithmConstraints, ng)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
return; // fatal() always throws, make the compiler happy.
}
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
if (ke == null) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"No key exchange for named group " + ng.name);
return; // fatal() always throws, make the compiler happy.
}
SSLCredentials credentials = null;
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
try {
ECDHECredentials ecdhec =
ECDHECredentials.valueOf(ng, keyShare.keyExchange);
if (ecdhec != null) {
if (!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
ecdhec.popPublicKey)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"ECDHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials = ecdhec;
}
}
} catch (IOException | GeneralSecurityException ex) {
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.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
dhec.popPublicKey)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"DHE key share entry does not " +
"comply to algorithm constraints");
} else {
credentials = dhec;
}
}
} catch (IOException | GeneralSecurityException ex) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Cannot decode named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
} else {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported named group: " +
NamedGroup.nameOf(keyShare.namedGroupId));
}
if (credentials == null) {
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)) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
return null; // make the compiler happy.
}
if (shc.clientRequestedNamedGroups == null ||
shc.clientRequestedNamedGroups.isEmpty()) {
// No supported groups.
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in HelloRetryRequest");
return null; // make the compiler happy.
}
NamedGroup selectedGroup = null;
for (NamedGroup ng : shc.clientRequestedNamedGroups) {
if (SupportedGroups.isActivatable(
shc.sslConfig.algorithmConstraints, ng)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"HelloRetryRequest selected named group: " +
ng.name);
}
selectedGroup = ng;
break;
}
}
if (selectedGroup == null) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
new IOException("No common named group"));
return null; // make the complier happy
}
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)) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
return null; // make the compiler happy.
}
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)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported key_share extension in HelloRetryRequest");
return; // make the compiler happy.
}
if (chc.clientRequestedNamedGroups == null ||
chc.clientRequestedNamedGroups.isEmpty()) {
// No supported groups.
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected key_share extension in HelloRetryRequest");
return; // make the compiler happy.
}
// Parse the extension
HRRKeyShareSpec spec;
try {
spec = new HRRKeyShareSpec(buffer);
} catch (IOException ioe) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
if (serverGroup == null) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported HelloRetryRequest selected group: " +
NamedGroup.nameOf(spec.selectedGroup));
return; // fatal() always throws, make the compiler happy.
}
if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected HelloRetryRequest selected group: " +
serverGroup.name);
return; // fatal() always throws, make the compiler happy.
}
// 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,324 @@
/*
* 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.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) {
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) {
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, 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
hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
hc.conContext.protocolVersion);
return;
}
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
hc.conContext.inputRecord.readCipher.baseSecret);
if (skd == null) {
// unlikely
hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation");
return;
}
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());
hc.conContext.inputRecord.changeReadCiphers(rc);
hc.conContext.inputRecord.readCipher.baseSecret = nplus1;
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.fine("KeyUpdate: read key updated");
}
} catch (GeneralSecurityException gse) {
hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive read secrets", gse);
return;
}
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
hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Not supported key derivation: " +
hc.conContext.protocolVersion);
return null;
}
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
hc.conContext.outputRecord.writeCipher.baseSecret);
if (skd == null) {
// unlikely
hc.conContext.fatal(Alert.INTERNAL_ERROR, "no key derivation");
return null;
}
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) {
hc.conContext.fatal(Alert.INTERNAL_ERROR,
"Failure to derive write secrets", gse);
return null;
}
// Output the handshake message.
km.write(hc.handshakeOutput);
hc.handshakeOutput.flush();
// change write cipher
hc.conContext.outputRecord.changeWriteCiphers(wc, false);
hc.conContext.outputRecord.writeCipher.baseSecret = nplus1;
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

@ -1,199 +0,0 @@
/*
* Copyright (c) 1996, 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;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.nio.ByteBuffer;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import sun.security.ssl.CipherSuite.MacAlg;
import static sun.security.ssl.CipherSuite.*;
import static sun.security.ssl.CipherSuite.MacAlg.*;
/**
* This class computes the "Message Authentication Code" (MAC) for each
* SSL stream and block cipher message. This is essentially a shared-secret
* signature, used to provide integrity protection for SSL messages. The
* MAC is actually one of several keyed hashes, as associated with the cipher
* suite and protocol version. (SSL v3.0 uses one construct, TLS uses another.)
*
* @author David Brownell
* @author Andreas Sterbenz
*/
final class MAC extends Authenticator {
static final MAC TLS_NULL = new MAC(false);
// Value of the null MAC is fixed
private static final byte[] nullMAC = new byte[0];
// internal identifier for the MAC algorithm
private final MacAlg macAlg;
// JCE Mac object
private final Mac mac;
MAC(boolean isDTLS) {
super(isDTLS);
macAlg = M_NULL;
mac = null;
}
/**
* Set up, configured for the given MAC type and version.
*/
MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
throws NoSuchAlgorithmException, InvalidKeyException {
super(protocolVersion);
this.macAlg = macAlg;
String algorithm;
// using SSL MAC computation?
boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
if (macAlg == M_MD5) {
algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
} else if (macAlg == M_SHA) {
algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
} else if (macAlg == M_SHA256) {
algorithm = "HmacSHA256"; // TLS 1.2+
} else if (macAlg == M_SHA384) {
algorithm = "HmacSHA384"; // TLS 1.2+
} else {
throw new RuntimeException("Unknown Mac " + macAlg);
}
mac = JsseJce.getMac(algorithm);
mac.init(key);
}
/**
* Returns the length of the MAC.
*/
int MAClen() {
return macAlg.size;
}
/**
* Returns the hash function block length of the MAC alorithm.
*/
int hashBlockLen() {
return macAlg.hashBlockSize;
}
/**
* Returns the hash function minimal padding length of the MAC alorithm.
*/
int minimalPaddingLen() {
return macAlg.minimalPaddingSize;
}
/**
* Computes and returns the MAC for the data in this byte array.
*
* @param type record type
* @param buf compressed record on which the MAC is computed
* @param offset start of compressed record data
* @param len the size of the compressed record
* @param isSimulated if true, simulate the MAC computation
*
* @return the MAC result
*/
final byte[] compute(byte type, byte buf[],
int offset, int len, boolean isSimulated) {
if (macAlg.size == 0) {
return nullMAC;
}
if (!isSimulated) {
// Uses the implicit sequence number for the computation.
byte[] additional = acquireAuthenticationBytes(type, len, null);
mac.update(additional);
}
mac.update(buf, offset, len);
return mac.doFinal();
}
/**
* 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
*/
final byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
if (macAlg.size == 0) {
return nullMAC;
}
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();
}
/**
* 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
*/
final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
// Uses the implicit sequence number for the computation.
return compute(type, bb, null, isSimulated);
}
}

View file

@ -0,0 +1,620 @@
/*
* 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;
if (chc.sslContext.isDTLS()) {
requestedMFLength -= DTLSRecord.maxPlaintextPlusSize;
} else {
requestedMFLength -= 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
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,
shc.sslContext.isDTLS());
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) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
if (spec.id != requestedSpec.id) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
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,
chc.sslContext.isDTLS());
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,
shc.sslContext.isDTLS());
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) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
if (spec.id != requestedSpec.id) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"The maximum fragment length response is not requested");
}
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
if (mfle == null) {
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,
chc.sslContext.isDTLS());
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

@ -1,139 +0,0 @@
/*
* 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;
import java.io.IOException;
import javax.net.ssl.SSLProtocolException;
/*
* [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
* 2^14 bytes. It may be desirable for constrained clients to negotiate
* a smaller maximum fragment length due to memory limitations or bandwidth
* limitations.
*
* In order to negotiate smaller maximum fragment lengths, clients MAY
* include an extension of type "max_fragment_length" in the (extended)
* client hello. The "extension_data" field of this extension SHALL
* contain:
*
* enum{
* 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
* } MaxFragmentLength;
*
* whose value is the desired maximum fragment length.
*/
final class MaxFragmentLengthExtension extends HelloExtension {
private static final int MAX_FRAGMENT_LENGTH_512 = 1; // 2^9
private static final int MAX_FRAGMENT_LENGTH_1024 = 2; // 2^10
private static final int MAX_FRAGMENT_LENGTH_2048 = 3; // 2^11
private static final int MAX_FRAGMENT_LENGTH_4096 = 4; // 2^12
final int maxFragmentLength;
MaxFragmentLengthExtension(int fragmentSize) {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
if (fragmentSize < 1024) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
} else if (fragmentSize < 2048) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
} else if (fragmentSize < 4096) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
} else {
maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
}
}
MaxFragmentLengthExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
// check the extension length
if (len != 1) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
maxFragmentLength = s.getInt8();
if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
}
// Length of the encoded extension, including the type and length fields
@Override
int length() {
return 5; // 4: extension type and length fields
// 1: MaxFragmentLength field
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(1);
s.putInt8(maxFragmentLength);
}
int getMaxFragLen() {
switch (maxFragmentLength) {
case MAX_FRAGMENT_LENGTH_512:
return 512;
case MAX_FRAGMENT_LENGTH_1024:
return 1024;
case MAX_FRAGMENT_LENGTH_2048:
return 2048;
case MAX_FRAGMENT_LENGTH_4096:
return 4096;
}
// unlikely to happen
return -1;
}
static boolean needFragLenNego(int fragmentSize) {
return (fragmentSize > 0) && (fragmentSize <= 4096);
}
static int getValidMaxFragLen(int fragmentSize) {
if (fragmentSize < 1024) {
return 512;
} else if (fragmentSize < 2048) {
return 1024;
} else if (fragmentSize < 4096) {
return 2048;
} else if (fragmentSize == 4096) {
return 4096;
} else {
return 16384;
}
}
@Override
public String toString() {
return "Extension " + type + ", max_fragment_length: " +
"(2^" + (maxFragmentLength + 8) + ")";
}
}

View file

@ -1,169 +0,0 @@
/*
* Copyright (c) 2017, 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.spec.ECParameterSpec;
import java.security.spec.ECGenParameterSpec;
import static sun.security.ssl.NamedGroupType.*;
enum NamedGroup {
// Elliptic Curves (RFC 4492)
//
// See sun.security.util.CurveDB for the OIDs
// NIST K-163
SECT163_K1(1, NAMED_GROUP_ECDHE, "sect163k1", "1.3.132.0.1", true),
SECT163_R1(2, NAMED_GROUP_ECDHE, "sect163r1", "1.3.132.0.2", false),
// NIST B-163
SECT163_R2(3, NAMED_GROUP_ECDHE, "sect163r2", "1.3.132.0.15", true),
SECT193_R1(4, NAMED_GROUP_ECDHE, "sect193r1", "1.3.132.0.24", false),
SECT193_R2(5, NAMED_GROUP_ECDHE, "sect193r2", "1.3.132.0.25", false),
// NIST K-233
SECT233_K1(6, NAMED_GROUP_ECDHE, "sect233k1", "1.3.132.0.26", true),
// NIST B-233
SECT233_R1(7, NAMED_GROUP_ECDHE, "sect233r1", "1.3.132.0.27", true),
SECT239_K1(8, NAMED_GROUP_ECDHE, "sect239k1", "1.3.132.0.3", false),
// NIST K-283
SECT283_K1(9, NAMED_GROUP_ECDHE, "sect283k1", "1.3.132.0.16", true),
// NIST B-283
SECT283_R1(10, NAMED_GROUP_ECDHE, "sect283r1", "1.3.132.0.17", true),
// NIST K-409
SECT409_K1(11, NAMED_GROUP_ECDHE, "sect409k1", "1.3.132.0.36", true),
// NIST B-409
SECT409_R1(12, NAMED_GROUP_ECDHE, "sect409r1", "1.3.132.0.37", true),
// NIST K-571
SECT571_K1(13, NAMED_GROUP_ECDHE, "sect571k1", "1.3.132.0.38", true),
// NIST B-571
SECT571_R1(14, NAMED_GROUP_ECDHE, "sect571r1", "1.3.132.0.39", true),
SECP160_K1(15, NAMED_GROUP_ECDHE, "secp160k1", "1.3.132.0.9", false),
SECP160_R1(16, NAMED_GROUP_ECDHE, "secp160r1", "1.3.132.0.8", false),
SECP160_R2(17, NAMED_GROUP_ECDHE, "secp160r2", "1.3.132.0.30", false),
SECP192_K1(18, NAMED_GROUP_ECDHE, "secp192k1", "1.3.132.0.31", false),
// NIST P-192
SECP192_R1(19, NAMED_GROUP_ECDHE, "secp192r1", "1.2.840.10045.3.1.1", true),
SECP224_K1(20, NAMED_GROUP_ECDHE, "secp224k1", "1.3.132.0.32", false),
// NIST P-224
SECP224_R1(21, NAMED_GROUP_ECDHE, "secp224r1", "1.3.132.0.33", true),
SECP256_K1(22, NAMED_GROUP_ECDHE, "secp256k1", "1.3.132.0.10", false),
// NIST P-256
SECP256_R1(23, NAMED_GROUP_ECDHE, "secp256r1", "1.2.840.10045.3.1.7", true),
// NIST P-384
SECP384_R1(24, NAMED_GROUP_ECDHE, "secp384r1", "1.3.132.0.34", true),
// NIST P-521
SECP521_R1(25, NAMED_GROUP_ECDHE, "secp521r1", "1.3.132.0.35", true),
// Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919)
FFDHE_2048(256, NAMED_GROUP_FFDHE, "ffdhe2048", true),
FFDHE_3072(257, NAMED_GROUP_FFDHE, "ffdhe3072", true),
FFDHE_4096(258, NAMED_GROUP_FFDHE, "ffdhe4096", true),
FFDHE_6144(259, NAMED_GROUP_FFDHE, "ffdhe6144", true),
FFDHE_8192(260, NAMED_GROUP_FFDHE, "ffdhe8192", true);
int id;
NamedGroupType type;
String name;
String oid;
String algorithm;
boolean isFips;
// Constructor used for Elliptic Curve Groups (ECDHE)
NamedGroup(int id, NamedGroupType type,
String name, String oid, boolean isFips) {
this.id = id;
this.type = type;
this.name = name;
this.oid = oid;
this.algorithm = "EC";
this.isFips = isFips;
}
// Constructor used for Finite Field Diffie-Hellman Groups (FFDHE)
NamedGroup(int id, NamedGroupType type, String name, boolean isFips) {
this.id = id;
this.type = type;
this.name = name;
this.oid = null;
this.algorithm = "DiffieHellman";
this.isFips = isFips;
}
static NamedGroup valueOf(int id) {
for (NamedGroup group : NamedGroup.values()) {
if (group.id == id) {
return group;
}
}
return null;
}
static NamedGroup nameOf(String name) {
for (NamedGroup group : NamedGroup.values()) {
if (group.name.equals(name)) {
return group;
}
}
return null;
}
static NamedGroup valueOf(ECParameterSpec params) {
String oid = JsseJce.getNamedCurveOid(params);
if ((oid != null) && (!oid.isEmpty())) {
for (NamedGroup group : NamedGroup.values()) {
if (oid.equals(group.oid)) {
return group;
}
}
}
return null;
}
@Override
public String toString() {
return this.name;
}
}

View file

@ -0,0 +1,392 @@
/*
* 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.GeneralSecurityException;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Optional;
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) {
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) {
context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
}
this.ticket = Record.getBytes16(m);
if (ticket.length == 0) {
context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No ticket in the NewSessionTicket handshake message");
}
if (m.remaining() < 2) {
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());
Optional<SecretKey> resumptionMasterSecret =
shc.handshakeSession.getResumptionMasterSecret();
if (!resumptionMasterSecret.isPresent()) {
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.get(), 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,
shc.handshakeSession.getSuite(), newId,
shc.handshakeSession.getCreationTime());
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;
Optional<SecretKey> resumptionMasterSecret =
sessionToSave.getResumptionMasterSecret();
if (!resumptionMasterSecret.isPresent()) {
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.get(),
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(
hc, sessionToSave.getSuite(), newId,
sessionToSave.getCreationTime());
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

@ -1,358 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.cert.Extension;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import javax.net.ssl.SSLException;
import sun.security.util.DerValue;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.provider.certpath.ResponderId;
/*
* RFC6066 defines the TLS extension,"status_request" (type 0x5),
* which allows the client to request that the server perform OCSP
* on the client's behalf.
*
* The RFC defines an OCSPStatusRequest structure:
*
* struct {
* ResponderID responder_id_list<0..2^16-1>;
* Extensions request_extensions;
* } OCSPStatusRequest;
*/
final class OCSPStatusRequest implements StatusRequest {
private final List<ResponderId> responderIds;
private final List<Extension> extensions;
private int encodedLen;
private int ridListLen;
private int extListLen;
/**
* Construct a default {@code OCSPStatusRequest} object with empty
* responder ID and code extension list fields.
*/
OCSPStatusRequest() {
responderIds = new ArrayList<>();
extensions = new ArrayList<>();
encodedLen = this.length();
}
/**
* Construct an {@code OCSPStatusRequest} object using the provided
* {@code ResponderId} and {@code Extension} lists.
*
* @param respIds the list of {@code ResponderId} objects to be placed
* into the {@code OCSPStatusRequest}. If the user wishes to place
* no {@code ResponderId} objects in the request, either an empty
* {@code List} or {@code null} is acceptable.
* @param exts the list of {@code Extension} objects to be placed into
* the {@code OCSPStatusRequest} If the user wishes to place
* no {@code Extension} objects in the request, either an empty
* {@code List} or {@code null} is acceptable.
*/
OCSPStatusRequest(List<ResponderId> respIds, List<Extension> exts) {
responderIds = new ArrayList<>(respIds != null ? respIds :
Collections.emptyList());
extensions = new ArrayList<>(exts != null ? exts :
Collections.emptyList());
encodedLen = this.length();
}
/**
* Construct an {@code OCSPStatusRequest} object from data read from
* a {@code HandshakeInputStream}
*
* @param s the {@code HandshakeInputStream} providing the encoded data
*
* @throws IOException if any decoding errors happen during object
* construction.
*/
OCSPStatusRequest(HandshakeInStream in) throws IOException {
responderIds = new ArrayList<>();
extensions = new ArrayList<>();
int ridListBytesRemaining = in.getInt16();
while (ridListBytesRemaining != 0) {
byte[] ridBytes = in.getBytes16();
responderIds.add(new ResponderId(ridBytes));
ridListBytesRemaining -= (ridBytes.length + 2);
// Make sure that no individual responder ID's length caused an
// overrun relative to the outer responder ID list length
if (ridListBytesRemaining < 0) {
throw new SSLException("Responder ID length overflow: " +
"current rid = " + ridBytes.length + ", remaining = " +
ridListBytesRemaining);
}
}
int extensionLength = in.getInt16();
if (extensionLength > 0) {
byte[] extensionData = new byte[extensionLength];
in.read(extensionData);
DerInputStream dis = new DerInputStream(extensionData);
DerValue[] extSeqContents = dis.getSequence(extensionData.length);
for (DerValue extDerVal : extSeqContents) {
extensions.add(new sun.security.x509.Extension(extDerVal));
}
}
}
/**
* Construct an {@code OCSPStatusRequest} from its encoded form
*
* @param requestBytes the status request extension bytes
*
* @throws IOException if any error occurs during decoding
*/
OCSPStatusRequest(byte[] requestBytes) throws IOException {
responderIds = new ArrayList<>();
extensions = new ArrayList<>();
ByteBuffer reqBuf = ByteBuffer.wrap(requestBytes);
// Get the ResponderId list length
encodedLen = requestBytes.length;
ridListLen = Short.toUnsignedInt(reqBuf.getShort());
int endOfRidList = reqBuf.position() + ridListLen;
// The end position of the ResponderId list in the ByteBuffer
// should be at least 2 less than the end of the buffer. This
// 2 byte defecit is the minimum length required to encode a
// zero-length extensions segment.
if (reqBuf.limit() - endOfRidList < 2) {
throw new SSLException
("ResponderId List length exceeds provided buffer - Len: "
+ ridListLen + ", Buffer: " + reqBuf.remaining());
}
while (reqBuf.position() < endOfRidList) {
int ridLength = Short.toUnsignedInt(reqBuf.getShort());
// Make sure an individual ResponderId length doesn't
// run past the end of the ResponderId list portion of the
// provided buffer.
if (reqBuf.position() + ridLength > endOfRidList) {
throw new SSLException
("ResponderId length exceeds list length - Off: "
+ reqBuf.position() + ", Length: " + ridLength
+ ", End offset: " + endOfRidList);
}
// Consume/add the ResponderId
if (ridLength > 0) {
byte[] ridData = new byte[ridLength];
reqBuf.get(ridData);
responderIds.add(new ResponderId(ridData));
}
}
// Get the Extensions length
int extensionsLen = Short.toUnsignedInt(reqBuf.getShort());
// The end of the extensions should also be the end of the
// encoded OCSPStatusRequest
if (extensionsLen != reqBuf.remaining()) {
throw new SSLException("Incorrect extensions length: Read "
+ extensionsLen + ", Data length: " + reqBuf.remaining());
}
// Extensions are a SEQUENCE of Extension
if (extensionsLen > 0) {
byte[] extensionData = new byte[extensionsLen];
reqBuf.get(extensionData);
DerInputStream dis = new DerInputStream(extensionData);
DerValue[] extSeqContents = dis.getSequence(extensionData.length);
for (DerValue extDerVal : extSeqContents) {
extensions.add(new sun.security.x509.Extension(extDerVal));
}
}
}
/**
* Obtain the length of the {@code OCSPStatusRequest} object in its
* encoded form
*
* @return the length of the {@code OCSPStatusRequest} object in its
* encoded form
*/
@Override
public int length() {
// If we've previously calculated encodedLen simply return it
if (encodedLen != 0) {
return encodedLen;
}
ridListLen = 0;
for (ResponderId rid : responderIds) {
ridListLen += rid.length() + 2;
}
extListLen = 0;
if (!extensions.isEmpty()) {
try {
DerOutputStream extSequence = new DerOutputStream();
DerOutputStream extEncoding = new DerOutputStream();
for (Extension ext : extensions) {
ext.encode(extEncoding);
}
extSequence.write(DerValue.tag_Sequence, extEncoding);
extListLen = extSequence.size();
} catch (IOException ioe) {
// Not sure what to do here
}
}
// Total length is the responder ID list length and extensions length
// plus each lists' 2-byte length fields.
encodedLen = ridListLen + extListLen + 4;
return encodedLen;
}
/**
* Send the encoded {@code OCSPStatusRequest} out through the provided
* {@code HandshakeOutputStream}
*
* @param s the {@code HandshakeOutputStream} on which to send the encoded
* data
*
* @throws IOException if any encoding errors occur
*/
@Override
public void send(HandshakeOutStream s) throws IOException {
s.putInt16(ridListLen);
for (ResponderId rid : responderIds) {
s.putBytes16(rid.getEncoded());
}
DerOutputStream seqOut = new DerOutputStream();
DerOutputStream extBytes = new DerOutputStream();
if (extensions.size() > 0) {
for (Extension ext : extensions) {
ext.encode(extBytes);
}
seqOut.write(DerValue.tag_Sequence, extBytes);
}
s.putBytes16(seqOut.toByteArray());
}
/**
* Determine if a provided {@code OCSPStatusRequest} objects is equal to
* this one.
*
* @param obj an {@code OCSPStatusRequest} object to be compared against
*
* @return {@code true} if the objects are equal, {@code false} otherwise.
* Equivalence is established if the lists of responder IDs and
* extensions between the two objects are also equal.
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
return true;
} else if (obj instanceof OCSPStatusRequest) {
OCSPStatusRequest respObj = (OCSPStatusRequest)obj;
return responderIds.equals(respObj.getResponderIds()) &&
extensions.equals(respObj.getExtensions());
}
return false;
}
/**
* Returns the hash code value for this {@code OCSPStatusRequest}
*
* @return the hash code value for this {@code OCSPStatusRequest}
*/
@Override
public int hashCode() {
int result = 17;
result = 31 * result + responderIds.hashCode();
result = 31 * result + extensions.hashCode();
return result;
}
/**
* Create a string representation of this {@code OCSPStatusRequest}
*
* @return a string representation of this {@code OCSPStatusRequest}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("OCSPStatusRequest\n");
sb.append(" ResponderIds:");
if (responderIds.isEmpty()) {
sb.append(" <EMPTY>");
} else {
for (ResponderId rid : responderIds) {
sb.append("\n ").append(rid.toString());
}
}
sb.append("\n").append(" Extensions:");
if (extensions.isEmpty()) {
sb.append(" <EMPTY>");
} else {
for (Extension ext : extensions) {
sb.append("\n ").append(ext.toString());
}
}
return sb.toString();
}
/**
* Get the list of {@code ResponderId} objects for this
* {@code OCSPStatusRequest}
*
* @return an unmodifiable {@code List} of {@code ResponderId} objects
*/
List<ResponderId> getResponderIds() {
return Collections.unmodifiableList(responderIds);
}
/**
* Get the list of {@code Extension} objects for this
* {@code OCSPStatusRequest}
*
* @return an unmodifiable {@code List} of {@code Extension} objects
*/
List<Extension> getExtensions() {
return Collections.unmodifiableList(extensions);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,30 +25,27 @@
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.Arrays;
import javax.net.ssl.SSLException;
import sun.security.util.HexDumpEncoder;
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/DTLS output
* records, including buffering, encryption, handshake messages marshal, etc.
* {@code OutputRecord} takes care of the management of SSL/(D)TLS
* output records, including buffering, encryption, handshake
* messages marshal, etc.
*
* @author David Brownell
*/
abstract class OutputRecord extends ByteArrayOutputStream
implements Record, Closeable {
abstract class OutputRecord
extends ByteArrayOutputStream implements Record, Closeable {
SSLWriteCipher writeCipher;
// Needed for KeyUpdate, used after Handshake.Finished
TransportContext tc;
/* Class and subclass dynamic debugging support */
static final Debug debug = Debug.getInstance("ssl");
Authenticator writeAuthenticator;
CipherBox writeCipher;
HandshakeHash handshakeHash;
final HandshakeHash handshakeHash;
boolean firstMessage;
// current protocol version, sent as record version
@ -75,16 +72,18 @@ abstract class OutputRecord extends ByteArrayOutputStream
* Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* This is taken from the SSL V3 specification, Appendix E.
*/
private static int[] V3toV2CipherMap1 =
private static final int[] V3toV2CipherMap1 =
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static int[] V3toV2CipherMap3 =
private static final int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
OutputRecord() {
this.writeCipher = CipherBox.NULL;
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.
}
@ -99,15 +98,6 @@ abstract class OutputRecord extends ByteArrayOutputStream
this.helloVersion = helloVersion;
}
/*
* For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them.
*/
void setHandshakeHash(HandshakeHash handshakeHash) {
this.handshakeHash = handshakeHash;
}
/*
* Return true iff the record is empty -- to avoid doing the work
* of sending empty records over the network.
@ -117,8 +107,8 @@ abstract class OutputRecord extends ByteArrayOutputStream
}
boolean seqNumIsHuge() {
return (writeAuthenticator != null) &&
writeAuthenticator.seqNumIsHuge();
return (writeCipher.authenticator != null) &&
writeCipher.authenticator.seqNumIsHuge();
}
// SSLEngine and SSLSocket
@ -132,8 +122,10 @@ abstract class OutputRecord extends ByteArrayOutputStream
abstract void encodeChangeCipherSpec() throws IOException;
// apply to SSLEngine only
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
throw new UnsupportedOperationException();
}
@ -143,7 +135,8 @@ abstract class OutputRecord extends ByteArrayOutputStream
}
// apply to SSLSocket only
void deliver(byte[] source, int offset, int length) throws IOException {
void deliver(
byte[] source, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
@ -152,15 +145,11 @@ abstract class OutputRecord extends ByteArrayOutputStream
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
throw new UnsupportedOperationException();
}
void changeWriteCiphers(Authenticator writeAuthenticator,
CipherBox writeCipher) throws IOException {
encodeChangeCipherSpec();
void changeWriteCiphers(SSLWriteCipher writeCipher,
boolean useChangeCipherSpec) throws IOException {
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
/*
* Dispose of any intermediate state in the underlying cipher.
@ -172,7 +161,6 @@ abstract class OutputRecord extends ByteArrayOutputStream
*/
writeCipher.dispose();
this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
@ -194,6 +182,11 @@ abstract class OutputRecord extends ByteArrayOutputStream
// blank
}
// apply to DTLS SSLEngine
void finishHandshake() {
// blank
}
// apply to DTLS SSLEngine
void launchRetransmission() {
// blank
@ -207,6 +200,10 @@ abstract class OutputRecord extends ByteArrayOutputStream
}
}
synchronized boolean isClosed() {
return isClosed;
}
//
// shared helpers
//
@ -216,72 +213,47 @@ abstract class OutputRecord extends ByteArrayOutputStream
// 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(Authenticator authenticator,
CipherBox encCipher, byte contentType, ByteBuffer destination,
static long encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion, boolean isDTLS) {
byte[] sequenceNumber = null;
int dstContent = destination.position();
// Acquire the current sequence number before using.
ProtocolVersion protocolVersion) {
boolean isDTLS = protocolVersion.isDTLS;
if (isDTLS) {
sequenceNumber = authenticator.sequenceNumber();
}
// The sequence number may be shared for different purpose.
boolean sharedSequenceNumber = false;
// "flip" but skip over header again, add MAC & encrypt
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
if (signer.MAClen() != 0) {
byte[] hash = signer.compute(contentType, destination, false);
/*
* position was advanced to limit in MAC compute above.
*
* Mark next area as writable (above layers should have
* established that we have plenty of room), then write
* out the hash.
*/
destination.limit(destination.limit() + hash.length);
destination.put(hash);
// reset the position and limit
destination.limit(destination.position());
destination.position(dstContent);
// The signer has used and increased the sequence number.
if (isDTLS) {
sharedSequenceNumber = true;
}
}
}
if (!encCipher.isNullCipher()) {
if (protocolVersion.useTLS11PlusSpec() &&
(encCipher.isCBCMode() || encCipher.isAEADMode())) {
byte[] nonce = encCipher.createExplicitNonce(
authenticator, contentType, destination.remaining());
destination.position(headerOffset + headerSize);
destination.put(nonce);
}
if (!encCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
destination.position(headerOffset + headerSize);
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
// Encrypt may pad, so again the limit may be changed.
encCipher.encrypt(destination, dstLim);
// The cipher has used and increased the sequence number.
if (isDTLS && encCipher.isAEADMode()) {
sharedSequenceNumber = true;
if (protocolVersion.useTLS13PlusSpec()) {
return d13Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
} else {
return d10Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
}
} else {
destination.position(destination.limit());
if (protocolVersion.useTLS13PlusSpec()) {
return t13Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
} else {
return t10Encrypt(encCipher,
contentType, destination, headerOffset,
dstLim, headerSize, protocolVersion);
}
}
}
static long d13Encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Not supported yet.");
}
private static long d10Encrypt(
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;
@ -289,31 +261,84 @@ abstract class OutputRecord extends ByteArrayOutputStream
destination.put(headerOffset, contentType); // content type
destination.put(headerOffset + 1, protocolVersion.major);
destination.put(headerOffset + 2, protocolVersion.minor);
if (!isDTLS) {
// fragment length
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
} else {
// epoch and sequence_number
destination.put(headerOffset + 3, sequenceNumber[0]);
destination.put(headerOffset + 4, sequenceNumber[1]);
destination.put(headerOffset + 5, sequenceNumber[2]);
destination.put(headerOffset + 6, sequenceNumber[3]);
destination.put(headerOffset + 7, sequenceNumber[4]);
destination.put(headerOffset + 8, sequenceNumber[5]);
destination.put(headerOffset + 9, sequenceNumber[6]);
destination.put(headerOffset + 10, sequenceNumber[7]);
// fragment length
destination.put(headerOffset + 11, (byte)(fragLen >> 8));
destination.put(headerOffset + 12, (byte)fragLen);
// epoch and sequence_number
destination.put(headerOffset + 3, sequenceNumber[0]);
destination.put(headerOffset + 4, sequenceNumber[1]);
destination.put(headerOffset + 5, sequenceNumber[2]);
destination.put(headerOffset + 6, sequenceNumber[3]);
destination.put(headerOffset + 7, sequenceNumber[4]);
destination.put(headerOffset + 8, sequenceNumber[5]);
destination.put(headerOffset + 9, sequenceNumber[6]);
destination.put(headerOffset + 10, sequenceNumber[7]);
// Increase the sequence number for next use if it is not shared.
if (!sharedSequenceNumber) {
authenticator.increaseSequenceNumber();
}
// fragment length
destination.put(headerOffset + 11, (byte)(fragLen >> 8));
destination.put(headerOffset + 12, (byte)fragLen);
// Update destination position to reflect the amount of data produced.
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
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();
destination.limit(endOfPt + 1);
destination.put(endOfPt, contentType);
}
// 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);
}
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());
@ -324,56 +349,79 @@ abstract class OutputRecord extends ByteArrayOutputStream
//
// Uses the internal expandable buf variable and the current
// protocolVersion variable.
void encrypt(Authenticator authenticator,
CipherBox encCipher, byte contentType, int headerSize) {
int position = headerSize + writeCipher.getExplicitNonceSize();
// "flip" but skip over header again, add MAC & encrypt
int macLen = 0;
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
macLen = signer.MAClen();
if (macLen != 0) {
byte[] hash = signer.compute(contentType,
buf, position, (count - position), false);
write(hash, 0, hash.length);
}
long encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (protocolVersion.useTLS13PlusSpec()) {
return t13Encrypt(encCipher, contentType, headerSize);
} else {
return t10Encrypt(encCipher, contentType, headerSize);
}
}
private static final class T13PaddingHolder {
private static final byte[] zeros = new byte[16];
}
long t13Encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (!encCipher.isNullCipher()) {
// Requires explicit IV/nonce for CBC/AEAD cipher suites for
// TLS 1.1 or later.
if (protocolVersion.useTLS11PlusSpec() &&
(encCipher.isCBCMode() || encCipher.isAEADMode())) {
byte[] nonce = encCipher.createExplicitNonce(
authenticator, contentType, (count - position));
int noncePos = position - nonce.length;
System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
}
if (!encCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
position = headerSize;
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
// increase buf capacity if necessary
int fragSize = count - position;
int packetSize =
encCipher.calculatePacketSize(fragSize, macLen, headerSize);
if (packetSize > (buf.length - position)) {
byte[] newBuf = new byte[position + packetSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
// Encrypt may pad, so again the count may be changed.
count = position +
encCipher.encrypt(buf, position, (count - position));
// 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 packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
if (packetSize > buf.length) {
byte[] newBuf = new byte[packetSize];
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);
}
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 packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
if (packetSize > buf.length) {
byte[] newBuf = new byte[packetSize];
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;
@ -381,11 +429,12 @@ abstract class OutputRecord extends ByteArrayOutputStream
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
@ -449,7 +498,7 @@ abstract class OutputRecord extends ByteArrayOutputStream
dstBuf.position(0);
dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
dstBuf.put(HandshakeMessage.ht_client_hello); // pos: 2
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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -34,16 +34,16 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
final class Plaintext {
static final Plaintext PLAINTEXT_NULL = new Plaintext();
byte contentType;
byte majorVersion;
byte minorVersion;
int recordEpoch; // incremented on every cipher state change
long recordSN; // contains epcoh number (epoch | sequence)
ByteBuffer fragment; // null if need to be reassembled
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
HandshakeStatus handshakeStatus; // null if not used or not handshaking
Plaintext() {
private Plaintext() {
this.contentType = 0;
this.majorVersion = 0;
this.minorVersion = 0;
@ -53,7 +53,8 @@ final class Plaintext {
this.handshakeStatus = null;
}
Plaintext(byte contentType, byte majorVersion, byte minorVersion,
Plaintext(byte contentType,
byte majorVersion, byte minorVersion,
int recordEpoch, long recordSN, ByteBuffer fragment) {
this.contentType = contentType;
@ -66,6 +67,7 @@ final class Plaintext {
this.handshakeStatus = null;
}
@Override
public String toString() {
return "contentType: " + contentType + "/" +
"majorVersion: " + majorVersion + "/" +

View file

@ -0,0 +1,76 @@
/*
* 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.LinkedHashMap;
import java.util.Map;
/**
* A compact implementation of HandshakeContext for post-handshake messages
*/
final class PostHandshakeContext extends HandshakeContext {
private final static Map<Byte, SSLConsumer> consumers = Map.of(
SSLHandshake.KEY_UPDATE.id, SSLHandshake.KEY_UPDATE,
SSLHandshake.NEW_SESSION_TICKET.id, SSLHandshake.NEW_SESSION_TICKET);
PostHandshakeContext(TransportContext context) throws IOException {
super(context);
if (!negotiatedProtocol.useTLS13PlusSpec()) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Post-handshake not supported in " + negotiatedProtocol.name);
}
handshakeConsumers = new LinkedHashMap<>(consumers);
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) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected post-handshake message: " +
SSLHandshake.nameOf(handshakeType));
return;
}
try {
consumer.consume(this, fragment);
} catch (UnsupportedOperationException unsoe) {
conContext.fatal(Alert.UNEXPECTED_MESSAGE,
"Unsupported post-handshake message: " +
SSLHandshake.nameOf(handshakeType), unsoe);
}
}
}

View file

@ -0,0 +1,796 @@
/*
* 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.*;
import java.text.MessageFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Arrays;
import java.util.Optional;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
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) {
context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid pre_shared_key extension: " +
"insufficient data (length=" + m.remaining() + ")");
}
int idEncodedLength = Record.getInt16(m);
if (idEncodedLength < 7) {
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) {
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) {
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) {
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) {
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) {
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 {
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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// The "psk_key_exchange_modes" extension should have been loaded.
if (!shc.handshakeExtensions.containsKey(
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
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()) {
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.get(requestedId.identity);
if (s != null && s.isRejoinable() &&
s.getPreSharedKey().isPresent()) {
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 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) {
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 {
Optional<SecretKey> pskOpt = session.getPreSharedKey();
if (!pskOpt.isPresent()) {
shc.conContext.fatal(Alert.INTERNAL_ERROR,
"Session has no PSK");
}
SecretKey psk = pskOpt.get();
SecretKey binderKey = deriveBinderKey(psk, session);
byte[] computedBinder =
computeBinder(binderKey, session, pskBinderHash);
if (!Arrays.equals(binder, computedBinder)) {
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;
}
Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey();
if (!pskOpt.isPresent()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Existing session has no PSK.");
}
return null;
}
SecretKey psk = pskOpt.get();
Optional<byte[]> pskIdOpt = chc.resumingSession.getPskIdentity();
if (!pskIdOpt.isPresent()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"PSK has no identity, or identity was already used");
}
return null;
}
byte[] pskId = pskIdOpt.get();
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(pskId, obfuscatedAge));
SecretKey binderKey = deriveBinderKey(psk, chc.resumingSession);
ClientHelloMessage clientHello = (ClientHelloMessage)message;
CHPreSharedKeySpec pskPrototype = createPskPrototype(
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
byte[] binder = computeBinder(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(SecretKey binderKey,
SSLSessionImpl session,
HandshakeHash pskBinderHash) throws IOException {
pskBinderHash.determine(
session.getProtocolVersion(), session.getSuite());
pskBinderHash.update();
byte[] digest = pskBinderHash.digest();
return computeBinder(binderKey, session, digest);
}
private static byte[] computeBinder(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(binderKey, session, digest);
}
private static byte[] computeBinder(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 new IOException(ex);
}
} catch(GeneralSecurityException ex) {
throw new IOException(ex);
}
}
private static SecretKey deriveBinderKey(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.toString());;
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
label, md.digest(new byte[0]), hashAlg.hashLength);
return hkdf.expand(earlySecret,
hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
} catch (GeneralSecurityException ex) {
throw new IOException(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)) {
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);
}
// The PSK identity should not be reused, even if it is
// not selected.
chc.resumingSession.consumePskIdentity();
if (shPsk.selectedIdentity != 0) {
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);
}
// remove the session from the cache
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
chc.sslContext.engineGetClientSessionContext();
sessionCache.remove(chc.resumingSession.getSessionId());
}
}
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.");
}
if (chc.handshakeExtensions.containsKey(
SSLExtension.CH_PRE_SHARED_KEY)) {
// The PSK identity should not be reused, even if it is
// not selected.
chc.resumingSession.consumePskIdentity();
}
// 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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,21 +25,19 @@
package sun.security.ssl;
import java.security.*;
import java.math.BigInteger;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Map;
import java.util.HashMap;
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;
/**
* Predefined default DH ephemeral parameters.
*/
final class PredefinedDHParameterSpecs {
private final static boolean debugIsOn =
(Debug.getInstance("ssl") != null) && Debug.isOn("sslctx");
//
// Default DH ephemeral parameters
@ -209,15 +207,15 @@ final class PredefinedDHParameterSpecs {
// a measure of the uncertainty that prime modulus p is not a prime
//
// see BigInteger.isProbablePrime(int certainty)
private final static int PRIME_CERTAINTY = 120;
private static final int PRIME_CERTAINTY = 120;
// the known security property, jdk.tls.server.defaultDHEParameters
private final static String PROPERTY_NAME =
private static final String PROPERTY_NAME =
"jdk.tls.server.defaultDHEParameters";
private static final Pattern spacesPattern = Pattern.compile("\\s+");
private final static Pattern syntaxPattern = Pattern.compile(
private static final Pattern syntaxPattern = Pattern.compile(
"(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
"(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
@ -225,10 +223,10 @@ final class PredefinedDHParameterSpecs {
"\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
// cache of predefined default DH ephemeral parameters
final static Map<Integer, DHParameterSpec> definedParams;
static final Map<Integer, DHParameterSpec> definedParams;
// cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE)
final static Map<Integer, DHParameterSpec> ffdheParams;
static final Map<Integer, DHParameterSpec> ffdheParams;
static {
String property = AccessController.doPrivileged(
@ -252,8 +250,9 @@ final class PredefinedDHParameterSpecs {
Matcher spacesMatcher = spacesPattern.matcher(property);
property = spacesMatcher.replaceAll("");
if (debugIsOn) {
System.out.println("The Security Property " +
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine(
"The Security Property " +
PROPERTY_NAME + ": " + property);
}
}
@ -267,8 +266,8 @@ final class PredefinedDHParameterSpecs {
String primeModulus = paramsFinder.group(1);
BigInteger p = new BigInteger(primeModulus, 16);
if (!p.isProbablePrime(PRIME_CERTAINTY)) {
if (debugIsOn) {
System.out.println(
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine(
"Prime modulus p in Security Property, " +
PROPERTY_NAME + ", is not a prime: " +
primeModulus);
@ -284,8 +283,8 @@ final class PredefinedDHParameterSpecs {
int primeLen = p.bitLength();
defaultParams.put(primeLen, spec);
}
} else if (debugIsOn) {
System.out.println("Invalid Security Property, " +
} else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
SSLLogger.fine("Invalid Security Property, " +
PROPERTY_NAME + ", definition");
}
}

View file

@ -1,155 +0,0 @@
/*
* Copyright (c) 2002, 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;
import java.util.*;
/**
* A list of ProtocolVersions. Also maintains the list of supported protocols.
* Instances of this class are immutable. Some member variables are final
* and can be accessed directly without method accessors.
*
* @author Andreas Sterbenz
* @since 1.4.1
*/
final class ProtocolList {
// the sorted protocol version list
private final ArrayList<ProtocolVersion> protocols;
private String[] protocolNames;
// the minimum and maximum ProtocolVersions in this list
final ProtocolVersion min, max;
// the format for the hello version to use
final ProtocolVersion helloVersion;
ProtocolList(String[] names) {
this(convert(names));
}
ProtocolList(ArrayList<ProtocolVersion> versions) {
this.protocols = versions;
if ((protocols.size() == 1) &&
protocols.contains(ProtocolVersion.SSL20Hello)) {
throw new IllegalArgumentException("SSLv2Hello cannot be " +
"enabled unless at least one other supported version " +
"is also enabled.");
}
if (protocols.size() != 0) {
Collections.sort(protocols);
min = protocols.get(0);
max = protocols.get(protocols.size() - 1);
helloVersion = protocols.get(0);
} else {
min = ProtocolVersion.NONE;
max = ProtocolVersion.NONE;
helloVersion = ProtocolVersion.NONE;
}
}
private static ArrayList<ProtocolVersion> convert(String[] names) {
if (names == null) {
throw new IllegalArgumentException("Protocols may not be null");
}
ArrayList<ProtocolVersion> versions = new ArrayList<>(names.length);
for (int i = 0; i < names.length; i++ ) {
ProtocolVersion version = ProtocolVersion.valueOf(names[i]);
if (versions.contains(version) == false) {
versions.add(version);
}
}
return versions;
}
/**
* Return whether this list contains the specified protocol version.
* SSLv2Hello is not a real protocol version we support, we always
* return false for it.
*/
boolean contains(ProtocolVersion protocolVersion) {
if (protocolVersion == ProtocolVersion.SSL20Hello) {
return false;
}
return protocols.contains(protocolVersion);
}
/**
* Return a reference to the internal Collection of CipherSuites.
* The Collection MUST NOT be modified.
*/
Collection<ProtocolVersion> collection() {
return protocols;
}
/**
* Select a protocol version from the list.
*
* Return the lower of the protocol version of that suggested by
* the <code>protocolVersion</code> and the highest version of this
* protocol list, or null if no protocol version is available.
*
* The method is used by TLS server to negotiated the protocol
* version between client suggested protocol version in the
* client hello and protocol versions supported by the server.
*/
ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
ProtocolVersion selectedVersion = null;
for (ProtocolVersion pv : protocols) {
if (pv.compareTo(protocolVersion) > 0) {
break; // Safe to break here as this.protocols is sorted,
// and DTLS and SSL/TLS protocols are not mixed.
}
selectedVersion = pv;
}
return selectedVersion;
}
/**
* Return an array with the names of the ProtocolVersions in this list.
*/
synchronized String[] toStringArray() {
if (protocolNames == null) {
protocolNames = new String[protocols.size()];
int i = 0;
for (ProtocolVersion version : protocols) {
protocolNames[i++] = version.name;
}
}
return protocolNames.clone();
}
@Override
public String toString() {
return protocols.toString();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,33 +25,40 @@
package sun.security.ssl;
import java.util.*;
import java.security.CryptoPrimitive;
import sun.security.ssl.CipherSuite.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
/**
* Type safe enum for an SSL/TLS protocol version. Instances are obtained
* using the static factory methods or by referencing the static members
* in this class. Member variables are final and can be accessed without
* accessor methods.
*
* There is only ever one instance per supported protocol version, this
* means == can be used for comparision instead of equals() if desired.
*
* Checks for a particular version number should generally take this form:
*
* <pre>{@code
* if (protocolVersion.v >= ProtocolVersion.TLS10) {
* // TLS 1.0 code goes here
* } else {
* // SSL 3.0 code here
* }
* }</pre>
* Enum for an SSL/TLS/DTLS protocol version.
*
* @author Andreas Sterbenz
* @since 1.4.1
*/
public final class ProtocolVersion implements Comparable<ProtocolVersion> {
enum ProtocolVersion {
// TLS13 (0x0304, "TLSv1.3", false),
TLS13 (SSLConfiguration.tls13VN, "TLSv1.3", false),
TLS12 (0x0303, "TLSv1.2", false),
TLS11 (0x0302, "TLSv1.1", false),
TLS10 (0x0301, "TLSv1", false),
SSL30 (0x0300, "SSLv3", false),
SSL20Hello (0x0002, "SSLv2Hello", false),
DTLS12 (0xFEFD, "DTLSv1.2", true),
DTLS10 (0xFEFF, "DTLSv1.0", true),
// Dummy protocol version value for invalid SSLSession
NONE (-1, "NONE", false);
final int id;
final String name;
final boolean isDTLS;
final byte major;
final byte minor;
final boolean isAvailable;
// The limit of maximum protocol version
static final int LIMIT_MAX_VALUE = 0xFFFF;
@ -59,257 +66,348 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// The limit of minimum protocol version
static final int LIMIT_MIN_VALUE = 0x0000;
// Dummy protocol version value for invalid SSLSession
static final ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
// (D)TLS ProtocolVersion array for TLS 1.0 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_10 = new ProtocolVersion[] {
TLS10, SSL30
};
// If enabled, send/accept SSLv2 hello messages
static final ProtocolVersion SSL20Hello =
new ProtocolVersion(0x0002, "SSLv2Hello");
// (D)TLS ProtocolVersion array for TLS 1.1/DTLS 1.0 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_11 = new ProtocolVersion[] {
TLS11, TLS10, SSL30, DTLS10
};
// SSL 3.0
static final ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
// (D)TLS ProtocolVersion array for (D)TLS 1.2 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10
};
// TLS 1.0
static final ProtocolVersion TLS10 = new ProtocolVersion(0x0301, "TLSv1");
// (D)TLS ProtocolVersion array for (D)TLS 1.3 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_13 = new ProtocolVersion[] {
TLS13, TLS12, TLS11, TLS10, SSL30, DTLS12, DTLS10
};
// TLS 1.1
static final ProtocolVersion TLS11 = new ProtocolVersion(0x0302, "TLSv1.1");
// No protocol version specified.
static final ProtocolVersion[] PROTOCOLS_OF_NONE = new ProtocolVersion[] {
NONE
};
// TLS 1.2
static final ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
// (D)TLS ProtocolVersion array for SSL 3.0.
static final ProtocolVersion[] PROTOCOLS_OF_30 = new ProtocolVersion[] {
SSL30
};
// DTLS 1.0
// {254, 255}, the version value of DTLS 1.0.
static final ProtocolVersion DTLS10 =
new ProtocolVersion(0xFEFF, "DTLSv1.0");
// (D)TLS ProtocolVersion array for TLS 1.1/DTSL 1.0.
static final ProtocolVersion[] PROTOCOLS_OF_11 = new ProtocolVersion[] {
TLS11, DTLS10
};
// No DTLS 1.1, that version number was skipped in order to harmonize
// version numbers with TLS.
// (D)TLS ProtocolVersion array for (D)TLS 1.2.
static final ProtocolVersion[] PROTOCOLS_OF_12 = new ProtocolVersion[] {
TLS12, DTLS12
};
// DTLS 1.2
// {254, 253}, the version value of DTLS 1.2.
static final ProtocolVersion DTLS12 =
new ProtocolVersion(0xFEFD, "DTLSv1.2");
// (D)TLS ProtocolVersion array for (D)TLS 1.3.
static final ProtocolVersion[] PROTOCOLS_OF_13 = new ProtocolVersion[] {
TLS13
};
private static final boolean FIPS = SunJSSE.isFIPS();
// (D)TLS ProtocolVersion array for TSL 1.0/1.1 and DTLS 1.0.
static final ProtocolVersion[] PROTOCOLS_10_11 = new ProtocolVersion[] {
TLS11, TLS10, DTLS10
};
// minimum version we implement (SSL 3.0)
static final ProtocolVersion MIN = FIPS ? TLS10 : SSL30;
// (D)TLS ProtocolVersion array for TSL 1.1/1.2 and DTLS 1.0/1.2.
static final ProtocolVersion[] PROTOCOLS_11_12 = new ProtocolVersion[] {
TLS12, TLS11, DTLS12, DTLS10
};
// maximum version we implement (TLS 1.2)
static final ProtocolVersion MAX = TLS12;
// (D)TLS ProtocolVersion array for TSL 1.2/1.3 and DTLS 1.2/1.3.
static final ProtocolVersion[] PROTOCOLS_12_13 = new ProtocolVersion[] {
TLS13, TLS12, DTLS12
};
// SSL/TLS ProtocolVersion to use by default (TLS 1.2)
static final ProtocolVersion DEFAULT_TLS = TLS12;
// (D)TLS ProtocolVersion array for TSL 1.0/1.1/1.2 and DTLS 1.0/1.2.
static final ProtocolVersion[] PROTOCOLS_10_12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10, DTLS12, DTLS10
};
// DTLS ProtocolVersion to use by default (TLS 1.2)
static final ProtocolVersion DEFAULT_DTLS = DTLS12;
// TLS ProtocolVersion array for TLS 1.2 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS12 = new ProtocolVersion[] {
TLS12, TLS11, TLS10, SSL30
};
// Default version for hello messages (SSLv2Hello)
static final ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
// TLS ProtocolVersion array for TLS 1.1 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS11 = new ProtocolVersion[] {
TLS11, TLS10, SSL30
};
// Available protocols
//
// Including all supported protocols except the disabled ones.
static final Set<ProtocolVersion> availableProtocols;
// TLS ProtocolVersion array for TLS 1.0 and previous versions.
static final ProtocolVersion[] PROTOCOLS_TO_TLS10 = new ProtocolVersion[] {
TLS10, SSL30
};
// version in 16 bit MSB format as it appears in records and
// messages, i.e. 0x0301 for TLS 1.0
public final int v;
// Empty ProtocolVersion array
static final ProtocolVersion[] PROTOCOLS_EMPTY = new ProtocolVersion[0];
// major and minor version
public final byte major, minor;
// name used in JSSE (e.g. TLSv1 for TLS 1.0)
final String name;
// Initialize the available protocols.
static {
Set<ProtocolVersion> protocols = new HashSet<>(7);
ProtocolVersion[] pvs = new ProtocolVersion[] {
SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
EnumSet<CryptoPrimitive> cryptoPrimitives =
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
for (ProtocolVersion p : pvs) {
if (SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
cryptoPrimitives, p.name, null)) {
protocols.add(p);
}
}
availableProtocols =
Collections.<ProtocolVersion>unmodifiableSet(protocols);
}
// private
private ProtocolVersion(int v, String name) {
this.v = v;
private ProtocolVersion(int id, String name, boolean isDTLS) {
this.id = id;
this.name = name;
major = (byte)(v >>> 8);
minor = (byte)(v & 0xFF);
}
this.isDTLS = isDTLS;
this.major = (byte)((id >>> 8) & 0xFF);
this.minor = (byte)(id & 0xFF);
// private
private static ProtocolVersion valueOf(int v) {
if (v == SSL30.v) {
return SSL30;
} else if (v == TLS10.v) {
return TLS10;
} else if (v == TLS11.v) {
return TLS11;
} else if (v == TLS12.v) {
return TLS12;
} else if (v == SSL20Hello.v) {
return SSL20Hello;
} else if (v == DTLS10.v) {
return DTLS10;
} else if (v == DTLS12.v) {
return DTLS12;
} else {
int major = (v >>> 8) & 0xFF;
int minor = v & 0xFF;
return new ProtocolVersion(v, "Unknown-" + major + "." + minor);
}
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. Never throws exceptions.
* Return a ProtocolVersion with the specified major and minor
* version numbers.
*/
public static ProtocolVersion valueOf(int major, int minor) {
return valueOf(((major & 0xFF) << 8) | (minor & 0xFF));
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 for the given name.
* 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 (D)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 "(D)TLS-" + major + "." + minor;
}
/**
* Return name of a (D)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 (D)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 (D)TLS protocol is negotiable.
*
* @exception IllegalArgumentException if name is null or does not
* identify a supported protocol
* Used to filter out SSLv2Hello and protocol numbers less than the
* minimal supported protocol versions.
*/
static ProtocolVersion valueOf(String name) {
if (name == null) {
throw new IllegalArgumentException("Protocol cannot be null");
}
if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
throw new IllegalArgumentException(
"Only TLS 1.0 or later allowed in FIPS mode");
}
if (name.equals(SSL30.name)) {
return SSL30;
} else if (name.equals(TLS10.name)) {
return TLS10;
} else if (name.equals(TLS11.name)) {
return TLS11;
} else if (name.equals(TLS12.name)) {
return TLS12;
} else if (name.equals(SSL20Hello.name)) {
return SSL20Hello;
} else if (name.equals(DTLS10.name)) {
return DTLS10;
} else if (name.equals(DTLS12.name)) {
return DTLS12;
static boolean isNegotiable(
byte major, byte minor, boolean isDTLS, boolean allowSSL20Hello) {
int v = ((major & 0xFF) << 8) | (minor & 0xFF);
if (isDTLS) {
return v <= DTLS10.id;
} else {
throw new IllegalArgumentException(name);
if (v < SSL30.id) {
if (!allowSSL20Hello || (v != SSL20Hello.id)) {
return false;
}
}
return true;
}
}
@Override
public String toString() {
return name;
}
/**
* Compares this object with the specified object for order.
* Get names of a list of ProtocolVersion objects.
*/
@Override
public int compareTo(ProtocolVersion protocolVersion) {
if (maybeDTLSProtocol()) {
if (!protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not DTLS protocol");
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 protocolVersion.v - this.v;
} else {
if (protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not TLS protocol");
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 this.v - protocolVersion.v;
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 (D)TLS 1.2 or newer version.
*/
static boolean useTLS12PlusSpec(String name) {
ProtocolVersion pv = ProtocolVersion.nameOf(name);
if (pv != null && pv != NONE) {
return pv.isDTLS ? (pv.id <= DTLS12.id) : (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;
}
if (isDTLS) {
return that.id - this.id;
} else {
return this.id - that.id;
}
}
/**
* Returns true if a ProtocolVersion represents a DTLS protocol.
* Return true if this ProtocolVersion object is of (D)TLS 1.3 or
* newer version.
*/
boolean isDTLSProtocol() {
return this.v == DTLS12.v || this.v == DTLS10.v;
boolean useTLS13PlusSpec() {
return isDTLS ? (this.id < DTLS12.id) : (this.id >= TLS13.id);
}
/**
* Returns true if a ProtocolVersion may represent a DTLS protocol.
* Return true if this ProtocolVersion object is of (D)TLS 1.2 or
* newer version.
*/
boolean maybeDTLSProtocol() {
return (this.major & 0x80) != 0;
}
boolean useTLS12PlusSpec() {
return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
return isDTLS ? (this.id <= DTLS12.id) : (this.id >= TLS12.id);
}
/**
* Return true if this ProtocolVersion object is of
* TLS 1.1/DTLS 1.0 or newer version.
*/
boolean useTLS11PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
return isDTLS ? true : (this.id >= TLS11.id);
}
/**
* Return true if this ProtocolVersion object is of TLS 1.0 or
* newer version.
*/
boolean useTLS10PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
return isDTLS ? true : (this.id >= TLS10.id);
}
boolean obsoletes(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return true;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.obsoleted);
/**
* Return true if this ProtocolVersion object is of TLS 1.0 or
* newer version.
*/
static boolean useTLS10PlusSpec(int id, boolean isDTLS) {
return isDTLS ? true : (id >= TLS10.id);
}
boolean supports(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return false;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.supported);
/**
* Return true if this ProtocolVersion object is of (D)TLS 1.3 or
* newer version.
*/
static boolean useTLS13PlusSpec(int id, boolean isDTLS) {
return isDTLS ? (id < DTLS12.id) : (id >= TLS13.id);
}
// Map a specified protocol to the corresponding TLS version.
//
// DTLS 1.2 -> TLS 1.2
// DTLS 1.0 -> TLS 1.1
private static ProtocolVersion mapToTLSProtocol(
ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLSProtocol()) {
if (protocolVersion.v == DTLS10.v) {
protocolVersion = TLS11;
} else { // DTLS12
protocolVersion = TLS12;
/**
* 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.isDTLS) {
if (pv.id > suggestedVersion && pv.id < selectedVersion.id) {
selectedVersion = pv;
}
} else {
if (pv.id < suggestedVersion && pv.id > selectedVersion.id) {
selectedVersion = pv;
}
}
}
return protocolVersion;
return selectedVersion;
}
}

View file

@ -0,0 +1,333 @@
/*
* 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
// 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) {
shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"pre_shared_key key extension is offered " +
"without a psk_key_exchange_modes extension");
}
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -23,247 +23,299 @@
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.net.ssl.*;
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
import sun.security.util.KeyUtil;
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.security.util.HexDumpEncoder;
/**
* This is the client key exchange message (CLIENT --> SERVER) used with
* all RSA key exchanges; it holds the RSA-encrypted pre-master secret.
*
* The message is encrypted using PKCS #1 block type 02 encryption with the
* server's public key. The padding and resulting message size is a function
* of this server's public key modulus size, but the pre-master secret is
* always exactly 48 bytes.
*
* Pack of the "ClientKeyExchange" handshake message.
*/
final class RSAClientKeyExchange extends HandshakeMessage {
final class RSAClientKeyExchange {
static final SSLConsumer rsaHandshakeConsumer =
new RSAClientKeyExchangeConsumer();
static final HandshakeProducer rsaHandshakeProducer =
new RSAClientKeyExchangeProducer();
/*
* The following field values were encrypted with the server's public
* key (or temp key from server key exchange msg) and are presented
* here in DECRYPTED form.
/**
* The RSA ClientKeyExchange handshake message.
*/
private ProtocolVersion protocolVersion; // preMaster [0,1]
SecretKey preMaster;
private byte[] encrypted; // same size as public modulus
private static final
class RSAClientKeyExchangeMessage extends HandshakeMessage {
final int protocolVersion;
final boolean useTLS10PlusSpec;
final byte[] encrypted;
/*
* Client randomly creates a pre-master secret and encrypts it
* using the server's RSA public key; only the server can decrypt
* it, using its RSA private key. Result is the same size as the
* server's public key, and uses PKCS #1 block format 02.
*/
@SuppressWarnings("deprecation")
RSAClientKeyExchange(ProtocolVersion protocolVersion,
ProtocolVersion maxVersion,
SecureRandom generator, PublicKey publicKey) throws IOException {
if (publicKey.getAlgorithm().equals("RSA") == false) {
throw new SSLKeyException("Public key not of type RSA: " +
publicKey.getAlgorithm());
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, context.sslContext.isDTLS());
}
this.protocolVersion = protocolVersion;
try {
String s = protocolVersion.useTLS12PlusSpec() ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, protocolVersion.v), generator);
preMaster = kg.generateKey();
RSAClientKeyExchangeMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
cipher.init(Cipher.WRAP_MODE, publicKey, generator);
encrypted = cipher.wrap(preMaster);
} catch (GeneralSecurityException e) {
throw (SSLKeyException)new SSLKeyException
("RSA premaster secret error").initCause(e);
}
}
/*
* 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 (debug != null && Debug.isOn("handshake")) {
System.out.println("Retrieving The Cipher provider name" +
" caused exception " + e.getMessage());
if (m.remaining() < 2) {
context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid RSA ClientKeyExchange message: insufficient data");
}
}
try {
return cipher.toString() + " (provider name not available)";
} catch (Exception e) {
if (debug != null && Debug.isOn("handshake")) {
System.out.println("Retrieving The Cipher name" +
" caused exception " + e.getMessage());
}
}
return "(cipher/provider names not available)";
}
/*
* Server gets the PKCS #1 (block format 02) data, decrypts
* it with its private key.
*/
@SuppressWarnings("deprecation")
RSAClientKeyExchange(ProtocolVersion currentVersion,
ProtocolVersion maxVersion,
SecureRandom generator, HandshakeInStream input,
int messageSize, PrivateKey privateKey) throws IOException {
if (privateKey.getAlgorithm().equals("RSA") == false) {
throw new SSLKeyException("Private key not of type RSA: " +
privateKey.getAlgorithm());
}
if (currentVersion.useTLS10PlusSpec()) {
encrypted = input.getBytes16();
} else {
encrypted = new byte [messageSize];
if (input.read(encrypted) != messageSize) {
throw new SSLProtocolException(
"SSL: read PreMasterSecret: short read");
this.protocolVersion = context.clientHelloVersion;
this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
protocolVersion, context.sslContext.isDTLS());
if (useTLS10PlusSpec) {
this.encrypted = Record.getBytes16(m);
} else { // SSL 3.0
this.encrypted = new byte[m.remaining()];
m.get(encrypted);
}
}
byte[] encoded = null;
try {
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(
maxVersion.v, currentVersion.v),
generator);
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CLIENT_KEY_EXCHANGE;
}
// 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 (debug != null && Debug.isOn("handshake")) {
System.out.println("The Cipher provider "
+ safeProviderName(cipher)
+ " caused exception: " + iue.getMessage());
}
needFailover = true;
}
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(
maxVersion.v, currentVersion.v,
generator, encoded, failed);
preMaster = generatePreMasterSecret(
maxVersion.v, currentVersion.v,
encoded, generator);
@Override
public int messageLength() {
if (useTLS10PlusSpec) {
return encrypted.length + 2;
} else {
// the cipher should have been initialized
preMaster = (SecretKey)cipher.unwrap(encrypted,
"TlsRsaPremasterSecret", Cipher.SECRET_KEY);
return encrypted.length;
}
} catch (InvalidKeyException ibk) {
// the message is too big to process with RSA
throw new SSLException(
"Unable to process PreMasterSecret", ibk);
} catch (Exception e) {
// unlikely to happen, otherwise, must be a provider exception
if (debug != null && Debug.isOn("handshake")) {
System.out.println("RSA premaster secret decryption error:");
e.printStackTrace(System.out);
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
if (useTLS10PlusSpec) {
hos.putBytes16(encrypted);
} else {
hos.write(encrypted);
}
throw new RuntimeException("Could not generate dummy secret", e);
}
@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);
}
}
// generate a premaster secret with the specified version number
@SuppressWarnings("deprecation")
private static SecretKey generatePreMasterSecret(
int clientVersion, int serverVersion,
byte[] encodedSecret, SecureRandom generator) {
if (debug != null && Debug.isOn("handshake")) {
System.out.println("Generating a premaster secret");
/**
* The RSA "ClientKeyExchange" handshake message producer.
*/
private static final
class RSAClientKeyExchangeProducer implements HandshakeProducer {
// Prevent instantiation of this class.
private RSAClientKeyExchangeProducer() {
// blank
}
try {
String s = ((clientVersion >= ProtocolVersion.TLS12.v) ?
"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 (debug != null && Debug.isOn("handshake")) {
System.out.println("RSA premaster secret generation error:");
iae.printStackTrace(System.out);
@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;
}
}
}
throw new RuntimeException("Could not generate premaster secret", iae);
if (rsaCredentials == null && x509Credentials == null) {
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
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) {
chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Cannot generate RSA premaster secret", gse);
return null; // make the compiler happy
}
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
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
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;
}
}
@Override
int messageType() {
return ht_client_key_exchange;
}
@Override
int messageLength() {
if (protocolVersion.useTLS10PlusSpec()) {
return encrypted.length + 2;
} else {
return encrypted.length;
}
}
@Override
void send(HandshakeOutStream s) throws IOException {
if (protocolVersion.useTLS10PlusSpec()) {
s.putBytes16(encrypted);
} else {
s.write(encrypted);
}
}
@Override
void print(PrintStream s) throws IOException {
String version = "version not available/extractable";
byte[] ba = preMaster.getEncoded();
if (ba != null && ba.length >= 2) {
version = ProtocolVersion.valueOf(ba[0], ba[1]).name;
/**
* The RSA "ClientKeyExchange" handshake message consumer.
*/
private static final
class RSAClientKeyExchangeConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private RSAClientKeyExchangeConsumer() {
// blank
}
s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + version);
@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
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
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) {
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
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
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) {
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,340 @@
/*
* 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.security.util.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) {
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) {
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)) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid signature of RSA ServerKeyExchange message");
}
} catch (NoSuchAlgorithmException |
InvalidKeyException | SignatureException ex) {
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
shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"No RSA certificate negotiated for server key exchange");
} else if (!"RSA".equals(
x509Possession.popPrivateKey.getAlgorithm())) {
// unlikely
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) {
chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
"Could not generate RSAPublicKey", gse);
return; // make the compiler happy
}
if (!chc.algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
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

@ -23,7 +23,6 @@
* questions.
*/
package sun.security.ssl;
import java.security.*;
@ -46,71 +45,38 @@ import java.security.spec.AlgorithmParameterSpec;
* getInternalInstance() method.
*
* This class is not thread safe.
*
*/
public final class RSASignature extends SignatureSpi {
private final Signature rawRsa;
private MessageDigest md5, sha;
// flag indicating if the MessageDigests are in reset state
private boolean isReset;
private final MessageDigest mdMD5;
private final MessageDigest mdSHA;
public RSASignature() throws NoSuchAlgorithmException {
super();
rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
isReset = true;
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.
* 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);
}
/**
* Get an internal implementation for the RSA signature. Used for RSA
* client authentication, which needs the ability to set the digests
* to externally provided values via the setHashes() method.
*/
static Signature getInternalInstance()
throws NoSuchAlgorithmException, NoSuchProviderException {
return Signature.getInstance(JsseJce.SIGNATURE_SSLRSA, "SunJSSE");
}
/**
* Set the MD5 and SHA hashes to the provided objects.
*/
@SuppressWarnings("deprecation")
static void setHashes(Signature sig, MessageDigest md5, MessageDigest sha) {
sig.setParameter("hashes", new MessageDigest[] {md5, sha});
}
/**
* Reset the MessageDigests unless they are already reset.
*/
private void reset() {
if (isReset == false) {
md5.reset();
sha.reset();
isReset = true;
}
}
private static void checkNull(Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("Key must not be null");
}
}
@Override
protected void engineInitVerify(PublicKey publicKey)
throws InvalidKeyException {
checkNull(publicKey);
reset();
if (publicKey == null) {
throw new InvalidKeyException("Public key must not be null");
}
mdMD5.reset();
mdSHA.reset();
rawRsa.initVerify(publicKey);
}
@ -123,42 +89,31 @@ public final class RSASignature extends SignatureSpi {
@Override
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
throws InvalidKeyException {
checkNull(privateKey);
reset();
rawRsa.initSign(privateKey, random);
}
// lazily initialize the MessageDigests
private void initDigests() {
if (md5 == null) {
md5 = JsseJce.getMD5();
sha = JsseJce.getSHA();
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) {
initDigests();
isReset = false;
md5.update(b);
sha.update(b);
mdMD5.update(b);
mdSHA.update(b);
}
@Override
protected void engineUpdate(byte[] b, int off, int len) {
initDigests();
isReset = false;
md5.update(b, off, len);
sha.update(b, off, len);
mdMD5.update(b, off, len);
mdSHA.update(b, off, len);
}
private byte[] getDigest() throws SignatureException {
try {
initDigests();
byte[] data = new byte[36];
md5.digest(data, 0, 16);
sha.digest(data, 16, 20);
isReset = true;
mdMD5.digest(data, 0, 16);
mdSHA.digest(data, 16, 20);
return data;
} catch (DigestException e) {
// should never occur
@ -186,19 +141,9 @@ public final class RSASignature extends SignatureSpi {
@Override
@SuppressWarnings("deprecation")
protected void engineSetParameter(String param, Object value)
throws InvalidParameterException {
if (param.equals("hashes") == false) {
throw new InvalidParameterException
("Parameter not supported: " + param);
}
if (value instanceof MessageDigest[] == false) {
throw new InvalidParameterException
("value must be MessageDigest[]");
}
MessageDigest[] digests = (MessageDigest[])value;
md5 = digests[0];
sha = digests[1];
protected void engineSetParameter(String param,
Object value) throws InvalidParameterException {
throw new InvalidParameterException("Parameters not supported");
}
@Override
@ -211,8 +156,8 @@ public final class RSASignature extends SignatureSpi {
@Override
@SuppressWarnings("deprecation")
protected Object engineGetParameter(String param)
throws InvalidParameterException {
protected Object engineGetParameter(
String param) throws InvalidParameterException {
throw new InvalidParameterException("Parameters not supported");
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -23,11 +23,12 @@
* questions.
*/
package sun.security.ssl;
import java.io.*;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Arrays;
/*
* RandomCookie ... SSL hands standard format random cookies (nonces)
@ -37,33 +38,102 @@ import java.security.SecureRandom;
* @author David Brownell
*/
final class RandomCookie {
final byte[] randomBytes = new byte[32]; // exactly 32 bytes
byte[] random_bytes; // 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) {
random_bytes = new byte[32];
generator.nextBytes(random_bytes);
generator.nextBytes(randomBytes);
}
RandomCookie(HandshakeInStream m) throws IOException {
random_bytes = new byte[32];
m.read(random_bytes, 0, 32);
}
// Used for server random generation with version downgrade protection.
RandomCookie(HandshakeContext context) {
SecureRandom generator = context.sslContext.getSecureRandom();
generator.nextBytes(randomBytes);
void send(HandshakeOutStream out) throws IOException {
out.write(random_bytes, 0, 32);
}
void print(PrintStream s) {
s.print("random_bytes = {");
for (int i = 0; i < 32; i++) {
int k = random_bytes[i] & 0xFF;
if (i != 0) {
s.print(' ');
// 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;
}
s.print(Utilities.hexDigits[k >>> 4]);
s.print(Utilities.hexDigits[k & 0xf]);
}
s.println("}");
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 Arrays.equals(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 Arrays.equals(randomBytes, 24, 32, t12Protection, 0, 8);
}
private boolean isT11Downgrade() {
return Arrays.equals(randomBytes, 24, 32, t11Protection, 0, 8);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,27 +25,19 @@
package sun.security.ssl;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
/**
* SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream. This is
* the base interface, which defines common information and interfaces
* SSL/(D)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 {
/*
* There are four record types, which are part of the interface
* to this level (along with the maximum record size).
*
* enum { change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255) } ContentType;
*/
static final byte ct_change_cipher_spec = 20;
static final byte ct_alert = 21;
static final byte ct_handshake = 22;
static final byte ct_application_data = 23;
static final int maxMacSize = 48; // the max supported MAC or
// AEAD tag size
static final int maxDataSize = 16384; // 2^14 bytes of data
@ -59,35 +51,146 @@ interface Record {
* System property to enable/disable CBC protection in SSL3/TLS1.
*/
static final boolean enableCBCProtection =
Debug.getBooleanProperty("jsse.enableCBCProtection", true);
Utilities.getBooleanProperty("jsse.enableCBCProtection", true);
/*
* The overflow values of integers of 8, 16 and 24 bits.
*/
static final int OVERFLOW_OF_INT08 = (1 << 8);
static final int OVERFLOW_OF_INT16 = (1 << 16);
static final int OVERFLOW_OF_INT24 = (1 << 24);
static final int OVERFLOW_OF_INT08 = (0x01 << 8);
static final int OVERFLOW_OF_INT16 = (0x01 << 16);
static final int OVERFLOW_OF_INT24 = (0x01 << 24);
/**
* Return a description for the given content type.
/*
* Read 8, 16, 24, and 32 bit integer data types, encoded
* in standard big-endian form.
*/
static String contentName(byte contentType) {
switch (contentType) {
case ct_change_cipher_spec:
return "Change Cipher Spec";
case ct_alert:
return "Alert";
case ct_handshake:
return "Handshake";
case ct_application_data:
return "Application Data";
default:
return "contentType = " + contentType;
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 boolean isValidContentType(byte contentType) {
return (contentType == 20) || (contentType == 21) ||
(contentType == 22) || (contentType == 23);
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,558 @@
/*
* 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.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;
/**
* 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.
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"insecure renegotiation is not allowed");
}
}
return null;
}
}
/**
* 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) {
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
if (!shc.conContext.isNegotiated) {
// initial handshaking.
if (spec.renegotiatedConnection.length != 0) {
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.
shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"The renegotiation_info is present in a insecure " +
"renegotiation");
} else {
// verify the client_verify_data value
if (!Arrays.equals(shc.conContext.clientVerifyData,
spec.renegotiatedConnection)) {
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) {
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.
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");
}
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)) {
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) {
chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
return; // fatal() always throws, make the compiler happy.
}
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) {
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) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"invalid renegotiated_connection length (" +
spec.renegotiatedConnection.length + ")");
}
byte[] cvd = chc.conContext.clientVerifyData;
if (!Arrays.equals(spec.renegotiatedConnection,
0, cvd.length, cvd, 0, cvd.length)) {
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Invalid renegotiation_info in ServerHello: " +
"unmatched client_verify_data value");
}
byte[] svd = chc.conContext.serverVerifyData;
if (!Arrays.equals(spec.renegotiatedConnection,
cvd.length, infoLen, svd, 0, svd.length)) {
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)) {
chc.conContext.fatal(Alert.INTERNAL_ERROR,
"Missing renegotiation_info and SCSV detected in " +
"ClientHello");
}
if (!chc.conContext.isNegotiated) {
// initial handshaking.
if (!HandshakeContext.allowLegacyHelloMessages) {
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.
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");
}
chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Unsafe renegotiation is not allowed");
}
}
}
}
}

View file

@ -1,115 +0,0 @@
/*
* Copyright (c) 2006, 2012, 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.net.ssl.SSLProtocolException;
/*
* For secure renegotiation, RFC5746 defines a new TLS extension,
* "renegotiation_info" (with extension type 0xff01), which contains a
* cryptographic binding to the enclosing TLS connection (if any) for
* which the renegotiation is being performed. The "extension data"
* field of this extension contains a "RenegotiationInfo" structure:
*
* struct {
* opaque renegotiated_connection<0..255>;
* } RenegotiationInfo;
*/
final class RenegotiationInfoExtension extends HelloExtension {
private final byte[] renegotiated_connection;
RenegotiationInfoExtension(byte[] clientVerifyData,
byte[] serverVerifyData) {
super(ExtensionType.EXT_RENEGOTIATION_INFO);
if (clientVerifyData.length != 0) {
renegotiated_connection =
new byte[clientVerifyData.length + serverVerifyData.length];
System.arraycopy(clientVerifyData, 0, renegotiated_connection,
0, clientVerifyData.length);
if (serverVerifyData.length != 0) {
System.arraycopy(serverVerifyData, 0, renegotiated_connection,
clientVerifyData.length, serverVerifyData.length);
}
} else {
// ignore both the client and server verify data.
renegotiated_connection = new byte[0];
}
}
RenegotiationInfoExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_RENEGOTIATION_INFO);
// check the extension length
if (len < 1) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
int renegoInfoDataLen = s.getInt8();
if (renegoInfoDataLen + 1 != len) { // + 1 = the byte we just read
throw new SSLProtocolException("Invalid " + type + " extension");
}
renegotiated_connection = new byte[renegoInfoDataLen];
if (renegoInfoDataLen != 0) {
s.read(renegotiated_connection, 0, renegoInfoDataLen);
}
}
// Length of the encoded extension, including the type and length fields
@Override
int length() {
return 5 + renegotiated_connection.length;
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(renegotiated_connection.length + 1);
s.putBytes8(renegotiated_connection);
}
boolean isEmpty() {
return renegotiated_connection.length == 0;
}
byte[] getRenegotiatedConnection() {
return renegotiated_connection;
}
@Override
public String toString() {
return "Extension " + type + ", renegotiated_connection: " +
(renegotiated_connection.length == 0 ? "<empty>" :
Debug.toString(renegotiated_connection));
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,18 +26,13 @@
package sun.security.ssl;
import java.security.AlgorithmConstraints;
import java.security.CryptoPrimitive;
import java.security.AlgorithmParameters;
import javax.net.ssl.*;
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.*;
import sun.security.ssl.CipherSuite.*;
/**
* Algorithm constraints for disabled algorithms property
@ -55,10 +50,10 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS,
new SSLAlgorithmDecomposer(true));
private AlgorithmConstraints userAlgConstraints = null;
private AlgorithmConstraints peerAlgConstraints = null;
private final AlgorithmConstraints userSpecifiedConstraints;
private final AlgorithmConstraints peerSpecifiedConstraints;
private boolean enabledX509DisabledAlgConstraints = true;
private final boolean enabledX509DisabledAlgConstraints;
// the default algorithm constraints
static final AlgorithmConstraints DEFAULT =
@ -68,60 +63,86 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
static final AlgorithmConstraints DEFAULT_SSL_ONLY =
new SSLAlgorithmConstraints((SSLSocket)null, false);
SSLAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) {
userAlgConstraints = algorithmConstraints;
SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints) {
this.userSpecifiedConstraints = userSpecifiedConstraints;
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = true;
}
SSLAlgorithmConstraints(SSLSocket socket,
boolean withDefaultCertPathConstraints) {
AlgorithmConstraints configuredConstraints = null;
if (socket != null) {
userAlgConstraints =
socket.getSSLParameters().getAlgorithmConstraints();
}
if (!withDefaultCertPathConstraints) {
enabledX509DisabledAlgConstraints = false;
HandshakeContext hc =
((SSLSocketImpl)socket).conContext.handshakeContext;
if (hc != null) {
configuredConstraints = hc.sslConfig.algorithmConstraints;
} else {
configuredConstraints = null;
}
}
this.userSpecifiedConstraints = configuredConstraints;
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLEngine engine,
boolean withDefaultCertPathConstraints) {
AlgorithmConstraints configuredConstraints = null;
if (engine != null) {
userAlgConstraints =
engine.getSSLParameters().getAlgorithmConstraints();
}
if (!withDefaultCertPathConstraints) {
enabledX509DisabledAlgConstraints = false;
HandshakeContext hc =
((SSLEngineImpl)engine).conContext.handshakeContext;
if (hc != null) {
configuredConstraints = hc.sslConfig.algorithmConstraints;
} else {
configuredConstraints = null;
}
}
this.userSpecifiedConstraints = configuredConstraints;
this.peerSpecifiedConstraints = null;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms,
boolean withDefaultCertPathConstraints) {
AlgorithmConstraints configuredConstraints = null;
AlgorithmConstraints negotiatedConstraints = null;
if (socket != null) {
userAlgConstraints =
socket.getSSLParameters().getAlgorithmConstraints();
peerAlgConstraints =
HandshakeContext hc =
((SSLSocketImpl)socket).conContext.handshakeContext;
if (hc != null) {
configuredConstraints = hc.sslConfig.algorithmConstraints;
} else {
configuredConstraints = null;
}
negotiatedConstraints =
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
}
if (!withDefaultCertPathConstraints) {
enabledX509DisabledAlgConstraints = false;
}
this.userSpecifiedConstraints = configuredConstraints;
this.peerSpecifiedConstraints = negotiatedConstraints;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms,
boolean withDefaultCertPathConstraints) {
AlgorithmConstraints configuredConstraints = null;
AlgorithmConstraints negotiatedConstraints = null;
if (engine != null) {
userAlgConstraints =
engine.getSSLParameters().getAlgorithmConstraints();
peerAlgConstraints =
HandshakeContext hc =
((SSLEngineImpl)engine).conContext.handshakeContext;
if (hc != null) {
configuredConstraints = hc.sslConfig.algorithmConstraints;
} else {
configuredConstraints = null;
}
negotiatedConstraints =
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
}
if (!withDefaultCertPathConstraints) {
enabledX509DisabledAlgConstraints = false;
}
this.userSpecifiedConstraints = configuredConstraints;
this.peerSpecifiedConstraints = negotiatedConstraints;
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
}
@Override
@ -130,13 +151,13 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
boolean permitted = true;
if (peerAlgConstraints != null) {
permitted = peerAlgConstraints.permits(
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(
primitives, algorithm, parameters);
}
if (permitted && userAlgConstraints != null) {
permitted = userAlgConstraints.permits(
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(
primitives, algorithm, parameters);
}
@ -158,12 +179,12 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
boolean permitted = true;
if (peerAlgConstraints != null) {
permitted = peerAlgConstraints.permits(primitives, key);
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(primitives, key);
}
if (permitted && userAlgConstraints != null) {
permitted = userAlgConstraints.permits(primitives, key);
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(primitives, key);
}
if (permitted) {
@ -183,13 +204,13 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
boolean permitted = true;
if (peerAlgConstraints != null) {
permitted = peerAlgConstraints.permits(
if (peerSpecifiedConstraints != null) {
permitted = peerSpecifiedConstraints.permits(
primitives, algorithm, key, parameters);
}
if (permitted && userAlgConstraints != null) {
permitted = userAlgConstraints.permits(
if (permitted && userSpecifiedConstraints != null) {
permitted = userSpecifiedConstraints.permits(
primitives, algorithm, key, parameters);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -27,9 +27,12 @@ package sun.security.ssl;
import java.util.HashSet;
import java.util.Set;
import sun.security.util.AlgorithmDecomposer;
import static sun.security.ssl.CipherSuite.*;
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.
@ -126,18 +129,13 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
}
break;
default:
if (ClientKeyExchangeService.find(keyExchange.name) != null) {
if (!onlyX509) {
components.add(keyExchange.name);
}
}
// otherwise ignore
}
return components;
}
private Set<String> decomposes(CipherSuite.BulkCipher bulkCipher) {
private Set<String> decomposes(SSLCipher bulkCipher) {
Set<String> components = new HashSet<>();
if (bulkCipher.transformation != null) {
@ -185,7 +183,7 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
}
private Set<String> decomposes(CipherSuite.MacAlg macAlg,
BulkCipher cipher) {
SSLCipher cipher) {
Set<String> components = new HashSet<>();
if (macAlg == CipherSuite.MacAlg.M_NULL
@ -211,8 +209,26 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
return components;
}
private Set<String> decompose(KeyExchange keyExchange, BulkCipher cipher,
MacAlg macAlg) {
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) {
@ -233,6 +249,10 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
components.addAll(decomposes(macAlg, cipher));
}
if (hashAlg != null) {
components.addAll(decomposes(hashAlg));
}
return components;
}
@ -241,18 +261,19 @@ class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) {
CipherSuite cipherSuite = null;
try {
cipherSuite = CipherSuite.valueOf(algorithm);
cipherSuite = CipherSuite.nameOf(algorithm);
} catch (IllegalArgumentException iae) {
// ignore: unknown or unsupported ciphersuite
}
if (cipherSuite != null) {
return decompose(cipherSuite.keyExchange, cipherSuite.cipher,
cipherSuite.macAlg);
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
}

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