# HG changeset patch # User xuelei # Date 1529959299 25200 # Node ID 68fa3d4026ea172f214587925ee164d3c4495da8 # Parent 356eaea05bf056b32d5944ce7e60880586861961 8196584: TLS 1.3 Implementation Reviewed-by: ascarpino, coffeys, dfuchs, jjiang, jnimeh, mullan, rhalade, ssahoo, valeriep, weijun, wetmore, xuelei Contributed-by: Adam Petcher , Amanda Jiang , Anthony Scarpino , Bradford Wetmore , Jamil Nimeh , John Jiang , Rajan Halade , Sibabrata Sahoo , Valerie Peng , Weijun Wang , Xuelei Fan diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java --- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Mon Jun 25 13:41:39 2018 -0700 @@ -95,7 +95,7 @@ premasterMajor = premaster[0] & 0xff; premasterMinor = premaster[1] & 0xff; } else { - // DH, KRB5, others + // DH, others premasterMajor = -1; premasterMinor = -1; } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java --- a/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java Mon Jun 25 13:41:39 2018 -0700 @@ -113,27 +113,19 @@ * 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; } @@ -144,23 +136,6 @@ } /* - * 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. * * Note this code is essentially a subset of the hostname extraction diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/module-info.java --- a/src/java.base/share/classes/module-info.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/module-info.java Mon Jun 25 13:41:39 2018 -0700 @@ -360,7 +360,6 @@ // 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; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java Mon Jun 25 13:41:39 2018 -0700 @@ -608,26 +608,17 @@ 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; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ALPNExtension.java --- a/src/java.base/share/classes/sun/security/ssl/ALPNExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 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 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(""); - } else { - for (String protocolName : protocolNames) { - sb.append("[" + protocolName + "]"); - } - } - - return "Extension " + type + - ", protocol names: " + sb; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Alert.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Alerts.java --- a/src/java.base/share/classes/sun/security/ssl/Alerts.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 ""; - } - } - - 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; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/AlpnExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/AlpnExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 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 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 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 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 = ""; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/AppInputStream.java --- a/src/java.base/share/classes/sun/security/ssl/AppInputStream.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/AppOutputStream.java --- a/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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() -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Authenticator.java --- a/src/java.base/share/classes/sun/security/ssl/Authenticator.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/Authenticator.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,92 +25,78 @@ 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; - - // 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; + protected final byte[] block; // at least 8 bytes for sequence number - /** - * 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; + static Authenticator valueOf(ProtocolVersion protocolVersion) { + if (protocolVersion.isDTLS) { + if (protocolVersion.useTLS13PlusSpec()) { + return new DTLS13Authenticator(protocolVersion); + } else { + return new DTLS10Authenticator(protocolVersion); + } + } else { + if (protocolVersion.useTLS13PlusSpec()) { + return new TLS13Authenticator(protocolVersion); + } else if (protocolVersion.useTLS10PlusSpec()) { + return new TLS10Authenticator(protocolVersion); + } else { + return new SSL30Authenticator(); + } + } + } - this.isDTLS = true; - } else if (protocolVersion.v >= ProtocolVersion.TLS10.v) { - block = new byte[BLOCK_SIZE_TLS]; - block[9] = protocolVersion.major; - block[10] = protocolVersion.minor; + @SuppressWarnings({"unchecked"}) + static 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)); + } + } + } - this.isDTLS = false; - } else { - block = new byte[BLOCK_SIZE_SSL]; + static Authenticator nullTlsMac() { + return new SSLNullMac(); + } - this.isDTLS = false; - } + static Authenticator nullDtlsMac() { + return new DTLSNullMac(); } /** @@ -122,25 +108,7 @@ * * @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 @@ * * @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 @@ /** * 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 @@ /** * 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 @@ * * @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"); + } + + private static class SSLAuthenticator extends Authenticator { + private SSLAuthenticator(byte[] block) { + super(block); + } + + @Override + boolean seqNumOverflow() { + /* + * Conservatively, we don't allow more records to be generated + * when there are only 2^8 sequence numbers left. + */ + return (block.length != 0 && + block[0] == (byte)0xFF && block[1] == (byte)0xFF && + block[2] == (byte)0xFF && block[3] == (byte)0xFF && + block[4] == (byte)0xFF && block[5] == (byte)0xFF && + block[6] == (byte)0xFF); + } + + @Override + boolean seqNumIsHuge() { + return (block.length != 0 && + block[0] == (byte)0xFF && block[1] == (byte)0xFF && + block[2] == (byte)0xFF && block[3] == (byte)0xFF); + } + } + + // For null MAC only. + private static class SSLNullAuthenticator extends SSLAuthenticator { + private SSLNullAuthenticator() { + super(new byte[8]); + } + } + + // For SSL 3.0 + private static class SSL30Authenticator extends SSLAuthenticator { + // Block size of SSL v3.0: + // sequence number + record type + + record length + private static final int BLOCK_SIZE = 11; // 8 + 1 + 2 - byte[] copy = block.clone(); - if (sequence != null) { - if (sequence.length != 8) { - throw new RuntimeException( - "Insufficient explicit sequence number bytes"); + 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(); } - System.arraycopy(sequence, 0, copy, 0, sequence.length); - } // Otherwise, use the implicit sequence number. + ad[8] = type; + ad[11] = (byte)(length >> 8); + ad[12] = (byte)(length); + + return ad; + } + } + + // For TLS 1.3 + private static final class TLS13Authenticator extends SSLAuthenticator { + // Block size of TLS v1.3: + // record type + protocol version + record length + sequence number + private static final int BLOCK_SIZE = 13; // 1 + 2 + 2 + 8 + + private TLS13Authenticator(ProtocolVersion protocolVersion) { + super(new byte[BLOCK_SIZE]); + block[9] = ProtocolVersion.TLS12.major; + block[10] = ProtocolVersion.TLS12.minor; + } + + @Override + byte[] acquireAuthenticationBytes( + byte type, int length, byte[] sequence) { + byte[] ad = Arrays.copyOfRange(block, 8, 13); + + // Increase the implicit sequence number in the block array. + increaseSequenceNumber(); + + ad[0] = type; + ad[3] = (byte)(length >> 8); + ad[4] = (byte)(length & 0xFF); + + return ad; + } + } + + private static class DTLSAuthenticator extends Authenticator { + private DTLSAuthenticator(byte[] block) { + super(block); + } - if (block.length != 0) { - copy[8] = type; + @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); + } + } - copy[copy.length - 2] = (byte)(length >> 8); - copy[copy.length - 1] = (byte)(length); + // 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; - if (sequence == null || sequence.length != 0) { + 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; } - return copy; + @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) { diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java --- a/src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/BaseSSLSocketImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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.net.*; import java.nio.channels.SocketChannel; -import java.net.*; 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 @@ "com.sun.net.ssl.requireCloseNotify"; static final boolean requireCloseNotify = - Debug.getBooleanProperty(PROP_NAME, false); + Utilities.getBooleanProperty(PROP_NAME, false); // // MISC SOCKET METHODS diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java --- a/src/java.base/share/classes/sun/security/ssl/ByteBufferInputStream.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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. - *

- * 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 n 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 - * mark 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 mark and - * reset methods. - */ - @Override - public boolean markSupported() { - return false; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 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 shemes = + SignatureScheme.getSupportedAlgorithms( + chc.algorithmConstraints, chc.negotiatedProtocol, + spec.signatureSchemes); + chc.peerRequestedCertSignSchemes = shemes; + chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1219 @@ +/* + * 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.io.ByteArrayInputStream; +import java.nio.ByteBuffer; +import java.security.cert.Extension; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import javax.net.ssl.SSLProtocolException; +import sun.security.provider.certpath.OCSPResponse; +import sun.security.provider.certpath.ResponderId; +import static sun.security.ssl.SSLExtension.CH_STATUS_REQUEST; +import static sun.security.ssl.SSLExtension.CH_STATUS_REQUEST_V2; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import static sun.security.ssl.SSLExtension.SH_STATUS_REQUEST; +import static sun.security.ssl.SSLExtension.SH_STATUS_REQUEST_V2; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.DerInputStream; +import sun.security.util.DerValue; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of "status_request" and "status_request_v2" extensions. + */ +final class CertStatusExtension { + static final HandshakeProducer chNetworkProducer = + new CHCertStatusReqProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHCertStatusReqConsumer(); + + static final HandshakeProducer shNetworkProducer = + new SHCertStatusReqProducer(); + static final ExtensionConsumer shOnLoadConsumer = + new SHCertStatusReqConsumer(); + + static final HandshakeProducer ctNetworkProducer = + new CTCertStatusResponseProducer(); + static final ExtensionConsumer ctOnLoadConsumer = + new CTCertStatusResponseConsumer(); + + static final SSLStringizer certStatusReqStringizer = + new CertStatusRequestStringizer(); + + static final HandshakeProducer chV2NetworkProducer = + new CHCertStatusReqV2Producer(); + static final ExtensionConsumer chV2OnLoadConsumer = + new CHCertStatusReqV2Consumer(); + + static final HandshakeProducer shV2NetworkProducer = + new SHCertStatusReqV2Producer(); + static final ExtensionConsumer shV2OnLoadConsumer = + new SHCertStatusReqV2Consumer(); + + static final SSLStringizer certStatusReqV2Stringizer = + new CertStatusRequestsStringizer(); + + static final SSLStringizer certStatusRespStringizer = + new CertStatusRespStringizer(); + + /** + * The "status_request" extension. + * + * 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>; + */ + static final class CertStatusRequestSpec implements SSLExtensionSpec { + static final CertStatusRequestSpec DEFAULT = + new CertStatusRequestSpec(OCSPStatusRequest.EMPTY_OCSP); + + final CertStatusRequest statusRequest; + + private CertStatusRequestSpec(CertStatusRequest statusRequest) { + this.statusRequest = statusRequest; + } + + private CertStatusRequestSpec(ByteBuffer buffer) throws IOException { + // Is it a empty extension_data? + if (buffer.remaining() == 0) { + // server response + this.statusRequest = null; + return; + } + + if (buffer.remaining() < 1) { + throw new SSLProtocolException( + "Invalid status_request extension: insufficient data"); + } + + byte statusType = (byte)Record.getInt8(buffer); + byte[] encoded = new byte[buffer.remaining()]; + if (encoded.length != 0) { + buffer.get(encoded); + } + if (statusType == CertStatusRequestType.OCSP.id) { + this.statusRequest = new OCSPStatusRequest(statusType, encoded); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.info( + "Unknown certificate status request " + + "(status type: " + statusType + ")"); + } + + this.statusRequest = new CertStatusRequest(statusType, encoded); + } + } + + @Override + public String toString() { + return statusRequest == null ? + "" : statusRequest.toString(); + } + } + + /** + * Defines the CertificateStatus response structure as outlined in + * RFC 6066. This will contain a status response type, plus a single, + * non-empty OCSP response in DER-encoded form. + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * } response; + * } CertificateStatus; + */ + static final class CertStatusResponseSpec implements SSLExtensionSpec { + final CertStatusResponse statusResponse; + + private CertStatusResponseSpec(CertStatusResponse resp) { + this.statusResponse = resp; + } + + private CertStatusResponseSpec(ByteBuffer buffer) throws IOException { + if (buffer.remaining() < 2) { + throw new SSLProtocolException( + "Invalid status_request extension: insufficient data"); + } + + // Get the status type (1 byte) and response data (vector) + byte type = (byte)Record.getInt8(buffer); + byte[] respData = Record.getBytes24(buffer); + + // Create the CertStatusResponse based on the type + if (type == CertStatusRequestType.OCSP.id) { + this.statusResponse = new OCSPStatusResponse(type, respData); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.info( + "Unknown certificate status response " + + "(status type: " + type + ")"); + } + + this.statusResponse = new CertStatusResponse(type, respData); + } + } + + @Override + public String toString() { + return statusResponse == null ? + "" : statusResponse.toString(); + } + } + + private static final + class CertStatusRequestStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new CertStatusRequestSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + private static final + class CertStatusRespStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new CertStatusResponseSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + static enum CertStatusRequestType { + OCSP ((byte)0x01, "ocsp"), // RFC 6066/6961 + OCSP_MULTI ((byte)0x02, "ocsp_multi"); // RFC 6961 + + final byte id; + final String name; + + private CertStatusRequestType(byte id, String name) { + this.id = id; + this.name = name; + } + + /** + * Returns the enum constant of the specified id (see RFC 6066). + */ + static CertStatusRequestType valueOf(byte id) { + for (CertStatusRequestType srt : CertStatusRequestType.values()) { + if (srt.id == id) { + return srt; + } + } + + return null; + } + + static String nameOf(byte id) { + for (CertStatusRequestType srt : CertStatusRequestType.values()) { + if (srt.id == id) { + return srt.name; + } + } + + return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")"; + } + } + + static class CertStatusRequest { + final byte statusType; + final byte[] encodedRequest; + + protected CertStatusRequest(byte statusType, byte[] encodedRequest) { + this.statusType = statusType; + this.encodedRequest = encodedRequest; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"certificate status type\": {0}\n" + + "\"encoded certificate status\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + String encoded = hexEncoder.encodeBuffer(encodedRequest); + + Object[] messageFields = { + CertStatusRequestType.nameOf(statusType), + Utilities.indent(encoded) + }; + + return messageFormat.format(messageFields); + } + } + + /* + * 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; + */ + static final class OCSPStatusRequest extends CertStatusRequest { + static final OCSPStatusRequest EMPTY_OCSP; + static final OCSPStatusRequest EMPTY_OCSP_MULTI; + + final List responderIds; + final List extensions; + private final int ridListLen; + private final int extListLen; + + static { + OCSPStatusRequest ocspReq = null; + OCSPStatusRequest multiReq = null; + + try { + ocspReq = new OCSPStatusRequest( + CertStatusRequestType.OCSP.id, + new byte[] {0x00, 0x00, 0x00, 0x00}); + multiReq = new OCSPStatusRequest( + CertStatusRequestType.OCSP_MULTI.id, + new byte[] {0x00, 0x00, 0x00, 0x00}); + } catch (IOException ioe) { + // unlikely + } + + EMPTY_OCSP = ocspReq; + EMPTY_OCSP_MULTI = multiReq; + } + + private OCSPStatusRequest(byte statusType, + byte[] encoded) throws IOException { + super(statusType, encoded); + + if (encoded == null || encoded.length < 4) { + // 2: length of responder_id_list + // +2: length of request_extensions + throw new SSLProtocolException( + "Invalid OCSP status request: insufficient data"); + } + + List rids = new ArrayList<>(); + List exts = new ArrayList<>(); + ByteBuffer m = ByteBuffer.wrap(encoded); + + this.ridListLen = Record.getInt16(m); + if (m.remaining() < (ridListLen + 2)) { + throw new SSLProtocolException( + "Invalid OCSP status request: insufficient data"); + } + + int ridListBytesRemaining = ridListLen; + while (ridListBytesRemaining >= 2) { // 2: length of responder_id + byte[] ridBytes = Record.getBytes16(m); + try { + rids.add(new ResponderId(ridBytes)); + } catch (IOException ioe) { + throw new SSLProtocolException( + "Invalid OCSP status request: invalid responder ID"); + } + ridListBytesRemaining -= ridBytes.length + 2; + } + + if (ridListBytesRemaining != 0) { + throw new SSLProtocolException( + "Invalid OCSP status request: incomplete data"); + } + + byte[] extListBytes = Record.getBytes16(m); + this.extListLen = extListBytes.length; + if (extListLen > 0) { + try { + DerInputStream dis = new DerInputStream(extListBytes); + DerValue[] extSeqContents = + dis.getSequence(extListBytes.length); + for (DerValue extDerVal : extSeqContents) { + exts.add(new sun.security.x509.Extension(extDerVal)); + } + } catch (IOException ioe) { + throw new SSLProtocolException( + "Invalid OCSP status request: invalid extension"); + } + } + + this.responderIds = rids; + this.extensions = exts; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"certificate status type\": {0}\n" + + "\"OCSP status request\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + MessageFormat requestFormat = new MessageFormat( + "\"responder_id\": {0}\n" + + "\"request extensions\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + String ridStr = ""; + if (!responderIds.isEmpty()) { + ridStr = responderIds.toString(); + } + + String extsStr = ""; + if (!extensions.isEmpty()) { + StringBuilder extBuilder = new StringBuilder(512); + boolean isFirst = true; + for (Extension ext : this.extensions) { + if (isFirst) { + isFirst = false; + } else { + extBuilder.append(",\n"); + } + extBuilder.append( + "{\n" + Utilities.indent(ext.toString()) + "}"); + } + + extsStr = extBuilder.toString(); + } + + Object[] requestFields = { + ridStr, + Utilities.indent(extsStr) + }; + String ocspStatusRequest = requestFormat.format(requestFields); + + Object[] messageFields = { + CertStatusRequestType.nameOf(statusType), + Utilities.indent(ocspStatusRequest) + }; + + return messageFormat.format(messageFields); + } + } + + static class CertStatusResponse { + final byte statusType; + final byte[] encodedResponse; + + protected CertStatusResponse(byte statusType, byte[] respDer) { + this.statusType = statusType; + this.encodedResponse = respDer; + } + + byte[] toByteArray() throws IOException { + // Create a byte array large enough to handle the status_type + // field (1) + OCSP length (3) + OCSP data (variable) + byte[] outData = new byte[encodedResponse.length + 4]; + ByteBuffer buf = ByteBuffer.wrap(outData); + Record.putInt8(buf, statusType); + Record.putBytes24(buf, encodedResponse); + return buf.array(); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"certificate status response type\": {0}\n" + + "\"encoded certificate status\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + String encoded = hexEncoder.encodeBuffer(encodedResponse); + + Object[] messageFields = { + CertStatusRequestType.nameOf(statusType), + Utilities.indent(encoded) + }; + + return messageFormat.format(messageFields); + } + } + + static final class OCSPStatusResponse extends CertStatusResponse { + final OCSPResponse ocspResponse; + + private OCSPStatusResponse(byte statusType, + byte[] encoded) throws IOException { + super(statusType, encoded); + + // The DER-encoded OCSP response must not be zero length + if (encoded == null || encoded.length < 1) { + throw new SSLProtocolException( + "Invalid OCSP status response: insufficient data"); + } + + // Otherwise, make an OCSPResponse object from the data + ocspResponse = new OCSPResponse(encoded); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"certificate status response type\": {0}\n" + + "\"OCSP status response\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + Object[] messageFields = { + CertStatusRequestType.nameOf(statusType), + Utilities.indent(ocspResponse.toString()) + }; + + return messageFormat.format(messageFields); + } + } + + /** + * Network data producer of a "status_request" extension in the + * ClientHello handshake message. + */ + private static final + class CHCertStatusReqProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHCertStatusReqProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + if (!chc.sslContext.isStaplingEnabled(true)) { + return null; + } + + if (!chc.sslConfig.isAvailable(CH_STATUS_REQUEST)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + CH_STATUS_REQUEST.name); + } + return null; + } + + // Produce the extension. + // + // We are using empty OCSPStatusRequest at present. May extend to + // support specific responder or extensions later. + byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00}; + + // Update the context. + chc.handshakeExtensions.put( + CH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT); + + return extData; + } + } + + /** + * Network data consumer of a "status_request" extension in the + * ClientHello handshake message. + */ + private static final + class CHCertStatusReqConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHCertStatusReqConsumer() { + // 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_STATUS_REQUEST)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore unavailable extension: " + + CH_STATUS_REQUEST.name); + } + return; // ignore the extension + } + + // Parse the extension. + CertStatusRequestSpec spec; + try { + spec = new CertStatusRequestSpec(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(CH_STATUS_REQUEST, spec); + if (!shc.isResumption && + !shc.negotiatedProtocol.useTLS13PlusSpec()) { + shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id, + SSLHandshake.CERTIFICATE_STATUS); + } // Otherwise, the certificate status presents in server cert. + + // No impact on session resumption. + } + } + + /** + * Network data producer of a "status_request" extension in the + * ServerHello handshake message. + */ + private static final + class SHCertStatusReqProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private SHCertStatusReqProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // The StaplingParameters in the ServerHandshakeContext will + // contain the info about what kind of stapling (if any) to + // perform and whether this status_request extension should be + // produced or the status_request_v2 (found in a different producer) + // No explicit check is required for isStaplingEnabled here. If + // it is false then stapleParams will be null. If it is true + // then stapleParams may or may not be false and the check below + // is sufficient. + if ((shc.stapleParams == null) || + (shc.stapleParams.statusRespExt != + SSLExtension.CH_STATUS_REQUEST)) { + return null; // Do not produce status_request in ServerHello + } + + // In response to "status_request" extension request only. + CertStatusRequestSpec spec = (CertStatusRequestSpec) + shc.handshakeExtensions.get(CH_STATUS_REQUEST); + if (spec == null) { + // Ignore, no status_request extension requested. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable extension: " + + CH_STATUS_REQUEST.name); + } + + return null; // ignore the extension + } + + // Is it a session resuming? + if (shc.isResumption) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "No status_request response for session resuming"); + } + + return null; // ignore the extension + } + + // The "extension_data" in the extended ServerHello handshake + // message MUST be empty. + byte[] extData = new byte[0]; + + // Update the context. + shc.handshakeExtensions.put( + SH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT); + + return extData; + } + } + + /** + * Network data consumer of a "status_request" extension in the + * ServerHello handshake message. + */ + private static final + class SHCertStatusReqConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private SHCertStatusReqConsumer() { + // 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 "status_request" extension request only. + CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec) + chc.handshakeExtensions.get(CH_STATUS_REQUEST); + if (requestedCsr == null) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected status_request extension in ServerHello"); + } + + // Parse the extension. + if (buffer.hasRemaining()) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Invalid status_request extension in ServerHello message: " + + "the extension data must be empty"); + } + + // Update the context. + chc.handshakeExtensions.put( + SH_STATUS_REQUEST, CertStatusRequestSpec.DEFAULT); + chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id, + SSLHandshake.CERTIFICATE_STATUS); + + // Since we've received a legitimate status_request in the + // ServerHello, stapling is active if it's been enabled. + chc.staplingActive = chc.sslContext.isStaplingEnabled(true); + + // No impact on session resumption. + } + } + + /** + * The "status_request_v2" extension. + * + * 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; + * 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>; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + static final class CertStatusRequestV2Spec implements SSLExtensionSpec { + static final CertStatusRequestV2Spec DEFAULT = + new CertStatusRequestV2Spec(new CertStatusRequest[] { + OCSPStatusRequest.EMPTY_OCSP_MULTI}); + + final CertStatusRequest[] certStatusRequests; + + private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) { + this.certStatusRequests = certStatusRequests; + } + + private CertStatusRequestV2Spec(ByteBuffer message) throws IOException { + // Is it a empty extension_data? + if (message.remaining() == 0) { + // server response + this.certStatusRequests = new CertStatusRequest[0]; + return; + } + + if (message.remaining() < 5) { // 2: certificate_status_req_list + // +1: status_type + // +2: request_length + throw new SSLProtocolException( + "Invalid status_request_v2 extension: insufficient data"); + } + + int listLen = Record.getInt16(message); + if (listLen <= 0) { + throw new SSLProtocolException( + "certificate_status_req_list length must be positive " + + "(received length: " + listLen + ")"); + } + + int remaining = listLen; + List statusRequests = new ArrayList<>(); + while (remaining > 0) { + byte statusType = (byte)Record.getInt8(message); + int requestLen = Record.getInt16(message); + + if (message.remaining() < requestLen) { + throw new SSLProtocolException( + "Invalid status_request_v2 extension: " + + "insufficient data (request_length=" + requestLen + + ", remining=" + message.remaining() + ")"); + } + + byte[] encoded = new byte[requestLen]; + if (encoded.length != 0) { + message.get(encoded); + } + remaining -= 3; // 1(status type) + 2(request_length) bytes + remaining -= requestLen; + + if (statusType == CertStatusRequestType.OCSP.id || + statusType == CertStatusRequestType.OCSP_MULTI.id) { + if (encoded.length < 4) { + // 2: length of responder_id_list + // +2: length of request_extensions + throw new SSLProtocolException( + "Invalid status_request_v2 extension: " + + "insufficient data"); + } + statusRequests.add( + new OCSPStatusRequest(statusType, encoded)); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.info( + "Unknown certificate status request " + + "(status type: " + statusType + ")"); + } + statusRequests.add( + new CertStatusRequest(statusType, encoded)); + } + } + + certStatusRequests = + statusRequests.toArray(new CertStatusRequest[0]); + } + + @Override + public String toString() { + if (certStatusRequests == null || certStatusRequests.length == 0) { + return ""; + } else { + MessageFormat messageFormat = new MessageFormat( + "\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH); + + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (CertStatusRequest csr : certStatusRequests) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } + Object[] messageFields = { + Utilities.indent(csr.toString()) + }; + builder.append(messageFormat.format(messageFields)); + } + + return builder.toString(); + } + } + } + + private static final + class CertStatusRequestsStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new CertStatusRequestV2Spec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "status_request_v2" extension in the + * ClientHello handshake message. + */ + private static final + class CHCertStatusReqV2Producer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHCertStatusReqV2Producer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + if (!chc.sslContext.isStaplingEnabled(true)) { + return null; + } + + if (!chc.sslConfig.isAvailable(CH_STATUS_REQUEST_V2)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable status_request_v2 extension"); + } + + return null; + } + + // Produce the extension. + // + // We are using empty OCSPStatusRequest at present. May extend to + // support specific responder or extensions later. + byte[] extData = new byte[] { + 0x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; + + // Update the context. + chc.handshakeExtensions.put( + CH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT); + + return extData; + } + } + + /** + * Network data consumer of a "status_request_v2" extension in the + * ClientHello handshake message. + */ + private static final + class CHCertStatusReqV2Consumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHCertStatusReqV2Consumer() { + // 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_STATUS_REQUEST_V2)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable status_request_v2 extension"); + } + + return; // ignore the extension + } + + // Parse the extension. + CertStatusRequestV2Spec spec; + try { + spec = new CertStatusRequestV2Spec(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(CH_STATUS_REQUEST_V2, spec); + if (!shc.isResumption) { + shc.handshakeProducers.putIfAbsent( + SSLHandshake.CERTIFICATE_STATUS.id, + SSLHandshake.CERTIFICATE_STATUS); + } + + // No impact on session resumption. + } + } + + /** + * Network data producer of a "status_request_v2" extension in the + * ServerHello handshake message. + */ + private static final + class SHCertStatusReqV2Producer implements HandshakeProducer { + // Prevent instantiation of this class. + private SHCertStatusReqV2Producer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + + ServerHandshakeContext shc = (ServerHandshakeContext)context; + // The StaplingParameters in the ServerHandshakeContext will + // contain the info about what kind of stapling (if any) to + // perform and whether this status_request extension should be + // produced or the status_request_v2 (found in a different producer) + // No explicit check is required for isStaplingEnabled here. If + // it is false then stapleParams will be null. If it is true + // then stapleParams may or may not be false and the check below + // is sufficient. + if ((shc.stapleParams == null) || + (shc.stapleParams.statusRespExt != + SSLExtension.CH_STATUS_REQUEST_V2)) { + return null; // Do not produce status_request_v2 in SH + } + + // In response to "status_request_v2" extension request only + CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec) + shc.handshakeExtensions.get(CH_STATUS_REQUEST_V2); + if (spec == null) { + // Ignore, no status_request_v2 extension requested. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable status_request_v2 extension"); + } + + return null; // ignore the extension + } + + // Is it a session resuming? + if (shc.isResumption) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "No status_request_v2 response for session resumption"); + } + return null; // ignore the extension + } + + // The "extension_data" in the extended ServerHello handshake + // message MUST be empty. + byte[] extData = new byte[0]; + + // Update the context. + shc.handshakeExtensions.put( + SH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT); + + return extData; + } + } + + /** + * Network data consumer of a "status_request_v2" extension in the + * ServerHello handshake message. + */ + private static final + class SHCertStatusReqV2Consumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private SHCertStatusReqV2Consumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + + // The consumption happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // In response to "status_request" extension request only + CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec) + chc.handshakeExtensions.get(CH_STATUS_REQUEST_V2); + if (requestedCsr == null) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected status_request_v2 extension in ServerHello"); + } + + // Parse the extension. + if (buffer.hasRemaining()) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Invalid status_request_v2 extension in ServerHello: " + + "the extension data must be empty"); + } + + // Update the context. + chc.handshakeExtensions.put( + SH_STATUS_REQUEST_V2, CertStatusRequestV2Spec.DEFAULT); + chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id, + SSLHandshake.CERTIFICATE_STATUS); + + // Since we've received a legitimate status_request in the + // ServerHello, stapling is active if it's been enabled. + chc.staplingActive = chc.sslContext.isStaplingEnabled(true); + + // No impact on session resumption. + } + } + + private static final + class CTCertStatusResponseProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CTCertStatusResponseProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + ServerHandshakeContext shc = (ServerHandshakeContext)context; + byte[] producedData = null; + + // Stapling needs to be active and have valid data to proceed + if (shc.stapleParams == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Stapling is disabled for this connection"); + } + return null; + } + + // There needs to be a non-null CertificateEntry to proceed + if (shc.currentCertEntry == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Found null CertificateEntry in context"); + } + return null; + } + + // Pull the certificate from the CertificateEntry and find + // a response from the response map. If one exists we will + // staple it. + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate x509Cert = + (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream( + shc.currentCertEntry.encoded)); + byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert); + if (respBytes == null) { + // We're done with this entry. Clear it from the context + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest("No status response found for " + + x509Cert.getSubjectX500Principal()); + } + shc.currentCertEntry = null; + return null; + } + + // Build a proper response buffer from the stapling information + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest("Found status response for " + + x509Cert.getSubjectX500Principal() + + ", response length: " + respBytes.length); + } + CertStatusResponse certResp = (shc.stapleParams.statReqType == + CertStatusRequestType.OCSP) ? + new OCSPStatusResponse(shc.stapleParams.statReqType.id, + respBytes) : + new CertStatusResponse(shc.stapleParams.statReqType.id, + respBytes); + producedData = certResp.toByteArray(); + } catch (CertificateException ce) { + shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } catch (IOException ioe) { + shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE, + "Failed to parse certificate status response", ioe); + } + + // Clear the pinned CertificateEntry from the context + shc.currentCertEntry = null; + return producedData; + } + } + + private static final + class CTCertStatusResponseConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CTCertStatusResponseConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + // The consumption happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // Parse the extension. + CertStatusResponseSpec spec; + try { + spec = new CertStatusResponseSpec(buffer); + } catch (IOException ioe) { + chc.conContext.fatal(Alert.DECODE_ERROR, ioe); + return; // fatal() always throws, make the compiler happy. + } + + if (chc.sslContext.isStaplingEnabled(true)) { + // Activate stapling + chc.staplingActive = true; + } else { + // Do no further processing of stapled responses + return; + } + + // Get response list from the session. This is unmodifiable + // so we need to create a new list. Then add this new response + // to the end and submit it back to the session object. + if ((chc.handshakeSession != null) && (!chc.isResumption)) { + List respList = new ArrayList<>( + chc.handshakeSession.getStatusResponses()); + respList.add(spec.statusResponse.encodedResponse); + chc.handshakeSession.setStatusResponses(respList); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Ignoring stapled data on resumed session"); + } + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java --- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java --- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqItemV2.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java --- a/src/java.base/share/classes/sun/security/ssl/CertStatusReqListV2Extension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 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 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 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 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; - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertificateMessage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1370 @@ +/* + * 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.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.PublicKey; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CertPathValidatorException.Reason; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedTrustManager; +import javax.net.ssl.X509TrustManager; +import javax.security.auth.x500.X500Principal; +import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED; +import sun.security.ssl.ClientHello.ClientHelloMessage; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.X509Authentication.X509Credentials; +import sun.security.ssl.X509Authentication.X509Possession; + +/** + * Pack of the CertificateMessage handshake message. + */ +final class CertificateMessage { + static final SSLConsumer t12HandshakeConsumer = + new T12CertificateConsumer(); + static final HandshakeProducer t12HandshakeProducer = + new T12CertificateProducer(); + + static final SSLConsumer t13HandshakeConsumer = + new T13CertificateConsumer(); + static final HandshakeProducer t13HandshakeProducer = + new T13CertificateProducer(); + + /** + * The Certificate handshake message for TLS 1.2 and previous + * SSL/TLS protocol versions. + * + * In server mode, the certificate handshake message is sent whenever the + * agreed-upon key exchange method uses certificates for authentication. + * In client mode, this message is only sent if the server requests a + * certificate for client authentication. + * + * opaque ASN.1Cert<1..2^24-1>; + * + * SSL 3.0: + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + * Note: For SSL 3.0 client authentication, if no suitable certificate + * is available, the client should send a no_certificate alert instead. + * This alert is only a warning; however, the server may respond with + * a fatal handshake failure alert if client authentication is required. + * + * TLS 1.0/1.1/1.2: + * struct { + * ASN.1Cert certificate_list<0..2^24-1>; + * } Certificate; + */ + static final class T12CertificateMessage extends HandshakeMessage { + final List encodedCertChain; + + T12CertificateMessage(HandshakeContext handshakeContext, + X509Certificate[] certChain) throws SSLException { + super(handshakeContext); + + List encodedCerts = new ArrayList<>(certChain.length); + for (X509Certificate cert : certChain) { + try { + encodedCerts.add(cert.getEncoded()); + } catch (CertificateEncodingException cee) { + // unlikely + handshakeContext.conContext.fatal(Alert.INTERNAL_ERROR, + "Could not encode certificate (" + + cert.getSubjectX500Principal() + ")", cee); + break; + } + } + + this.encodedCertChain = encodedCerts; + } + + T12CertificateMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + int listLen = Record.getInt24(m); + if (listLen > m.remaining()) { + handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Error parsing certificate message:no sufficient data"); + } + if (listLen > 0) { + List encodedCerts = new LinkedList<>(); + while (listLen > 0) { + byte[] encodedCert = Record.getBytes24(m); + listLen -= (3 + encodedCert.length); + encodedCerts.add(encodedCert); + } + this.encodedCertChain = encodedCerts; + } else { + this.encodedCertChain = Collections.emptyList(); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE; + } + + @Override + public int messageLength() { + int msgLen = 3; + for (byte[] encodedCert : encodedCertChain) { + msgLen += (encodedCert.length + 3); + } + + return msgLen; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + int listLen = 0; + for (byte[] encodedCert : encodedCertChain) { + listLen += (encodedCert.length + 3); + } + + hos.putInt24(listLen); + for (byte[] encodedCert : encodedCertChain) { + hos.putBytes24(encodedCert); + } + } + + @Override + public String toString() { + if (encodedCertChain.isEmpty()) { + return "\"Certificates\": "; + } + + Object[] x509Certs = new Object[encodedCertChain.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCertChain) { + Object obj; + try { + obj = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } catch (CertificateException ce) { + obj = encodedCert; + } + x509Certs[i++] = obj; + } + } catch (CertificateException ce) { + // no X.509 certificate factory service + int i = 0; + for (byte[] encodedCert : encodedCertChain) { + x509Certs[i++] = encodedCert; + } + } + + MessageFormat messageFormat = new MessageFormat( + "\"Certificates\": [\n" + + "{0}\n" + + "]", + Locale.ENGLISH); + Object[] messageFields = { + SSLLogger.toString(x509Certs) + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "Certificate" handshake message producer for TLS 1.2 and + * previous SSL/TLS protocol versions. + */ + private static final + class T12CertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T12CertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + return onProduceCertificate( + (ClientHandshakeContext)context, message); + } else { + return onProduceCertificate( + (ServerHandshakeContext)context, message); + } + } + + private byte[] onProduceCertificate(ServerHandshakeContext shc, + SSLHandshake.HandshakeMessage message) throws IOException { + X509Possession x509Possession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null) { // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "No expected X.509 certificate for server authentication"); + + return null; // make the compiler happy + } + + shc.handshakeSession.setLocalPrivateKey( + x509Possession.popPrivateKey); + shc.handshakeSession.setLocalCertificates(x509Possession.popCerts); + T12CertificateMessage cm = + new T12CertificateMessage(shc, x509Possession.popCerts); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced server Certificate handshake message", cm); + } + + // Output the handshake message. + cm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + + private byte[] onProduceCertificate(ClientHandshakeContext chc, + SSLHandshake.HandshakeMessage message) throws IOException { + X509Possession x509Possession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + // Report to the server if no appropriate cert was found. For + // SSL 3.0, send a no_certificate alert; TLS 1.0/1.1/1.2 uses + // an empty cert chain instead. + if (x509Possession == null) { + if (chc.negotiatedProtocol.useTLS10PlusSpec()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 certificate for client authentication, " + + "use empty Certificate message instead"); + } + + x509Possession = + new X509Possession(null, new X509Certificate[0]); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 certificate for client authentication, " + + "send a no_certificate alert"); + } + + chc.conContext.warning(Alert.NO_CERTIFICATE); + return null; + } + } + + chc.handshakeSession.setLocalPrivateKey( + x509Possession.popPrivateKey); + if (x509Possession.popCerts != null && + x509Possession.popCerts.length != 0) { + chc.handshakeSession.setLocalCertificates( + x509Possession.popCerts); + } else { + chc.handshakeSession.setLocalCertificates(null); + } + T12CertificateMessage cm = + new T12CertificateMessage(chc, x509Possession.popCerts); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced client Certificate handshake message", cm); + } + + // Output the handshake message. + cm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "Certificate" handshake message consumer for TLS 1.2 and + * previous SSL/TLS protocol versions. + */ + static final + class T12CertificateConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T12CertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); + + T12CertificateMessage cm = new T12CertificateMessage(hc, message); + if (hc.sslConfig.isClientMode) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming server Certificate handshake message", cm); + } + onCertificate((ClientHandshakeContext)context, cm); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming client Certificate handshake message", cm); + } + onCertificate((ServerHandshakeContext)context, cm); + } + } + + private void onCertificate(ServerHandshakeContext shc, + T12CertificateMessage certificateMessage )throws IOException { + List encodedCerts = certificateMessage.encodedCertChain; + if (encodedCerts == null || encodedCerts.isEmpty()) { + if (shc.sslConfig.clientAuthType != + ClientAuthType.CLIENT_AUTH_REQUESTED) { + // unexpected or require client authentication + shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty server certificate chain"); + } else { + return; + } + } + + X509Certificate[] x509Certs = + new X509Certificate[encodedCerts.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCerts) { + x509Certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } + } catch (CertificateException ce) { + shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + checkClientCerts(shc, x509Certs); + + // + // update + // + shc.handshakeCredentials.add( + new X509Credentials(x509Certs[0].getPublicKey(), x509Certs)); + shc.handshakeSession.setPeerCertificates(x509Certs); + } + + private void onCertificate(ClientHandshakeContext chc, + T12CertificateMessage certificateMessage) throws IOException { + List encodedCerts = certificateMessage.encodedCertChain; + if (encodedCerts == null || encodedCerts.isEmpty()) { + chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty server certificate chain"); + } + + X509Certificate[] x509Certs = + new X509Certificate[encodedCerts.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (byte[] encodedCert : encodedCerts) { + x509Certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(encodedCert)); + } + } catch (CertificateException ce) { + chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + // Allow server certificate change in client side during + // renegotiation after a session-resumption abbreviated + // initial handshake? + // + // DO NOT need to check allowUnsafeServerCertChange here. We only + // reserve server certificates when allowUnsafeServerCertChange is + // false. + if (chc.reservedServerCerts != null && + !chc.handshakeSession.useExtendedMasterSecret) { + // It is not necessary to check the certificate update if + // endpoint identification is enabled. + String identityAlg = chc.sslConfig.identificationProtocol; + if ((identityAlg == null || identityAlg.length() == 0) && + !isIdentityEquivalent(x509Certs[0], + chc.reservedServerCerts[0])) { + chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "server certificate change is restricted " + + "during renegotiation"); + } + } + + // ask the trust manager to verify the chain + if (chc.staplingActive) { + // Defer the certificate check until after we've received the + // CertificateStatus message. If that message doesn't come in + // immediately following this message we will execute the + // check from CertificateStatus' absent handler. + chc.deferredCerts = x509Certs; + } else { + // We're not doing stapling, so perform the check right now + checkServerCerts(chc, x509Certs); + } + + // + // update + // + chc.handshakeCredentials.add( + new X509Credentials(x509Certs[0].getPublicKey(), x509Certs)); + chc.handshakeSession.setPeerCertificates(x509Certs); + } + + /* + * Whether the certificates can represent the same identity? + * + * The certificates can be used to represent the same identity: + * 1. If the subject alternative names of IP address are present + * in both certificates, they should be identical; otherwise, + * 2. if the subject alternative names of DNS name are present in + * both certificates, they should be identical; otherwise, + * 3. if the subject fields are present in both certificates, the + * certificate subjects and issuers should be identical. + */ + private static boolean isIdentityEquivalent(X509Certificate thisCert, + X509Certificate prevCert) { + if (thisCert.equals(prevCert)) { + return true; + } + + // check subject alternative names + Collection> thisSubjectAltNames = null; + try { + thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.fine( + "Attempt to obtain subjectAltNames extension failed!"); + } + } + + Collection> prevSubjectAltNames = null; + try { + prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.fine( + "Attempt to obtain subjectAltNames extension failed!"); + } + } + + if (thisSubjectAltNames != null && prevSubjectAltNames != null) { + // check the iPAddress field in subjectAltName extension + // + // 7: subject alternative name of type IP. + Collection thisSubAltIPAddrs = + getSubjectAltNames(thisSubjectAltNames, 7); + Collection prevSubAltIPAddrs = + getSubjectAltNames(prevSubjectAltNames, 7); + if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null && + isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) { + return true; + } + + // check the dNSName field in subjectAltName extension + // 2: subject alternative name of type IP. + Collection thisSubAltDnsNames = + getSubjectAltNames(thisSubjectAltNames, 2); + Collection prevSubAltDnsNames = + getSubjectAltNames(prevSubjectAltNames, 2); + if (thisSubAltDnsNames != null && prevSubAltDnsNames != null && + isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) { + return true; + } + } + + // check the certificate subject and issuer + X500Principal thisSubject = thisCert.getSubjectX500Principal(); + X500Principal prevSubject = prevCert.getSubjectX500Principal(); + X500Principal thisIssuer = thisCert.getIssuerX500Principal(); + X500Principal prevIssuer = prevCert.getIssuerX500Principal(); + + return (!thisSubject.getName().isEmpty() && + !prevSubject.getName().isEmpty() && + thisSubject.equals(prevSubject) && + thisIssuer.equals(prevIssuer)); + } + + /* + * Returns the subject alternative name of the specified type in the + * subjectAltNames extension of a certificate. + * + * Note that only those subjectAltName types that use String data + * should be passed into this function. + */ + private static Collection getSubjectAltNames( + Collection> subjectAltNames, int type) { + HashSet subAltDnsNames = null; + for (List subjectAltName : subjectAltNames) { + int subjectAltNameType = (Integer)subjectAltName.get(0); + if (subjectAltNameType == type) { + String subAltDnsName = (String)subjectAltName.get(1); + if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { + if (subAltDnsNames == null) { + subAltDnsNames = + new HashSet<>(subjectAltNames.size()); + } + subAltDnsNames.add(subAltDnsName); + } + } + } + + return subAltDnsNames; + } + + private static boolean isEquivalent(Collection thisSubAltNames, + Collection prevSubAltNames) { + for (String thisSubAltName : thisSubAltNames) { + for (String prevSubAltName : prevSubAltNames) { + // Only allow the exactly match. No wildcard character + // checking. + if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { + return true; + } + } + } + + return false; + } + + /** + * Perform client-side checking of server certificates. + * + * @param certs an array of {@code X509Certificate} objects presented + * by the server in the ServerCertificate message. + * + * @throws IOException if a failure occurs during validation or + * the trust manager associated with the {@code SSLContext} is not + * an {@code X509ExtendedTrustManager}. + */ + static void checkServerCerts(ClientHandshakeContext chc, + X509Certificate[] certs) throws IOException { + + X509TrustManager tm = chc.sslContext.getX509TrustManager(); + + // find out the key exchange algorithm used + // use "RSA" for non-ephemeral "RSA_EXPORT" + String keyExchangeString; + if (chc.negotiatedCipherSuite.keyExchange == + CipherSuite.KeyExchange.K_RSA_EXPORT || + chc.negotiatedCipherSuite.keyExchange == + CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) { + keyExchangeString = CipherSuite.KeyExchange.K_RSA.name; + } else { + keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name; + } + + try { + if (tm instanceof X509ExtendedTrustManager) { + if (chc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + keyExchangeString, + engine); + } else { + SSLSocket socket = (SSLSocket)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + keyExchangeString, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + + // Once the server certificate chain has been validated, set + // the certificate chain in the TLS session. + chc.handshakeSession.setPeerCertificates(certs); + } catch (CertificateException ce) { + chc.conContext.fatal(getCertificateAlert(chc, ce), ce); + } + } + + private static void checkClientCerts(ServerHandshakeContext shc, + X509Certificate[] certs) throws IOException { + X509TrustManager tm = shc.sslContext.getX509TrustManager(); + + // find out the types of client authentication used + PublicKey key = certs[0].getPublicKey(); + String keyAlgorithm = key.getAlgorithm(); + String authType; + switch (keyAlgorithm) { + case "RSA": + case "DSA": + case "EC": + case "RSASSA-PSS": + authType = keyAlgorithm; + break; + default: + // unknown public key type + authType = "UNKNOWN"; + } + + try { + if (tm instanceof X509ExtendedTrustManager) { + if (shc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + engine); + } else { + SSLSocket socket = (SSLSocket)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + } catch (CertificateException ce) { + shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce); + } + } + + /** + * When a failure happens during certificate checking from an + * {@link X509TrustManager}, determine what TLS alert description + * to use. + * + * @param cexc The exception thrown by the {@link X509TrustManager} + * + * @return A byte value corresponding to a TLS alert description number. + */ + private static Alert getCertificateAlert( + ClientHandshakeContext chc, CertificateException cexc) { + // The specific reason for the failure will determine how to + // set the alert description value + Alert alert = Alert.CERTIFICATE_UNKNOWN; + + Throwable baseCause = cexc.getCause(); + if (baseCause instanceof CertPathValidatorException) { + CertPathValidatorException cpve = + (CertPathValidatorException)baseCause; + Reason reason = cpve.getReason(); + if (reason == BasicReason.REVOKED) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_REVOKED; + } else if ( + reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_UNKNOWN; + } + } + + return alert; + } + + } + + /** + * The certificate entry used in Certificate handshake message for TLS 1.3. + */ + static final class CertificateEntry { + final byte[] encoded; // encoded cert or public key + private final SSLExtensions extensions; + + CertificateEntry(byte[] encoded, SSLExtensions extensions) { + this.encoded = encoded; + this.extensions = extensions; + } + + private int getEncodedSize() { + int extLen = extensions.length(); + if (extLen == 0) { + extLen = 2; // empty extensions + } + return 3 + encoded.length + extLen; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\n'{'\n" + + "{0}\n" + // X.509 certificate + " \"extensions\": '{'\n" + + "{1}\n" + + " '}'\n" + + "'}',", Locale.ENGLISH); + + Object x509Certs; + try { + // Don't support certificate type extension (RawPublicKey) yet. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + x509Certs = + cf.generateCertificate(new ByteArrayInputStream(encoded)); + } catch (CertificateException ce) { + // no X.509 certificate factory service + x509Certs = encoded; + } + + Object[] messageFields = { + SSLLogger.toString(x509Certs), + Utilities.indent(extensions.toString(), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The Certificate handshake message for TLS 1.3. + */ + static final class T13CertificateMessage extends HandshakeMessage { + private final byte[] requestContext; + private final List certEntries; + + T13CertificateMessage(HandshakeContext context, + byte[] requestContext, X509Certificate[] certificates) + throws SSLException, CertificateException { + super(context); + + this.requestContext = requestContext.clone(); + this.certEntries = new LinkedList<>(); + for (X509Certificate cert : certificates) { + byte[] encoded = cert.getEncoded(); + SSLExtensions extensions = new SSLExtensions(this); + certEntries.add(new CertificateEntry(encoded, extensions)); + } + } + + T13CertificateMessage(HandshakeContext handshakeContext, + byte[] requestContext, List certificates) { + super(handshakeContext); + + this.requestContext = requestContext.clone(); + this.certEntries = certificates; + } + + T13CertificateMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // struct { + // opaque certificate_request_context<0..2^8-1>; + // CertificateEntry certificate_list<0..2^24-1>; + // } Certificate; + if (m.remaining() < 4) { + throw new SSLProtocolException( + "Invalid Certificate message: " + + "insufficient data (length=" + m.remaining() + ")"); + } + this.requestContext = Record.getBytes8(m); + + if (m.remaining() < 3) { + throw new SSLProtocolException( + "Invalid Certificate message: " + + "insufficient certificate entries data (length=" + + m.remaining() + ")"); + } + + int listLen = Record.getInt24(m); + if (listLen != m.remaining()) { + throw new SSLProtocolException( + "Invalid Certificate message: " + + "incorrect list length (length=" + listLen + ")"); + } + + SSLExtension[] enabledExtensions = + handshakeContext.sslConfig.getEnabledExtensions( + SSLHandshake.CERTIFICATE); + List certList = new LinkedList<>(); + while (m.hasRemaining()) { + // Note: support only X509 CertificateType right now. + byte[] encodedCert = Record.getBytes24(m); + if (encodedCert.length == 0) { + throw new SSLProtocolException( + "Invalid Certificate message: empty cert_data"); + } + + SSLExtensions extensions = + new SSLExtensions(this, m, enabledExtensions); + certList.add(new CertificateEntry(encodedCert, extensions)); + } + + this.certEntries = Collections.unmodifiableList(certList); + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE; + } + + @Override + public int messageLength() { + int msgLen = 4 + requestContext.length; + for (CertificateEntry entry : certEntries) { + msgLen += entry.getEncodedSize(); + } + + return msgLen; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + int entryListLen = 0; + for (CertificateEntry entry : certEntries) { + entryListLen += entry.getEncodedSize(); + } + + hos.putBytes8(requestContext); + hos.putInt24(entryListLen); + for (CertificateEntry entry : certEntries) { + hos.putBytes24(entry.encoded); + // Is it an empty extensions? + if (entry.extensions.length() == 0) { + hos.putInt16(0); + } else { + entry.extensions.send(hos); + } + } + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"Certificate\": '{'\n" + + " \"certificate_request_context\": \"{0}\",\n" + + " \"certificate_list\": [{1}\n]\n" + + "'}'", + Locale.ENGLISH); + + StringBuilder builder = new StringBuilder(512); + for (CertificateEntry entry : certEntries) { + builder.append(entry.toString()); + } + + Object[] messageFields = { + Utilities.toHexString(requestContext), + Utilities.indent(builder.toString()) + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "Certificate" handshake message producer for TLS 1.3. + */ + private static final + class T13CertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13CertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + return onProduceCertificate( + (ClientHandshakeContext)context, message); + } else { + return onProduceCertificate( + (ServerHandshakeContext)context, message); + } + } + + private byte[] onProduceCertificate(ServerHandshakeContext shc, + HandshakeMessage message) throws IOException { + ClientHelloMessage clientHello = (ClientHelloMessage)message; + + SSLPossession pos = choosePossession(shc, clientHello); + if (pos == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No available authentication scheme"); + return null; // make the complier happy + } + + if (!(pos instanceof X509Possession)) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X.509 certificate for server authentication"); + } + + X509Possession x509Possession = (X509Possession)pos; + X509Certificate[] localCerts = x509Possession.popCerts; + if (localCerts == null || localCerts.length == 0) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X.509 certificate for server authentication"); + return null; // make the complier happy + } + + // update the context + shc.handshakePossessions.add(x509Possession); + shc.handshakeSession.setLocalPrivateKey( + x509Possession.popPrivateKey); + shc.handshakeSession.setLocalCertificates(localCerts); + T13CertificateMessage cm; + try { + cm = new T13CertificateMessage(shc, (new byte[0]), localCerts); + } catch (SSLException | CertificateException ce) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Failed to produce server Certificate message", ce); + return null; // make the complier happy + } + + // Check the OCSP stapling extensions and attempt + // to get responses. If the resulting stapleParams is non + // null, it implies that stapling is enabled on the server side. + shc.stapleParams = StatusResponseManager.processStapling(shc); + shc.staplingActive = (shc.stapleParams != null); + + // Process extensions for each CertificateEntry. + // Since there can be multiple CertificateEntries within a + // single CT message, we will pin a specific CertificateEntry + // into the ServerHandshakeContext so individual extension + // producers know which X509Certificate it is processing in + // each call. + SSLExtension[] enabledCTExts = shc.sslConfig.getEnabledExtensions( + SSLHandshake.CERTIFICATE, + Arrays.asList(ProtocolVersion.PROTOCOLS_OF_13)); + for (CertificateEntry certEnt : cm.certEntries) { + shc.currentCertEntry = certEnt; + certEnt.extensions.produce(shc, enabledCTExts); + } + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced server Certificate message", cm); + } + + // Output the handshake message. + cm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + + private static SSLPossession choosePossession( + HandshakeContext hc, + ClientHelloMessage clientHello) throws IOException { + if (hc.peerRequestedCertSignSchemes == null || + hc.peerRequestedCertSignSchemes.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "No signature_algorithms(_cert) in ClientHello"); + } + return null; + } + + Collection checkedKeyTypes = new HashSet<>(); + for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) { + if (checkedKeyTypes.contains(ss.keyAlgorithm)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unsupported authentication scheme: " + ss.name); + } + continue; + } + + // Don't select a signature scheme unless we will be able to + // produce a CertificateVerify message later + if (SignatureScheme.getPreferableAlgorithm( + hc.peerRequestedSignatureSchemes, + ss, hc.negotiatedProtocol) == null) { + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unable to produce CertificateVerify for " + + "signature scheme: " + ss.name); + } + checkedKeyTypes.add(ss.keyAlgorithm); + continue; + } + + SSLAuthentication ka = X509Authentication.valueOf(ss); + if (ka == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unsupported authentication scheme: " + ss.name); + } + checkedKeyTypes.add(ss.keyAlgorithm); + continue; + } + + SSLPossession pos = ka.createPossession(hc); + if (pos == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unavailable authentication scheme: " + ss.name); + } + continue; + } + + return pos; + } + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning("No available authentication scheme"); + } + return null; + } + + private byte[] onProduceCertificate(ClientHandshakeContext chc, + HandshakeMessage message) throws IOException { + ClientHelloMessage clientHello = (ClientHelloMessage)message; + SSLPossession pos = choosePossession(chc, clientHello); + X509Certificate[] localCerts; + if (pos == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("No available client authentication scheme"); + } + localCerts = new X509Certificate[0]; + } else { + chc.handshakePossessions.add(pos); + if (!(pos instanceof X509Possession)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 certificate for client authentication"); + } + localCerts = new X509Certificate[0]; + } else { + X509Possession x509Possession = (X509Possession)pos; + localCerts = x509Possession.popCerts; + chc.handshakeSession.setLocalPrivateKey( + x509Possession.popPrivateKey); + } + } + + if (localCerts != null && localCerts.length != 0) { + chc.handshakeSession.setLocalCertificates(localCerts); + } else { + chc.handshakeSession.setLocalCertificates(null); + } + + T13CertificateMessage cm; + try { + cm = new T13CertificateMessage( + chc, chc.certRequestContext, localCerts); + } catch (SSLException | CertificateException ce) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Failed to produce client Certificate message", ce); + return null; + } + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced client Certificate message", cm); + } + + // Output the handshake message. + cm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "Certificate" handshake message consumer for TLS 1.3. + */ + private static final class T13CertificateConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T13CertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); + T13CertificateMessage cm = new T13CertificateMessage(hc, message); + if (hc.sslConfig.isClientMode) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming server Certificate handshake message", cm); + } + onConsumeCertificate((ClientHandshakeContext)context, cm); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming client Certificate handshake message", cm); + } + onConsumeCertificate((ServerHandshakeContext)context, cm); + } + } + + private void onConsumeCertificate(ServerHandshakeContext shc, + T13CertificateMessage certificateMessage )throws IOException { + if (certificateMessage.certEntries == null || + certificateMessage.certEntries.isEmpty()) { + if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) { + shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty client certificate chain"); + } else { + // optional client authentication + return; + } + } + + // check client certificate entries + X509Certificate[] cliCerts = + checkClientCerts(shc, certificateMessage.certEntries); + + // + // update + // + shc.handshakeCredentials.add( + new X509Credentials(cliCerts[0].getPublicKey(), cliCerts)); + shc.handshakeSession.setPeerCertificates(cliCerts); + } + + private void onConsumeCertificate(ClientHandshakeContext chc, + T13CertificateMessage certificateMessage )throws IOException { + if (certificateMessage.certEntries == null || + certificateMessage.certEntries.isEmpty()) { + chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Empty server certificate chain"); + } + + // Each CertificateEntry will have its own set of extensions + // which must be consumed. + SSLExtension[] enabledExtensions = + chc.sslConfig.getEnabledExtensions(SSLHandshake.CERTIFICATE); + for (CertificateEntry certEnt : certificateMessage.certEntries) { + certEnt.extensions.consumeOnLoad(chc, enabledExtensions); + } + + // check server certificate entries + X509Certificate[] srvCerts = + checkServerCerts(chc, certificateMessage.certEntries); + + // + // update + // + chc.handshakeCredentials.add( + new X509Credentials(srvCerts[0].getPublicKey(), srvCerts)); + chc.handshakeSession.setPeerCertificates(srvCerts); + } + + private static X509Certificate[] checkClientCerts( + ServerHandshakeContext shc, + List certEntries) throws IOException { + X509Certificate[] certs = + new X509Certificate[certEntries.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (CertificateEntry entry : certEntries) { + certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(entry.encoded)); + } + } catch (CertificateException ce) { + shc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + // find out the types of client authentication used + String keyAlgorithm = certs[0].getPublicKey().getAlgorithm(); + String authType; + switch (keyAlgorithm) { + case "RSA": + case "DSA": + case "EC": + case "RSASSA-PSS": + authType = keyAlgorithm; + break; + default: + // unknown public key type + authType = "UNKNOWN"; + } + + try { + X509TrustManager tm = shc.sslContext.getX509TrustManager(); + if (tm instanceof X509ExtendedTrustManager) { + if (shc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + engine); + } else { + SSLSocket socket = (SSLSocket)shc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkClientTrusted( + certs.clone(), + authType, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + + // Once the client certificate chain has been validated, set + // the certificate chain in the TLS session. + shc.handshakeSession.setPeerCertificates(certs); + } catch (CertificateException ce) { + shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce); + } + + return certs; + } + + private static X509Certificate[] checkServerCerts( + ClientHandshakeContext chc, + List certEntries) throws IOException { + X509Certificate[] certs = + new X509Certificate[certEntries.size()]; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + int i = 0; + for (CertificateEntry entry : certEntries) { + certs[i++] = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(entry.encoded)); + } + } catch (CertificateException ce) { + chc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Failed to parse server certificates", ce); + } + + // find out the types of server authentication used + // + // Note that the "UNKNOWN" authentication type is sufficient to + // check the required digitalSignature KeyUsage for TLS 1.3. + String authType = "UNKNOWN"; + + try { + X509TrustManager tm = chc.sslContext.getX509TrustManager(); + if (tm instanceof X509ExtendedTrustManager) { + if (chc.conContext.transport instanceof SSLEngine) { + SSLEngine engine = (SSLEngine)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + authType, + engine); + } else { + SSLSocket socket = (SSLSocket)chc.conContext.transport; + ((X509ExtendedTrustManager)tm).checkServerTrusted( + certs.clone(), + authType, + socket); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + + // Once the server certificate chain has been validated, set + // the certificate chain in the TLS session. + chc.handshakeSession.setPeerCertificates(certs); + } catch (CertificateException ce) { + chc.conContext.fatal(getCertificateAlert(chc, ce), ce); + } + + return certs; + } + + /** + * When a failure happens during certificate checking from an + * {@link X509TrustManager}, determine what TLS alert description + * to use. + * + * @param cexc The exception thrown by the {@link X509TrustManager} + * + * @return A byte value corresponding to a TLS alert description number. + */ + private static Alert getCertificateAlert( + ClientHandshakeContext chc, CertificateException cexc) { + // The specific reason for the failure will determine how to + // set the alert description value + Alert alert = Alert.CERTIFICATE_UNKNOWN; + + Throwable baseCause = cexc.getCause(); + if (baseCause instanceof CertPathValidatorException) { + CertPathValidatorException cpve = + (CertPathValidatorException)baseCause; + Reason reason = cpve.getReason(); + if (reason == BasicReason.REVOKED) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_REVOKED; + } else if ( + reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { + alert = chc.staplingActive ? + Alert.BAD_CERT_STATUS_RESPONSE : + Alert.CERTIFICATE_UNKNOWN; + } + } + + return alert; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertificateRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 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 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 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 typeNames = new ArrayList<>(types.length); + for (byte type : types) { + typeNames.add(ClientCertificateType.nameOf(type)); + } + + List 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 authorities; // certificate authorities + + T12CertificateRequestMessage(HandshakeContext handshakeContext, + X509Certificate[] trustedCerts, KeyExchange keyExchange, + List 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 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 typeNames = new ArrayList<>(types.length); + for (byte type : types) { + typeNames.add(ClientCertificateType.nameOf(type)); + } + + List algorithmNames = new ArrayList<>(algorithmIds.length); + for (int algorithmId : algorithmIds) { + algorithmNames.add(SignatureScheme.nameOf(algorithmId)); + } + + List 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 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); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertificateStatus.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 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("\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); + } + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CertificateVerify.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CertificateVerify.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1143 @@ + /* + * 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.Arrays; +import java.util.Locale; +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 CertificateVerify handshake message. + */ +final class CertificateVerify { + static final SSLConsumer s30HandshakeConsumer = + new S30CertificateVerifyConsumer(); + static final HandshakeProducer s30HandshakeProducer = + new S30CertificateVerifyProducer(); + + static final SSLConsumer t10HandshakeConsumer = + new T10CertificateVerifyConsumer(); + static final HandshakeProducer t10HandshakeProducer = + new T10CertificateVerifyProducer(); + + static final SSLConsumer t12HandshakeConsumer = + new T12CertificateVerifyConsumer(); + static final HandshakeProducer t12HandshakeProducer = + new T12CertificateVerifyProducer(); + + static final SSLConsumer t13HandshakeConsumer = + new T13CertificateVerifyConsumer(); + static final HandshakeProducer t13HandshakeProducer = + new T13CertificateVerifyProducer(); + + /** + * The CertificateVerify handshake message (SSL 3.0). + */ + static final class S30CertificateVerifyMessage extends HandshakeMessage { + // signature bytes + private final byte[] signature; + + S30CertificateVerifyMessage(HandshakeContext context, + X509Possession x509Possession) throws IOException { + super(context); + + // This happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + byte[] temproary = null; + String algorithm = x509Possession.popPrivateKey.getAlgorithm(); + try { + Signature signer = + getSignature(algorithm, x509Possession.popPrivateKey); + byte[] hashes = chc.handshakeHash.digest(algorithm, + chc.handshakeSession.getMasterSecret()); + signer.update(hashes); + temproary = signer.sign(); + } catch (NoSuchAlgorithmException nsae) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + algorithm + + ") used in CertificateVerify handshake message", nsae); + } catch (GeneralSecurityException gse) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot produce CertificateVerify signature", gse); + } + + this.signature = temproary; + } + + S30CertificateVerifyMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + // This happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // digitally-signed struct { + // select(SignatureAlgorithm) { + // case anonymous: struct { }; + // case rsa: + // opaque md5_hash[16]; + // opaque sha_hash[20]; + // case dsa: + // opaque sha_hash[20]; + // }; + // } Signature; + if (m.remaining() < 2) { + shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateVerify message: no sufficient data"); + } + + // read and verify the signature + this.signature = Record.getBytes16(m); + X509Credentials x509Credentials = null; + for (SSLCredentials cd : shc.handshakeCredentials) { + if (cd instanceof X509Credentials) { + x509Credentials = (X509Credentials)cd; + break; + } + } + + if (x509Credentials == null || + x509Credentials.popPublicKey == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X509 credentials negotiated for CertificateVerify"); + } + + String algorithm = x509Credentials.popPublicKey.getAlgorithm(); + try { + Signature signer = + getSignature(algorithm, x509Credentials.popPublicKey); + byte[] hashes = shc.handshakeHash.digest(algorithm, + shc.handshakeSession.getMasterSecret()); + signer.update(hashes); + if (!signer.verify(signature)) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CertificateVerify message: invalid signature"); + } + } catch (NoSuchAlgorithmException nsae) { + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + algorithm + + ") used in CertificateVerify handshake message", nsae); + } catch (GeneralSecurityException gse) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify CertificateVerify signature", gse); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_VERIFY; + } + + @Override + public int messageLength() { + return 2 + signature.length; // 2: length of signature + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putBytes16(signature); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateVerify\": '{'\n" + + " \"signature\": '{'\n" + + "{0}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent( + hexEncoder.encodeBuffer(signature), " ") + }; + + return messageFormat.format(messageFields); + } + + /* + * Get the Signature object appropriate for verification using the + * given signature algorithm. + */ + private static Signature getSignature(String algorithm, + Key key) throws GeneralSecurityException { + Signature signer = null; + switch (algorithm) { + case "RSA": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA); + break; + case "DSA": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); + break; + case "EC": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); + break; + default: + throw new SignatureException("Unrecognized algorithm: " + + algorithm); + } + + if (signer != null) { + if (key instanceof PublicKey) { + signer.initVerify((PublicKey)(key)); + } else { + signer.initSign((PrivateKey)key); + } + } + + return signer; + } + } + + /** + * The "CertificateVerify" handshake message producer. + */ + private static final + class S30CertificateVerifyProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private S30CertificateVerifyProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + X509Possession x509Possession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null || + x509Possession.popPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 credentials negotiated for CertificateVerify"); + } + + return null; + } + + S30CertificateVerifyMessage cvm = + new S30CertificateVerifyMessage(chc, x509Possession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateVerify" handshake message consumer. + */ + private static final + class S30CertificateVerifyConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private S30CertificateVerifyConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + S30CertificateVerifyMessage cvm = + new S30CertificateVerifyMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateVerify handshake message", cvm); + } + + // + // update + // + // Need no additional validation. + + // + // produce + // + // Need no new handshake message producers here. + } + } + + /** + * The CertificateVerify handshake message (TLS 1.0/1.1). + */ + static final class T10CertificateVerifyMessage extends HandshakeMessage { + // signature bytes + private final byte[] signature; + + T10CertificateVerifyMessage(HandshakeContext context, + X509Possession x509Possession) throws IOException { + super(context); + + // This happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + byte[] temproary = null; + String algorithm = x509Possession.popPrivateKey.getAlgorithm(); + try { + Signature signer = + getSignature(algorithm, x509Possession.popPrivateKey); + byte[] hashes = chc.handshakeHash.digest(algorithm); + signer.update(hashes); + temproary = signer.sign(); + } catch (NoSuchAlgorithmException nsae) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + algorithm + + ") used in CertificateVerify handshake message", nsae); + } catch (GeneralSecurityException gse) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot produce CertificateVerify signature", gse); + } + + this.signature = temproary; + } + + T10CertificateVerifyMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + // This happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // digitally-signed struct { + // select(SignatureAlgorithm) { + // case anonymous: struct { }; + // case rsa: + // opaque md5_hash[16]; + // opaque sha_hash[20]; + // case dsa: + // opaque sha_hash[20]; + // }; + // } Signature; + if (m.remaining() < 2) { + shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateVerify message: no sufficient data"); + } + + // read and verify the signature + this.signature = Record.getBytes16(m); + X509Credentials x509Credentials = null; + for (SSLCredentials cd : shc.handshakeCredentials) { + if (cd instanceof X509Credentials) { + x509Credentials = (X509Credentials)cd; + break; + } + } + + if (x509Credentials == null || + x509Credentials.popPublicKey == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X509 credentials negotiated for CertificateVerify"); + } + + String algorithm = x509Credentials.popPublicKey.getAlgorithm(); + try { + Signature signer = + getSignature(algorithm, x509Credentials.popPublicKey); + byte[] hashes = shc.handshakeHash.digest(algorithm); + signer.update(hashes); + if (!signer.verify(signature)) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CertificateVerify message: invalid signature"); + } + } catch (NoSuchAlgorithmException nsae) { + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + algorithm + + ") used in CertificateVerify handshake message", nsae); + } catch (GeneralSecurityException gse) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify CertificateVerify signature", gse); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_VERIFY; + } + + @Override + public int messageLength() { + return 2 + signature.length; // 2: length of signature + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putBytes16(signature); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateVerify\": '{'\n" + + " \"signature\": '{'\n" + + "{0}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent( + hexEncoder.encodeBuffer(signature), " ") + }; + + return messageFormat.format(messageFields); + } + + /* + * Get the Signature object appropriate for verification using the + * given signature algorithm. + */ + private static Signature getSignature(String algorithm, + Key key) throws GeneralSecurityException { + Signature signer = null; + switch (algorithm) { + case "RSA": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA); + break; + case "DSA": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); + break; + case "EC": + signer = JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); + break; + default: + throw new SignatureException("Unrecognized algorithm: " + + algorithm); + } + + if (signer != null) { + if (key instanceof PublicKey) { + signer.initVerify((PublicKey)(key)); + } else { + signer.initSign((PrivateKey)key); + } + } + + return signer; + } + } + + /** + * The "CertificateVerify" handshake message producer. + */ + private static final + class T10CertificateVerifyProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T10CertificateVerifyProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + X509Possession x509Possession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null || + x509Possession.popPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 credentials negotiated for CertificateVerify"); + } + + return null; + } + + T10CertificateVerifyMessage cvm = + new T10CertificateVerifyMessage(chc, x509Possession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateVerify" handshake message consumer. + */ + private static final + class T10CertificateVerifyConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T10CertificateVerifyConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + T10CertificateVerifyMessage cvm = + new T10CertificateVerifyMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateVerify handshake message", cvm); + } + + // + // update + // + // Need no additional validation. + + // + // produce + // + // Need no new handshake message producers here. } + } + } + + /** + * The CertificateVerify handshake message (TLS 1.2). + */ + static final class T12CertificateVerifyMessage extends HandshakeMessage { + // the signature algorithm + private final SignatureScheme signatureScheme; + + // signature bytes + private final byte[] signature; + + T12CertificateVerifyMessage(HandshakeContext context, + X509Possession x509Possession) throws IOException { + super(context); + + // This happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + this.signatureScheme = SignatureScheme.getPreferableAlgorithm( + chc.peerRequestedSignatureSchemes, + x509Possession.popPrivateKey, + chc.negotiatedProtocol); + if (signatureScheme == null) { + // Unlikely, the credentials generator should have + // selected the preferable signature algorithm properly. + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "No preferred signature algorithm for CertificateVerify"); + } + + byte[] temproary = null; + try { + Signature signer = + signatureScheme.getSignature(x509Possession.popPrivateKey); + signer.update(chc.handshakeHash.archived()); + temproary = signer.sign(); + } catch (NoSuchAlgorithmException | + InvalidAlgorithmParameterException nsae) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message", nsae); + } catch (InvalidKeyException | SignatureException ikse) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot produce CertificateVerify signature", ikse); + } + + this.signature = temproary; + } + + T12CertificateVerifyMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // This happens in server side only. + ServerHandshakeContext shc = + (ServerHandshakeContext)handshakeContext; + + // struct { + // SignatureAndHashAlgorithm algorithm; + // opaque signature<0..2^16-1>; + // } DigitallySigned; + if (m.remaining() < 4) { + shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateVerify message: no sufficient data"); + } + + // SignatureAndHashAlgorithm algorithm + int ssid = Record.getInt16(m); + this.signatureScheme = SignatureScheme.valueOf(ssid); + if (signatureScheme == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid signature algorithm (" + ssid + + ") used in CertificateVerify handshake message"); + } + + if (!shc.localSupportedSignAlgs.contains(signatureScheme)) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message"); + } + + // read and verify the signature + X509Credentials x509Credentials = null; + for (SSLCredentials cd : shc.handshakeCredentials) { + if (cd instanceof X509Credentials) { + x509Credentials = (X509Credentials)cd; + break; + } + } + + if (x509Credentials == null || + x509Credentials.popPublicKey == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X509 credentials negotiated for CertificateVerify"); + } + + // opaque signature<0..2^16-1>; + this.signature = Record.getBytes16(m); + try { + Signature signer = + signatureScheme.getSignature(x509Credentials.popPublicKey); + signer.update(shc.handshakeHash.archived()); + if (!signer.verify(signature)) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CertificateVerify signature"); + } + } catch (NoSuchAlgorithmException | + InvalidAlgorithmParameterException nsae) { + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message", nsae); + } catch (InvalidKeyException | SignatureException ikse) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify CertificateVerify signature", ikse); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_VERIFY; + } + + @Override + public int messageLength() { + return 4 + signature.length; // 2: signature algorithm + // +2: length of signature + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt16(signatureScheme.id); + hos.putBytes16(signature); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateVerify\": '{'\n" + + " \"signature algorithm\": {0}\n" + + " \"signature\": '{'\n" + + "{1}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + signatureScheme.name, + Utilities.indent( + hexEncoder.encodeBuffer(signature), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "CertificateVerify" handshake message producer. + */ + private static final + class T12CertificateVerifyProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T12CertificateVerifyProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + X509Possession x509Possession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null || + x509Possession.popPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 credentials negotiated for CertificateVerify"); + } + + return null; + } + + T12CertificateVerifyMessage cvm = + new T12CertificateVerifyMessage(chc, x509Possession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateVerify" handshake message consumer. + */ + private static final + class T12CertificateVerifyConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T12CertificateVerifyConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + T12CertificateVerifyMessage cvm = + new T12CertificateVerifyMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateVerify handshake message", cvm); + } + + // + // update + // + // Need no additional validation. + + // + // produce + // + // Need no new handshake message producers here. + } + } + + /** + * The CertificateVerify handshake message (TLS 1.3). + */ + static final class T13CertificateVerifyMessage extends HandshakeMessage { + private static final byte[] serverSignHead = new byte[] { + // repeated 0x20 for 64 times + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + + // "TLS 1.3, server CertificateVerify" + 0x00 + (byte)0x54, (byte)0x4c, (byte)0x53, (byte)0x20, + (byte)0x31, (byte)0x2e, (byte)0x33, (byte)0x2c, + (byte)0x20, (byte)0x73, (byte)0x65, (byte)0x72, + (byte)0x76, (byte)0x65, (byte)0x72, (byte)0x20, + (byte)0x43, (byte)0x65, (byte)0x72, (byte)0x74, + (byte)0x69, (byte)0x66, (byte)0x69, (byte)0x63, + (byte)0x61, (byte)0x74, (byte)0x65, (byte)0x56, + (byte)0x65, (byte)0x72, (byte)0x69, (byte)0x66, + (byte)0x79, (byte)0x00 + }; + + private static final byte[] clientSignHead = new byte[] { + // repeated 0x20 for 64 times + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + (byte)0x20, (byte)0x20, (byte)0x20, (byte)0x20, + + // "TLS 1.3, client CertificateVerify" + 0x00 + (byte)0x54, (byte)0x4c, (byte)0x53, (byte)0x20, + (byte)0x31, (byte)0x2e, (byte)0x33, (byte)0x2c, + (byte)0x20, (byte)0x63, (byte)0x6c, (byte)0x69, + (byte)0x65, (byte)0x6e, (byte)0x74, (byte)0x20, + (byte)0x43, (byte)0x65, (byte)0x72, (byte)0x74, + (byte)0x69, (byte)0x66, (byte)0x69, (byte)0x63, + (byte)0x61, (byte)0x74, (byte)0x65, (byte)0x56, + (byte)0x65, (byte)0x72, (byte)0x69, (byte)0x66, + (byte)0x79, (byte)0x00 + }; + + + // the signature algorithm + private final SignatureScheme signatureScheme; + + // signature bytes + private final byte[] signature; + + T13CertificateVerifyMessage(HandshakeContext context, + X509Possession x509Possession) throws IOException { + super(context); + + this.signatureScheme = SignatureScheme.getPreferableAlgorithm( + context.peerRequestedSignatureSchemes, + x509Possession.popPrivateKey, + context.negotiatedProtocol); + if (signatureScheme == null) { + // Unlikely, the credentials generator should have + // selected the preferable signature algorithm properly. + context.conContext.fatal(Alert.INTERNAL_ERROR, + "No preferred signature algorithm for CertificateVerify"); + } + + byte[] hashValue = context.handshakeHash.digest(); + byte[] contentCovered; + if (context.sslConfig.isClientMode) { + contentCovered = Arrays.copyOf(clientSignHead, + clientSignHead.length + hashValue.length); + System.arraycopy(hashValue, 0, contentCovered, + clientSignHead.length, hashValue.length); + } else { + contentCovered = Arrays.copyOf(serverSignHead, + serverSignHead.length + hashValue.length); + System.arraycopy(hashValue, 0, contentCovered, + serverSignHead.length, hashValue.length); + } + + byte[] temproary = null; + try { + Signature signer = + signatureScheme.getSignature(x509Possession.popPrivateKey); + signer.update(contentCovered); + temproary = signer.sign(); + } catch (NoSuchAlgorithmException | + InvalidAlgorithmParameterException nsae) { + context.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message", nsae); + } catch (InvalidKeyException | SignatureException ikse) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot produce CertificateVerify signature", ikse); + } + + this.signature = temproary; + } + + T13CertificateVerifyMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + // struct { + // SignatureAndHashAlgorithm algorithm; + // opaque signature<0..2^16-1>; + // } DigitallySigned; + if (m.remaining() < 4) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid CertificateVerify message: no sufficient data"); + } + + // SignatureAndHashAlgorithm algorithm + int ssid = Record.getInt16(m); + this.signatureScheme = SignatureScheme.valueOf(ssid); + if (signatureScheme == null) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid signature algorithm (" + ssid + + ") used in CertificateVerify handshake message"); + } + + if (!context.localSupportedSignAlgs.contains(signatureScheme)) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message"); + } + + // read and verify the signature + X509Credentials x509Credentials = null; + for (SSLCredentials cd : context.handshakeCredentials) { + if (cd instanceof X509Credentials) { + x509Credentials = (X509Credentials)cd; + break; + } + } + + if (x509Credentials == null || + x509Credentials.popPublicKey == null) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No X509 credentials negotiated for CertificateVerify"); + } + + // opaque signature<0..2^16-1>; + this.signature = Record.getBytes16(m); + + byte[] hashValue = context.handshakeHash.digest(); + byte[] contentCovered; + if (context.sslConfig.isClientMode) { + contentCovered = Arrays.copyOf(serverSignHead, + serverSignHead.length + hashValue.length); + System.arraycopy(hashValue, 0, contentCovered, + serverSignHead.length, hashValue.length); + } else { + contentCovered = Arrays.copyOf(clientSignHead, + clientSignHead.length + hashValue.length); + System.arraycopy(hashValue, 0, contentCovered, + clientSignHead.length, hashValue.length); + } + + try { + Signature signer = + signatureScheme.getSignature(x509Credentials.popPublicKey); + signer.update(contentCovered); + if (!signer.verify(signature)) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CertificateVerify signature"); + } + } catch (NoSuchAlgorithmException | + InvalidAlgorithmParameterException nsae) { + context.conContext.fatal(Alert.INTERNAL_ERROR, + "Unsupported signature algorithm (" + + signatureScheme.name + + ") used in CertificateVerify handshake message", nsae); + } catch (InvalidKeyException | SignatureException ikse) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Cannot verify CertificateVerify signature", ikse); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CERTIFICATE_VERIFY; + } + + @Override + public int messageLength() { + return 4 + signature.length; // 2: signature algorithm + // +2: length of signature + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt16(signatureScheme.id); + hos.putBytes16(signature); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"CertificateVerify\": '{'\n" + + " \"signature algorithm\": {0}\n" + + " \"signature\": '{'\n" + + "{1}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + signatureScheme.name, + Utilities.indent( + hexEncoder.encodeBuffer(signature), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "CertificateVerify" handshake message producer. + */ + private static final + class T13CertificateVerifyProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13CertificateVerifyProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + X509Possession x509Possession = null; + for (SSLPossession possession : hc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null || + x509Possession.popPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "No X.509 credentials negotiated for CertificateVerify"); + } + + return null; + } + + if (hc.sslConfig.isClientMode) { + return onProduceCertificateVerify( + (ClientHandshakeContext)context, x509Possession); + } else { + return onProduceCertificateVerify( + (ServerHandshakeContext)context, x509Possession); + } + } + + private byte[] onProduceCertificateVerify(ServerHandshakeContext shc, + X509Possession x509Possession) throws IOException { + T13CertificateVerifyMessage cvm = + new T13CertificateVerifyMessage(shc, x509Possession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced server CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + + private byte[] onProduceCertificateVerify(ClientHandshakeContext chc, + X509Possession x509Possession) throws IOException { + T13CertificateVerifyMessage cvm = + new T13CertificateVerifyMessage(chc, x509Possession); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced client CertificateVerify handshake message", cvm); + } + + // Output the handshake message. + cvm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "CertificateVerify" handshake message consumer. + */ + private static final + class T13CertificateVerifyConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T13CertificateVerifyConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + T13CertificateVerifyMessage cvm = + new T13CertificateVerifyMessage(hc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CertificateVerify handshake message", cvm); + } + + // + // update + // + // Need no additional validation. + + // + // produce + // + // Need no new handshake message producers here. + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ChangeCipherSpec.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CipherBox.java --- a/src/java.base/share/classes/sun/security/ssl/CipherBox.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1150 +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.util.Hashtable; -import java.util.Arrays; - -import java.security.*; -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.GCMParameterSpec; - -import java.nio.*; - -import sun.security.ssl.CipherSuite.*; -import static sun.security.ssl.CipherSuite.*; -import static sun.security.ssl.CipherSuite.CipherType.*; - -import sun.security.util.HexDumpEncoder; - - -/** - * This class handles bulk data enciphering/deciphering for each SSLv3 - * message. This provides data confidentiality. Stream ciphers (such - * as RC4) don't need to do padding; block ciphers (e.g. DES) need it. - * - * Individual instances are obtained by calling the static method - * newCipherBox(), which should only be invoked by BulkCipher.newCipher(). - * - * In RFC 2246, with bock ciphers in CBC mode, the Initialization - * Vector (IV) for the first record is generated with the other keys - * and secrets when the security parameters are set. The IV for - * subsequent records is the last ciphertext block from the previous - * record. - * - * In RFC 4346, the implicit Initialization Vector (IV) is replaced - * with an explicit IV to protect against CBC attacks. RFC 4346 - * recommends two algorithms used to generated the per-record IV. - * The implementation uses the algorithm (2)(b), as described at - * section 6.2.3.2 of RFC 4346. - * - * The usage of IV in CBC block cipher can be illustrated in - * the following diagrams. - * - * (random) - * R P1 IV C1 - * | | | | - * SIV---+ |-----+ |-... |----- |------ - * | | | | | | | | - * +----+ | +----+ | +----+ | +----+ | - * | Ek | | + Ek + | | Dk | | | Dk | | - * +----+ | +----+ | +----+ | +----+ | - * | | | | | | | | - * |----| |----| SIV--+ |----| |-... - * | | | | - * IV C1 R P1 - * (discard) - * - * CBC Encryption CBC Decryption - * - * NOTE that any ciphering involved in key exchange (e.g. with RSA) is - * handled separately. - * - * @author David Brownell - * @author Andreas Sterbenz - */ -final class CipherBox { - - // A CipherBox that implements the identity operation - static final CipherBox NULL = new CipherBox(); - - /* Class and subclass dynamic debugging support */ - private static final Debug debug = Debug.getInstance("ssl"); - - // the protocol version this cipher conforms to - private final ProtocolVersion protocolVersion; - - // cipher object - private final Cipher cipher; - - /** - * secure random - */ - private SecureRandom random; - - /** - * fixed IV, the implicit nonce of AEAD cipher suite, only apply to - * AEAD cipher suites - */ - private final byte[] fixedIv; - - /** - * the key, reserved only for AEAD cipher initialization - */ - private final Key key; - - /** - * the operation mode, reserved for AEAD cipher initialization - */ - private final int mode; - - /** - * the authentication tag size, only apply to AEAD cipher suites - */ - private final int tagSize; - - /** - * the record IV length, only apply to AEAD cipher suites - */ - private final int recordIvSize; - - /** - * cipher type - */ - private final CipherType cipherType; - - /** - * Fixed masks of various block size, as the initial decryption IVs - * for TLS 1.1 or later. - * - * For performance, we do not use random IVs. As the initial decryption - * IVs will be discarded by TLS decryption processes, so the fixed masks - * do not hurt cryptographic strength. - */ - private static Hashtable masks; - - /** - * NULL cipherbox. Identity operation, no encryption. - */ - private CipherBox() { - this.protocolVersion = ProtocolVersion.DEFAULT_TLS; - this.cipher = null; - this.cipherType = NULL_CIPHER; - this.fixedIv = new byte[0]; - this.key = null; - this.mode = Cipher.ENCRYPT_MODE; // choose at random - this.random = null; - this.tagSize = 0; - this.recordIvSize = 0; - } - - /** - * Construct a new CipherBox using the cipher transformation. - * - * @exception NoSuchAlgorithmException if no appropriate JCE Cipher - * implementation could be found. - */ - private CipherBox(ProtocolVersion protocolVersion, BulkCipher bulkCipher, - SecretKey key, IvParameterSpec iv, SecureRandom random, - boolean encrypt) throws NoSuchAlgorithmException { - try { - this.protocolVersion = protocolVersion; - this.cipher = JsseJce.getCipher(bulkCipher.transformation); - this.mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; - - if (random == null) { - random = JsseJce.getSecureRandom(); - } - this.random = random; - this.cipherType = bulkCipher.cipherType; - - /* - * RFC 4346 recommends two algorithms used to generated the - * per-record IV. The implementation uses the algorithm (2)(b), - * as described at section 6.2.3.2 of RFC 4346. - * - * As we don't care about the initial IV value for TLS 1.1 or - * later, so if the "iv" parameter is null, we use the default - * value generated by Cipher.init() for encryption, and a fixed - * mask for decryption. - */ - if (iv == null && bulkCipher.ivSize != 0 && - mode == Cipher.DECRYPT_MODE && - protocolVersion.useTLS11PlusSpec()) { - iv = getFixedMask(bulkCipher.ivSize); - } - - if (cipherType == AEAD_CIPHER) { - // AEAD must completely initialize the cipher for each packet, - // and so we save initialization parameters for packet - // processing time. - - // Set the tag size for AEAD cipher - tagSize = bulkCipher.tagSize; - - // Reserve the key for AEAD cipher initialization - this.key = key; - - fixedIv = iv.getIV(); - if (fixedIv == null || - fixedIv.length != bulkCipher.fixedIvSize) { - throw new RuntimeException("Improper fixed IV for AEAD"); - } - - // Set the record IV length for AEAD cipher - recordIvSize = bulkCipher.ivSize - bulkCipher.fixedIvSize; - - // DON'T initialize the cipher for AEAD! - } else { - // CBC only requires one initialization during its lifetime - // (future packets/IVs set the proper CBC state), so we can - // initialize now. - - // Zeroize the variables that only apply to AEAD cipher - this.tagSize = 0; - this.fixedIv = new byte[0]; - this.recordIvSize = 0; - this.key = null; - - // Initialize the cipher - cipher.init(mode, key, iv, random); - } - } catch (NoSuchAlgorithmException e) { - throw e; - } catch (Exception e) { - throw new NoSuchAlgorithmException - ("Could not create cipher " + bulkCipher, e); - } catch (ExceptionInInitializerError e) { - throw new NoSuchAlgorithmException - ("Could not create cipher " + bulkCipher, e); - } - } - - /* - * Factory method to obtain a new CipherBox object. - */ - static CipherBox newCipherBox(ProtocolVersion version, BulkCipher cipher, - SecretKey key, IvParameterSpec iv, SecureRandom random, - boolean encrypt) throws NoSuchAlgorithmException { - if (cipher.allowed == false) { - throw new NoSuchAlgorithmException("Unsupported cipher " + cipher); - } - - if (cipher == BulkCipher.B_NULL) { - return NULL; - } else { - return new CipherBox(version, cipher, key, iv, random, encrypt); - } - } - - /* - * Get a fixed mask, as the initial decryption IVs for TLS 1.1 or later. - */ - private static IvParameterSpec getFixedMask(int ivSize) { - if (masks == null) { - masks = new Hashtable(5); - } - - IvParameterSpec iv = masks.get(ivSize); - if (iv == null) { - iv = new IvParameterSpec(new byte[ivSize]); - masks.put(ivSize, iv); - } - - return iv; - } - - /* - * Encrypts a block of data, returning the size of the - * resulting block. - */ - int encrypt(byte[] buf, int offset, int len) { - if (cipher == null) { - return len; - } - - try { - int blockSize = cipher.getBlockSize(); - if (cipherType == BLOCK_CIPHER) { - len = addPadding(buf, offset, len, blockSize); - } - - if (debug != null && Debug.isOn("plaintext")) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - - System.out.println( - "Padded plaintext before ENCRYPTION: len = " - + len); - hd.encodeBuffer( - new ByteArrayInputStream(buf, offset, len), - System.out); - } catch (IOException e) { } - } - - - if (cipherType == AEAD_CIPHER) { - try { - return cipher.doFinal(buf, offset, len, buf, offset); - } catch (IllegalBlockSizeException | BadPaddingException ibe) { - // unlikely to happen - throw new RuntimeException( - "Cipher error in AEAD mode in JCE provider " + - cipher.getProvider().getName(), ibe); - } - } else { - int newLen = cipher.update(buf, offset, len, buf, offset); - if (newLen != len) { - // catch BouncyCastle buffering error - throw new RuntimeException("Cipher buffering error " + - "in JCE provider " + cipher.getProvider().getName()); - } - return newLen; - } - } catch (ShortBufferException e) { - // unlikely to happen, we should have enough buffer space here - throw new ArrayIndexOutOfBoundsException(e.toString()); - } - } - - /* - * Encrypts a ByteBuffer block of data, returning the size of the - * resulting block. - * - * The byte buffers position and limit initially define the amount - * to encrypt. On return, the position and limit are - * set to last position padded/encrypted. The limit may have changed - * because of the added padding bytes. - */ - int encrypt(ByteBuffer bb, int outLimit) { - - int len = bb.remaining(); - - if (cipher == null) { - bb.position(bb.limit()); - return len; - } - - int pos = bb.position(); - - int blockSize = cipher.getBlockSize(); - if (cipherType == BLOCK_CIPHER) { - // addPadding adjusts pos/limit - len = addPadding(bb, blockSize); - bb.position(pos); - } - - if (debug != null && Debug.isOn("plaintext")) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - - System.out.println( - "Padded plaintext before ENCRYPTION: len = " - + len); - hd.encodeBuffer(bb.duplicate(), System.out); - - } catch (IOException e) { } - } - - /* - * Encrypt "in-place". This does not add its own padding. - */ - ByteBuffer dup = bb.duplicate(); - if (cipherType == AEAD_CIPHER) { - try { - int outputSize = cipher.getOutputSize(dup.remaining()); - if (outputSize > bb.remaining()) { - // need to expand the limit of the output buffer for - // the authentication tag. - // - // DON'T worry about the buffer's capacity, we have - // reserved space for the authentication tag. - if (outLimit < pos + outputSize) { - // unlikely to happen - throw new ShortBufferException( - "need more space in output buffer"); - } - bb.limit(pos + outputSize); - } - int newLen = cipher.doFinal(dup, bb); - if (newLen != outputSize) { - throw new RuntimeException( - "Cipher buffering error in JCE provider " + - cipher.getProvider().getName()); - } - return newLen; - } catch (IllegalBlockSizeException | - BadPaddingException | ShortBufferException ibse) { - // unlikely to happen - throw new RuntimeException( - "Cipher error in AEAD mode in JCE provider " + - cipher.getProvider().getName(), ibse); - } - } else { - int newLen; - try { - newLen = cipher.update(dup, bb); - } catch (ShortBufferException sbe) { - // unlikely to happen - throw new RuntimeException("Cipher buffering error " + - "in JCE provider " + cipher.getProvider().getName()); - } - - if (bb.position() != dup.position()) { - throw new RuntimeException("bytebuffer padding error"); - } - - if (newLen != len) { - // catch BouncyCastle buffering error - throw new RuntimeException("Cipher buffering error " + - "in JCE provider " + cipher.getProvider().getName()); - } - return newLen; - } - } - - - /* - * Decrypts a block of data, returning the size of the - * resulting block if padding was required. - * - * For SSLv3 and TLSv1.0, with block ciphers in CBC mode the - * Initialization Vector (IV) for the first record is generated by - * the handshake protocol, the IV for subsequent records is the - * last ciphertext block from the previous record. - * - * From TLSv1.1, the implicit IV is replaced with an explicit IV to - * protect against CBC attacks. - * - * Differentiating between bad_record_mac and decryption_failed alerts - * may permit certain attacks against CBC mode. It is preferable to - * uniformly use the bad_record_mac alert to hide the specific type of - * the error. - */ - int decrypt(byte[] buf, int offset, int len, - int tagLen) throws BadPaddingException { - if (cipher == null) { - return len; - } - - try { - int newLen; - if (cipherType == AEAD_CIPHER) { - try { - newLen = cipher.doFinal(buf, offset, len, buf, offset); - } catch (IllegalBlockSizeException ibse) { - // unlikely to happen - throw new RuntimeException( - "Cipher error in AEAD mode in JCE provider " + - cipher.getProvider().getName(), ibse); - } - } else { - newLen = cipher.update(buf, offset, len, buf, offset); - if (newLen != len) { - // catch BouncyCastle buffering error - throw new RuntimeException("Cipher buffering error " + - "in JCE provider " + cipher.getProvider().getName()); - } - } - if (debug != null && Debug.isOn("plaintext")) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - - System.out.println( - "Padded plaintext after DECRYPTION: len = " - + newLen); - hd.encodeBuffer( - new ByteArrayInputStream(buf, offset, newLen), - System.out); - } catch (IOException e) { } - } - - if (cipherType == BLOCK_CIPHER) { - int blockSize = cipher.getBlockSize(); - newLen = removePadding( - buf, offset, newLen, tagLen, blockSize, protocolVersion); - - if (protocolVersion.useTLS11PlusSpec()) { - if (newLen < blockSize) { - throw new BadPaddingException("The length after " + - "padding removal (" + newLen + ") should be larger " + - "than <" + blockSize + "> since explicit IV used"); - } - } - } - return newLen; - } catch (ShortBufferException e) { - // unlikely to happen, we should have enough buffer space here - throw new ArrayIndexOutOfBoundsException(e.toString()); - } - } - - /* - * Decrypts a block of data, returning the size of the - * resulting block if padding was required. position and limit - * point to the end of the decrypted/depadded data. The initial - * limit and new limit may be different, given we may - * have stripped off some padding bytes. - * - * @see decrypt(byte[], int, int) - */ - int decrypt(ByteBuffer bb, int tagLen) throws BadPaddingException { - - int len = bb.remaining(); - - if (cipher == null) { - bb.position(bb.limit()); - return len; - } - - try { - /* - * Decrypt "in-place". - */ - int pos = bb.position(); - ByteBuffer dup = bb.duplicate(); - int newLen; - if (cipherType == AEAD_CIPHER) { - try { - newLen = cipher.doFinal(dup, bb); - } catch (IllegalBlockSizeException ibse) { - // unlikely to happen - throw new RuntimeException( - "Cipher error in AEAD mode \"" + ibse.getMessage() + - " \"in JCE provider " + cipher.getProvider().getName()); - } - } else { - newLen = cipher.update(dup, bb); - if (newLen != len) { - // catch BouncyCastle buffering error - throw new RuntimeException("Cipher buffering error " + - "in JCE provider " + cipher.getProvider().getName()); - } - } - - // reset the limit to the end of the decryted data - bb.limit(pos + newLen); - - if (debug != null && Debug.isOn("plaintext")) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - - System.out.println( - "Padded plaintext after DECRYPTION: len = " - + newLen); - - hd.encodeBuffer( - bb.duplicate().position(pos), System.out); - } catch (IOException e) { } - } - - /* - * Remove the block padding. - */ - if (cipherType == BLOCK_CIPHER) { - int blockSize = cipher.getBlockSize(); - bb.position(pos); - newLen = removePadding(bb, tagLen, blockSize, protocolVersion); - - // check the explicit IV of TLS v1.1 or later - if (protocolVersion.useTLS11PlusSpec()) { - if (newLen < blockSize) { - throw new BadPaddingException("The length after " + - "padding removal (" + newLen + ") should be larger " + - "than <" + blockSize + "> since explicit IV used"); - } - - // reset the position to the end of the decrypted data - bb.position(bb.limit()); - } - } - return newLen; - } catch (ShortBufferException e) { - // unlikely to happen, we should have enough buffer space here - throw new ArrayIndexOutOfBoundsException(e.toString()); - } - } - - private static int addPadding(byte[] buf, int offset, int len, - int blockSize) { - int newlen = len + 1; - byte pad; - int i; - - if ((newlen % blockSize) != 0) { - newlen += blockSize - 1; - newlen -= newlen % blockSize; - } - pad = (byte) (newlen - len); - - if (buf.length < (newlen + offset)) { - throw new IllegalArgumentException("no space to pad buffer"); - } - - /* - * TLS version of the padding works for both SSLv3 and TLSv1 - */ - for (i = 0, offset += len; i < pad; i++) { - buf [offset++] = (byte) (pad - 1); - } - return newlen; - } - - /* - * Apply the padding to the buffer. - * - * Limit is advanced to the new buffer length. - * Position is equal to limit. - */ - private static int addPadding(ByteBuffer bb, int blockSize) { - - int len = bb.remaining(); - int offset = bb.position(); - - int newlen = len + 1; - byte pad; - int i; - - if ((newlen % blockSize) != 0) { - newlen += blockSize - 1; - newlen -= newlen % blockSize; - } - pad = (byte) (newlen - len); - - /* - * Update the limit to what will be padded. - */ - bb.limit(newlen + offset); - - /* - * TLS version of the padding works for both SSLv3 and TLSv1 - */ - for (i = 0, offset += len; i < pad; i++) { - bb.put(offset++, (byte) (pad - 1)); - } - - bb.position(offset); - bb.limit(offset); - - return newlen; - } - - /* - * A constant-time check of the padding. - * - * NOTE that we are checking both the padding and the padLen bytes here. - * - * The caller MUST ensure that the len parameter is a positive number. - */ - private static int[] checkPadding( - byte[] buf, int offset, int len, byte pad) { - - if (len <= 0) { - throw new RuntimeException("padding len must be positive"); - } - - // An array of hits is used to prevent Hotspot optimization for - // the purpose of a constant-time check. - int[] results = {0, 0}; // {missed #, matched #} - for (int i = 0; i <= 256;) { - for (int j = 0; j < len && i <= 256; j++, i++) { // j <= i - if (buf[offset + j] != pad) { - results[0]++; // mismatched padding data - } else { - results[1]++; // matched padding data - } - } - } - - return results; - } - - /* - * A constant-time check of the padding. - * - * NOTE that we are checking both the padding and the padLen bytes here. - * - * The caller MUST ensure that the bb parameter has remaining. - */ - private static int[] checkPadding(ByteBuffer bb, byte pad) { - - if (!bb.hasRemaining()) { - throw new RuntimeException("hasRemaining() must be positive"); - } - - // An array of hits is used to prevent Hotspot optimization for - // the purpose of a constant-time check. - int[] results = {0, 0}; // {missed #, matched #} - bb.mark(); - for (int i = 0; i <= 256; bb.reset()) { - for (; bb.hasRemaining() && i <= 256; i++) { - if (bb.get() != pad) { - results[0]++; // mismatched padding data - } else { - results[1]++; // matched padding data - } - } - } - - return results; - } - - /* - * Typical TLS padding format for a 64 bit block cipher is as follows: - * xx xx xx xx xx xx xx 00 - * xx xx xx xx xx xx 01 01 - * ... - * xx 06 06 06 06 06 06 06 - * 07 07 07 07 07 07 07 07 - * TLS also allows any amount of padding from 1 and 256 bytes as long - * as it makes the data a multiple of the block size - */ - private static int removePadding(byte[] buf, int offset, int len, - int tagLen, int blockSize, - ProtocolVersion protocolVersion) throws BadPaddingException { - - // last byte is length byte (i.e. actual padding length - 1) - int padOffset = offset + len - 1; - int padLen = buf[padOffset] & 0xFF; - - int newLen = len - (padLen + 1); - if ((newLen - tagLen) < 0) { - // If the buffer is not long enough to contain the padding plus - // a MAC tag, do a dummy constant-time padding check. - // - // Note that it is a dummy check, so we won't care about what is - // the actual padding data. - checkPadding(buf, offset, len, (byte)(padLen & 0xFF)); - - throw new BadPaddingException("Invalid Padding length: " + padLen); - } - - // The padding data should be filled with the padding length value. - int[] results = checkPadding(buf, offset + newLen, - padLen + 1, (byte)(padLen & 0xFF)); - if (protocolVersion.useTLS10PlusSpec()) { - if (results[0] != 0) { // padding data has invalid bytes - throw new BadPaddingException("Invalid TLS padding data"); - } - } else { // SSLv3 - // SSLv3 requires 0 <= length byte < block size - // some implementations do 1 <= length byte <= block size, - // so accept that as well - // v3 does not require any particular value for the other bytes - if (padLen > blockSize) { - throw new BadPaddingException("Padding length (" + - padLen + ") of SSLv3 message should not be bigger " + - "than the block size (" + blockSize + ")"); - } - } - return newLen; - } - - /* - * Position/limit is equal the removed padding. - */ - private static int removePadding(ByteBuffer bb, - int tagLen, int blockSize, - ProtocolVersion protocolVersion) throws BadPaddingException { - - int len = bb.remaining(); - int offset = bb.position(); - - // last byte is length byte (i.e. actual padding length - 1) - int padOffset = offset + len - 1; - int padLen = bb.get(padOffset) & 0xFF; - - int newLen = len - (padLen + 1); - if ((newLen - tagLen) < 0) { - // If the buffer is not long enough to contain the padding plus - // a MAC tag, do a dummy constant-time padding check. - // - // Note that it is a dummy check, so we won't care about what is - // the actual padding data. - checkPadding(bb.duplicate(), (byte)(padLen & 0xFF)); - - throw new BadPaddingException("Invalid Padding length: " + padLen); - } - - // The padding data should be filled with the padding length value. - int[] results = checkPadding( - bb.duplicate().position(offset + newLen), - (byte)(padLen & 0xFF)); - if (protocolVersion.useTLS10PlusSpec()) { - if (results[0] != 0) { // padding data has invalid bytes - throw new BadPaddingException("Invalid TLS padding data"); - } - } else { // SSLv3 - // SSLv3 requires 0 <= length byte < block size - // some implementations do 1 <= length byte <= block size, - // so accept that as well - // v3 does not require any particular value for the other bytes - if (padLen > blockSize) { - throw new BadPaddingException("Padding length (" + - padLen + ") of SSLv3 message should not be bigger " + - "than the block size (" + blockSize + ")"); - } - } - - /* - * Reset buffer limit to remove padding. - */ - bb.position(offset + newLen); - bb.limit(offset + newLen); - - return newLen; - } - - /* - * Dispose of any intermediate state in the underlying cipher. - * For PKCS11 ciphers, this will release any attached sessions, and - * thus make finalization faster. - */ - void dispose() { - try { - if (cipher != null) { - // ignore return value. - cipher.doFinal(); - } - } catch (Exception e) { - // swallow all types of exceptions. - } - } - - /* - * Does the cipher use CBC mode? - * - * @return true if the cipher use CBC mode, false otherwise. - */ - boolean isCBCMode() { - return cipherType == BLOCK_CIPHER; - } - - /* - * Does the cipher use AEAD mode? - * - * @return true if the cipher use AEAD mode, false otherwise. - */ - boolean isAEADMode() { - return cipherType == AEAD_CIPHER; - } - - /* - * Is the cipher null? - * - * @return true if the cipher is null, false otherwise. - */ - boolean isNullCipher() { - return cipher == null; - } - - /* - * Gets the explicit nonce/IV size of the cipher. - * - * The returned value is the SecurityParameters.record_iv_length in - * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the - * size of explicit nonce for AEAD mode. - * - * @return the explicit nonce size of the cipher. - */ - int getExplicitNonceSize() { - switch (cipherType) { - case BLOCK_CIPHER: - // For block ciphers, the explicit IV length is of length - // SecurityParameters.record_iv_length, which is equal to - // the SecurityParameters.block_size. - if (protocolVersion.useTLS11PlusSpec()) { - return cipher.getBlockSize(); - } - break; - case AEAD_CIPHER: - return recordIvSize; - // It is also the length of sequence number, which is - // used as the nonce_explicit for AEAD cipher suites. - } - - return 0; - } - - /* - * Applies the explicit nonce/IV to this cipher. This method is used to - * decrypt an SSL/TLS input record. - * - * The returned value is the SecurityParameters.record_iv_length in - * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the - * size of explicit nonce for AEAD mode. - * - * @param authenticator the authenticator to get the additional - * authentication data - * @param contentType the content type of the input record - * @param bb the byte buffer to get the explicit nonce from - * - * @return the explicit nonce size of the cipher. - */ - int applyExplicitNonce(Authenticator authenticator, byte contentType, - ByteBuffer bb, byte[] sequence) throws BadPaddingException { - switch (cipherType) { - case BLOCK_CIPHER: - // sanity check length of the ciphertext - int tagLen = (authenticator instanceof MAC) ? - ((MAC)authenticator).MAClen() : 0; - if (tagLen != 0) { - if (!sanityCheck(tagLen, bb.remaining())) { - throw new BadPaddingException( - "ciphertext sanity check failed"); - } - } - - // For block ciphers, the explicit IV length is of length - // SecurityParameters.record_iv_length, which is equal to - // the SecurityParameters.block_size. - if (protocolVersion.useTLS11PlusSpec()) { - return cipher.getBlockSize(); - } - break; - case AEAD_CIPHER: - if (bb.remaining() < (recordIvSize + tagSize)) { - throw new BadPaddingException( - "Insufficient buffer remaining for AEAD cipher " + - "fragment (" + bb.remaining() + "). Needs to be " + - "more than or equal to IV size (" + recordIvSize + - ") + tag size (" + tagSize + ")"); - } - - // initialize the AEAD cipher for the unique IV - byte[] iv = Arrays.copyOf(fixedIv, - fixedIv.length + recordIvSize); - bb.get(iv, fixedIv.length, recordIvSize); - bb.position(bb.position() - recordIvSize); - GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); - try { - cipher.init(mode, key, spec, random); - } catch (InvalidKeyException | - InvalidAlgorithmParameterException ikae) { - // unlikely to happen - throw new RuntimeException( - "invalid key or spec in GCM mode", ikae); - } - - // update the additional authentication data - byte[] aad = authenticator.acquireAuthenticationBytes( - contentType, bb.remaining() - recordIvSize - tagSize, - sequence); - cipher.updateAAD(aad); - - return recordIvSize; - // It is also the length of sequence number, which is - // used as the nonce_explicit for AEAD cipher suites. - } - - return 0; - } - - /* - * Creates the explicit nonce/IV to this cipher. This method is used to - * encrypt an SSL/TLS output record. - * - * The size of the returned array is the SecurityParameters.record_iv_length - * in RFC 4346/5246. It is the size of explicit IV for CBC mode, and the - * size of explicit nonce for AEAD mode. - * - * @param authenticator the authenticator to get the additional - * authentication data - * @param contentType the content type of the input record - * @param fragmentLength the fragment length of the output record, it is - * the TLSCompressed.length in RFC 4346/5246. - * - * @return the explicit nonce of the cipher. - */ - byte[] createExplicitNonce(Authenticator authenticator, - byte contentType, int fragmentLength) { - - byte[] nonce = new byte[0]; - switch (cipherType) { - case BLOCK_CIPHER: - if (protocolVersion.useTLS11PlusSpec()) { - // For block ciphers, the explicit IV length is of length - // SecurityParameters.record_iv_length, which is equal to - // the SecurityParameters.block_size. - // - // Generate a random number as the explicit IV parameter. - nonce = new byte[cipher.getBlockSize()]; - random.nextBytes(nonce); - } - break; - case AEAD_CIPHER: - // To be unique and aware of overflow-wrap, sequence number - // is used as the nonce_explicit of AEAD cipher suites. - nonce = authenticator.sequenceNumber(); - - // initialize the AEAD cipher for the unique IV - byte[] iv = Arrays.copyOf(fixedIv, - fixedIv.length + nonce.length); - System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); - GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); - try { - cipher.init(mode, key, spec, random); - } catch (InvalidKeyException | - InvalidAlgorithmParameterException ikae) { - // unlikely to happen - throw new RuntimeException( - "invalid key or spec in GCM mode", ikae); - } - - // Update the additional authentication data, using the - // implicit sequence number of the authenticator. - byte[] aad = authenticator.acquireAuthenticationBytes( - contentType, fragmentLength, null); - cipher.updateAAD(aad); - break; - } - - return nonce; - } - - // See also CipherSuite.calculatePacketSize(). - int calculatePacketSize(int fragmentSize, int macLen, int headerSize) { - int packetSize = fragmentSize; - if (cipher != null) { - int blockSize = cipher.getBlockSize(); - switch (cipherType) { - case BLOCK_CIPHER: - packetSize += macLen; - packetSize += 1; // 1 byte padding length field - packetSize += // use the minimal padding - (blockSize - (packetSize % blockSize)) % blockSize; - if (protocolVersion.useTLS11PlusSpec()) { - packetSize += blockSize; // explicit IV - } - - break; - case AEAD_CIPHER: - packetSize += recordIvSize; - packetSize += tagSize; - - break; - default: // NULL_CIPHER or STREAM_CIPHER - packetSize += macLen; - } - } - - return packetSize + headerSize; - } - - // See also CipherSuite.calculateFragSize(). - int calculateFragmentSize(int packetLimit, int macLen, int headerSize) { - int fragLen = packetLimit - headerSize; - if (cipher != null) { - int blockSize = cipher.getBlockSize(); - switch (cipherType) { - case BLOCK_CIPHER: - if (protocolVersion.useTLS11PlusSpec()) { - fragLen -= blockSize; // explicit IV - } - fragLen -= (fragLen % blockSize); // cannot hold a block - // No padding for a maximum fragment. - fragLen -= 1; // 1 byte padding length field: 0x00 - fragLen -= macLen; - - break; - case AEAD_CIPHER: - fragLen -= recordIvSize; - fragLen -= tagSize; - - break; - default: // NULL_CIPHER or STREAM_CIPHER - fragLen -= macLen; - } - } - - return fragLen; - } - - // Estimate the maximum fragment size of a received packet. - int estimateFragmentSize(int packetSize, int macLen, int headerSize) { - int fragLen = packetSize - headerSize; - if (cipher != null) { - int blockSize = cipher.getBlockSize(); - switch (cipherType) { - case BLOCK_CIPHER: - if (protocolVersion.useTLS11PlusSpec()) { - fragLen -= blockSize; // explicit IV - } - // No padding for a maximum fragment. - fragLen -= 1; // 1 byte padding length field: 0x00 - fragLen -= macLen; - - break; - case AEAD_CIPHER: - fragLen -= recordIvSize; - fragLen -= tagSize; - - break; - default: // NULL_CIPHER or STREAM_CIPHER - fragLen -= macLen; - } - } - - return fragLen; - } - - /** - * Sanity check the length of a fragment before decryption. - * - * In CBC mode, check that the fragment length is one or multiple times - * of the block size of the cipher suite, and is at least one (one is the - * smallest size of padding in CBC mode) bigger than the tag size of the - * MAC algorithm except the explicit IV size for TLS 1.1 or later. - * - * In non-CBC mode, check that the fragment length is not less than the - * tag size of the MAC algorithm. - * - * @return true if the length of a fragment matches above requirements - */ - private boolean sanityCheck(int tagLen, int fragmentLen) { - if (!isCBCMode()) { - return fragmentLen >= tagLen; - } - - int blockSize = cipher.getBlockSize(); - if ((fragmentLen % blockSize) == 0) { - int minimal = tagLen + 1; - minimal = (minimal >= blockSize) ? minimal : blockSize; - if (protocolVersion.useTLS11PlusSpec()) { - minimal += blockSize; // plus the size of the explicit IV - } - - return (fragmentLen >= minimal); - } - - return false; - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CipherSuite.java --- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -23,171 +23,939 @@ * questions. */ - package sun.security.ssl; -import java.util.*; - -import java.security.NoSuchAlgorithmException; -import java.security.InvalidKeyException; -import java.security.SecureRandom; -import java.security.KeyManagementException; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import static sun.security.ssl.CipherSuite.HashAlg.*; import static sun.security.ssl.CipherSuite.KeyExchange.*; -import static sun.security.ssl.CipherSuite.PRF.*; -import static sun.security.ssl.CipherSuite.CipherType.*; import static sun.security.ssl.CipherSuite.MacAlg.*; -import static sun.security.ssl.CipherSuite.BulkCipher.*; -import static sun.security.ssl.JsseJce.*; -import static sun.security.ssl.NamedGroupType.*; +import static sun.security.ssl.SSLCipher.*; +import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*; /** - * An SSL/TLS CipherSuite. Constants for the standard key exchange, cipher, - * and mac algorithms are also defined in this class. - * - * The CipherSuite class and the inner classes defined in this file roughly - * follow the type safe enum pattern described in Effective Java. This means: - * - * . instances are immutable, classes are final - * - * . there is a unique instance of every value, i.e. there are never two - * instances representing the same CipherSuite, etc. This means equality - * tests can be performed using == instead of equals() (although that works - * as well). [A minor exception are *unsupported* CipherSuites read from a - * handshake message, but this is usually irrelevant] + * Enum for SSL/(D)TLS cipher suites. * - * . instances are obtained using the static valueOf() factory methods. - * - * . properties are defined as final variables and made available as - * package private variables without method accessors - * - * . if the member variable allowed is false, the given algorithm is either - * unavailable or disabled at compile time - * + * Please refer to the "TLS Cipher Suite Registry" section for more details + * about each cipher suite: + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xml */ -final class CipherSuite implements Comparable { +enum CipherSuite { + // + // in preference order + // + + // Definition of the CipherSuites that are enabled by default. + // + // They are listed in preference order, most preferred first, using + // the following criteria: + // 1. Prefer Suite B compliant cipher suites, see RFC6460 (To be + // changed later, see below). + // 2. Prefer the stronger bulk cipher, in the order of AES_256(GCM), + // AES_128(GCM), AES_256, AES_128, 3DES-EDE. + // 3. Prefer the stronger MAC algorithm, in the order of SHA384, + // SHA256, SHA, MD5. + // 4. Prefer the better performance of key exchange and digital + // signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, + // RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS. + + TLS_AES_128_GCM_SHA256( + 0x1301, true, "TLS_AES_128_GCM_SHA256", + ProtocolVersion.PROTOCOLS_OF_13, B_AES_128_GCM_IV, H_SHA256), + TLS_AES_256_GCM_SHA384( + 0x1302, true, "TLS_AES_256_GCM_SHA384", + ProtocolVersion.PROTOCOLS_OF_13, B_AES_256_GCM_IV, H_SHA384), + + // Suite B compliant cipher suites, see RFC 6460. + // + // Note that, at present this provider is not Suite B compliant. The + // preference order of the GCM cipher suites does not follow the spec + // of RFC 6460. In this section, only two cipher suites are listed + // so that applications can make use of Suite-B compliant cipher + // suite firstly. + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384( + 0xC02C, true, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_ECDSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256( + 0xC02B, true, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_ECDSA, B_AES_128_GCM, M_NULL, H_SHA256), + + // AES_256(GCM) + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384( + 0xC030, true, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_RSA_WITH_AES_256_GCM_SHA384( + 0x009D, true, "TLS_RSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_RSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384( + 0xC02E, true, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_ECDSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384( + 0xC032, true, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_RSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384( + 0x009F, true, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_RSA, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384( + 0x00A3, true, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_DSS, B_AES_256_GCM, M_NULL, H_SHA384), + + // AES_128(GCM) + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256( + 0xC02F, true, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_RSA, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_RSA_WITH_AES_128_GCM_SHA256( + 0x009C, true, "TLS_RSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_RSA, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256( + 0xC02D, true, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_ECDSA, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256( + 0xC031, true, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_RSA, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256( + 0x009E, true, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_RSA, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256( + 0x00A2, true, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_DSS, B_AES_128_GCM, M_NULL, H_SHA256), + + // AES_256(CBC) + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384( + 0xC024, true, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_ECDSA, B_AES_256, M_SHA384, H_SHA384), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384( + 0xC028, true, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_RSA, B_AES_256, M_SHA384, H_SHA384), + TLS_RSA_WITH_AES_256_CBC_SHA256( + 0x003D, true, "TLS_RSA_WITH_AES_256_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_RSA, B_AES_256, M_SHA256, H_SHA256), + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384( + 0xC026, true, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_ECDSA, B_AES_256, M_SHA384, H_SHA384), + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384( + 0xC02A, true, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_RSA, B_AES_256, M_SHA384, H_SHA384), + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256( + 0x006B, true, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_RSA, B_AES_256, M_SHA256, H_SHA256), + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256( + 0x006A, true, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_DSS, B_AES_256, M_SHA256, H_SHA256), + + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA( + 0xC00A, true, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_ECDSA, B_AES_256, M_SHA, H_SHA256), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA( + 0xC014, true, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_RSA, B_AES_256, M_SHA, H_SHA256), + TLS_RSA_WITH_AES_256_CBC_SHA( + 0x0035, true, "TLS_RSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_RSA, B_AES_256, M_SHA, H_SHA256), + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA( + 0xC005, true, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ECDSA, B_AES_256, M_SHA, H_SHA256), + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA( + 0xC00F, true, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_RSA, B_AES_256, M_SHA, H_SHA256), + TLS_DHE_RSA_WITH_AES_256_CBC_SHA( + 0x0039, true, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_RSA, B_AES_256, M_SHA, H_SHA256), + TLS_DHE_DSS_WITH_AES_256_CBC_SHA( + 0x0038, true, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_DSS, B_AES_256, M_SHA, H_SHA256), - // minimum priority for supported CipherSuites - static final int SUPPORTED_SUITES_PRIORITY = 1; + // AES_128(CBC) + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256( + 0xC023, true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_ECDSA, B_AES_128, M_SHA256, H_SHA256), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256( + 0xC027, true, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDHE_RSA, B_AES_128, M_SHA256, H_SHA256), + TLS_RSA_WITH_AES_128_CBC_SHA256( + 0x003C, true, "TLS_RSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_RSA, B_AES_128, M_SHA256, H_SHA256), + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256( + 0xC025, true, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_ECDSA, B_AES_128, M_SHA256, H_SHA256), + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256( + 0xC029, true, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_ECDH_RSA, B_AES_128, M_SHA256, H_SHA256), + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256( + 0x0067, true, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_RSA, B_AES_128, M_SHA256, H_SHA256), + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256( + 0x0040, true, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256), + + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA( + 0xC009, true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_ECDSA, B_AES_128, M_SHA, H_SHA256), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA( + 0xC013, true, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_RSA, B_AES_128, M_SHA, H_SHA256), + TLS_RSA_WITH_AES_128_CBC_SHA( + 0x002F, true, "TLS_RSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_RSA, B_AES_128, M_SHA, H_SHA256), + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA( + 0xC004, true, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ECDSA, B_AES_128, M_SHA, H_SHA256), + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA( + 0xC00E, true, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_RSA, B_AES_128, M_SHA, H_SHA256), + TLS_DHE_RSA_WITH_AES_128_CBC_SHA( + 0x0033, true, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_RSA, B_AES_128, M_SHA, H_SHA256), + TLS_DHE_DSS_WITH_AES_128_CBC_SHA( + 0x0032, true, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_DSS, B_AES_128, M_SHA, H_SHA256), + + // 3DES_EDE + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA( + 0xC008, true, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_ECDSA, B_3DES, M_SHA, H_SHA256), + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA( + 0xC012, true, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_RSA, B_3DES, M_SHA, H_SHA256), + SSL_RSA_WITH_3DES_EDE_CBC_SHA( + 0x000A, true, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_12, + K_RSA, B_3DES, M_SHA, H_SHA256), + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA( + 0xC003, true, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ECDSA, B_3DES, M_SHA, H_SHA256), + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA( + 0xC00D, true, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_RSA, B_3DES, M_SHA, H_SHA256), + SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA( + 0x0016, true, "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_RSA, B_3DES, M_SHA, H_SHA256), + SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA( + 0x0013, true, "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_12, + K_DHE_DSS, B_3DES, M_SHA, H_SHA256), + + // Renegotiation protection request Signalling Cipher Suite Value (SCSV). + TLS_EMPTY_RENEGOTIATION_INFO_SCSV( // RFC 5746, TLS 1.2 and prior + 0x00FF, true, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_SCSV, B_NULL, M_NULL, H_NONE), - // minimum priority for default enabled CipherSuites - static final int DEFAULT_SUITES_PRIORITY = 300; + // Definition of the CipherSuites that are supported but not enabled + // by default. + // They are listed in preference order, preferred first, using the + // following criteria: + // 1. If a cipher suite has been obsoleted, we put it at the end of + // the list. + // 2. Prefer the stronger bulk cipher, in the order of AES_256, + // AES_128, 3DES-EDE, RC-4, DES, DES40, RC4_40, NULL. + // 3. Prefer the stronger MAC algorithm, in the order of SHA384, + // SHA256, SHA, MD5. + // 4. Prefer the better performance of key exchange and digital + // signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, + // RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS, anonymous. + TLS_DH_anon_WITH_AES_256_GCM_SHA384( + 0x00A7, false, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DH_ANON, B_AES_256_GCM, M_NULL, H_SHA384), + TLS_DH_anon_WITH_AES_128_GCM_SHA256( + 0x00A6, false, "TLS_DH_anon_WITH_AES_128_GCM_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DH_ANON, B_AES_128_GCM, M_NULL, H_SHA256), + TLS_DH_anon_WITH_AES_256_CBC_SHA256( + 0x006D, false, "TLS_DH_anon_WITH_AES_256_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DH_ANON, B_AES_256, M_SHA256, H_SHA256), + TLS_ECDH_anon_WITH_AES_256_CBC_SHA( + 0xC019, false, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ANON, B_AES_256, M_SHA, H_SHA256), + TLS_DH_anon_WITH_AES_256_CBC_SHA( + 0x003A, false, "TLS_DH_anon_WITH_AES_256_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DH_ANON, B_AES_256, M_SHA, H_SHA256), + TLS_DH_anon_WITH_AES_128_CBC_SHA256( + 0x006C, false, "TLS_DH_anon_WITH_AES_128_CBC_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_DH_ANON, B_AES_128, M_SHA256, H_SHA256), + TLS_ECDH_anon_WITH_AES_128_CBC_SHA( + 0xC018, false, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ANON, B_AES_128, M_SHA, H_SHA256), + TLS_DH_anon_WITH_AES_128_CBC_SHA( + 0x0034, false, "TLS_DH_anon_WITH_AES_128_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_DH_ANON, B_AES_128, M_SHA, H_SHA256), + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA( + 0xC017, false, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ANON, B_3DES, M_SHA, H_SHA256), + SSL_DH_anon_WITH_3DES_EDE_CBC_SHA( + 0x001B, false, "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", + "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_12, + K_DH_ANON, B_3DES, M_SHA, H_SHA256), + + // RC4 + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA( + 0xC007, false, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_ECDHE_ECDSA, B_RC4_128, M_SHA, H_SHA256), + TLS_ECDHE_RSA_WITH_RC4_128_SHA( + 0xC011, false, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_ECDHE_RSA, B_RC4_128, M_SHA, H_SHA256), + SSL_RSA_WITH_RC4_128_SHA( + 0x0005, false, "SSL_RSA_WITH_RC4_128_SHA", + "TLS_RSA_WITH_RC4_128_SHA", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_RSA, B_RC4_128, M_SHA, H_SHA256), + TLS_ECDH_ECDSA_WITH_RC4_128_SHA( + 0xC002, false, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_ECDH_ECDSA, B_RC4_128, M_SHA, H_SHA256), + TLS_ECDH_RSA_WITH_RC4_128_SHA( + 0xC00C, false, "TLS_ECDH_RSA_WITH_RC4_128_SHA", "", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_ECDH_RSA, B_RC4_128, M_SHA, H_SHA256), + SSL_RSA_WITH_RC4_128_MD5( + 0x0004, false, "SSL_RSA_WITH_RC4_128_MD5", + "TLS_RSA_WITH_RC4_128_MD5", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_RSA, B_RC4_128, M_MD5, H_SHA256), + TLS_ECDH_anon_WITH_RC4_128_SHA( + 0xC016, false, "TLS_ECDH_anon_WITH_RC4_128_SHA", "", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_ECDH_ANON, B_RC4_128, M_SHA, H_SHA256), + SSL_DH_anon_WITH_RC4_128_MD5( + 0x0018, false, "SSL_DH_anon_WITH_RC4_128_MD5", + "TLS_DH_anon_WITH_RC4_128_MD5", + ProtocolVersion.PROTOCOLS_TO_TLS12, + K_DH_ANON, B_RC4_128, M_MD5, H_SHA256), + + // weak cipher suites obsoleted in TLS 1.2 [RFC 5246] + SSL_RSA_WITH_DES_CBC_SHA( + 0x0009, false, "SSL_RSA_WITH_DES_CBC_SHA", + "TLS_RSA_WITH_DES_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_11, + K_RSA, B_DES, M_SHA, H_NONE), + SSL_DHE_RSA_WITH_DES_CBC_SHA( + 0x0015, false, "SSL_DHE_RSA_WITH_DES_CBC_SHA", + "TLS_DHE_RSA_WITH_DES_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_11, + K_DHE_RSA, B_DES, M_SHA, H_NONE), + SSL_DHE_DSS_WITH_DES_CBC_SHA( + 0x0012, false, "SSL_DHE_DSS_WITH_DES_CBC_SHA", + "TLS_DHE_DSS_WITH_DES_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_11, + K_DHE_DSS, B_DES, M_SHA, H_NONE), + SSL_DH_anon_WITH_DES_CBC_SHA( + 0x001A, false, "SSL_DH_anon_WITH_DES_CBC_SHA", + "TLS_DH_anon_WITH_DES_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_11, + K_DH_ANON, B_DES, M_SHA, H_NONE), - private static final boolean ALLOW_ECC = Debug.getBooleanProperty - ("com.sun.net.ssl.enableECC", true); + // weak cipher suites obsoleted in TLS 1.1 [RFC 4346] + SSL_RSA_EXPORT_WITH_DES40_CBC_SHA( + 0x0008, false, "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", + "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_10, + K_RSA_EXPORT, B_DES_40, M_SHA, H_NONE), + SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA( + 0x0014, false, "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_10, + K_DHE_RSA_EXPORT, B_DES_40, M_SHA, H_NONE), + SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA( + 0x0011, false, "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_10, + K_DHE_DSS_EXPORT, B_DES_40, M_SHA, H_NONE), + SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA( + 0x0019, false, "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + ProtocolVersion.PROTOCOLS_TO_10, + K_DH_ANON_EXPORT, B_DES_40, M_SHA, H_NONE), + SSL_RSA_EXPORT_WITH_RC4_40_MD5( + 0x0003, false, "SSL_RSA_EXPORT_WITH_RC4_40_MD5", + "TLS_RSA_EXPORT_WITH_RC4_40_MD5", + ProtocolVersion.PROTOCOLS_TO_10, + K_RSA_EXPORT, B_DES_40, M_MD5, H_NONE), + SSL_DH_anon_EXPORT_WITH_RC4_40_MD5( + 0x0017, false, "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", + "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", + ProtocolVersion.PROTOCOLS_TO_10, + K_DH_ANON, B_DES_40, M_MD5, H_NONE), + + // no traffic encryption cipher suites + TLS_RSA_WITH_NULL_SHA256( + 0x003B, false, "TLS_RSA_WITH_NULL_SHA256", "", + ProtocolVersion.PROTOCOLS_OF_12, + K_RSA, B_NULL, M_SHA256, H_SHA256), + TLS_ECDHE_ECDSA_WITH_NULL_SHA( + 0xC006, false, "TLS_ECDHE_ECDSA_WITH_NULL_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_ECDSA, B_NULL, M_SHA, H_SHA256), + TLS_ECDHE_RSA_WITH_NULL_SHA( + 0xC010, false, "TLS_ECDHE_RSA_WITH_NULL_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDHE_RSA, B_NULL, M_SHA, H_SHA256), + SSL_RSA_WITH_NULL_SHA( + 0x0002, false, "SSL_RSA_WITH_NULL_SHA", + "TLS_RSA_WITH_NULL_SHA", + ProtocolVersion.PROTOCOLS_TO_12, + K_RSA, B_NULL, M_SHA, H_SHA256), + TLS_ECDH_ECDSA_WITH_NULL_SHA( + 0xC001, false, "TLS_ECDH_ECDSA_WITH_NULL_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ECDSA, B_NULL, M_SHA, H_SHA256), + TLS_ECDH_RSA_WITH_NULL_SHA( + 0xC00B, false, "TLS_ECDH_RSA_WITH_NULL_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_RSA, B_NULL, M_SHA, H_SHA256), + TLS_ECDH_anon_WITH_NULL_SHA( + 0xC015, false, "TLS_ECDH_anon_WITH_NULL_SHA", "", + ProtocolVersion.PROTOCOLS_TO_12, + K_ECDH_ANON, B_NULL, M_SHA, H_SHA256), + SSL_RSA_WITH_NULL_MD5( + 0x0001, false, "SSL_RSA_WITH_NULL_MD5", + "TLS_RSA_WITH_NULL_MD5", + ProtocolVersion.PROTOCOLS_TO_12, + K_RSA, B_NULL, M_MD5, H_SHA256), + + // Definition of the CipherSuites that are not supported but the names + // are known. + TLS_CHACHA20_POLY1305_SHA256( // TLS 1.3 + "TLS_CHACHA20_POLY1305_SHA256", 0x1303), + TLS_AES_128_CCM_SHA256( // TLS 1.3 + "TLS_AES_128_CCM_SHA256", 0x1304), + TLS_AES_128_CCM_8_SHA256( // TLS 1.3 + "TLS_AES_128_CCM_8_SHA256", 0x1305), - // Map Integer(id) -> CipherSuite - // contains all known CipherSuites - private static final Map idMap; + // remaining unsupported ciphersuites defined in RFC2246. + CS_0006("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", 0x0006), + CS_0007("SSL_RSA_WITH_IDEA_CBC_SHA", 0x0007), + CS_000B("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x000b), + CS_000C("SSL_DH_DSS_WITH_DES_CBC_SHA", 0x000c), + CS_000D("SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", 0x000d), + CS_000E("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x000e), + CS_000F("SSL_DH_RSA_WITH_DES_CBC_SHA", 0x000f), + CS_0010("SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", 0x0010), + + // SSL 3.0 Fortezza ciphersuites + CS_001C("SSL_FORTEZZA_DMS_WITH_NULL_SHA", 0x001c), + CS_001D("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", 0x001d), + + // 1024/56 bit exportable ciphersuites from expired internet draft + CS_0062("SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA", 0x0062), + CS_0063("SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA", 0x0063), + CS_0064("SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", 0x0064), + CS_0065("SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", 0x0065), + CS_0066("SSL_DHE_DSS_WITH_RC4_128_SHA", 0x0066), + + // Netscape old and new SSL 3.0 FIPS ciphersuites + // see http://www.mozilla.org/projects/security/pki/nss/ssl/fips-ssl-ciphersuites.html + CS_FFE0("NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xffe0), + CS_FFE1("NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA", 0xffe1), + CS_FEFE("SSL_RSA_FIPS_WITH_DES_CBC_SHA", 0xfefe), + CS_FEFF("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xfeff), + + // Unsupported Kerberos cipher suites from RFC 2712 + CS_001E("TLS_KRB5_WITH_DES_CBC_SHA", 0x001E), + CS_001F("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", 0x001F), + CS_0020("TLS_KRB5_WITH_RC4_128_SHA", 0x0020), + CS_0021("TLS_KRB5_WITH_IDEA_CBC_SHA", 0x0021), + CS_0022("TLS_KRB5_WITH_DES_CBC_MD5", 0x0022), + CS_0023("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", 0x0023), + CS_0024("TLS_KRB5_WITH_RC4_128_MD5", 0x0024), + CS_0025("TLS_KRB5_WITH_IDEA_CBC_MD5", 0x0025), + CS_0026("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0026), + CS_0027("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", 0x0027), + CS_0028("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0028), + CS_0029("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0029), + CS_002A("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", 0x002a), + CS_002B("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x002B), + + // Unsupported cipher suites from RFC 4162 + CS_0096("TLS_RSA_WITH_SEED_CBC_SHA", 0x0096), + CS_0097("TLS_DH_DSS_WITH_SEED_CBC_SHA", 0x0097), + CS_0098("TLS_DH_RSA_WITH_SEED_CBC_SHA", 0x0098), + CS_0099("TLS_DHE_DSS_WITH_SEED_CBC_SHA", 0x0099), + CS_009A("TLS_DHE_RSA_WITH_SEED_CBC_SHA", 0x009a), + CS_009B("TLS_DH_anon_WITH_SEED_CBC_SHA", 0x009b), + + // Unsupported cipher suites from RFC 4279 + CS_008A("TLS_PSK_WITH_RC4_128_SHA", 0x008a), + CS_008B("TLS_PSK_WITH_3DES_EDE_CBC_SHA", 0x008b), + CS_008C("TLS_PSK_WITH_AES_128_CBC_SHA", 0x008c), + CS_008D("TLS_PSK_WITH_AES_256_CBC_SHA", 0x008d), + CS_008E("TLS_DHE_PSK_WITH_RC4_128_SHA", 0x008e), + CS_008F("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", 0x008f), + CS_0090("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", 0x0090), + CS_0091("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", 0x0091), + CS_0092("TLS_RSA_PSK_WITH_RC4_128_SHA", 0x0092), + CS_0093("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", 0x0093), + CS_0094("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", 0x0094), + CS_0095("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", 0x0095), + + // Unsupported cipher suites from RFC 4785 + CS_002C("TLS_PSK_WITH_NULL_SHA", 0x002c), + CS_002D("TLS_DHE_PSK_WITH_NULL_SHA", 0x002d), + CS_002E("TLS_RSA_PSK_WITH_NULL_SHA", 0x002e), + + // Unsupported cipher suites from RFC 5246 + CS_0030("TLS_DH_DSS_WITH_AES_128_CBC_SHA", 0x0030), + CS_0031("TLS_DH_RSA_WITH_AES_128_CBC_SHA", 0x0031), + CS_0036("TLS_DH_DSS_WITH_AES_256_CBC_SHA", 0x0036), + CS_0037("TLS_DH_RSA_WITH_AES_256_CBC_SHA", 0x0037), + CS_003E("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", 0x003e), + CS_003F("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", 0x003f), + CS_0068("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", 0x0068), + CS_0069("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", 0x0069), + + // Unsupported cipher suites from RFC 5288 + CS_00A0("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", 0x00a0), + CS_00A1("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", 0x00a1), + CS_00A4("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", 0x00a4), + CS_00A5("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", 0x00a5), - // Map String(name) -> CipherSuite - // contains only supported CipherSuites (i.e. allowed == true) - private static final Map nameMap; + // Unsupported cipher suites from RFC 5487 + CS_00A8("TLS_PSK_WITH_AES_128_GCM_SHA256", 0x00a8), + CS_00A9("TLS_PSK_WITH_AES_256_GCM_SHA384", 0x00a9), + CS_00AA("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", 0x00aa), + CS_00AB("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", 0x00ab), + CS_00AC("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", 0x00ac), + CS_00AD("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", 0x00ad), + CS_00AE("TLS_PSK_WITH_AES_128_CBC_SHA256", 0x00ae), + CS_00AF("TLS_PSK_WITH_AES_256_CBC_SHA384", 0x00af), + CS_00B0("TLS_PSK_WITH_NULL_SHA256", 0x00b0), + CS_00B1("TLS_PSK_WITH_NULL_SHA384", 0x00b1), + CS_00B2("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", 0x00b2), + CS_00B3("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", 0x00b3), + CS_00B4("TLS_DHE_PSK_WITH_NULL_SHA256", 0x00b4), + CS_00B5("TLS_DHE_PSK_WITH_NULL_SHA384", 0x00b5), + CS_00B6("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", 0x00b6), + CS_00B7("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", 0x00b7), + CS_00B8("TLS_RSA_PSK_WITH_NULL_SHA256", 0x00b8), + CS_00B9("TLS_RSA_PSK_WITH_NULL_SHA384", 0x00b9), + + // Unsupported cipher suites from RFC 5932 + CS_0041("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0041), + CS_0042("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0042), + CS_0043("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0043), + CS_0044("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0044), + CS_0045("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0045), + CS_0046("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", 0x0046), + CS_0084("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0084), + CS_0085("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0085), + CS_0086("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0086), + CS_0087("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0087), + CS_0088("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0088), + CS_0089("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", 0x0089), + CS_00BA("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00ba), + CS_00BB("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bb), + CS_00BC("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00bc), + CS_00BD("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bd), + CS_00BE("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00be), + CS_00BF("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", 0x00bf), + CS_00C0("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c0), + CS_00C1("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c1), + CS_00C2("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c2), + CS_00C3("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c3), + CS_00C4("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c4), + CS_00C5("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", 0x00c5), + + // TLS Fallback Signaling Cipher Suite Value (SCSV) RFC 7507 + CS_5600("TLS_FALLBACK_SCSV", 0x5600), + + // Unsupported cipher suites from RFC 5054 + CS_C01A("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", 0xc01a), + CS_C01B("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", 0xc01b), + CS_C01C("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", 0xc01c), + CS_C01D("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", 0xc01d), + CS_C01E("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", 0xc01e), + CS_C01F("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", 0xc01f), + CS_C020("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", 0xc020), + CS_C021("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", 0xc021), + CS_C022("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", 0xc022), + + // Unsupported cipher suites from RFC 5489 + CS_C033("TLS_ECDHE_PSK_WITH_RC4_128_SHA", 0xc033), + CS_C034("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", 0xc034), + CS_C035("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", 0xc035), + CS_C036("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", 0xc036), + CS_C037("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", 0xc037), + CS_C038("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", 0xc038), + CS_C039("TLS_ECDHE_PSK_WITH_NULL_SHA", 0xc039), + CS_C03A("TLS_ECDHE_PSK_WITH_NULL_SHA256", 0xc03a), + CS_C03B("TLS_ECDHE_PSK_WITH_NULL_SHA384", 0xc03b), - // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5 - // we use TLS_* only for new CipherSuites, still SSL_* for old ones - final String name; + // Unsupported cipher suites from RFC 6209 + CS_C03C("TLS_RSA_WITH_ARIA_128_CBC_SHA256", 0xc03c), + CS_C03D("TLS_RSA_WITH_ARIA_256_CBC_SHA384", 0xc03d), + CS_C03E("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", 0xc03e), + CS_C03F("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", 0xc03f), + CS_C040("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc040), + CS_C041("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc041), + CS_C042("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", 0xc042), + CS_C043("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", 0xc043), + CS_C044("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc044), + CS_C045("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc045), + CS_C046("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", 0xc046), + CS_C047("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", 0xc047), + CS_C048("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc048), + CS_C049("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc049), + CS_C04A("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc04a), + CS_C04B("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc04b), + CS_C04C("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04c), + CS_C04D("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04d), + CS_C04E("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04e), + CS_C04F("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04f), + CS_C050("TLS_RSA_WITH_ARIA_128_GCM_SHA256", 0xc050), + CS_C051("TLS_RSA_WITH_ARIA_256_GCM_SHA384", 0xc051), + CS_C052("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc052), + CS_C053("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc053), + CS_C054("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc054), + CS_C055("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc055), + CS_C056("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", 0xc056), + CS_C057("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", 0xc057), + CS_C058("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", 0xc058), + CS_C059("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", 0xc059), + CS_C05A("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", 0xc05a), + CS_C05B("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", 0xc05b), + CS_C05C("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05c), + CS_C05D("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05d), + CS_C05E("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05e), + CS_C05F("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05f), + CS_C060("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc060), + CS_C061("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc061), + CS_C062("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc062), + CS_C063("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc063), + CS_C064("TLS_PSK_WITH_ARIA_128_CBC_SHA256", 0xc064), + CS_C065("TLS_PSK_WITH_ARIA_256_CBC_SHA384", 0xc065), + CS_C066("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc066), + CS_C067("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc067), + CS_C068("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", 0xc068), + CS_C069("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", 0xc069), + CS_C06A("TLS_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06a), + CS_C06B("TLS_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06b), + CS_C06C("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06c), + CS_C06D("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06d), + CS_C06E("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06e), + CS_C06F("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06f), + CS_C070("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc070), + CS_C071("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc071), - // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5 + // Unsupported cipher suites from RFC 6367 + CS_C072("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072), + CS_C073("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073), + CS_C074("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc074), + CS_C075("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc075), + CS_C076("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc076), + CS_C077("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc077), + CS_C078("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc078), + CS_C079("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc079), + CS_C07A("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07a), + CS_C07B("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07b), + CS_C07C("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07c), + CS_C07D("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07d), + CS_C07E("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07e), + CS_C07F("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07f), + CS_C080("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc080), + CS_C081("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc081), + CS_C082("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc082), + CS_C083("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc083), + CS_C084("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", 0xc084), + CS_C085("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", 0xc085), + CS_C086("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086), + CS_C087("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087), + CS_C088("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc088), + CS_C089("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc089), + CS_C08A("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08a), + CS_C08B("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08b), + CS_C08C("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08c), + CS_C08D("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08d), + CS_C08E("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc08e), + CS_C08F("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc08f), + CS_C090("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc090), + CS_C091("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc091), + CS_C092("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc092), + CS_C093("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc093), + CS_C094("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc094), + CS_C095("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc095), + CS_C096("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc096), + CS_C097("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc097), + CS_C098("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc098), + CS_C099("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc099), + CS_C09A("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc09a), + CS_C09B("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc09b), + + // Unsupported cipher suites from RFC 6655 + CS_C09C("TLS_RSA_WITH_AES_128_CCM", 0xc09c), + CS_C09D("TLS_RSA_WITH_AES_256_CCM", 0xc09d), + CS_C09E("TLS_DHE_RSA_WITH_AES_128_CCM", 0xc09e), + CS_C09F("TLS_DHE_RSA_WITH_AES_256_CCM", 0xc09f), + CS_C0A0("TLS_RSA_WITH_AES_128_CCM_8", 0xc0A0), + CS_C0A1("TLS_RSA_WITH_AES_256_CCM_8", 0xc0A1), + CS_C0A2("TLS_DHE_RSA_WITH_AES_128_CCM_8", 0xc0A2), + CS_C0A3("TLS_DHE_RSA_WITH_AES_256_CCM_8", 0xc0A3), + CS_C0A4("TLS_PSK_WITH_AES_128_CCM", 0xc0A4), + CS_C0A5("TLS_PSK_WITH_AES_256_CCM", 0xc0A5), + CS_C0A6("TLS_DHE_PSK_WITH_AES_128_CCM", 0xc0A6), + CS_C0A7("TLS_DHE_PSK_WITH_AES_256_CCM", 0xc0A7), + CS_C0A8("TLS_PSK_WITH_AES_128_CCM_8", 0xc0A8), + CS_C0A9("TLS_PSK_WITH_AES_256_CCM_8", 0xc0A9), + CS_C0AA("TLS_PSK_DHE_WITH_AES_128_CCM_8", 0xc0Aa), + CS_C0AB("TLS_PSK_DHE_WITH_AES_256_CCM_8", 0xc0Ab), + + // Unsupported cipher suites from RFC 7251 + CS_C0AC("TLS_ECDHE_ECDSA_WITH_AES_128_CCM", 0xc0Ac), + CS_C0AD("TLS_ECDHE_ECDSA_WITH_AES_256_CCM", 0xc0Ad), + CS_C0AE("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", 0xc0Ae), + CS_C0AF("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", 0xc0Af), + + C_NULL("SSL_NULL_WITH_NULL_NULL", 0x0000); + final int id; - - // priority for the internal default preference order. the higher the - // better. Each supported CipherSuite *must* have a unique priority. - // Ciphersuites with priority >= DEFAULT_SUITES_PRIORITY are enabled - // by default - final int priority; + final boolean isDefaultEnabled; + final String name; + final List aliases; + final List supportedProtocols; + final KeyExchange keyExchange; + final SSLCipher bulkCipher; + final MacAlg macAlg; + final HashAlg hashAlg; - // key exchange, bulk cipher, mac and prf algorithms. See those - // classes below. - final KeyExchange keyExchange; - final BulkCipher cipher; - final MacAlg macAlg; - final PRF prfAlg; - - // whether a CipherSuite qualifies as exportable under 512/40 bit rules. - // TLS 1.1+ (RFC 4346) must not negotiate to these suites. final boolean exportable; - // true iff implemented and enabled at compile time - final boolean allowed; + // known but unsupported cipher suite + private CipherSuite(String name, int id) { + this(id, false, name, "", + ProtocolVersion.PROTOCOLS_EMPTY, null, null, null, null); + } + + // TLS 1.3 cipher suite + private CipherSuite(int id, boolean isDefaultEnabled, + String name, ProtocolVersion[] supportedProtocols, + SSLCipher bulkCipher, HashAlg hashAlg) { + this(id, isDefaultEnabled, name, "", + supportedProtocols, null, bulkCipher, M_NULL, hashAlg); + } - // obsoleted since protocol version - // - // TLS version is used. If checking DTLS versions, please map to - // TLS version firstly. See ProtocolVersion.mapToTLSProtocol(). - final int obsoleted; + private CipherSuite(int id, boolean isDefaultEnabled, + String name, String aliases, + ProtocolVersion[] supportedProtocols, + KeyExchange keyExchange, SSLCipher cipher, + MacAlg macAlg, HashAlg hashAlg) { + this.id = id; + this.isDefaultEnabled = isDefaultEnabled; + this.name = name; + if (aliases.isEmpty()) { + this.aliases = Arrays.asList(aliases.split(",")); + } else { + this.aliases = Collections.emptyList(); + } + this.supportedProtocols = Arrays.asList(supportedProtocols); + this.keyExchange = keyExchange; + this.bulkCipher = cipher; + this.macAlg = macAlg; + this.hashAlg = hashAlg; - // supported since protocol version (TLS version is used) - // - // TLS version is used. If checking DTLS versions, please map to - // TLS version firstly. See ProtocolVersion.mapToTLSProtocol(). - final int supported; + this.exportable = (cipher == null ? false : cipher.exportable); + } + + static CipherSuite nameOf(String ciperSuiteName) { + for (CipherSuite cs : CipherSuite.values()) { + if (cs.name.equals(ciperSuiteName) || + cs.aliases.contains(ciperSuiteName)) { + return cs; + } + } + + return null; + } - /** - * Constructor for implemented CipherSuites. - */ - private CipherSuite(String name, int id, int priority, - KeyExchange keyExchange, BulkCipher cipher, MacAlg mac, - boolean allowed, int obsoleted, int supported, PRF prfAlg) { - this.name = name; - this.id = id; - this.priority = priority; - this.keyExchange = keyExchange; - this.cipher = cipher; - this.macAlg = mac; - this.exportable = cipher.exportable; - allowed &= keyExchange.allowed; - allowed &= cipher.allowed; - this.allowed = allowed; - this.obsoleted = obsoleted; - this.supported = supported; - this.prfAlg = prfAlg; + static CipherSuite valueOf(int id) { + for (CipherSuite cs : CipherSuite.values()) { + if (cs.id == id) { + return cs; + } + } + + return null; + } + + static String nameOf(int id) { + for (CipherSuite cs : CipherSuite.values()) { + if (cs.id == id) { + return cs.name; + } + } + + return "UNKNOWN-CIPHER-SUITE(" + Utilities.byte16HexString(id) + ")"; + } + + static Collection allowedCipherSuites() { + Collection cipherSuites = new LinkedList<>(); + for (CipherSuite cs : CipherSuite.values()) { + if (!cs.supportedProtocols.isEmpty()) { + cipherSuites.add(cs); + } else { + // values() is ordered, remaining cipher suites are + // not supported. + break; + } + } + return cipherSuites; + } + + static Collection defaultCipherSuites() { + Collection cipherSuites = new LinkedList<>(); + for (CipherSuite cs : CipherSuite.values()) { + if (cs.isDefaultEnabled) { + cipherSuites.add(cs); + } else { + // values() is ordered, remaining cipher suites are + // not enabled. + break; + } + } + return cipherSuites; } /** - * Constructor for unimplemented CipherSuites. + * Validates and converts an array of cipher suite names. + * + * @throws IllegalArgumentException when one or more of the ciphers named + * by the parameter is not supported, or when the parameter is null. */ - private CipherSuite(String name, int id) { - this.name = name; - this.id = id; - this.allowed = false; + static List validValuesOf(String[] names) { + if (names == null) { + throw new IllegalArgumentException("CipherSuites cannot be null"); + } + + List cipherSuites = new ArrayList<>(names.length); + for (String name : names) { + if (name == null) { + throw new IllegalArgumentException( + "The specified CipherSuites array contain null element"); + } - this.priority = 0; - this.keyExchange = null; - this.cipher = null; - this.macAlg = null; - this.exportable = false; - this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE; - this.supported = ProtocolVersion.LIMIT_MIN_VALUE; - this.prfAlg = P_NONE; + boolean found = false; + for (CipherSuite cs : CipherSuite.values()) { + if (!cs.supportedProtocols.isEmpty()) { + if (cs.name.equals(name) || + cs.aliases.contains(name)) { + cipherSuites.add(cs); + found = true; + break; + } + } else { + // values() is ordered, remaining cipher suites are + // not supported. + break; + } + } + if (!found) { + throw new IllegalArgumentException( + "Unsupported CipherSuite: " + name); + } + } + + return Collections.unmodifiableList(cipherSuites); } - /** - * Return whether this CipherSuite is available for use. A - * CipherSuite may be unavailable even if it is supported - * (i.e. allowed == true) if the required JCE cipher is not installed. - */ + static String[] namesOf(List cipherSuites) { + String[] names = new String[cipherSuites.size()]; + int i = 0; + for (CipherSuite cipherSuite : cipherSuites) { + names[i++] = cipherSuite.name; + } + + return names; + } + boolean isAvailable() { - return allowed && keyExchange.isAvailable() && cipher.isAvailable(); + // Note: keyExchange is null for TLS 1.3 CipherSuites. + return !supportedProtocols.isEmpty() && + (keyExchange == null || keyExchange.isAvailable()) && + bulkCipher != null && bulkCipher.isAvailable(); + } + + public boolean supports(ProtocolVersion protocolVersion) { + return supportedProtocols.contains(protocolVersion); } boolean isNegotiable() { - return this != C_SCSV && isAvailable(); + return this != TLS_EMPTY_RENEGOTIATION_INFO_SCSV && isAvailable(); } - // See also CipherBox.calculatePacketSize(). + boolean isAnonymous() { + return (keyExchange != null && keyExchange.isAnonymous); + } + + // See also SSLWriteCipher.calculatePacketSize(). int calculatePacketSize(int fragmentSize, ProtocolVersion protocolVersion, boolean isDTLS) { - int packetSize = fragmentSize; - if (cipher != B_NULL) { - int blockSize = cipher.ivSize; - switch (cipher.cipherType) { + if (bulkCipher != null && bulkCipher != B_NULL) { + int blockSize = bulkCipher.ivSize; + switch (bulkCipher.cipherType) { case BLOCK_CIPHER: packetSize += macAlg.size; packetSize += 1; // 1 byte padding length field @@ -198,13 +966,17 @@ } break; - case AEAD_CIPHER: - packetSize += cipher.ivSize - cipher.fixedIvSize; // record IV - packetSize += cipher.tagSize; + case AEAD_CIPHER: + if (protocolVersion == ProtocolVersion.TLS12 || + protocolVersion == ProtocolVersion.DTLS12) { + packetSize += + bulkCipher.ivSize - bulkCipher.fixedIvSize; + } + packetSize += bulkCipher.tagSize; - break; - default: // NULL_CIPHER or STREAM_CIPHER - packetSize += macAlg.size; + break; + default: // NULL_CIPHER or STREAM_CIPHER + packetSize += macAlg.size; } } @@ -215,29 +987,28 @@ // See also CipherBox.calculateFragmentSize(). int calculateFragSize(int packetLimit, ProtocolVersion protocolVersion, boolean isDTLS) { - int fragSize = packetLimit - (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize); - if (cipher != B_NULL) { - int blockSize = cipher.ivSize; - switch (cipher.cipherType) { - case BLOCK_CIPHER: - if (protocolVersion.useTLS11PlusSpec()) { - fragSize -= blockSize; // explicit IV - } - fragSize -= (fragSize % blockSize); // cannot hold a block - // No padding for a maximum fragment. - fragSize -= 1; // 1 byte padding length field: 0x00 - fragSize -= macAlg.size; + if (bulkCipher != null && bulkCipher != B_NULL) { + int blockSize = bulkCipher.ivSize; + switch (bulkCipher.cipherType) { + case BLOCK_CIPHER: + if (protocolVersion.useTLS11PlusSpec()) { + fragSize -= blockSize; // explicit IV + } + fragSize -= (fragSize % blockSize); // cannot hold a block + // No padding for a maximum fragment. + fragSize -= 1; // 1 byte padding length field: 0x00 + fragSize -= macAlg.size; - break; - case AEAD_CIPHER: - fragSize -= cipher.tagSize; - fragSize -= cipher.ivSize - cipher.fixedIvSize; // record IV + break; + case AEAD_CIPHER: + fragSize -= bulkCipher.tagSize; + fragSize -= bulkCipher.ivSize - bulkCipher.fixedIvSize; - break; - default: // NULL_CIPHER or STREAM_CIPHER - fragSize -= macAlg.size; + break; + default: // NULL_CIPHER or STREAM_CIPHER + fragSize -= macAlg.size; } } @@ -245,172 +1016,48 @@ } /** - * Compares CipherSuites based on their priority. Has the effect of - * sorting CipherSuites when put in a sorted collection, which is - * used by CipherSuiteList. Follows standard Comparable contract. - * - * Note that for unsupported CipherSuites parsed from a handshake - * message we violate the equals() contract. - */ - @Override - public int compareTo(CipherSuite o) { - return o.priority - priority; - } - - /** - * Returns this.name. - */ - @Override - public String toString() { - return name; - } - - /** - * Return a CipherSuite for the given name. The returned CipherSuite - * is supported by this implementation but may not actually be - * currently useable. See isAvailable(). - * - * @exception IllegalArgumentException if the CipherSuite is unknown or - * unsupported. - */ - static CipherSuite valueOf(String s) { - if (s == null) { - throw new IllegalArgumentException("Name must not be null"); - } - - CipherSuite c = nameMap.get(s); - if ((c == null) || (c.allowed == false)) { - throw new IllegalArgumentException("Unsupported ciphersuite " + s); - } - - return c; - } - - /** - * Return a CipherSuite with the given ID. A temporary object is - * constructed if the ID is unknown. Use isAvailable() to verify that - * the CipherSuite can actually be used. - */ - static CipherSuite valueOf(int id1, int id2) { - id1 &= 0xff; - id2 &= 0xff; - int id = (id1 << 8) | id2; - CipherSuite c = idMap.get(id); - if (c == null) { - String h1 = Integer.toString(id1, 16); - String h2 = Integer.toString(id2, 16); - c = new CipherSuite("Unknown 0x" + h1 + ":0x" + h2, id); - } - return c; - } - - // for use by SSLContextImpl only - static Collection allowedCipherSuites() { - return nameMap.values(); - } - - /* - * Use this method when all of the values need to be specified. - * This is primarily used when defining a new ciphersuite for - * TLS 1.2+ that doesn't use the "default" PRF. - */ - private static void add(String name, int id, int priority, - KeyExchange keyExchange, BulkCipher cipher, MacAlg mac, - boolean allowed, int obsoleted, int supported, PRF prf) { - - CipherSuite c = new CipherSuite(name, id, priority, keyExchange, - cipher, mac, allowed, obsoleted, supported, prf); - if (idMap.put(id, c) != null) { - throw new RuntimeException("Duplicate ciphersuite definition: " - + id + ", " + name); - } - if (c.allowed) { - if (nameMap.put(name, c) != null) { - throw new RuntimeException("Duplicate ciphersuite definition: " - + id + ", " + name); - } - } - } - - /* - * Use this method when there is no lower protocol limit where this - * suite can be used, and the PRF is P_SHA256. That is, the - * existing ciphersuites. From RFC 5246: - * - * All cipher suites in this document use P_SHA256. - */ - private static void add(String name, int id, int priority, - KeyExchange keyExchange, BulkCipher cipher, MacAlg mac, - boolean allowed, int obsoleted) { - PRF prf = obsoleted < ProtocolVersion.TLS12.v ? P_NONE : P_SHA256; - - add(name, id, priority, keyExchange, cipher, mac, allowed, obsoleted, - ProtocolVersion.LIMIT_MIN_VALUE, prf); - } - - /* - * Use this method when there is no upper protocol limit. That is, - * suites which have not been obsoleted. - */ - private static void add(String name, int id, int priority, - KeyExchange keyExchange, BulkCipher cipher, MacAlg mac, - boolean allowed) { - add(name, id, priority, keyExchange, cipher, mac, allowed, - ProtocolVersion.LIMIT_MAX_VALUE); - } - - /* - * Use this method to define an unimplemented suite. This provides - * a number<->name mapping that can be used for debugging. - */ - private static void add(String name, int id) { - CipherSuite c = new CipherSuite(name, id); - if (idMap.put(id, c) != null) { - throw new RuntimeException("Duplicate ciphersuite definition: " - + id + ", " + name); - } - } - - /** * An SSL/TLS key exchange algorithm. */ static enum KeyExchange { + K_NULL ("NULL", false, true, NAMED_GROUP_NONE), + K_RSA ("RSA", true, false, NAMED_GROUP_NONE), + K_RSA_EXPORT ("RSA_EXPORT", true, false, NAMED_GROUP_NONE), + K_DH_RSA ("DH_RSA", false, false, NAMED_GROUP_NONE), + K_DH_DSS ("DH_DSS", false, false, NAMED_GROUP_NONE), + K_DHE_DSS ("DHE_DSS", true, false, NAMED_GROUP_FFDHE), + K_DHE_DSS_EXPORT("DHE_DSS_EXPORT", true, false, NAMED_GROUP_NONE), + K_DHE_RSA ("DHE_RSA", true, false, NAMED_GROUP_FFDHE), + K_DHE_RSA_EXPORT("DHE_RSA_EXPORT", true, false, NAMED_GROUP_NONE), + K_DH_ANON ("DH_anon", true, true, NAMED_GROUP_FFDHE), + K_DH_ANON_EXPORT("DH_anon_EXPORT", true, true, NAMED_GROUP_NONE), - // key exchange algorithms - K_NULL ("NULL", false, NAMED_GROUP_NONE), - K_RSA ("RSA", true, NAMED_GROUP_NONE), - K_RSA_EXPORT ("RSA_EXPORT", true, NAMED_GROUP_NONE), - K_DH_RSA ("DH_RSA", false, NAMED_GROUP_NONE), - K_DH_DSS ("DH_DSS", false, NAMED_GROUP_NONE), - K_DHE_DSS ("DHE_DSS", true, NAMED_GROUP_FFDHE), - K_DHE_RSA ("DHE_RSA", true, NAMED_GROUP_FFDHE), - K_DH_ANON ("DH_anon", true, NAMED_GROUP_FFDHE), - - K_ECDH_ECDSA ("ECDH_ECDSA", ALLOW_ECC, NAMED_GROUP_ECDHE), - K_ECDH_RSA ("ECDH_RSA", ALLOW_ECC, NAMED_GROUP_ECDHE), - K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC, NAMED_GROUP_ECDHE), - K_ECDHE_RSA ("ECDHE_RSA", ALLOW_ECC, NAMED_GROUP_ECDHE), - K_ECDH_ANON ("ECDH_anon", ALLOW_ECC, NAMED_GROUP_ECDHE), - - // Kerberos cipher suites - K_KRB5 ("KRB5", true, NAMED_GROUP_NONE), - K_KRB5_EXPORT("KRB5_EXPORT", true, NAMED_GROUP_NONE), + K_ECDH_ECDSA ("ECDH_ECDSA", true, false, NAMED_GROUP_ECDHE), + K_ECDH_RSA ("ECDH_RSA", true, false, NAMED_GROUP_ECDHE), + K_ECDHE_ECDSA ("ECDHE_ECDSA", true, false, NAMED_GROUP_ECDHE), + K_ECDHE_RSA ("ECDHE_RSA", true, false, NAMED_GROUP_ECDHE), + K_ECDH_ANON ("ECDH_anon", true, true, NAMED_GROUP_ECDHE), // renegotiation protection request signaling cipher suite - K_SCSV ("SCSV", true, NAMED_GROUP_NONE); + K_SCSV ("SCSV", true, true, NAMED_GROUP_NONE); // name of the key exchange algorithm, e.g. DHE_DSS final String name; final boolean allowed; final NamedGroupType groupType; private final boolean alwaysAvailable; + private final boolean isAnonymous; - KeyExchange(String name, boolean allowed, NamedGroupType groupType) { + KeyExchange(String name, boolean allowed, + boolean isAnonymous, NamedGroupType groupType) { this.name = name; - this.allowed = allowed; + if (groupType == NAMED_GROUP_ECDHE) { + this.allowed = JsseJce.ALLOW_ECC; + } else { + this.allowed = allowed; + } this.groupType = groupType; - this.alwaysAvailable = allowed && - (!name.startsWith("EC")) && (!name.startsWith("KRB")); + this.alwaysAvailable = allowed && (!name.startsWith("EC")); + this.isAnonymous = isAnonymous; } boolean isAvailable() { @@ -420,8 +1067,6 @@ if (groupType == NAMED_GROUP_ECDHE) { return (allowed && JsseJce.isEcAvailable()); - } else if (name.startsWith("KRB")) { - return (allowed && JsseJce.isKerberosAvailable()); } else { return allowed; } @@ -433,192 +1078,6 @@ } } - static enum CipherType { - NULL_CIPHER, // null cipher - STREAM_CIPHER, // stream cipher - BLOCK_CIPHER, // block cipher in CBC mode - AEAD_CIPHER // AEAD cipher - } - - /** - * An SSL/TLS bulk cipher algorithm. One instance per combination of - * cipher and key length. - * - * Also contains a factory method to obtain in initialized CipherBox - * for this algorithm. - */ - static enum BulkCipher { - - // export strength ciphers - B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true), - B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true), - B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false), - B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true), - - // domestic strength ciphers - B_RC4_128(CIPHER_RC4, STREAM_CIPHER, 16, 0, 0, true), - B_DES(CIPHER_DES, BLOCK_CIPHER, 8, 8, 0, true), - B_3DES(CIPHER_3DES, BLOCK_CIPHER, 24, 8, 0, true), - B_IDEA("IDEA", BLOCK_CIPHER, 16, 8, 0, false), - B_AES_128(CIPHER_AES, BLOCK_CIPHER, 16, 16, 0, true), - B_AES_256(CIPHER_AES, BLOCK_CIPHER, 32, 16, 0, true), - B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true), - B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true); - - // descriptive name including key size, e.g. AES/128 - final String description; - - // JCE cipher transformation string, e.g. AES/CBC/NoPadding - final String transformation; - - // algorithm name, e.g. AES - final String algorithm; - - // supported and compile time enabled. Also see isAvailable() - final boolean allowed; - - // number of bytes of entropy in the key - final int keySize; - - // length of the actual cipher key in bytes. - // for non-exportable ciphers, this is the same as keySize - final int expandedKeySize; - - // size of the IV - final int ivSize; - - // size of fixed IV - // - // record_iv_length = ivSize - fixedIvSize - final int fixedIvSize; - - // exportable under 512/40 bit rules - final boolean exportable; - - // Is the cipher algorithm of Cipher Block Chaining (CBC) mode? - final CipherType cipherType; - - // size of the authentication tag, only applicable to cipher suites in - // Galois Counter Mode (GCM) - // - // As far as we know, all supported GCM cipher suites use 128-bits - // authentication tags. - final int tagSize = 16; - - // The secure random used to detect the cipher availability. - private static final SecureRandom secureRandom; - - // runtime availability - private final boolean isAvailable; - - static { - try { - secureRandom = JsseJce.getSecureRandom(); - } catch (KeyManagementException kme) { - throw new RuntimeException(kme); - } - } - - BulkCipher(String transformation, CipherType cipherType, int keySize, - int expandedKeySize, int ivSize, - int fixedIvSize, boolean allowed) { - - this.transformation = transformation; - String[] splits = transformation.split("/"); - this.algorithm = splits[0]; - this.cipherType = cipherType; - this.description = this.algorithm + "/" + (keySize << 3); - this.keySize = keySize; - this.ivSize = ivSize; - this.fixedIvSize = fixedIvSize; - this.allowed = allowed; - - this.expandedKeySize = expandedKeySize; - this.exportable = true; - - // availability of this bulk cipher - // - // Currently all supported ciphers except AES are always available - // via the JSSE internal implementations. We also assume AES/128 of - // CBC mode is always available since it is shipped with the SunJCE - // provider. However, AES/256 is unavailable when the default JCE - // policy jurisdiction files are installed because of key length - // restrictions. - this.isAvailable = - allowed ? isUnlimited(keySize, transformation) : false; - } - - BulkCipher(String transformation, CipherType cipherType, int keySize, - int ivSize, int fixedIvSize, boolean allowed) { - this.transformation = transformation; - String[] splits = transformation.split("/"); - this.algorithm = splits[0]; - this.cipherType = cipherType; - this.description = this.algorithm + "/" + (keySize << 3); - this.keySize = keySize; - this.ivSize = ivSize; - this.fixedIvSize = fixedIvSize; - this.allowed = allowed; - - this.expandedKeySize = keySize; - this.exportable = false; - - // availability of this bulk cipher - // - // Currently all supported ciphers except AES are always available - // via the JSSE internal implementations. We also assume AES/128 of - // CBC mode is always available since it is shipped with the SunJCE - // provider. However, AES/256 is unavailable when the default JCE - // policy jurisdiction files are installed because of key length - // restrictions. - this.isAvailable = - allowed ? isUnlimited(keySize, transformation) : false; - } - - /** - * Return an initialized CipherBox for this BulkCipher. - * IV must be null for stream ciphers. - * - * @exception NoSuchAlgorithmException if anything goes wrong - */ - CipherBox newCipher(ProtocolVersion version, SecretKey key, - IvParameterSpec iv, SecureRandom random, - boolean encrypt) throws NoSuchAlgorithmException { - return CipherBox.newCipherBox(version, this, - key, iv, random, encrypt); - } - - /** - * Test if this bulk cipher is available. For use by CipherSuite. - */ - boolean isAvailable() { - return this.isAvailable; - } - - private static boolean isUnlimited(int keySize, String transformation) { - int keySizeInBits = keySize * 8; - if (keySizeInBits > 128) { // need the JCE unlimited - // strength jurisdiction policy - try { - if (Cipher.getMaxAllowedKeyLength( - transformation) < keySizeInBits) { - - return false; - } - } catch (Exception e) { - return false; - } - } - - return true; - } - - @Override - public String toString() { - return description; - } - } - /** * An SSL/TLS key MAC algorithm. * @@ -626,7 +1085,6 @@ * for this algorithm. */ static enum MacAlg { - // MACs M_NULL ("NULL", 0, 0, 0), M_MD5 ("MD5", 16, 64, 9), M_SHA ("SHA", 20, 64, 9), @@ -653,17 +1111,6 @@ this.minimalPaddingSize = minimalPaddingSize; } - /** - * Return an initialized MAC for this MacAlg. ProtocolVersion - * must either be SSL30 (SSLv3 custom MAC) or TLS10 (std. HMAC). - * - * @exception NoSuchAlgorithmException if anything goes wrong - */ - MAC newMac(ProtocolVersion protocolVersion, SecretKey secret) - throws NoSuchAlgorithmException, InvalidKeyException { - return new MAC(this, protocolVersion, secret); - } - @Override public String toString() { return name; @@ -671,975 +1118,29 @@ } /** - * PRFs (PseudoRandom Function) from TLS specifications. - * - * TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for generating - * the necessary material. + * The hash algorithms used for PRF (PseudoRandom Function) or HKDF. * - * In TLS 1.2+, all existing/known CipherSuites use SHA256, however - * new Ciphersuites (e.g. RFC 5288) can define specific PRF hash - * algorithms. + * Note that TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for + * generating the necessary material. */ - static enum PRF { + static enum HashAlg { + H_NONE ("NONE", 0, 0), + H_SHA256 ("SHA-256", 32, 64), + H_SHA384 ("SHA-384", 48, 128); - // PRF algorithms - P_NONE( "NONE", 0, 0), - P_SHA256("SHA-256", 32, 64), - P_SHA384("SHA-384", 48, 128), - P_SHA512("SHA-512", 64, 128); // not currently used. + final String name; + final int hashLength; + final int blockSize; - // PRF characteristics - private final String prfHashAlg; - private final int prfHashLength; - private final int prfBlockSize; - - PRF(String prfHashAlg, int prfHashLength, int prfBlockSize) { - this.prfHashAlg = prfHashAlg; - this.prfHashLength = prfHashLength; - this.prfBlockSize = prfBlockSize; + HashAlg(String hashAlg, int hashLength, int blockSize) { + this.name = hashAlg; + this.hashLength = hashLength; + this.blockSize = blockSize; } - String getPRFHashAlg() { - return prfHashAlg; - } - - int getPRFHashLength() { - return prfHashLength; - } - - int getPRFBlockSize() { - return prfBlockSize; + @Override + public String toString() { + return name; } } - - static { - idMap = new HashMap(); - nameMap = new HashMap(); - - final boolean F = false; - final boolean T = true; - // N: ciphersuites only allowed if we are not in FIPS mode - final boolean N = (SunJSSE.isFIPS() == false); - - /* - * TLS Cipher Suite Registry, as of November 2015. - * - * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml - * - * Range Registration Procedures Notes - * 000-191 Standards Action Refers to value of first byte - * 192-254 Specification Required Refers to value of first byte - * 255 Reserved for Private Use Refers to value of first byte - * - * Value Description Reference - * 0x00,0x00 TLS_NULL_WITH_NULL_NULL [RFC5246] - * 0x00,0x01 TLS_RSA_WITH_NULL_MD5 [RFC5246] - * 0x00,0x02 TLS_RSA_WITH_NULL_SHA [RFC5246] - * 0x00,0x03 TLS_RSA_EXPORT_WITH_RC4_40_MD5 [RFC4346] - * 0x00,0x04 TLS_RSA_WITH_RC4_128_MD5 [RFC5246] - * 0x00,0x05 TLS_RSA_WITH_RC4_128_SHA [RFC5246] - * 0x00,0x06 TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 [RFC4346] - * 0x00,0x07 TLS_RSA_WITH_IDEA_CBC_SHA [RFC5469] - * 0x00,0x08 TLS_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x09 TLS_RSA_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x0A TLS_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x0B TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x0C TLS_DH_DSS_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x0D TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x0E TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x0F TLS_DH_RSA_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x10 TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x11 TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x12 TLS_DHE_DSS_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x13 TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x14 TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x15 TLS_DHE_RSA_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x16 TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x17 TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 [RFC4346] - * 0x00,0x18 TLS_DH_anon_WITH_RC4_128_MD5 [RFC5246] - * 0x00,0x19 TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA [RFC4346] - * 0x00,0x1A TLS_DH_anon_WITH_DES_CBC_SHA [RFC5469] - * 0x00,0x1B TLS_DH_anon_WITH_3DES_EDE_CBC_SHA [RFC5246] - * 0x00,0x1C-1D Reserved to avoid conflicts with SSLv3 [RFC5246] - * 0x00,0x1E TLS_KRB5_WITH_DES_CBC_SHA [RFC2712] - * 0x00,0x1F TLS_KRB5_WITH_3DES_EDE_CBC_SHA [RFC2712] - * 0x00,0x20 TLS_KRB5_WITH_RC4_128_SHA [RFC2712] - * 0x00,0x21 TLS_KRB5_WITH_IDEA_CBC_SHA [RFC2712] - * 0x00,0x22 TLS_KRB5_WITH_DES_CBC_MD5 [RFC2712] - * 0x00,0x23 TLS_KRB5_WITH_3DES_EDE_CBC_MD5 [RFC2712] - * 0x00,0x24 TLS_KRB5_WITH_RC4_128_MD5 [RFC2712] - * 0x00,0x25 TLS_KRB5_WITH_IDEA_CBC_MD5 [RFC2712] - * 0x00,0x26 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA [RFC2712] - * 0x00,0x27 TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA [RFC2712] - * 0x00,0x28 TLS_KRB5_EXPORT_WITH_RC4_40_SHA [RFC2712] - * 0x00,0x29 TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 [RFC2712] - * 0x00,0x2A TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 [RFC2712] - * 0x00,0x2B TLS_KRB5_EXPORT_WITH_RC4_40_MD5 [RFC2712] - * 0x00,0x2C TLS_PSK_WITH_NULL_SHA [RFC4785] - * 0x00,0x2D TLS_DHE_PSK_WITH_NULL_SHA [RFC4785] - * 0x00,0x2E TLS_RSA_PSK_WITH_NULL_SHA [RFC4785] - * 0x00,0x2F TLS_RSA_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x30 TLS_DH_DSS_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x31 TLS_DH_RSA_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x32 TLS_DHE_DSS_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x33 TLS_DHE_RSA_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x34 TLS_DH_anon_WITH_AES_128_CBC_SHA [RFC5246] - * 0x00,0x35 TLS_RSA_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x36 TLS_DH_DSS_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x37 TLS_DH_RSA_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x38 TLS_DHE_DSS_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x39 TLS_DHE_RSA_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x3A TLS_DH_anon_WITH_AES_256_CBC_SHA [RFC5246] - * 0x00,0x3B TLS_RSA_WITH_NULL_SHA256 [RFC5246] - * 0x00,0x3C TLS_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x3D TLS_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x3E TLS_DH_DSS_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x3F TLS_DH_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x40 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x41 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x42 TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x43 TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x44 TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x45 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x46 TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA [RFC5932] - * 0x00,0x47-4F Reserved to avoid conflicts with - * deployed implementations [Pasi_Eronen] - * 0x00,0x50-58 Reserved to avoid conflicts [Pasi Eronen] - * 0x00,0x59-5C Reserved to avoid conflicts with - * deployed implementations [Pasi_Eronen] - * 0x00,0x5D-5F Unassigned - * 0x00,0x60-66 Reserved to avoid conflicts with widely - * deployed implementations [Pasi_Eronen] - * 0x00,0x67 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x68 TLS_DH_DSS_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x69 TLS_DH_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x6A TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x6B TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x6C TLS_DH_anon_WITH_AES_128_CBC_SHA256 [RFC5246] - * 0x00,0x6D TLS_DH_anon_WITH_AES_256_CBC_SHA256 [RFC5246] - * 0x00,0x6E-83 Unassigned - * 0x00,0x84 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x85 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x86 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x87 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x88 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x89 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA [RFC5932] - * 0x00,0x8A TLS_PSK_WITH_RC4_128_SHA [RFC4279] - * 0x00,0x8B TLS_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] - * 0x00,0x8C TLS_PSK_WITH_AES_128_CBC_SHA [RFC4279] - * 0x00,0x8D TLS_PSK_WITH_AES_256_CBC_SHA [RFC4279] - * 0x00,0x8E TLS_DHE_PSK_WITH_RC4_128_SHA [RFC4279] - * 0x00,0x8F TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] - * 0x00,0x90 TLS_DHE_PSK_WITH_AES_128_CBC_SHA [RFC4279] - * 0x00,0x91 TLS_DHE_PSK_WITH_AES_256_CBC_SHA [RFC4279] - * 0x00,0x92 TLS_RSA_PSK_WITH_RC4_128_SHA [RFC4279] - * 0x00,0x93 TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] - * 0x00,0x94 TLS_RSA_PSK_WITH_AES_128_CBC_SHA [RFC4279] - * 0x00,0x95 TLS_RSA_PSK_WITH_AES_256_CBC_SHA [RFC4279] - * 0x00,0x96 TLS_RSA_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x97 TLS_DH_DSS_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x98 TLS_DH_RSA_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x99 TLS_DHE_DSS_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x9A TLS_DHE_RSA_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x9B TLS_DH_anon_WITH_SEED_CBC_SHA [RFC4162] - * 0x00,0x9C TLS_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0x9D TLS_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0x9E TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0x9F TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0xA0 TLS_DH_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0xA1 TLS_DH_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0xA2 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0xA3 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0xA4 TLS_DH_DSS_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0xA5 TLS_DH_DSS_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0xA6 TLS_DH_anon_WITH_AES_128_GCM_SHA256 [RFC5288] - * 0x00,0xA7 TLS_DH_anon_WITH_AES_256_GCM_SHA384 [RFC5288] - * 0x00,0xA8 TLS_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] - * 0x00,0xA9 TLS_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] - * 0x00,0xAA TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] - * 0x00,0xAB TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] - * 0x00,0xAC TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] - * 0x00,0xAD TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] - * 0x00,0xAE TLS_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] - * 0x00,0xAF TLS_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] - * 0x00,0xB0 TLS_PSK_WITH_NULL_SHA256 [RFC5487] - * 0x00,0xB1 TLS_PSK_WITH_NULL_SHA384 [RFC5487] - * 0x00,0xB2 TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] - * 0x00,0xB3 TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] - * 0x00,0xB4 TLS_DHE_PSK_WITH_NULL_SHA256 [RFC5487] - * 0x00,0xB5 TLS_DHE_PSK_WITH_NULL_SHA384 [RFC5487] - * 0x00,0xB6 TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] - * 0x00,0xB7 TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] - * 0x00,0xB8 TLS_RSA_PSK_WITH_NULL_SHA256 [RFC5487] - * 0x00,0xB9 TLS_RSA_PSK_WITH_NULL_SHA384 [RFC5487] - * 0x00,0xBA TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xBB TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xBC TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xBD TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xBE TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xBF TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] - * 0x00,0xC0 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC1 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC2 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC3 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC4 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC5 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] - * 0x00,0xC6-FE Unassigned - * 0x00,0xFF TLS_EMPTY_RENEGOTIATION_INFO_SCSV [RFC5746] - * 0x01-55,* Unassigned - * 0x56,0x00 TLS_FALLBACK_SCSV [RFC7507] - * 0x56,0x01-0xC0,0x00 Unassigned - * 0xC0,0x01 TLS_ECDH_ECDSA_WITH_NULL_SHA [RFC4492] - * 0xC0,0x02 TLS_ECDH_ECDSA_WITH_RC4_128_SHA [RFC4492] - * 0xC0,0x03 TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA [RFC4492] - * 0xC0,0x04 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA [RFC4492] - * 0xC0,0x05 TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA [RFC4492] - * 0xC0,0x06 TLS_ECDHE_ECDSA_WITH_NULL_SHA [RFC4492] - * 0xC0,0x07 TLS_ECDHE_ECDSA_WITH_RC4_128_SHA [RFC4492] - * 0xC0,0x08 TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA [RFC4492] - * 0xC0,0x09 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA [RFC4492] - * 0xC0,0x0A TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA [RFC4492] - * 0xC0,0x0B TLS_ECDH_RSA_WITH_NULL_SHA [RFC4492] - * 0xC0,0x0C TLS_ECDH_RSA_WITH_RC4_128_SHA [RFC4492] - * 0xC0,0x0D TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA [RFC4492] - * 0xC0,0x0E TLS_ECDH_RSA_WITH_AES_128_CBC_SHA [RFC4492] - * 0xC0,0x0F TLS_ECDH_RSA_WITH_AES_256_CBC_SHA [RFC4492] - * 0xC0,0x10 TLS_ECDHE_RSA_WITH_NULL_SHA [RFC4492] - * 0xC0,0x11 TLS_ECDHE_RSA_WITH_RC4_128_SHA [RFC4492] - * 0xC0,0x12 TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA [RFC4492] - * 0xC0,0x13 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA [RFC4492] - * 0xC0,0x14 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA [RFC4492] - * 0xC0,0x15 TLS_ECDH_anon_WITH_NULL_SHA [RFC4492] - * 0xC0,0x16 TLS_ECDH_anon_WITH_RC4_128_SHA [RFC4492] - * 0xC0,0x17 TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA [RFC4492] - * 0xC0,0x18 TLS_ECDH_anon_WITH_AES_128_CBC_SHA [RFC4492] - * 0xC0,0x19 TLS_ECDH_anon_WITH_AES_256_CBC_SHA [RFC4492] - * 0xC0,0x1A TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA [RFC5054] - * 0xC0,0x1B TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA [RFC5054] - * 0xC0,0x1C TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA [RFC5054] - * 0xC0,0x1D TLS_SRP_SHA_WITH_AES_128_CBC_SHA [RFC5054] - * 0xC0,0x1E TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA [RFC5054] - * 0xC0,0x1F TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA [RFC5054] - * 0xC0,0x20 TLS_SRP_SHA_WITH_AES_256_CBC_SHA [RFC5054] - * 0xC0,0x21 TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA [RFC5054] - * 0xC0,0x22 TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA [RFC5054] - * 0xC0,0x23 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 [RFC5289] - * 0xC0,0x24 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 [RFC5289] - * 0xC0,0x25 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 [RFC5289] - * 0xC0,0x26 TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 [RFC5289] - * 0xC0,0x27 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 [RFC5289] - * 0xC0,0x28 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 [RFC5289] - * 0xC0,0x29 TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 [RFC5289] - * 0xC0,0x2A TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 [RFC5289] - * 0xC0,0x2B TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 [RFC5289] - * 0xC0,0x2C TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 [RFC5289] - * 0xC0,0x2D TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 [RFC5289] - * 0xC0,0x2E TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 [RFC5289] - * 0xC0,0x2F TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [RFC5289] - * 0xC0,0x30 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 [RFC5289] - * 0xC0,0x31 TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 [RFC5289] - * 0xC0,0x32 TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 [RFC5289] - * 0xC0,0x33 TLS_ECDHE_PSK_WITH_RC4_128_SHA [RFC5489] - * 0xC0,0x34 TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA [RFC5489] - * 0xC0,0x35 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA [RFC5489] - * 0xC0,0x36 TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA [RFC5489] - * 0xC0,0x37 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 [RFC5489] - * 0xC0,0x38 TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 [RFC5489] - * 0xC0,0x39 TLS_ECDHE_PSK_WITH_NULL_SHA [RFC5489] - * 0xC0,0x3A TLS_ECDHE_PSK_WITH_NULL_SHA256 [RFC5489] - * 0xC0,0x3B TLS_ECDHE_PSK_WITH_NULL_SHA384 [RFC5489] - * 0xC0,0x3C TLS_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x3D TLS_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x3E TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x3F TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x40 TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x41 TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x42 TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x43 TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x44 TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x45 TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x46 TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x47 TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x48 TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x49 TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x4A TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x4B TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x4C TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x4D TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x4E TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x4F TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x50 TLS_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x51 TLS_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x52 TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x53 TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x54 TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x55 TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x56 TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x57 TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x58 TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x59 TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x5A TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x5B TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x5C TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x5D TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x5E TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x5F TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x60 TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x61 TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x62 TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x63 TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x64 TLS_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x65 TLS_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x66 TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x67 TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x68 TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x69 TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x6A TLS_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x6B TLS_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x6C TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x6D TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x6E TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] - * 0xC0,0x6F TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] - * 0xC0,0x70 TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] - * 0xC0,0x71 TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] - * 0xC0,0x72 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x73 TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x74 TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x75 TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x76 TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x77 TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x78 TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x79 TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x7A TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x7B TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x7C TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x7D TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x7E TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x7F TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x80 TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x81 TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x82 TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x83 TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x84 TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x85 TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x86 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x87 TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x88 TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x89 TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x8A TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x8B TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x8C TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x8D TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x8E TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x8F TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x90 TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x91 TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x92 TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] - * 0xC0,0x93 TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] - * 0xC0,0x94 TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x95 TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x96 TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x97 TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x98 TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x99 TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x9A TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] - * 0xC0,0x9B TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] - * 0xC0,0x9C TLS_RSA_WITH_AES_128_CCM [RFC6655] - * 0xC0,0x9D TLS_RSA_WITH_AES_256_CCM [RFC6655] - * 0xC0,0x9E TLS_DHE_RSA_WITH_AES_128_CCM [RFC6655] - * 0xC0,0x9F TLS_DHE_RSA_WITH_AES_256_CCM [RFC6655] - * 0xC0,0xA0 TLS_RSA_WITH_AES_128_CCM_8 [RFC6655] - * 0xC0,0xA1 TLS_RSA_WITH_AES_256_CCM_8 [RFC6655] - * 0xC0,0xA2 TLS_DHE_RSA_WITH_AES_128_CCM_8 [RFC6655] - * 0xC0,0xA3 TLS_DHE_RSA_WITH_AES_256_CCM_8 [RFC6655] - * 0xC0,0xA4 TLS_PSK_WITH_AES_128_CCM [RFC6655] - * 0xC0,0xA5 TLS_PSK_WITH_AES_256_CCM [RFC6655] - * 0xC0,0xA6 TLS_DHE_PSK_WITH_AES_128_CCM [RFC6655] - * 0xC0,0xA7 TLS_DHE_PSK_WITH_AES_256_CCM [RFC6655] - * 0xC0,0xA8 TLS_PSK_WITH_AES_128_CCM_8 [RFC6655] - * 0xC0,0xA9 TLS_PSK_WITH_AES_256_CCM_8 [RFC6655] - * 0xC0,0xAA TLS_PSK_DHE_WITH_AES_128_CCM_8 [RFC6655] - * 0xC0,0xAB TLS_PSK_DHE_WITH_AES_256_CCM_8 [RFC6655] - * 0xC0,0xAC TLS_ECDHE_ECDSA_WITH_AES_128_CCM [RFC7251] - * 0xC0,0xAD TLS_ECDHE_ECDSA_WITH_AES_256_CCM [RFC7251] - * 0xC0,0xAE TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 [RFC7251] - * 0xC0,0xAF TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 [RFC7251] - * 0xC0,0xB0-FF Unassigned - * 0xC1-FD,* Unassigned - * 0xFE,0x00-FD Unassigned - * 0xFE,0xFE-FF Reserved to avoid conflicts with widely - * deployed implementations [Pasi_Eronen] - * 0xFF,0x00-FF Reserved for Private Use [RFC5246] - */ - - add("SSL_NULL_WITH_NULL_NULL", 0x0000, - 1, K_NULL, B_NULL, M_NULL, F); - - /* - * Definition of the CipherSuites that are enabled by default. - * They are listed in preference order, most preferred first, using - * the following criteria: - * 1. Prefer Suite B compliant cipher suites, see RFC6460 (To be - * changed later, see below). - * 2. Prefer the stronger bulk cipher, in the order of AES_256(GCM), - * AES_128(GCM), AES_256, AES_128, 3DES-EDE. - * 3. Prefer the stronger MAC algorithm, in the order of SHA384, - * SHA256, SHA, MD5. - * 4. Prefer the better performance of key exchange and digital - * signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, - * RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS. - */ - int p = DEFAULT_SUITES_PRIORITY * 2; - - // shorten names to fit the following table cleanly. - int max = ProtocolVersion.LIMIT_MAX_VALUE; - int tls11 = ProtocolVersion.TLS11.v; - int tls12 = ProtocolVersion.TLS12.v; - - // ID Key Exchange Cipher A obs suprt PRF - // ====== ============ ========= = === ===== ======== - - // Suite B compliant cipher suites, see RFC 6460. - // - // Note that, at present this provider is not Suite B compliant. The - // preference order of the GCM cipher suites does not follow the spec - // of RFC 6460. In this section, only two cipher suites are listed - // so that applications can make use of Suite-B compliant cipher - // suite firstly. - add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0xc02c, --p, - K_ECDHE_ECDSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02b, --p, - K_ECDHE_ECDSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - - // AES_256(GCM) - add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0xc030, --p, - K_ECDHE_RSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x009d, --p, - K_RSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0xc02e, --p, - K_ECDH_ECDSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0xc032, --p, - K_ECDH_RSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x009f, --p, - K_DHE_RSA, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x00a3, --p, - K_DHE_DSS, B_AES_256_GCM, M_NULL, T, max, tls12, P_SHA384); - - // AES_128(GCM) - add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0xc02f, --p, - K_ECDHE_RSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - add("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x009c, --p, - K_RSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02d, --p, - K_ECDH_ECDSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - add("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0xc031, --p, - K_ECDH_RSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x009e, --p, - K_DHE_RSA, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x00a2, --p, - K_DHE_DSS, B_AES_128_GCM, M_NULL, T, max, tls12, P_SHA256); - - // AES_256(CBC) - add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0xc024, --p, - K_ECDHE_ECDSA, B_AES_256, M_SHA384, T, max, tls12, P_SHA384); - add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0xc028, --p, - K_ECDHE_RSA, B_AES_256, M_SHA384, T, max, tls12, P_SHA384); - add("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x003d, --p, - K_RSA, B_AES_256, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0xc026, --p, - K_ECDH_ECDSA, B_AES_256, M_SHA384, T, max, tls12, P_SHA384); - add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0xc02a, --p, - K_ECDH_RSA, B_AES_256, M_SHA384, T, max, tls12, P_SHA384); - add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x006b, --p, - K_DHE_RSA, B_AES_256, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x006a, --p, - K_DHE_DSS, B_AES_256, M_SHA256, T, max, tls12, P_SHA256); - - add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 0xC00A, --p, - K_ECDHE_ECDSA, B_AES_256, M_SHA, T); - add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 0xC014, --p, - K_ECDHE_RSA, B_AES_256, M_SHA, T); - add("TLS_RSA_WITH_AES_256_CBC_SHA", 0x0035, --p, - K_RSA, B_AES_256, M_SHA, T); - add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", 0xC005, --p, - K_ECDH_ECDSA, B_AES_256, M_SHA, T); - add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", 0xC00F, --p, - K_ECDH_RSA, B_AES_256, M_SHA, T); - add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", 0x0039, --p, - K_DHE_RSA, B_AES_256, M_SHA, T); - add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", 0x0038, --p, - K_DHE_DSS, B_AES_256, M_SHA, T); - - // AES_128(CBC) - add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0xc023, --p, - K_ECDHE_ECDSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0xc027, --p, - K_ECDHE_RSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x003c, --p, - K_RSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0xc025, --p, - K_ECDH_ECDSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0xc029, --p, - K_ECDH_RSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0067, --p, - K_DHE_RSA, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0040, --p, - K_DHE_DSS, B_AES_128, M_SHA256, T, max, tls12, P_SHA256); - - add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 0xC009, --p, - K_ECDHE_ECDSA, B_AES_128, M_SHA, T); - add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 0xC013, --p, - K_ECDHE_RSA, B_AES_128, M_SHA, T); - add("TLS_RSA_WITH_AES_128_CBC_SHA", 0x002f, --p, - K_RSA, B_AES_128, M_SHA, T); - add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", 0xC004, --p, - K_ECDH_ECDSA, B_AES_128, M_SHA, T); - add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", 0xC00E, --p, - K_ECDH_RSA, B_AES_128, M_SHA, T); - add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", 0x0033, --p, - K_DHE_RSA, B_AES_128, M_SHA, T); - add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", 0x0032, --p, - K_DHE_DSS, B_AES_128, M_SHA, T); - - // 3DES_EDE - add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xC008, --p, - K_ECDHE_ECDSA, B_3DES, M_SHA, T); - add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", 0xC012, --p, - K_ECDHE_RSA, B_3DES, M_SHA, T); - add("SSL_RSA_WITH_3DES_EDE_CBC_SHA", 0x000a, --p, - K_RSA, B_3DES, M_SHA, T); - add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xC003, --p, - K_ECDH_ECDSA, B_3DES, M_SHA, T); - add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", 0xC00D, --p, - K_ECDH_RSA, B_3DES, M_SHA, T); - add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", 0x0016, --p, - K_DHE_RSA, B_3DES, M_SHA, T); - add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", 0x0013, --p, - K_DHE_DSS, B_3DES, M_SHA, N); - - // Renegotiation protection request Signalling Cipher Suite Value (SCSV) - add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0x00ff, --p, - K_SCSV, B_NULL, M_NULL, T); - - /* - * Definition of the CipherSuites that are supported but not enabled - * by default. - * They are listed in preference order, preferred first, using the - * following criteria: - * 1. CipherSuites for KRB5 need additional KRB5 service - * configuration, and these suites are not common in practice, - * so we put KRB5 based cipher suites at the end of the supported - * list. - * 2. If a cipher suite has been obsoleted, we put it at the end of - * the list. - * 3. Prefer the stronger bulk cipher, in the order of AES_256, - * AES_128, 3DES-EDE, RC-4, DES, DES40, RC4_40, NULL. - * 4. Prefer the stronger MAC algorithm, in the order of SHA384, - * SHA256, SHA, MD5. - * 5. Prefer the better performance of key exchange and digital - * signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, - * RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS, anonymous. - */ - p = DEFAULT_SUITES_PRIORITY; - - add("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x00a7, --p, - K_DH_ANON, B_AES_256_GCM, M_NULL, N, max, tls12, P_SHA384); - add("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x00a6, --p, - K_DH_ANON, B_AES_128_GCM, M_NULL, N, max, tls12, P_SHA256); - - add("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x006d, --p, - K_DH_ANON, B_AES_256, M_SHA256, N, max, tls12, P_SHA256); - add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", 0xC019, --p, - K_ECDH_ANON, B_AES_256, M_SHA, N); - add("TLS_DH_anon_WITH_AES_256_CBC_SHA", 0x003a, --p, - K_DH_ANON, B_AES_256, M_SHA, N); - - add("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x006c, --p, - K_DH_ANON, B_AES_128, M_SHA256, N, max, tls12, P_SHA256); - add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", 0xC018, --p, - K_ECDH_ANON, B_AES_128, M_SHA, N); - add("TLS_DH_anon_WITH_AES_128_CBC_SHA", 0x0034, --p, - K_DH_ANON, B_AES_128, M_SHA, N); - - add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", 0xC017, --p, - K_ECDH_ANON, B_3DES, M_SHA, N); - add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", 0x001b, --p, - K_DH_ANON, B_3DES, M_SHA, N); - - // RC-4 - add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", 0xC007, --p, - K_ECDHE_ECDSA, B_RC4_128, M_SHA, N); - add("TLS_ECDHE_RSA_WITH_RC4_128_SHA", 0xC011, --p, - K_ECDHE_RSA, B_RC4_128, M_SHA, N); - add("SSL_RSA_WITH_RC4_128_SHA", 0x0005, --p, - K_RSA, B_RC4_128, M_SHA, N); - add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", 0xC002, --p, - K_ECDH_ECDSA, B_RC4_128, M_SHA, N); - add("TLS_ECDH_RSA_WITH_RC4_128_SHA", 0xC00C, --p, - K_ECDH_RSA, B_RC4_128, M_SHA, N); - add("SSL_RSA_WITH_RC4_128_MD5", 0x0004, --p, - K_RSA, B_RC4_128, M_MD5, N); - - add("TLS_ECDH_anon_WITH_RC4_128_SHA", 0xC016, --p, - K_ECDH_ANON, B_RC4_128, M_SHA, N); - add("SSL_DH_anon_WITH_RC4_128_MD5", 0x0018, --p, - K_DH_ANON, B_RC4_128, M_MD5, N); - - // weak cipher suites obsoleted in TLS 1.2 - add("SSL_RSA_WITH_DES_CBC_SHA", 0x0009, --p, - K_RSA, B_DES, M_SHA, N, tls12); - add("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0015, --p, - K_DHE_RSA, B_DES, M_SHA, N, tls12); - add("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0012, --p, - K_DHE_DSS, B_DES, M_SHA, N, tls12); - add("SSL_DH_anon_WITH_DES_CBC_SHA", 0x001a, --p, - K_DH_ANON, B_DES, M_SHA, N, tls12); - - // weak cipher suites obsoleted in TLS 1.1 - add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0008, --p, - K_RSA_EXPORT, B_DES_40, M_SHA, N, tls11); - add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0014, --p, - K_DHE_RSA, B_DES_40, M_SHA, N, tls11); - add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0011, --p, - K_DHE_DSS, B_DES_40, M_SHA, N, tls11); - add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0019, --p, - K_DH_ANON, B_DES_40, M_SHA, N, tls11); - - add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0003, --p, - K_RSA_EXPORT, B_RC4_40, M_MD5, N, tls11); - add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0017, --p, - K_DH_ANON, B_RC4_40, M_MD5, N, tls11); - - add("TLS_RSA_WITH_NULL_SHA256", 0x003b, --p, - K_RSA, B_NULL, M_SHA256, N, max, tls12, P_SHA256); - add("TLS_ECDHE_ECDSA_WITH_NULL_SHA", 0xC006, --p, - K_ECDHE_ECDSA, B_NULL, M_SHA, N); - add("TLS_ECDHE_RSA_WITH_NULL_SHA", 0xC010, --p, - K_ECDHE_RSA, B_NULL, M_SHA, N); - add("SSL_RSA_WITH_NULL_SHA", 0x0002, --p, - K_RSA, B_NULL, M_SHA, N); - add("TLS_ECDH_ECDSA_WITH_NULL_SHA", 0xC001, --p, - K_ECDH_ECDSA, B_NULL, M_SHA, N); - add("TLS_ECDH_RSA_WITH_NULL_SHA", 0xC00B, --p, - K_ECDH_RSA, B_NULL, M_SHA, N); - add("TLS_ECDH_anon_WITH_NULL_SHA", 0xC015, --p, - K_ECDH_ANON, B_NULL, M_SHA, N); - add("SSL_RSA_WITH_NULL_MD5", 0x0001, --p, - K_RSA, B_NULL, M_MD5, N); - - // Supported Kerberos ciphersuites from RFC2712 - add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", 0x001f, --p, - K_KRB5, B_3DES, M_SHA, N); - add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", 0x0023, --p, - K_KRB5, B_3DES, M_MD5, N); - add("TLS_KRB5_WITH_RC4_128_SHA", 0x0020, --p, - K_KRB5, B_RC4_128, M_SHA, N); - add("TLS_KRB5_WITH_RC4_128_MD5", 0x0024, --p, - K_KRB5, B_RC4_128, M_MD5, N); - add("TLS_KRB5_WITH_DES_CBC_SHA", 0x001e, --p, - K_KRB5, B_DES, M_SHA, N, tls12); - add("TLS_KRB5_WITH_DES_CBC_MD5", 0x0022, --p, - K_KRB5, B_DES, M_MD5, N, tls12); - add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0026, --p, - K_KRB5_EXPORT, B_DES_40, M_SHA, N, tls11); - add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0029, --p, - K_KRB5_EXPORT, B_DES_40, M_MD5, N, tls11); - add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0028, --p, - K_KRB5_EXPORT, B_RC4_40, M_SHA, N, tls11); - add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x002b, --p, - K_KRB5_EXPORT, B_RC4_40, M_MD5, N, tls11); - - /* - * Other values from the TLS Cipher Suite Registry, as of August 2010. - * - * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml - * - * Range Registration Procedures Notes - * 000-191 Standards Action Refers to value of first byte - * 192-254 Specification Required Refers to value of first byte - * 255 Reserved for Private Use Refers to value of first byte - */ - - // Register the names of a few additional CipherSuites. - // Makes them show up as names instead of numbers in - // the debug output. - - // remaining unsupported ciphersuites defined in RFC2246. - add("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", 0x0006); - add("SSL_RSA_WITH_IDEA_CBC_SHA", 0x0007); - add("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x000b); - add("SSL_DH_DSS_WITH_DES_CBC_SHA", 0x000c); - add("SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", 0x000d); - add("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x000e); - add("SSL_DH_RSA_WITH_DES_CBC_SHA", 0x000f); - add("SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", 0x0010); - - // SSL 3.0 Fortezza ciphersuites - add("SSL_FORTEZZA_DMS_WITH_NULL_SHA", 0x001c); - add("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", 0x001d); - - // 1024/56 bit exportable ciphersuites from expired internet draft - add("SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA", 0x0062); - add("SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA", 0x0063); - add("SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", 0x0064); - add("SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", 0x0065); - add("SSL_DHE_DSS_WITH_RC4_128_SHA", 0x0066); - - // Netscape old and new SSL 3.0 FIPS ciphersuites - // see http://www.mozilla.org/projects/security/pki/nss/ssl/fips-ssl-ciphersuites.html - add("NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xffe0); - add("NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA", 0xffe1); - add("SSL_RSA_FIPS_WITH_DES_CBC_SHA", 0xfefe); - add("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xfeff); - - // Unsupported Kerberos cipher suites from RFC 2712 - add("TLS_KRB5_WITH_IDEA_CBC_SHA", 0x0021); - add("TLS_KRB5_WITH_IDEA_CBC_MD5", 0x0025); - add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", 0x0027); - add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", 0x002a); - - // Unsupported cipher suites from RFC 4162 - add("TLS_RSA_WITH_SEED_CBC_SHA", 0x0096); - add("TLS_DH_DSS_WITH_SEED_CBC_SHA", 0x0097); - add("TLS_DH_RSA_WITH_SEED_CBC_SHA", 0x0098); - add("TLS_DHE_DSS_WITH_SEED_CBC_SHA", 0x0099); - add("TLS_DHE_RSA_WITH_SEED_CBC_SHA", 0x009a); - add("TLS_DH_anon_WITH_SEED_CBC_SHA", 0x009b); - - // Unsupported cipher suites from RFC 4279 - add("TLS_PSK_WITH_RC4_128_SHA", 0x008a); - add("TLS_PSK_WITH_3DES_EDE_CBC_SHA", 0x008b); - add("TLS_PSK_WITH_AES_128_CBC_SHA", 0x008c); - add("TLS_PSK_WITH_AES_256_CBC_SHA", 0x008d); - add("TLS_DHE_PSK_WITH_RC4_128_SHA", 0x008e); - add("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", 0x008f); - add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", 0x0090); - add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", 0x0091); - add("TLS_RSA_PSK_WITH_RC4_128_SHA", 0x0092); - add("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", 0x0093); - add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", 0x0094); - add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", 0x0095); - - // Unsupported cipher suites from RFC 4785 - add("TLS_PSK_WITH_NULL_SHA", 0x002c); - add("TLS_DHE_PSK_WITH_NULL_SHA", 0x002d); - add("TLS_RSA_PSK_WITH_NULL_SHA", 0x002e); - - // Unsupported cipher suites from RFC 5246 - add("TLS_DH_DSS_WITH_AES_128_CBC_SHA", 0x0030); - add("TLS_DH_RSA_WITH_AES_128_CBC_SHA", 0x0031); - add("TLS_DH_DSS_WITH_AES_256_CBC_SHA", 0x0036); - add("TLS_DH_RSA_WITH_AES_256_CBC_SHA", 0x0037); - add("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", 0x003e); - add("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", 0x003f); - add("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", 0x0068); - add("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", 0x0069); - - // Unsupported cipher suites from RFC 5288 - add("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", 0x00a0); - add("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", 0x00a1); - add("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", 0x00a4); - add("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", 0x00a5); - - // Unsupported cipher suites from RFC 5487 - add("TLS_PSK_WITH_AES_128_GCM_SHA256", 0x00a8); - add("TLS_PSK_WITH_AES_256_GCM_SHA384", 0x00a9); - add("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", 0x00aa); - add("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", 0x00ab); - add("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", 0x00ac); - add("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", 0x00ad); - add("TLS_PSK_WITH_AES_128_CBC_SHA256", 0x00ae); - add("TLS_PSK_WITH_AES_256_CBC_SHA384", 0x00af); - add("TLS_PSK_WITH_NULL_SHA256", 0x00b0); - add("TLS_PSK_WITH_NULL_SHA384", 0x00b1); - add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", 0x00b2); - add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", 0x00b3); - add("TLS_DHE_PSK_WITH_NULL_SHA256", 0x00b4); - add("TLS_DHE_PSK_WITH_NULL_SHA384", 0x00b5); - add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", 0x00b6); - add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", 0x00b7); - add("TLS_RSA_PSK_WITH_NULL_SHA256", 0x00b8); - add("TLS_RSA_PSK_WITH_NULL_SHA384", 0x00b9); - - // Unsupported cipher suites from RFC 5932 - add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0041); - add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0042); - add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0043); - add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0044); - add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0045); - add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", 0x0046); - add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0084); - add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0085); - add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0086); - add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0087); - add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0088); - add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", 0x0089); - add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00ba); - add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bb); - add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00bc); - add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bd); - add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00be); - add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", 0x00bf); - add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c0); - add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c1); - add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c2); - add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c3); - add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c4); - add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", 0x00c5); - - // TLS Fallback Signaling Cipher Suite Value (SCSV) RFC 7507 - add("TLS_FALLBACK_SCSV", 0x5600); - - // Unsupported cipher suites from RFC 5054 - add("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", 0xc01a); - add("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", 0xc01b); - add("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", 0xc01c); - add("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", 0xc01d); - add("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", 0xc01e); - add("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", 0xc01f); - add("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", 0xc020); - add("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", 0xc021); - add("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", 0xc022); - - // Unsupported cipher suites from RFC 5489 - add("TLS_ECDHE_PSK_WITH_RC4_128_SHA", 0xc033); - add("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", 0xc034); - add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", 0xc035); - add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", 0xc036); - add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", 0xc037); - add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", 0xc038); - add("TLS_ECDHE_PSK_WITH_NULL_SHA", 0xc039); - add("TLS_ECDHE_PSK_WITH_NULL_SHA256", 0xc03a); - add("TLS_ECDHE_PSK_WITH_NULL_SHA384", 0xc03b); - - // Unsupported cipher suites from RFC 6209 - add("TLS_RSA_WITH_ARIA_128_CBC_SHA256", 0xc03c); - add("TLS_RSA_WITH_ARIA_256_CBC_SHA384", 0xc03d); - add("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", 0xc03e); - add("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", 0xc03f); - add("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc040); - add("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc041); - add("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", 0xc042); - add("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", 0xc043); - add("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc044); - add("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc045); - add("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", 0xc046); - add("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", 0xc047); - add("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc048); - add("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc049); - add("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc04a); - add("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc04b); - add("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04c); - add("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04d); - add("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04e); - add("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04f); - add("TLS_RSA_WITH_ARIA_128_GCM_SHA256", 0xc050); - add("TLS_RSA_WITH_ARIA_256_GCM_SHA384", 0xc051); - add("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc052); - add("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc053); - add("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc054); - add("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc055); - add("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", 0xc056); - add("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", 0xc057); - add("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", 0xc058); - add("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", 0xc059); - add("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", 0xc05a); - add("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", 0xc05b); - add("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05c); - add("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05d); - add("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05e); - add("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05f); - add("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc060); - add("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc061); - add("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc062); - add("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc063); - add("TLS_PSK_WITH_ARIA_128_CBC_SHA256", 0xc064); - add("TLS_PSK_WITH_ARIA_256_CBC_SHA384", 0xc065); - add("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc066); - add("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc067); - add("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", 0xc068); - add("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", 0xc069); - add("TLS_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06a); - add("TLS_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06b); - add("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06c); - add("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06d); - add("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06e); - add("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06f); - add("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc070); - add("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc071); - - // Unsupported cipher suites from RFC 6367 - add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072); - add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073); - add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc074); - add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc075); - add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc076); - add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc077); - add("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc078); - add("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc079); - add("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07a); - add("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07b); - add("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07c); - add("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07d); - add("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07e); - add("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07f); - add("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc080); - add("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc081); - add("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc082); - add("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc083); - add("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", 0xc084); - add("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", 0xc085); - add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086); - add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087); - add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc088); - add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc089); - add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08a); - add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08b); - add("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08c); - add("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08d); - add("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc08e); - add("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc08f); - add("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc090); - add("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc091); - add("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc092); - add("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc093); - add("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc094); - add("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc095); - add("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc096); - add("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc097); - add("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc098); - add("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc099); - add("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc09a); - add("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc09b); - - // Unsupported cipher suites from RFC 6655 - add("TLS_RSA_WITH_AES_128_CCM", 0xc09c); - add("TLS_RSA_WITH_AES_256_CCM", 0xc09d); - add("TLS_DHE_RSA_WITH_AES_128_CCM", 0xc09e); - add("TLS_DHE_RSA_WITH_AES_256_CCM", 0xc09f); - add("TLS_RSA_WITH_AES_128_CCM_8", 0xc0A0); - add("TLS_RSA_WITH_AES_256_CCM_8", 0xc0A1); - add("TLS_DHE_RSA_WITH_AES_128_CCM_8", 0xc0A2); - add("TLS_DHE_RSA_WITH_AES_256_CCM_8", 0xc0A3); - add("TLS_PSK_WITH_AES_128_CCM", 0xc0A4); - add("TLS_PSK_WITH_AES_256_CCM", 0xc0A5); - add("TLS_DHE_PSK_WITH_AES_128_CCM", 0xc0A6); - add("TLS_DHE_PSK_WITH_AES_256_CCM", 0xc0A7); - add("TLS_PSK_WITH_AES_128_CCM_8", 0xc0A8); - add("TLS_PSK_WITH_AES_256_CCM_8", 0xc0A9); - add("TLS_PSK_DHE_WITH_AES_128_CCM_8", 0xc0Aa); - add("TLS_PSK_DHE_WITH_AES_256_CCM_8", 0xc0Ab); - - // Unsupported cipher suites from RFC 7251 - add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM", 0xc0Ac); - add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM", 0xc0Ad); - add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", 0xc0Ae); - add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", 0xc0Af); - } - - // ciphersuite SSL_NULL_WITH_NULL_NULL - static final CipherSuite C_NULL = CipherSuite.valueOf(0, 0); - - // ciphersuite TLS_EMPTY_RENEGOTIATION_INFO_SCSV - static final CipherSuite C_SCSV = CipherSuite.valueOf(0x00, 0xff); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java --- a/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 cipherSuites; - private String[] suiteNames; - private final EnumSet groupsTypes = - EnumSet.noneOf(NamedGroupType.class); - - // for use by buildAvailableCache() and - // Handshaker.getKickstartMessage() only - CipherSuiteList(Collection cipherSuites) { - this.cipherSuites = cipherSuites; - for (CipherSuite suite : cipherSuites) { - updateGroupTypes(suite); - } - } - - /** - * Create a CipherSuiteList with a single element. - */ - CipherSuiteList(CipherSuite suite) { - cipherSuites = new ArrayList(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(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(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 iterator() { - return cipherSuites.iterator(); - } - - /** - * Return a reference to the internal Collection of CipherSuites. - * The Collection MUST NOT be modified. - */ - Collection 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); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CipherType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CipherType.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Ciphertext.java --- a/src/java.base/share/classes/sun/security/ssl/Ciphertext.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/Ciphertext.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 + ")"); - } - } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ClientHandshakeContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java --- a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2004 +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.io.*; -import java.math.BigInteger; -import java.security.*; -import java.util.*; - -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.ECParameterSpec; - -import java.security.cert.X509Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateParsingException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertPathValidatorException.Reason; -import java.security.cert.CertPathValidatorException.BasicReason; -import javax.security.auth.x500.X500Principal; - -import javax.crypto.SecretKey; - -import javax.net.ssl.*; - -import sun.security.ssl.HandshakeMessage.*; -import static sun.security.ssl.CipherSuite.KeyExchange.*; - -/** - * ClientHandshaker does the protocol handshaking from the point - * of view of a client. It is driven asychronously by handshake messages - * as delivered by the parent Handshaker class, and also uses - * common functionality (e.g. key generation) that is provided there. - * - * @author David Brownell - */ -final class ClientHandshaker extends Handshaker { - - // constants for subject alt names of type DNS and IP - private static final int ALTNAME_DNS = 2; - private static final int ALTNAME_IP = 7; - - // the server's public key from its certificate. - private PublicKey serverKey; - - // the server's ephemeral public key from the server key exchange message - // for ECDHE/ECDH_anon and RSA_EXPORT. - private PublicKey ephemeralServerKey; - - // server's ephemeral public value for DHE/DH_anon key exchanges - private BigInteger serverDH; - - private DHCrypt dh; - - private ECDHCrypt ecdh; - - private CertificateRequest certRequest; - - private boolean serverKeyExchangeReceived; - - private boolean staplingActive = false; - private X509Certificate[] deferredCerts; - - /* - * The RSA PreMasterSecret needs to know the version of - * ClientHello that was used on this handshake. This represents - * the "max version" this client is supporting. In the - * case of an initial handshake, it's the max version enabled, - * but in the case of a resumption attempt, it's the version - * of the session we're trying to resume. - */ - private ProtocolVersion maxProtocolVersion; - - // To switch off the SNI extension. - private static final boolean enableSNIExtension = - Debug.getBooleanProperty("jsse.enableSNIExtension", true); - - /* - * 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, endpoing 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 5764) 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 - * compactibility 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. - */ - private static final boolean allowUnsafeServerCertChange = - Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false); - - // To switch off the max_fragment_length extension. - private static final boolean enableMFLExtension = - Debug.getBooleanProperty("jsse.enableMFLExtension", false); - - // To switch off the supported_groups extension for DHE cipher suite. - private static final boolean enableFFDHE = - Debug.getBooleanProperty("jsse.enableFFDHE", true); - - // Whether an ALPN extension was sent in the ClientHello - private boolean alpnActive = false; - - private List requestedServerNames = - Collections.emptyList(); - - // maximum fragment length - private int requestedMFLength = -1; // -1: no fragment length limit - - private boolean serverNamesAccepted = false; - - private ClientHello initialClientHelloMsg = null; // DTLS only - - /* - * 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. - */ - private X509Certificate[] reservedServerCerts = null; - - /* - * Constructors - */ - ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context, - ProtocolList enabledProtocols, - ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { - - super(socket, context, enabledProtocols, true, true, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); - } - - ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context, - ProtocolList enabledProtocols, - ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData, - boolean isDTLS) { - - super(engine, context, enabledProtocols, true, true, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData, isDTLS); - } - - /* - * This routine handles all the client side handshake messages, one at - * a time. Given the message type (and in some cases the pending cipher - * spec) it parses the type-specific message. Then it calls a function - * that handles that specific message. - * - * It updates the state machine (need to verify it) as each message - * is processed, and writes responses as needed using the connection - * in the constructor. - */ - @Override - void processMessage(byte type, int messageLen) throws IOException { - // check the handshake state - List ignoredOptStates = handshakeState.check(type); - - // If the state machine has skipped over certificate status - // and stapling was enabled, we need to check the chain immediately - // because it was deferred, waiting for CertificateStatus. - if (staplingActive && ignoredOptStates.contains( - HandshakeMessage.ht_certificate_status)) { - checkServerCerts(deferredCerts); - serverKey = session.getPeerCertificates()[0].getPublicKey(); - } - - switch (type) { - case HandshakeMessage.ht_hello_request: - HelloRequest helloRequest = new HelloRequest(input); - handshakeState.update(helloRequest, resumingSession); - this.serverHelloRequest(helloRequest); - break; - - case HandshakeMessage.ht_hello_verify_request: - if (!isDTLS) { - throw new SSLProtocolException( - "hello_verify_request is not a SSL/TLS handshake message"); - } - - HelloVerifyRequest helloVerifyRequest = - new HelloVerifyRequest(input, messageLen); - handshakeState.update(helloVerifyRequest, resumingSession); - this.helloVerifyRequest(helloVerifyRequest); - break; - - case HandshakeMessage.ht_server_hello: - ServerHello serverHello = new ServerHello(input, messageLen); - this.serverHello(serverHello); - - // This handshake state update needs the resumingSession value - // set by serverHello(). - handshakeState.update(serverHello, resumingSession); - break; - - case HandshakeMessage.ht_certificate: - if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON - || ClientKeyExchangeService.find(keyExchange.name) != null) { - // No external key exchange provider needs a cert now. - fatalSE(Alerts.alert_unexpected_message, - "unexpected server cert chain"); - // NOTREACHED - } - CertificateMsg certificateMsg = new CertificateMsg(input); - handshakeState.update(certificateMsg, resumingSession); - this.serverCertificate(certificateMsg); - if (!staplingActive) { - // If we are not doing stapling, we can set serverKey right - // away. Otherwise, we will wait until verification of the - // chain has completed after CertificateStatus; - serverKey = session.getPeerCertificates()[0].getPublicKey(); - } - break; - - case HandshakeMessage.ht_certificate_status: - CertificateStatus certStatusMsg = new CertificateStatus(input); - handshakeState.update(certStatusMsg, resumingSession); - this.certificateStatus(certStatusMsg); - serverKey = session.getPeerCertificates()[0].getPublicKey(); - break; - - case HandshakeMessage.ht_server_key_exchange: - serverKeyExchangeReceived = true; - switch (keyExchange) { - case K_RSA_EXPORT: - /** - * The server key exchange message is sent by the server only - * when the server certificate message does not contain the - * proper amount of data to allow the client to exchange a - * premaster secret, such as when RSA_EXPORT is used and the - * public key in the server certificate is longer than 512 bits. - */ - if (serverKey == null) { - throw new SSLProtocolException - ("Server did not send certificate message"); - } - - if (!(serverKey instanceof RSAPublicKey)) { - throw new SSLProtocolException("Protocol violation:" + - " the certificate type must be appropriate for the" + - " selected cipher suite's key exchange algorithm"); - } - - if (JsseJce.getRSAKeyLength(serverKey) <= 512) { - throw new SSLProtocolException("Protocol violation:" + - " server sent a server key exchange message for" + - " key exchange " + keyExchange + - " when the public key in the server certificate" + - " is less than or equal to 512 bits in length"); - } - - try { - RSA_ServerKeyExchange rsaSrvKeyExchange = - new RSA_ServerKeyExchange(input); - handshakeState.update(rsaSrvKeyExchange, resumingSession); - this.serverKeyExchange(rsaSrvKeyExchange); - } catch (GeneralSecurityException e) { - throw new SSLException("Server key", e); - } - break; - case K_DH_ANON: - try { - DH_ServerKeyExchange dhSrvKeyExchange = - new DH_ServerKeyExchange(input, protocolVersion); - handshakeState.update(dhSrvKeyExchange, resumingSession); - this.serverKeyExchange(dhSrvKeyExchange); - } catch (GeneralSecurityException e) { - throw new SSLException("Server key", e); - } - break; - case K_DHE_DSS: - case K_DHE_RSA: - try { - DH_ServerKeyExchange dhSrvKeyExchange = - new DH_ServerKeyExchange( - input, serverKey, - clnt_random.random_bytes, svr_random.random_bytes, - messageLen, - getLocalSupportedSignAlgs(), protocolVersion); - handshakeState.update(dhSrvKeyExchange, resumingSession); - this.serverKeyExchange(dhSrvKeyExchange); - } catch (GeneralSecurityException e) { - throw new SSLException("Server key", e); - } - break; - case K_ECDHE_ECDSA: - case K_ECDHE_RSA: - case K_ECDH_ANON: - try { - ECDH_ServerKeyExchange ecdhSrvKeyExchange = - new ECDH_ServerKeyExchange - (input, serverKey, clnt_random.random_bytes, - svr_random.random_bytes, - getLocalSupportedSignAlgs(), protocolVersion); - handshakeState.update(ecdhSrvKeyExchange, resumingSession); - this.serverKeyExchange(ecdhSrvKeyExchange); - } catch (GeneralSecurityException e) { - throw new SSLException("Server key", e); - } - break; - case K_RSA: - case K_DH_RSA: - case K_DH_DSS: - case K_ECDH_ECDSA: - case K_ECDH_RSA: - throw new SSLProtocolException( - "Protocol violation: server sent a server key exchange" - + " message for key exchange " + keyExchange); - default: - throw new SSLProtocolException( - "unsupported or unexpected key exchange algorithm = " - + keyExchange); - } - break; - - case HandshakeMessage.ht_certificate_request: - // save for later, it's handled by serverHelloDone - if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) { - throw new SSLHandshakeException( - "Client authentication requested for "+ - "anonymous cipher suite."); - } else if (ClientKeyExchangeService.find(keyExchange.name) != null) { - // No external key exchange provider needs a cert now. - throw new SSLHandshakeException( - "Client certificate requested for "+ - "external cipher suite: " + keyExchange); - } - certRequest = new CertificateRequest(input, protocolVersion); - if (debug != null && Debug.isOn("handshake")) { - certRequest.print(System.out); - } - handshakeState.update(certRequest, resumingSession); - - if (protocolVersion.useTLS12PlusSpec()) { - Collection peerSignAlgs = - certRequest.getSignAlgorithms(); - if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No peer supported signature algorithms"); - } - - Collection supportedPeerSignAlgs = - SignatureAndHashAlgorithm.getSupportedAlgorithms( - algorithmConstraints, peerSignAlgs); - if (supportedPeerSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No supported signature and hash algorithm in common"); - } - - setPeerSupportedSignAlgs(supportedPeerSignAlgs); - session.setPeerSupportedSignatureAlgorithms( - supportedPeerSignAlgs); - } - - break; - - case HandshakeMessage.ht_server_hello_done: - ServerHelloDone serverHelloDone = new ServerHelloDone(input); - handshakeState.update(serverHelloDone, resumingSession); - this.serverHelloDone(serverHelloDone); - - break; - - case HandshakeMessage.ht_finished: - Finished serverFinished = - new Finished(protocolVersion, input, cipherSuite); - handshakeState.update(serverFinished, resumingSession); - this.serverFinished(serverFinished); - - break; - - default: - throw new SSLProtocolException( - "Illegal client handshake msg, " + type); - } - } - - /* - * Used by the server to kickstart negotiations -- this requests a - * "client hello" to renegotiate current cipher specs (e.g. maybe lots - * of data has been encrypted with the same keys, or the server needs - * the client to present a certificate). - */ - private void serverHelloRequest(HelloRequest mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // - // Could be (e.g. at connection setup) that we already - // sent the "client hello" but the server's not seen it. - // - if (!clientHelloDelivered) { - if (!secureRenegotiation && !allowUnsafeRenegotiation) { - // renegotiation is not allowed. - if (activeProtocolVersion.useTLS10PlusSpec()) { - // response with a no_renegotiation warning, - warningSE(Alerts.alert_no_renegotiation); - - // invalidate the handshake so that the caller can - // dispose this object. - invalidated = true; - - // If there is still unread block in the handshake - // input stream, it would be truncated with the disposal - // and the next handshake message will become incomplete. - // - // However, according to SSL/TLS specifications, no more - // handshake message should immediately follow ClientHello - // or HelloRequest. So just let it be. - } else { - // For SSLv3, send the handshake_failure fatal error. - // Note that SSLv3 does not define a no_renegotiation - // alert like TLSv1. However we cannot ignore the message - // simply, otherwise the other side was waiting for a - // response that would never come. - fatalSE(Alerts.alert_handshake_failure, - "Renegotiation is not allowed"); - } - } else { - if (!secureRenegotiation) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning: continue with insecure renegotiation"); - } - } - kickstart(); - } - } - } - - private void helloVerifyRequest( - HelloVerifyRequest mesg) throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // - // 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. - // - initialClientHelloMsg.cookie = mesg.cookie.clone(); - - if (debug != null && Debug.isOn("handshake")) { - initialClientHelloMsg.print(System.out); - } - - // deliver the ClientHello message with cookie - initialClientHelloMsg.write(output); - handshakeState.update(initialClientHelloMsg, resumingSession); - } - - /* - * Server chooses session parameters given options created by the - * client -- basically, cipher options, session id, and someday a - * set of compression options. - * - * There are two branches of the state machine, decided by the - * details of this message. One is the "fast" handshake, where we - * can resume the pre-existing session we asked resume. The other - * is a more expensive "full" handshake, with key exchange and - * probably authentication getting done. - */ - private void serverHello(ServerHello mesg) throws IOException { - // Dispose the reserved ClientHello message (if exists). - initialClientHelloMsg = null; - - serverKeyExchangeReceived = false; - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // check if the server selected protocol version is OK for us - ProtocolVersion mesgVersion = mesg.protocolVersion; - if (!isNegotiable(mesgVersion)) { - throw new SSLHandshakeException( - "Server chose " + mesgVersion + - ", but that protocol version is not enabled or not supported " + - "by the client."); - } - - handshakeHash.protocolDetermined(mesgVersion); - - // Set protocolVersion and propagate to SSLSocket and the - // Handshake streams - setVersion(mesgVersion); - - // check the "renegotiation_info" extension - RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension) - mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); - if (serverHelloRI != null) { - if (isInitialHandshake) { - // verify the length of the "renegotiated_connection" field - if (!serverHelloRI.isEmpty()) { - // abort the handshake with a fatal handshake_failure alert - fatalSE(Alerts.alert_handshake_failure, - "The renegotiation_info field is not empty"); - } - - secureRenegotiation = true; - } else { - // For a legacy renegotiation, the client MUST verify that - // it does not contain the "renegotiation_info" extension. - if (!secureRenegotiation) { - fatalSE(Alerts.alert_handshake_failure, - "Unexpected renegotiation indication extension"); - } - - // verify the client_verify_data and server_verify_data values - byte[] verifyData = - new byte[clientVerifyData.length + serverVerifyData.length]; - System.arraycopy(clientVerifyData, 0, verifyData, - 0, clientVerifyData.length); - System.arraycopy(serverVerifyData, 0, verifyData, - clientVerifyData.length, serverVerifyData.length); - if (!MessageDigest.isEqual(verifyData, - serverHelloRI.getRenegotiatedConnection())) { - fatalSE(Alerts.alert_handshake_failure, - "Incorrect verify data in ServerHello " + - "renegotiation_info message"); - } - } - } else { - // no renegotiation indication extension - if (isInitialHandshake) { - if (!allowLegacyHelloMessages) { - // abort the handshake with a fatal handshake_failure alert - fatalSE(Alerts.alert_handshake_failure, - "Failed to negotiate the use of secure renegotiation"); - } - - secureRenegotiation = false; - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning: No renegotiation " + - "indication extension in ServerHello"); - } - } else { - // For a secure renegotiation, the client must abort the - // handshake if no "renegotiation_info" extension is present. - if (secureRenegotiation) { - fatalSE(Alerts.alert_handshake_failure, - "No renegotiation indication extension"); - } - - // we have already allowed unsafe renegotation before request - // the renegotiation. - } - } - - // - // Save server nonce, we always use it to compute connection - // keys and it's also used to create the master secret if we're - // creating a new session (i.e. in the full handshake). - // - svr_random = mesg.svr_random; - - if (isNegotiable(mesg.cipherSuite) == false) { - fatalSE(Alerts.alert_illegal_parameter, - "Server selected improper ciphersuite " + mesg.cipherSuite); - } - - setCipherSuite(mesg.cipherSuite); - if (protocolVersion.useTLS12PlusSpec()) { - handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); - } - - if (mesg.compression_method != 0) { - fatalSE(Alerts.alert_illegal_parameter, - "compression type not supported, " - + mesg.compression_method); - // NOTREACHED - } - - // so far so good, let's look at the session - if (session != null) { - // we tried to resume, let's see what the server decided - if (session.getSessionId().equals(mesg.sessionId)) { - // server resumed the session, let's make sure everything - // checks out - - // Verify that the session ciphers are unchanged. - CipherSuite sessionSuite = session.getSuite(); - if (cipherSuite != sessionSuite) { - throw new SSLProtocolException - ("Server returned wrong cipher suite for session"); - } - - // verify protocol version match - ProtocolVersion sessionVersion = session.getProtocolVersion(); - if (protocolVersion != sessionVersion) { - throw new SSLProtocolException - ("Server resumed session with wrong protocol version"); - } - - // validate subject identity - ClientKeyExchangeService p = - ClientKeyExchangeService.find( - sessionSuite.keyExchange.name); - if (p != null) { - Principal localPrincipal = session.getLocalPrincipal(); - - if (p.isRelated(true, getAccSE(), localPrincipal)) { - if (debug != null && Debug.isOn("session")) - System.out.println("Subject identity is same"); - } else { - throw new SSLProtocolException( - "Server resumed session with " + - "wrong subject identity or no subject"); - } - } - - // looks fine; resume it. - resumingSession = true; - calculateConnectionKeys(session.getMasterSecret()); - if (debug != null && Debug.isOn("session")) { - System.out.println("%% Server resumed " + session); - } - } else { - // we wanted to resume, but the server refused - // - // Invalidate the session for initial handshake in case - // of reusing next time. - if (isInitialHandshake) { - session.invalidate(); - } - session = null; - if (!enableNewSession) { - throw new SSLException("New session creation is disabled"); - } - } - } - - // check the "max_fragment_length" extension - MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension) - mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH); - if (maxFragLenExt != null) { - if ((requestedMFLength == -1) || - maxFragLenExt.getMaxFragLen() != requestedMFLength) { - // If the client did not request this extension, or the - // response value is different from the length it requested, - // abort the handshake with a fatal illegal_parameter alert. - fatalSE(Alerts.alert_illegal_parameter, - "Failed to negotiate the max_fragment_length"); - } - } else if (!resumingSession) { - // no "max_fragment_length" extension - requestedMFLength = -1; - } // Otherwise, using the value negotiated during the original - // session initiation - - // check the "extended_master_secret" extension - ExtendedMasterSecretExtension extendedMasterSecretExt = - (ExtendedMasterSecretExtension)mesg.extensions.get( - ExtensionType.EXT_EXTENDED_MASTER_SECRET); - if (extendedMasterSecretExt != null) { - // Is it the expected server extension? - if (!useExtendedMasterSecret || - !mesgVersion.useTLS10PlusSpec() || !requestedToUseEMS) { - fatalSE(Alerts.alert_unsupported_extension, - "Server sent the extended_master_secret " + - "extension improperly"); - } - - // For abbreviated handshake, if the original session did not use - // the "extended_master_secret" extension but the new ServerHello - // contains the extension, the client MUST abort the handshake. - if (resumingSession && (session != null) && - !session.getUseExtendedMasterSecret()) { - fatalSE(Alerts.alert_unsupported_extension, - "Server sent an unexpected extended_master_secret " + - "extension on session resumption"); - } - } else { - if (useExtendedMasterSecret && !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. - fatalSE(Alerts.alert_handshake_failure, - "Extended Master Secret extension is required"); - } - - if (resumingSession && (session != null)) { - if (session.getUseExtendedMasterSecret()) { - // 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. - fatalSE(Alerts.alert_handshake_failure, - "Missing Extended Master Secret extension " + - "on session resumption"); - } else if (useExtendedMasterSecret && !allowLegacyResumption) { - // Unlikely, abbreviated handshake should be discarded. - fatalSE(Alerts.alert_handshake_failure, - "Extended Master Secret extension is required"); - } - } - } - - // check the ALPN extension - ALPNExtension serverHelloALPN = - (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN); - - if (serverHelloALPN != null) { - // Check whether an ALPN extension was sent in ClientHello message - if (!alpnActive) { - fatalSE(Alerts.alert_unsupported_extension, - "Server sent " + ExtensionType.EXT_ALPN + - " extension when not requested by client"); - } - - List protocols = serverHelloALPN.getPeerAPs(); - // Only one application protocol name should be present - String p; - if ((protocols.size() == 1) && - !((p = protocols.get(0)).isEmpty())) { - int i; - for (i = 0; i < localApl.length; i++) { - if (localApl[i].equals(p)) { - break; - } - } - if (i == localApl.length) { - fatalSE(Alerts.alert_handshake_failure, - "Server has selected an application protocol name " + - "which was not offered by the client: " + p); - } - applicationProtocol = p; - } else { - fatalSE(Alerts.alert_handshake_failure, - "Incorrect data in ServerHello " + ExtensionType.EXT_ALPN + - " message"); - } - } else { - applicationProtocol = ""; - } - - if (resumingSession && session != null) { - setHandshakeSessionSE(session); - // Reserve the handshake state if this is a session-resumption - // abbreviated initial handshake. - if (isInitialHandshake) { - session.setAsSessionResumption(true); - } - - return; - } - - // check extensions - for (HelloExtension ext : mesg.extensions.list()) { - ExtensionType type = ext.type; - if (type == ExtensionType.EXT_SERVER_NAME) { - serverNamesAccepted = true; - } else if (type == ExtensionType.EXT_STATUS_REQUEST || - type == ExtensionType.EXT_STATUS_REQUEST_V2) { - // Only enable the stapling feature if the client asserted - // these extensions. - if (sslContext.isStaplingEnabled(true)) { - staplingActive = true; - } else { - fatalSE(Alerts.alert_unexpected_message, "Server set " + - type + " extension when not requested by client"); - } - } else if ((type != ExtensionType.EXT_SUPPORTED_GROUPS) - && (type != ExtensionType.EXT_EC_POINT_FORMATS) - && (type != ExtensionType.EXT_SERVER_NAME) - && (type != ExtensionType.EXT_ALPN) - && (type != ExtensionType.EXT_RENEGOTIATION_INFO) - && (type != ExtensionType.EXT_STATUS_REQUEST) - && (type != ExtensionType.EXT_STATUS_REQUEST_V2) - && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) { - // Note: Better to check client requested extensions rather - // than all supported extensions. - fatalSE(Alerts.alert_unsupported_extension, - "Server sent an unsupported extension: " + type); - } - } - - // Create a new session, we need to do the full handshake - session = new SSLSessionImpl(protocolVersion, cipherSuite, - getLocalSupportedSignAlgs(), - mesg.sessionId, getHostSE(), getPortSE(), - (extendedMasterSecretExt != null)); - session.setRequestedServerNames(requestedServerNames); - session.setNegotiatedMaxFragSize(requestedMFLength); - session.setMaximumPacketSize(maximumPacketSize); - setHandshakeSessionSE(session); - if (debug != null && Debug.isOn("handshake")) { - System.out.println("** " + cipherSuite); - } - } - - /* - * Server's own key was either a signing-only key, or was too - * large for export rules ... this message holds an ephemeral - * RSA key to use for key exchange. - */ - private void serverKeyExchange(RSA_ServerKeyExchange mesg) - throws IOException, GeneralSecurityException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - if (!mesg.verify(serverKey, clnt_random, svr_random)) { - fatalSE(Alerts.alert_handshake_failure, - "server key exchange invalid"); - // NOTREACHED - } - ephemeralServerKey = mesg.getPublicKey(); - - // check constraints of RSA PublicKey - if (!algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) { - - throw new SSLHandshakeException("RSA ServerKeyExchange " + - "does not comply to algorithm constraints"); - } - } - - /* - * Diffie-Hellman key exchange. We save the server public key and - * our own D-H algorithm object so we can defer key calculations - * until after we've sent the client key exchange message (which - * gives client and server some useful parallelism). - * - * Note per section 3 of RFC 7919, if the server is not compatible with - * FFDHE specification, the client MAY decide to continue the connection - * if the selected DHE group is acceptable under local policy, or it MAY - * decide to terminate the connection with a fatal insufficient_security - * (71) alert. The algorithm constraints mechanism is JDK local policy - * used for additional DHE parameters checking. So this implementation - * does not check the server compatibility and just pass to the local - * algorithm constraints checking. The client will continue the - * connection if the server selected DHE group is acceptable by the - * specified algorithm constraints. - */ - private void serverKeyExchange(DH_ServerKeyExchange mesg) - throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), - sslContext.getSecureRandom()); - serverDH = mesg.getServerPublicKey(); - - // check algorithm constraints - dh.checkConstraints(algorithmConstraints, serverDH); - } - - private void serverKeyExchange(ECDH_ServerKeyExchange mesg) - throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - ECPublicKey key = mesg.getPublicKey(); - ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom()); - ephemeralServerKey = key; - - // check constraints of EC PublicKey - if (!algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), ephemeralServerKey)) { - - throw new SSLHandshakeException("ECDH ServerKeyExchange " + - "does not comply to algorithm constraints"); - } - } - - /* - * The server's "Hello Done" message is the client's sign that - * it's time to do all the hard work. - */ - private void serverHelloDone(ServerHelloDone mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - /* - * FIRST ... if requested, send an appropriate Certificate chain - * to authenticate the client, and remember the associated private - * key to sign the CertificateVerify message. - */ - PrivateKey signingKey = null; - - if (certRequest != null) { - X509ExtendedKeyManager km = sslContext.getX509KeyManager(); - - ArrayList keytypesTmp = new ArrayList<>(4); - - for (int i = 0; i < certRequest.types.length; i++) { - String typeName; - - switch (certRequest.types[i]) { - case CertificateRequest.cct_rsa_sign: - typeName = "RSA"; - break; - - case CertificateRequest.cct_dss_sign: - typeName = "DSA"; - break; - - case CertificateRequest.cct_ecdsa_sign: - // ignore if we do not have EC crypto available - typeName = JsseJce.isEcAvailable() ? "EC" : null; - break; - - // Fixed DH/ECDH client authentication not supported - // - // case CertificateRequest.cct_rsa_fixed_dh: - // case CertificateRequest.cct_dss_fixed_dh: - // case CertificateRequest.cct_rsa_fixed_ecdh: - // case CertificateRequest.cct_ecdsa_fixed_ecdh: - // - // Any other values (currently not used in TLS) - // - // case CertificateRequest.cct_rsa_ephemeral_dh: - // case CertificateRequest.cct_dss_ephemeral_dh: - default: - typeName = null; - break; - } - - if ((typeName != null) && (!keytypesTmp.contains(typeName))) { - keytypesTmp.add(typeName); - } - } - - String alias = null; - int keytypesTmpSize = keytypesTmp.size(); - if (keytypesTmpSize != 0) { - String[] keytypes = - keytypesTmp.toArray(new String[keytypesTmpSize]); - - if (conn != null) { - alias = km.chooseClientAlias(keytypes, - certRequest.getAuthorities(), conn); - } else { - alias = km.chooseEngineClientAlias(keytypes, - certRequest.getAuthorities(), engine); - } - } - - CertificateMsg m1 = null; - if (alias != null) { - X509Certificate[] certs = km.getCertificateChain(alias); - if ((certs != null) && (certs.length != 0)) { - PublicKey publicKey = certs[0].getPublicKey(); - if (publicKey != null) { - m1 = new CertificateMsg(certs); - signingKey = km.getPrivateKey(alias); - session.setLocalPrivateKey(signingKey); - session.setLocalCertificates(certs); - } - } - } - if (m1 == null) { - // - // No appropriate cert was found ... report this to the - // server. For SSLv3, send the no_certificate alert; - // TLS uses an empty cert chain instead. - // - if (protocolVersion.useTLS10PlusSpec()) { - m1 = new CertificateMsg(new X509Certificate [0]); - } else { - warningSE(Alerts.alert_no_certificate); - } - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning: no suitable certificate found - " + - "continuing without client authentication"); - } - } - - // - // At last ... send any client certificate chain. - // - if (m1 != null) { - if (debug != null && Debug.isOn("handshake")) { - m1.print(System.out); - } - m1.write(output); - handshakeState.update(m1, resumingSession); - } - } - - /* - * SECOND ... send the client key exchange message. The - * procedure used is a function of the cipher suite selected; - * one is always needed. - */ - HandshakeMessage m2; - - switch (keyExchange) { - - case K_RSA: - case K_RSA_EXPORT: - if (serverKey == null) { - throw new SSLProtocolException - ("Server did not send certificate message"); - } - - if (!(serverKey instanceof RSAPublicKey)) { - throw new SSLProtocolException - ("Server certificate does not include an RSA key"); - } - - /* - * For RSA key exchange, we randomly generate a new - * pre-master secret and encrypt it with the server's - * public key. Then we save that pre-master secret - * so that we can calculate the keying data later; - * it's a performance speedup not to do that until - * the client's waiting for the server response, but - * more of a speedup for the D-H case. - * - * If the RSA_EXPORT scheme is active, when the public - * key in the server certificate is less than or equal - * to 512 bits in length, use the cert's public key, - * otherwise, the ephemeral one. - */ - PublicKey key; - if (keyExchange == K_RSA) { - key = serverKey; - } else { // K_RSA_EXPORT - if (JsseJce.getRSAKeyLength(serverKey) <= 512) { - // extraneous ephemeralServerKey check done - // above in processMessage() - key = serverKey; - } else { - if (ephemeralServerKey == null) { - throw new SSLProtocolException("Server did not send" + - " a RSA_EXPORT Server Key Exchange message"); - } - key = ephemeralServerKey; - } - } - - m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion, - sslContext.getSecureRandom(), key); - break; - case K_DH_RSA: - case K_DH_DSS: - /* - * For DH Key exchange, we only need to make sure the server - * knows our public key, so we calculate the same pre-master - * secret. - * - * For certs that had DH keys in them, we send an empty - * handshake message (no key) ... we flag this case by - * passing a null "dhPublic" value. - * - * Otherwise we send ephemeral DH keys, unsigned. - */ - // if (useDH_RSA || useDH_DSS) - m2 = new DHClientKeyExchange(); - break; - case K_DHE_RSA: - case K_DHE_DSS: - case K_DH_ANON: - if (dh == null) { - throw new SSLProtocolException - ("Server did not send a DH Server Key Exchange message"); - } - m2 = new DHClientKeyExchange(dh.getPublicKey()); - break; - case K_ECDHE_RSA: - case K_ECDHE_ECDSA: - case K_ECDH_ANON: - if (ecdh == null) { - throw new SSLProtocolException - ("Server did not send a ECDH Server Key Exchange message"); - } - m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); - break; - case K_ECDH_RSA: - case K_ECDH_ECDSA: - if (serverKey == null) { - throw new SSLProtocolException - ("Server did not send certificate message"); - } - if (serverKey instanceof ECPublicKey == false) { - throw new SSLProtocolException - ("Server certificate does not include an EC key"); - } - ECParameterSpec params = ((ECPublicKey)serverKey).getParams(); - ecdh = new ECDHCrypt(params, sslContext.getSecureRandom()); - m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); - break; - default: - ClientKeyExchangeService p = - ClientKeyExchangeService.find(keyExchange.name); - if (p == null) { - // somethings very wrong - throw new RuntimeException - ("Unsupported key exchange: " + keyExchange); - } - String sniHostname = null; - for (SNIServerName serverName : requestedServerNames) { - if (serverName instanceof SNIHostName) { - sniHostname = ((SNIHostName) serverName).getAsciiName(); - break; - } - } - - ClientKeyExchange exMsg = null; - if (sniHostname != null) { - // use first requested SNI hostname - try { - exMsg = p.createClientExchange( - sniHostname, getAccSE(), protocolVersion, - sslContext.getSecureRandom()); - } catch(IOException e) { - if (serverNamesAccepted) { - // server accepted requested SNI hostname, - // so it must be used - throw e; - } - // fallback to using hostname - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning, cannot use Server Name Indication: " - + e.getMessage()); - } - } - } - - if (exMsg == null) { - String hostname = getHostSE(); - if (hostname == null) { - throw new IOException("Hostname is required" + - " to use " + keyExchange + " key exchange"); - } - exMsg = p.createClientExchange( - hostname, getAccSE(), protocolVersion, - sslContext.getSecureRandom()); - } - - // Record the principals involved in exchange - session.setPeerPrincipal(exMsg.getPeerPrincipal()); - session.setLocalPrincipal(exMsg.getLocalPrincipal()); - m2 = exMsg; - break; - } - if (debug != null && Debug.isOn("handshake")) { - m2.print(System.out); - } - m2.write(output); - handshakeState.update(m2, resumingSession); - - /* - * THIRD, send a "change_cipher_spec" record followed by the - * "Finished" message. We flush the messages we've queued up, to - * get concurrency between client and server. The concurrency is - * useful as we calculate the master secret, which is needed both - * to compute the "Finished" message, and to compute the keys used - * to protect all records following the change_cipher_spec. - */ - output.flush(); - - /* - * We deferred calculating the master secret and this connection's - * keying data; we do it now. Deferring this calculation is good - * from a performance point of view, since it lets us do it during - * some time that network delays and the server's own calculations - * would otherwise cause to be "dead" in the critical path. - */ - SecretKey preMasterSecret; - switch (keyExchange) { - case K_RSA: - case K_RSA_EXPORT: - preMasterSecret = ((RSAClientKeyExchange)m2).preMaster; - break; - case K_DHE_RSA: - case K_DHE_DSS: - case K_DH_ANON: - preMasterSecret = dh.getAgreedSecret(serverDH, true); - break; - case K_ECDHE_RSA: - case K_ECDHE_ECDSA: - case K_ECDH_ANON: - preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey); - break; - case K_ECDH_RSA: - case K_ECDH_ECDSA: - preMasterSecret = ecdh.getAgreedSecret(serverKey); - break; - default: - if (ClientKeyExchangeService.find(keyExchange.name) != null) { - preMasterSecret = - ((ClientKeyExchange) m2).clientKeyExchange(); - } else { - throw new IOException("Internal error: unknown key exchange " - + keyExchange); - } - } - - calculateKeys(preMasterSecret, null); - - /* - * FOURTH, if we sent a Certificate, we need to send a signed - * CertificateVerify (unless the key in the client's certificate - * was a Diffie-Hellman key). - * - * This uses a hash of the previous handshake messages ... either - * a nonfinal one (if the particular implementation supports it) - * or else using the third element in the arrays of hashes being - * computed. - */ - if (signingKey != null) { - CertificateVerify m3; - try { - SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; - if (protocolVersion.useTLS12PlusSpec()) { - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.getPreferableAlgorithm( - getPeerSupportedSignAlgs(), - signingKey.getAlgorithm(), signingKey); - - if (preferableSignatureAlgorithm == null) { - throw new SSLHandshakeException( - "No supported signature algorithm"); - } - - String hashAlg = - SignatureAndHashAlgorithm.getHashAlgorithmName( - preferableSignatureAlgorithm); - if (hashAlg == null || hashAlg.length() == 0) { - throw new SSLHandshakeException( - "No supported hash algorithm"); - } - } - - m3 = new CertificateVerify(protocolVersion, handshakeHash, - signingKey, session.getMasterSecret(), - sslContext.getSecureRandom(), - preferableSignatureAlgorithm); - } catch (GeneralSecurityException e) { - fatalSE(Alerts.alert_handshake_failure, - "Error signing certificate verify", e); - // NOTREACHED, make compiler happy - m3 = null; - } - if (debug != null && Debug.isOn("handshake")) { - m3.print(System.out); - } - m3.write(output); - handshakeState.update(m3, resumingSession); - output.flush(); - } - - /* - * OK, that's that! - */ - sendChangeCipherAndFinish(false); - - // expecting the final ChangeCipherSpec and Finished messages - expectingFinishFlightSE(); - } - - - /* - * "Finished" is the last handshake message sent. If we got this - * far, the MAC has been validated post-decryption. We validate - * the two hashes here as an additional sanity check, protecting - * the handshake against various active attacks. - */ - private void serverFinished(Finished mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - boolean verified = mesg.verify(handshakeHash, Finished.SERVER, - session.getMasterSecret()); - - if (!verified) { - fatalSE(Alerts.alert_illegal_parameter, - "server 'finished' message doesn't verify"); - // NOTREACHED - } - - /* - * save server verify data for secure renegotiation - */ - if (secureRenegotiation) { - serverVerifyData = mesg.getVerifyData(); - } - - /* - * Reset the handshake state if this is not an initial handshake. - */ - if (!isInitialHandshake) { - session.setAsSessionResumption(false); - } - - /* - * OK, it verified. If we're doing the fast handshake, add that - * "Finished" message to the hash of handshake messages, then send - * our own change_cipher_spec and Finished message for the server - * to verify in turn. These are the last handshake messages. - * - * In any case, update the session cache. We're done handshaking, - * so there are no threats any more associated with partially - * completed handshakes. - */ - if (resumingSession) { - sendChangeCipherAndFinish(true); - } else { - handshakeFinished = true; - } - session.setLastAccessedTime(System.currentTimeMillis()); - - if (!resumingSession) { - if (session.isRejoinable()) { - ((SSLSessionContextImpl) sslContext - .engineGetClientSessionContext()) - .put(session); - if (debug != null && Debug.isOn("session")) { - System.out.println("%% Cached client session: " + session); - } - } else if (debug != null && Debug.isOn("session")) { - System.out.println( - "%% Didn't cache non-resumable client session: " - + session); - } - } - } - - - /* - * Send my change-cipher-spec and Finished message ... done as the - * last handshake act in either the short or long sequences. In - * the short one, we've already seen the server's Finished; in the - * long one, we wait for it now. - */ - private void sendChangeCipherAndFinish(boolean finishedTag) - throws IOException { - - // Reload if this message has been reserved. - handshakeHash.reload(); - - Finished mesg = new Finished(protocolVersion, handshakeHash, - Finished.CLIENT, session.getMasterSecret(), cipherSuite); - - /* - * Send the change_cipher_spec message, then the Finished message - * which we just calculated (and protected using the keys we just - * calculated). Server responds with its Finished message, except - * in the "fast handshake" (resume session) case. - */ - sendChangeCipherSpec(mesg, finishedTag); - - /* - * save client verify data for secure renegotiation - */ - if (secureRenegotiation) { - clientVerifyData = mesg.getVerifyData(); - } - } - - - /* - * Returns a ClientHello message to kickstart renegotiations - */ - @Override - HandshakeMessage getKickstartMessage() throws SSLException { - // session ID of the ClientHello message - SessionId sessionId = SSLSessionImpl.nullSession.getSessionId(); - - // a list of cipher suites sent by the client - CipherSuiteList cipherSuites = getActiveCipherSuites(); - - // set the max protocol version this client is supporting. - maxProtocolVersion = protocolVersion; - - // - // Try to resume an existing session. This might be mandatory, - // given certain API options. - // - session = ((SSLSessionContextImpl)sslContext - .engineGetClientSessionContext()) - .get(getHostSE(), getPortSE()); - if (debug != null && Debug.isOn("session")) { - if (session != null) { - System.out.println("%% Client cached " - + session - + (session.isRejoinable() ? "" : " (not rejoinable)")); - } else { - System.out.println("%% No cached client session"); - } - } - if (session != null) { - // If unsafe server certificate change is not allowed, reserve - // current server certificates if the previous handshake is a - // session-resumption abbreviated initial handshake. - if (!allowUnsafeServerCertChange && session.isSessionResumption()) { - try { - // If existing, peer certificate chain cannot be null. - reservedServerCerts = - (X509Certificate[])session.getPeerCertificates(); - } catch (SSLPeerUnverifiedException puve) { - // Maybe not certificate-based, ignore the exception. - } - } - - if (!session.isRejoinable()) { - session = null; - } - } - - if (session != null) { - CipherSuite sessionSuite = session.getSuite(); - ProtocolVersion sessionVersion = session.getProtocolVersion(); - if (isNegotiable(sessionSuite) == false) { - if (debug != null && Debug.isOn("session")) { - System.out.println("%% can't resume, unavailable cipher"); - } - session = null; - } - - if ((session != null) && !isNegotiable(sessionVersion)) { - if (debug != null && Debug.isOn("session")) { - System.out.println("%% can't resume, protocol disabled"); - } - session = null; - } - - if ((session != null) && useExtendedMasterSecret) { - boolean isTLS10Plus = sessionVersion.useTLS10PlusSpec(); - if (isTLS10Plus && !session.getUseExtendedMasterSecret()) { - if (!allowLegacyResumption) { - // perform full handshake instead - // - // The client SHOULD NOT offer an abbreviated handshake - // to resume a session that does not use an extended - // master secret. Instead, it SHOULD offer a full - // handshake. - session = null; - } - } - - if ((session != null) && !allowUnsafeServerCertChange) { - // It is fine to move on with abbreviate handshake if - // endpoint identification is enabled. - String identityAlg = getEndpointIdentificationAlgorithmSE(); - if ((identityAlg == null || identityAlg.length() == 0)) { - if (isTLS10Plus) { - if (!session.getUseExtendedMasterSecret()) { - // perform full handshake instead - session = null; - } // Otherwise, use extended master secret. - } else { - // The extended master secret extension does not - // apply to SSL 3.0. Perform a full handshake - // instead. - // - // Note that the useExtendedMasterSecret is - // extended to protect SSL 3.0 connections, - // by discarding abbreviate handshake. - session = null; - } - } - } - } - - if (session != null) { - if (debug != null) { - if (Debug.isOn("handshake") || Debug.isOn("session")) { - System.out.println("%% Try resuming " + session - + " from port " + getLocalPortSE()); - } - } - - sessionId = session.getSessionId(); - maxProtocolVersion = sessionVersion; - - // Update SSL version number in underlying SSL socket and - // handshake output stream, so that the output records (at the - // record layer) have the correct version - setVersion(sessionVersion); - } - - /* - * Force use of the previous session ciphersuite, and - * add the SCSV if enabled. - */ - if (!enableNewSession) { - if (session == null) { - throw new SSLHandshakeException( - "Can't reuse existing SSL client session"); - } - - Collection cipherList = new ArrayList<>(2); - cipherList.add(sessionSuite); - if (!secureRenegotiation && - cipherSuites.contains(CipherSuite.C_SCSV)) { - cipherList.add(CipherSuite.C_SCSV); - } // otherwise, renegotiation_info extension will be used - - cipherSuites = new CipherSuiteList(cipherList); - } - } - - if (session == null && !enableNewSession) { - throw new SSLHandshakeException("No existing session to resume"); - } - - // exclude SCSV for secure renegotiation - if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) { - Collection cipherList = - new ArrayList<>(cipherSuites.size() - 1); - for (CipherSuite suite : cipherSuites.collection()) { - if (suite != CipherSuite.C_SCSV) { - cipherList.add(suite); - } - } - - cipherSuites = new CipherSuiteList(cipherList); - } - - // make sure there is a negotiable cipher suite. - boolean negotiable = false; - for (CipherSuite suite : cipherSuites.collection()) { - if (isNegotiable(suite)) { - negotiable = true; - break; - } - } - - if (!negotiable) { - throw new SSLHandshakeException("No negotiable cipher suite"); - } - - // Not a TLS1.2+ handshake - // For SSLv2Hello, HandshakeHash.reset() will be called, so we - // cannot call HandshakeHash.protocolDetermined() here. As it does - // not follow the spec that HandshakeHash.reset() can be only be - // called before protocolDetermined. - // if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) { - // handshakeHash.protocolDetermined(maxProtocolVersion); - // } - - // create the ClientHello message - ClientHello clientHelloMessage = new ClientHello( - sslContext.getSecureRandom(), maxProtocolVersion, - sessionId, cipherSuites, isDTLS); - - // Add named groups extension for ECDHE and FFDHE if necessary. - SupportedGroupsExtension sge = - SupportedGroupsExtension.createExtension( - algorithmConstraints, - cipherSuites, enableFFDHE); - if (sge != null) { - clientHelloMessage.extensions.add(sge); - // Add elliptic point format extensions - if (cipherSuites.contains(NamedGroupType.NAMED_GROUP_ECDHE)) { - clientHelloMessage.extensions.add( - EllipticPointFormatsExtension.DEFAULT); - } - } - - // add signature_algorithm extension - if (maxProtocolVersion.useTLS12PlusSpec()) { - // we will always send the signature_algorithm extension - Collection localSignAlgs = - getLocalSupportedSignAlgs(); - if (localSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No supported signature algorithm"); - } - - clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs); - } - - // add Extended Master Secret extension - if (useExtendedMasterSecret && maxProtocolVersion.useTLS10PlusSpec()) { - if ((session == null) || session.getUseExtendedMasterSecret()) { - clientHelloMessage.addExtendedMasterSecretExtension(); - requestedToUseEMS = true; - } - } - - // add server_name extension - if (enableSNIExtension) { - if (session != null) { - requestedServerNames = session.getRequestedServerNames(); - } else { - requestedServerNames = serverNames; - } - - if (!requestedServerNames.isEmpty()) { - clientHelloMessage.addSNIExtension(requestedServerNames); - } - } - - // add max_fragment_length extension - if (enableMFLExtension) { - if (session != null) { - // The same extension should be sent for resumption. - requestedMFLength = session.getNegotiatedMaxFragSize(); - } else if (maximumPacketSize != 0) { - // Maybe we can calculate the fragment size more accurate - // by condering the enabled cipher suites in the future. - requestedMFLength = maximumPacketSize; - if (isDTLS) { - requestedMFLength -= DTLSRecord.maxPlaintextPlusSize; - } else { - requestedMFLength -= SSLRecord.maxPlaintextPlusSize; - } - } else { - // Need no max_fragment_length extension. - requestedMFLength = -1; - } - - if ((requestedMFLength > 0) && - MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) { - - requestedMFLength = - MaxFragmentLengthExtension.getValidMaxFragLen( - requestedMFLength); - clientHelloMessage.addMFLExtension(requestedMFLength); - } else { - requestedMFLength = -1; - } - } - - // Add status_request and status_request_v2 extensions - if (sslContext.isStaplingEnabled(true)) { - clientHelloMessage.addCertStatusReqListV2Extension(); - clientHelloMessage.addCertStatusRequestExtension(); - } - - // Add ALPN extension - if (localApl != null && localApl.length > 0) { - clientHelloMessage.addALPNExtension(localApl); - alpnActive = true; - } - - // reset the client random cookie - clnt_random = clientHelloMessage.clnt_random; - - /* - * need to set the renegotiation_info extension for: - * 1: secure renegotiation - * 2: initial handshake and no SCSV in the ClientHello - * 3: insecure renegotiation and no SCSV in the ClientHello - */ - if (secureRenegotiation || - !cipherSuites.contains(CipherSuite.C_SCSV)) { - clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); - } - - if (isDTLS) { - // Cookie exchange need to reserve the initial ClientHello message. - initialClientHelloMsg = clientHelloMessage; - } - - return clientHelloMessage; - } - - /* - * Fault detected during handshake. - */ - @Override - void handshakeAlert(byte description) throws SSLProtocolException { - String message = Alerts.alertDescription(description); - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("SSL - handshake alert: " + message); - } - throw new SSLProtocolException("handshake alert: " + message); - } - - /* - * Unless we are using an anonymous ciphersuite, the server always - * sends a certificate message (for the CipherSuites we currently - * support). The trust manager verifies the chain for us. - */ - private void serverCertificate(CertificateMsg mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - X509Certificate[] peerCerts = mesg.getCertificateChain(); - if (peerCerts.length == 0) { - fatalSE(Alerts.alert_bad_certificate, "empty certificate chain"); - } - - // Allow server certificate change in client side during renegotiation - // after a session-resumption abbreviated initial handshake? - // - // DO NOT need to check allowUnsafeServerCertChange here. We only - // reserve server certificates when allowUnsafeServerCertChange is - // flase. - // - // Allow server certificate change if it is negotiated to use the - // extended master secret. - if ((reservedServerCerts != null) && - !session.getUseExtendedMasterSecret()) { - // It is not necessary to check the certificate update if endpoint - // identification is enabled. - String identityAlg = getEndpointIdentificationAlgorithmSE(); - if ((identityAlg == null || identityAlg.length() == 0) && - !isIdentityEquivalent(peerCerts[0], reservedServerCerts[0])) { - - fatalSE(Alerts.alert_bad_certificate, - "server certificate change is restricted " + - "during renegotiation"); - } - } - - // ask the trust manager to verify the chain - if (staplingActive) { - // Defer the certificate check until after we've received the - // CertificateStatus message. If that message doesn't come in - // immediately following this message we will execute the check - // directly from processMessage before any other SSL/TLS processing. - deferredCerts = peerCerts; - } else { - // We're not doing stapling, so perform the check right now - checkServerCerts(peerCerts); - } - } - - /** - * If certificate status stapling has been enabled, the server will send - * one or more status messages to the client. - * - * @param mesg a {@code CertificateStatus} object built from the data - * sent by the server. - * - * @throws IOException if any parsing errors occur. - */ - private void certificateStatus(CertificateStatus mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // Perform the certificate check using the deferred certificates - // and responses that we have obtained. - session.setStatusResponses(mesg.getResponses()); - checkServerCerts(deferredCerts); - } - - /* - * Whether the certificates can represent the same identity? - * - * The certificates can be used to represent the same identity: - * 1. If the subject alternative names of IP address are present in - * both certificates, they should be identical; otherwise, - * 2. if the subject alternative names of DNS name are present in - * both certificates, they should be identical; otherwise, - * 3. if the subject fields are present in both certificates, the - * certificate subjects and issuers should be identical. - */ - private static boolean isIdentityEquivalent(X509Certificate thisCert, - X509Certificate prevCert) { - if (thisCert.equals(prevCert)) { - return true; - } - - // check subject alternative names - Collection> thisSubjectAltNames = null; - try { - thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); - } catch (CertificateParsingException cpe) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Attempt to obtain subjectAltNames extension failed!"); - } - } - - Collection> prevSubjectAltNames = null; - try { - prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); - } catch (CertificateParsingException cpe) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Attempt to obtain subjectAltNames extension failed!"); - } - } - - if ((thisSubjectAltNames != null) && (prevSubjectAltNames != null)) { - // check the iPAddress field in subjectAltName extension - Collection thisSubAltIPAddrs = - getSubjectAltNames(thisSubjectAltNames, ALTNAME_IP); - Collection prevSubAltIPAddrs = - getSubjectAltNames(prevSubjectAltNames, ALTNAME_IP); - if ((thisSubAltIPAddrs != null) && (prevSubAltIPAddrs != null) && - (isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs))) { - - return true; - } - - // check the dNSName field in subjectAltName extension - Collection thisSubAltDnsNames = - getSubjectAltNames(thisSubjectAltNames, ALTNAME_DNS); - Collection prevSubAltDnsNames = - getSubjectAltNames(prevSubjectAltNames, ALTNAME_DNS); - if ((thisSubAltDnsNames != null) && (prevSubAltDnsNames != null) && - (isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames))) { - - return true; - } - } - - // check the certificate subject and issuer - X500Principal thisSubject = thisCert.getSubjectX500Principal(); - X500Principal prevSubject = prevCert.getSubjectX500Principal(); - X500Principal thisIssuer = thisCert.getIssuerX500Principal(); - X500Principal prevIssuer = prevCert.getIssuerX500Principal(); - if (!thisSubject.getName().isEmpty() && - !prevSubject.getName().isEmpty() && - thisSubject.equals(prevSubject) && - thisIssuer.equals(prevIssuer)) { - return true; - } - - return false; - } - - /* - * Returns the subject alternative name of the specified type in the - * subjectAltNames extension of a certificate. - * - * Note that only those subjectAltName types that use String data - * should be passed into this function. - */ - private static Collection getSubjectAltNames( - Collection> subjectAltNames, int type) { - - HashSet subAltDnsNames = null; - for (List subjectAltName : subjectAltNames) { - int subjectAltNameType = (Integer)subjectAltName.get(0); - if (subjectAltNameType == type) { - String subAltDnsName = (String)subjectAltName.get(1); - if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { - if (subAltDnsNames == null) { - subAltDnsNames = - new HashSet<>(subjectAltNames.size()); - } - subAltDnsNames.add(subAltDnsName); - } - } - } - - return subAltDnsNames; - } - - private static boolean isEquivalent(Collection thisSubAltNames, - Collection prevSubAltNames) { - - for (String thisSubAltName : thisSubAltNames) { - for (String prevSubAltName : prevSubAltNames) { - // Only allow the exactly match. Check no wildcard character. - if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { - return true; - } - } - } - - return false; - } - - /** - * Perform client-side checking of server certificates. - * - * @param certs an array of {@code X509Certificate} objects presented - * by the server in the ServerCertificate message. - * - * @throws IOException if a failure occurs during validation or - * the trust manager associated with the {@code SSLContext} is not - * an {@code X509ExtendedTrustManager}. - */ - private void checkServerCerts(X509Certificate[] certs) - throws IOException { - X509TrustManager tm = sslContext.getX509TrustManager(); - - // find out the key exchange algorithm used - // use "RSA" for non-ephemeral "RSA_EXPORT" - String keyExchangeString; - if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) { - keyExchangeString = K_RSA.name; - } else { - keyExchangeString = keyExchange.name; - } - - try { - if (tm instanceof X509ExtendedTrustManager) { - if (conn != null) { - ((X509ExtendedTrustManager)tm).checkServerTrusted( - certs.clone(), - keyExchangeString, - conn); - } else { - ((X509ExtendedTrustManager)tm).checkServerTrusted( - certs.clone(), - keyExchangeString, - engine); - } - } else { - // Unlikely to happen, because we have wrapped the old - // X509TrustManager with the new X509ExtendedTrustManager. - throw new CertificateException( - "Improper X509TrustManager implementation"); - } - - // Once the server certificate chain has been validated, set - // the certificate chain in the TLS session. - session.setPeerCertificates(certs); - } catch (CertificateException ce) { - fatalSE(getCertificateAlert(ce), ce); - } - } - - /** - * When a failure happens during certificate checking from an - * {@link X509TrustManager}, determine what TLS alert description to use. - * - * @param cexc The exception thrown by the {@link X509TrustManager} - * - * @return A byte value corresponding to a TLS alert description number. - */ - private byte getCertificateAlert(CertificateException cexc) { - // The specific reason for the failure will determine how to - // set the alert description value - byte alertDesc = Alerts.alert_certificate_unknown; - - Throwable baseCause = cexc.getCause(); - if (baseCause instanceof CertPathValidatorException) { - CertPathValidatorException cpve = - (CertPathValidatorException)baseCause; - Reason reason = cpve.getReason(); - if (reason == BasicReason.REVOKED) { - alertDesc = staplingActive ? - Alerts.alert_bad_certificate_status_response : - Alerts.alert_certificate_revoked; - } else if (reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) { - alertDesc = staplingActive ? - Alerts.alert_bad_certificate_status_response : - Alerts.alert_certificate_unknown; - } - } - - return alertDesc; - } -} - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ClientHello.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ClientHello.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1397 @@ +/* + * 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.SecureRandom; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLProtocolException; +import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.SupportedVersionsExtension.CHSupportedVersionsSpec; + +/** + * Pack of the ClientHello handshake message. + */ +final class ClientHello { + static final SSLProducer kickstartProducer = + new ClientHelloKickstartProducer(); + static final SSLConsumer handshakeConsumer = + new ClientHelloConsumer(); + static final HandshakeProducer handshakeProducer = + new ClientHelloProducer(); + + private static final HandshakeConsumer t12HandshakeConsumer = + new T12ClientHelloConsumer(); + private static final HandshakeConsumer t13HandshakeConsumer = + new T13ClientHelloConsumer(); + private static final HandshakeConsumer d12HandshakeConsumer = + new D12ClientHelloConsumer(); + private static final HandshakeConsumer d13HandshakeConsumer = + new D13ClientHelloConsumer(); + + /** + * The ClientHello handshake message. + * + * See RFC 5264/4346/2246/6347 for the specifications. + */ + static final class ClientHelloMessage extends HandshakeMessage { + private final boolean isDTLS; + + final int clientVersion; + final RandomCookie clientRandom; + final SessionId sessionId; + private byte[] cookie; // DTLS only + final int[] cipherSuiteIds; + final List cipherSuites; // known cipher suites only + final byte[] compressionMethod; + final SSLExtensions extensions; + + private static final byte[] NULL_COMPRESSION = new byte[] {0}; + + ClientHelloMessage(HandshakeContext handshakeContext, + int clientVersion, SessionId sessionId, + List cipherSuites, SecureRandom generator) { + super(handshakeContext); + this.isDTLS = handshakeContext.sslContext.isDTLS(); + + this.clientVersion = clientVersion; + this.clientRandom = new RandomCookie(generator); + this.sessionId = sessionId; + if (isDTLS) { + this.cookie = new byte[0]; + } else { + this.cookie = null; + } + + this.cipherSuites = cipherSuites; + this.cipherSuiteIds = getCipherSuiteIds(cipherSuites); + this.extensions = new SSLExtensions(this); + + // Don't support compression. + this.compressionMethod = NULL_COMPRESSION; + } + + /* Read up to the binders in the PSK extension. After this method + * returns, the ByteBuffer position will be at end of the message + * fragment that should be hashed to produce the PSK binder values. + * The client of this method can use this position to determine the + * message fragment and produce the binder values. + */ + static void readPartial(TransportContext tc, + ByteBuffer m) throws IOException { + boolean isDTLS = tc.sslContext.isDTLS(); + + // version + Record.getInt16(m); + + new RandomCookie(m); + + // session ID + Record.getBytes8(m); + + // DTLS cookie + if (isDTLS) { + Record.getBytes8(m); + } + + // cipher suite IDs + Record.getBytes16(m); + // compression method + Record.getBytes8(m); + // read extensions, if present + if (m.remaining() >= 2) { + int remaining = Record.getInt16(m); + while (remaining > 0) { + int id = Record.getInt16(m); + int extLen = Record.getInt16(m); + remaining -= extLen + 4; + + if (id == SSLExtension.CH_PRE_SHARED_KEY.id) { + // ensure pre_shared_key is the last extension + if (remaining > 0) { + tc.fatal(Alert.ILLEGAL_PARAMETER, + "pre_shared_key extension is not last"); + } + // read only up to the IDs + Record.getBytes16(m); + return; + } else { + m.position(m.position() + extLen); + + } + } + } // Otherwise, ignore the remaining bytes. + } + + ClientHelloMessage(HandshakeContext handshakeContext, ByteBuffer m, + SSLExtension[] supportedExtensions) throws IOException { + super(handshakeContext); + this.isDTLS = handshakeContext.sslContext.isDTLS(); + + this.clientVersion = ((m.get() & 0xFF) << 8) | (m.get() & 0xFF); + this.clientRandom = new RandomCookie(m); + this.sessionId = new SessionId(Record.getBytes8(m)); + try { + sessionId.checkLength(clientVersion); + } catch (SSLProtocolException ex) { + handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, ex); + } + if (isDTLS) { + this.cookie = Record.getBytes8(m); + } else { + this.cookie = null; + } + + byte[] encodedIds = Record.getBytes16(m); + if (encodedIds.length == 0 || (encodedIds.length & 0x01) != 0) { + handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Invalid ClientHello message"); + } + + this.cipherSuiteIds = new int[encodedIds.length >> 1]; + for (int i = 0, j = 0; i < encodedIds.length; i++, j++) { + cipherSuiteIds[j] = + ((encodedIds[i++] & 0xFF) << 8) | (encodedIds[i] & 0xFF); + } + this.cipherSuites = getCipherSuites(cipherSuiteIds); + + this.compressionMethod = Record.getBytes8(m); + // In TLS 1.3, use of certain extensions is mandatory. + if (m.hasRemaining()) { + this.extensions = + new SSLExtensions(this, m, supportedExtensions); + } else { + this.extensions = new SSLExtensions(this); + } + } + + void setHelloCookie(byte[] cookie) { + this.cookie = cookie; + } + + // DTLS 1.0/1.2, for cookie generation. + byte[] getHelloCookieBytes() { + HandshakeOutStream hos = new HandshakeOutStream(null); + try { + // copied from send() method + hos.putInt8((byte)((clientVersion >>> 8) & 0xFF)); + hos.putInt8((byte)(clientVersion & 0xFF)); + hos.write(clientRandom.randomBytes, 0, 32); + hos.putBytes8(sessionId.getId()); + // ignore cookie + hos.putBytes16(getEncodedCipherSuites()); + hos.putBytes8(compressionMethod); + extensions.send(hos); // In TLS 1.3, use of certain + // extensions is mandatory. + } catch (IOException ioe) { + // unlikely + } + + return hos.toByteArray(); + } + + // (D)TLS 1.3, for cookie generation. + byte[] getHeaderBytes() { + HandshakeOutStream hos = new HandshakeOutStream(null); + try { + // copied from send() method + hos.putInt8((byte)((clientVersion >>> 8) & 0xFF)); + hos.putInt8((byte)(clientVersion & 0xFF)); + hos.write(clientRandom.randomBytes, 0, 32); + hos.putBytes8(sessionId.getId()); + hos.putBytes16(getEncodedCipherSuites()); + hos.putBytes8(compressionMethod); + } catch (IOException ioe) { + // unlikely + } + + return hos.toByteArray(); + } + + private static int[] getCipherSuiteIds( + List cipherSuites) { + if (cipherSuites != null) { + int[] ids = new int[cipherSuites.size()]; + int i = 0; + for (CipherSuite cipherSuite : cipherSuites) { + ids[i++] = cipherSuite.id; + } + + return ids; + } + + return new int[0]; + } + + private static List getCipherSuites(int[] ids) { + List cipherSuites = new LinkedList<>(); + for (int id : ids) { + CipherSuite cipherSuite = CipherSuite.valueOf(id); + if (cipherSuite != null) { + cipherSuites.add(cipherSuite); + } + } + + return Collections.unmodifiableList(cipherSuites); + } + + private List getCipherSuiteNames() { + List names = new LinkedList<>(); + for (int id : cipherSuiteIds) { + names.add(CipherSuite.nameOf(id) + + "(" + Utilities.byte16HexString(id) + ")"); } + + return names; + } + + private byte[] getEncodedCipherSuites() { + byte[] encoded = new byte[cipherSuiteIds.length << 1]; + int i = 0; + for (int id : cipherSuiteIds) { + encoded[i++] = (byte)(id >> 8); + encoded[i++] = (byte)id; + } + return encoded; + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CLIENT_HELLO; + } + + @Override + public int messageLength() { + /* + * Add fixed size parts of each field... + * version + random + session + cipher + compress + */ + return (2 + 32 + 1 + 2 + 1 + + sessionId.length() /* ... + variable parts */ + + (isDTLS ? (1 + cookie.length) : 0) + + (cipherSuiteIds.length * 2) + + compressionMethod.length) + + extensions.length(); // In TLS 1.3, use of certain + // extensions is mandatory. + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + sendCore(hos); + extensions.send(hos); // In TLS 1.3, use of certain + // extensions is mandatory. + } + + void sendCore(HandshakeOutStream hos) throws IOException { + hos.putInt8((byte) (clientVersion >>> 8)); + hos.putInt8((byte) clientVersion); + hos.write(clientRandom.randomBytes, 0, 32); + hos.putBytes8(sessionId.getId()); + if (isDTLS) { + hos.putBytes8(cookie); + } + hos.putBytes16(getEncodedCipherSuites()); + hos.putBytes8(compressionMethod); + } + + @Override + public String toString() { + if (isDTLS) { + MessageFormat messageFormat = new MessageFormat( + "\"ClientHello\": '{'\n" + + " \"client version\" : \"{0}\",\n" + + " \"random\" : \"{1}\",\n" + + " \"session id\" : \"{2}\",\n" + + " \"cookie\" : \"{3}\",\n" + + " \"cipher suites\" : \"{4}\",\n" + + " \"compression methods\" : \"{5}\",\n" + + " \"extensions\" : [\n" + + "{6}\n" + + " ]\n" + + "'}'", + Locale.ENGLISH); + Object[] messageFields = { + ProtocolVersion.nameOf(clientVersion), + Utilities.toHexString(clientRandom.randomBytes), + sessionId.toString(), + Utilities.toHexString(cookie), + getCipherSuiteNames().toString(), + Utilities.toHexString(compressionMethod), + Utilities.indent(Utilities.indent(extensions.toString())) + }; + + return messageFormat.format(messageFields); + } else { + MessageFormat messageFormat = new MessageFormat( + "\"ClientHello\": '{'\n" + + " \"client version\" : \"{0}\",\n" + + " \"random\" : \"{1}\",\n" + + " \"session id\" : \"{2}\",\n" + + " \"cipher suites\" : \"{3}\",\n" + + " \"compression methods\" : \"{4}\",\n" + + " \"extensions\" : [\n" + + "{5}\n" + + " ]\n" + + "'}'", + Locale.ENGLISH); + Object[] messageFields = { + ProtocolVersion.nameOf(clientVersion), + Utilities.toHexString(clientRandom.randomBytes), + sessionId.toString(), + getCipherSuiteNames().toString(), + Utilities.toHexString(compressionMethod), + Utilities.indent(Utilities.indent(extensions.toString())) + }; + + return messageFormat.format(messageFields); + } + } + } + + /** + * The "ClientHello" handshake message kick start producer. + */ + private static final + class ClientHelloKickstartProducer implements SSLProducer { + // Prevent instantiation of this class. + private ClientHelloKickstartProducer() { + // blank + } + + // Produce kickstart handshake message. + @Override + public byte[] produce(ConnectionContext context) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // clean up this producer + chc.handshakeProducers.remove(SSLHandshake.CLIENT_HELLO.id); + + // the max protocol version this client is supporting. + ProtocolVersion maxProtocolVersion = chc.maximumActiveProtocol; + + // session ID of the ClientHello message + SessionId sessionId = SSLSessionImpl.nullSession.getSessionId(); + + // a list of cipher suites sent by the client + List cipherSuites = chc.activeCipherSuites; + + // + // Try to resume an existing session. + // + SSLSessionContextImpl ssci = (SSLSessionContextImpl) + chc.sslContext.engineGetClientSessionContext(); + SSLSessionImpl session = ssci.get( + chc.conContext.transport.getPeerHost(), + chc.conContext.transport.getPeerPort()); + if (session != null) { + // If unsafe server certificate change is not allowed, reserve + // current server certificates if the previous handshake is a + // session-resumption abbreviated initial handshake. + if (!ClientHandshakeContext.allowUnsafeServerCertChange && + session.isSessionResumption()) { + try { + // If existing, peer certificate chain cannot be null. + chc.reservedServerCerts = + (X509Certificate[])session.getPeerCertificates(); + } catch (SSLPeerUnverifiedException puve) { + // Maybe not certificate-based, ignore the exception. + } + } + + if (!session.isRejoinable()) { + session = null; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, the session is not rejoinable"); + } + } + } + + CipherSuite sessionSuite = null; + if (session != null) { + sessionSuite = session.getSuite(); + if (!chc.isNegotiable(sessionSuite)) { + session = null; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, unavailable session cipher suite"); + } + } + } + + ProtocolVersion sessionVersion = null; + if (session != null) { + sessionVersion = session.getProtocolVersion(); + if (!chc.isNegotiable(sessionVersion)) { + session = null; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, unavailable protocol version"); + } + } + } + + if (session != null && + !sessionVersion.useTLS13PlusSpec() && + SSLConfiguration.useExtendedMasterSecret) { + + boolean isEmsAvailable = chc.sslConfig.isAvailable( + SSLExtension.CH_EXTENDED_MASTER_SECRET, sessionVersion); + if (isEmsAvailable && !session.useExtendedMasterSecret && + !SSLConfiguration.allowLegacyResumption) { + // perform full handshake instead + // + // The client SHOULD NOT offer an abbreviated handshake + // to resume a session that does not use an extended + // master secret. Instead, it SHOULD offer a full + // handshake. + session = null; + } + + if ((session != null) && + !ClientHandshakeContext.allowUnsafeServerCertChange) { + // It is fine to move on with abbreviate handshake if + // endpoint identification is enabled. + String identityAlg = chc.sslConfig.identificationProtocol; + if ((identityAlg == null || identityAlg.length() == 0)) { + if (isEmsAvailable) { + if (!session.useExtendedMasterSecret) { + // perform full handshake instead + session = null; + } // Otherwise, use extended master secret. + } else { + // The extended master secret extension does not + // apply to SSL 3.0. Perform a full handshake + // instead. + // + // Note that the useExtendedMasterSecret is + // extended to protect SSL 3.0 connections, + // by discarding abbreviate handshake. + session = null; + } + } + } + } + + if (session != null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest("Try resuming session", session); + } + + // only set session id if session is 1.2 or earlier + if (!session.getProtocolVersion().useTLS13PlusSpec()) { + sessionId = session.getSessionId(); + } + if (!maxProtocolVersion.equals(sessionVersion)) { + maxProtocolVersion = sessionVersion; + + // Update protocol version number in underlying socket and + // handshake output stream, so that the output records + // (at the record layer) have the correct version + chc.setVersion(sessionVersion); + } + + // If no new session is allowed, force use of the previous + // session ciphersuite, and add the renegotiation SCSV if + // necessary. + if (!chc.sslConfig.enableSessionCreation) { + if (!chc.conContext.isNegotiated && + !sessionVersion.useTLS13PlusSpec() && + cipherSuites.contains( + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { + cipherSuites = Arrays.asList(sessionSuite, + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } else { // otherwise, use renegotiation_info extension + cipherSuites = Arrays.asList(sessionSuite); + } + + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "No new session is allowed, so try to resume " + + "the session cipher suite only", sessionSuite); + } + } + + chc.isResumption = true; + chc.resumingSession = session; + } + + if (session == null) { + if (!chc.sslConfig.enableSessionCreation) { + throw new SSLHandshakeException( + "No new session is allowed and " + + "no existing session can be resumed"); + } + + if (maxProtocolVersion.useTLS13PlusSpec() && + SSLConfiguration.useCompatibilityMode) { + // In compatibility mode, the TLS 1.3 legacy_session_id + // field MUST be non-empty, so a client not offering a + // pre-TLS 1.3 session MUST generate a new 32-byte value. + sessionId = + new SessionId(true, chc.sslContext.getSecureRandom()); + } + } + + ProtocolVersion minimumVersion = ProtocolVersion.NONE; + for (ProtocolVersion pv : chc.activeProtocols) { + if (minimumVersion == ProtocolVersion.NONE || + pv.compare(minimumVersion) < 0) { + minimumVersion = pv; + } + } + + // exclude SCSV for secure renegotiation + if (!minimumVersion.useTLS13PlusSpec()) { + if (chc.conContext.secureRenegotiation && + cipherSuites.contains( + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { + // The cipherSuites may be unmodifiable + cipherSuites = new LinkedList<>(cipherSuites); + cipherSuites.remove( + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + } + + // make sure there is a negotiable cipher suite. + boolean negotiable = false; + for (CipherSuite suite : cipherSuites) { + if (chc.isNegotiable(suite)) { + negotiable = true; + break; + } + } + if (!negotiable) { + throw new SSLHandshakeException("No negotiable cipher suite"); + } + + // Create the handshake message. + ProtocolVersion clientHelloVersion = maxProtocolVersion; + if (clientHelloVersion.useTLS13PlusSpec()) { + // In (D)TLS 1.3, the client indicates its version preferences + // in the "supported_versions" extension and the client_version + // (legacy_version) field MUST be set to (D)TLS 1.2. + if (clientHelloVersion.isDTLS) { + clientHelloVersion = ProtocolVersion.DTLS12; + } else { + clientHelloVersion = ProtocolVersion.TLS12; + } + } + + ClientHelloMessage chm = new ClientHelloMessage(chc, + clientHelloVersion.id, sessionId, cipherSuites, + chc.sslContext.getSecureRandom()); + + // cache the client random number for further using + chc.clientHelloRandom = chm.clientRandom; + chc.clientHelloVersion = clientHelloVersion.id; + + // Produce extensions for ClientHello handshake message. + SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO, chc.activeProtocols); + chm.extensions.produce(chc, extTypes); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced ClientHello handshake message", chm); + } + + // Output the handshake message. + chm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // Reserve the initial ClientHello message for the follow on + // cookie exchange if needed. + chc.initialClientHelloMsg = chm; + + // What's the expected response? + chc.handshakeConsumers.put( + SSLHandshake.SERVER_HELLO.id, SSLHandshake.SERVER_HELLO); + if (chc.sslContext.isDTLS() && + !minimumVersion.useTLS13PlusSpec()) { + chc.handshakeConsumers.put( + SSLHandshake.HELLO_VERIFY_REQUEST.id, + SSLHandshake.HELLO_VERIFY_REQUEST); + } + + // The handshake message has been delivered. + return null; + } + } + + private static final + class ClientHelloProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private ClientHelloProducer() { + // blank + } + + // Response to one of the following handshake message: + // HelloRequest (SSL 3.0/TLS 1.0/1.1/1.2) + // ServerHello(HelloRetryRequest) (TLS 1.3) + // HelloVerifyRequest (DTLS 1.0/1.2) + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + SSLHandshake ht = message.handshakeType(); + if (ht == null) { + throw new UnsupportedOperationException("Not supported yet."); + } + + switch (ht) { + case HELLO_REQUEST: + // SSL 3.0/TLS 1.0/1.1/1.2 + try { + chc.kickstart(); + } catch (IOException ioe) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, ioe); + } + + // The handshake message has been delivered. + return null; + case HELLO_VERIFY_REQUEST: + // DTLS 1.0/1.2 + // + // The HelloVerifyRequest consumer should have updated the + // ClientHello handshake message with cookie. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced ClientHello(cookie) handshake message", + chc.initialClientHelloMsg); + } + + // Output the handshake message. + chc.initialClientHelloMsg.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // What's the expected response? + chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO.id, + SSLHandshake.SERVER_HELLO); + + ProtocolVersion minimumVersion = ProtocolVersion.NONE; + for (ProtocolVersion pv : chc.activeProtocols) { + if (minimumVersion == ProtocolVersion.NONE || + pv.compare(minimumVersion) < 0) { + minimumVersion = pv; + } + } + if (chc.sslContext.isDTLS() && + !minimumVersion.useTLS13PlusSpec()) { + chc.handshakeConsumers.put( + SSLHandshake.HELLO_VERIFY_REQUEST.id, + SSLHandshake.HELLO_VERIFY_REQUEST); + } + + // The handshake message has been delivered. + return null; + case HELLO_RETRY_REQUEST: + // TLS 1.3 + // The HelloRetryRequest consumer should have updated the + // ClientHello handshake message with cookie. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced ClientHello(HRR) handshake message", + chc.initialClientHelloMsg); + } + + // Output the handshake message. + chc.initialClientHelloMsg.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // What's the expected response? + chc.conContext.consumers.putIfAbsent( + ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t13Consumer); + chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO.id, + SSLHandshake.SERVER_HELLO); + + // The handshake message has been delivered. + return null; + default: + throw new UnsupportedOperationException( + "Not supported yet."); + } + } + } + + /** + * The "ClientHello" handshake message consumer. + */ + private static final class ClientHelloConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ClientHelloConsumer() { + // 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_HELLO.id); + if (!shc.handshakeConsumers.isEmpty()) { + shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "No more handshake message allowed " + + "in a ClientHello flight"); + } + + // Get enabled extension types in ClientHello handshake message. + SSLExtension[] enabledExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO); + + ClientHelloMessage chm = + new ClientHelloMessage(shc, message, enabledExtensions); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Consuming ClientHello handshake message", chm); + } + + shc.clientHelloVersion = chm.clientVersion; + onClientHello(shc, chm); + } + + private void onClientHello(ServerHandshakeContext context, + ClientHelloMessage clientHello) throws IOException { + // Negotiate protocol version. + // + // Check and launch SupportedVersions. + SSLExtension[] extTypes = new SSLExtension[] { + SSLExtension.CH_SUPPORTED_VERSIONS + }; + clientHello.extensions.consumeOnLoad(context, extTypes); + + ProtocolVersion negotiatedProtocol; + CHSupportedVersionsSpec svs = + (CHSupportedVersionsSpec)context.handshakeExtensions.get( + SSLExtension.CH_SUPPORTED_VERSIONS); + if (svs != null) { + negotiatedProtocol = + negotiateProtocol(context, svs.requestedProtocols); + } else { + negotiatedProtocol = + negotiateProtocol(context, clientHello.clientVersion); + } + context.negotiatedProtocol = negotiatedProtocol; + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Negotiated protocol version: " + negotiatedProtocol.name); + } + + // Consume the handshake message for the specific protocol version. + if (negotiatedProtocol.isDTLS) { + if (negotiatedProtocol.useTLS13PlusSpec()) { + d13HandshakeConsumer.consume(context, clientHello); + } else { + d12HandshakeConsumer.consume(context, clientHello); + } + } else { + if (negotiatedProtocol.useTLS13PlusSpec()) { + t13HandshakeConsumer.consume(context, clientHello); + } else { + t12HandshakeConsumer.consume(context, clientHello); + } + } + } + + // Select a protocol version according to the + // ClientHello.client_version. + private ProtocolVersion negotiateProtocol( + ServerHandshakeContext context, + int clientHelloVersion) throws SSLException { + + // Per TLS 1.3 specification, server MUST negotiate TLS 1.2 or prior + // even if ClientHello.client_version is 0x0304 or later. + int chv = clientHelloVersion; + if (context.sslContext.isDTLS()) { + if (chv < ProtocolVersion.DTLS12.id) { + chv = ProtocolVersion.DTLS12.id; + } + } else { + if (chv > ProtocolVersion.TLS12.id) { + chv = ProtocolVersion.TLS12.id; + } + } + + // Select a protocol version from the activated protocols. + ProtocolVersion pv = ProtocolVersion.selectedFrom( + context.activeProtocols, chv); + if (pv == null || pv == ProtocolVersion.NONE || + pv == ProtocolVersion.SSL20Hello) { + context.conContext.fatal(Alert.PROTOCOL_VERSION, + "Client requested protocol " + + ProtocolVersion.nameOf(clientHelloVersion) + + " is not enabled or supported in server context"); + } + + return pv; + } + + // Select a protocol version according to the + // supported_versions extension. + private ProtocolVersion negotiateProtocol( + ServerHandshakeContext context, + int[] clientSupportedVersions) throws SSLException { + + // The client supported protocol versions are present in client + // preference order. This implementation chooses to use the server + // preference of protocol versions instead. + for (ProtocolVersion spv : context.activeProtocols) { + if (spv == ProtocolVersion.SSL20Hello) { + continue; + } + for (int cpv : clientSupportedVersions) { + if (cpv == ProtocolVersion.SSL20Hello.id) { + continue; + } + if (spv.id == cpv) { + return spv; + } + } + } + + // No protocol version can be negotiated. + context.conContext.fatal(Alert.PROTOCOL_VERSION, + "The client supported protocol versions " + Arrays.toString( + ProtocolVersion.toStringArray(clientSupportedVersions)) + + " are not accepted by server preferences " + + context.activeProtocols); + + return null; // make the compiler happy + } + } + + /** + * The "ClientHello" handshake message consumer for TLS 1.2 and + * prior SSL/TLS protocol versions. + */ + private static final + class T12ClientHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private T12ClientHelloConsumer() { + // 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; + + // + // validate + // + + // Reject client initiated renegotiation? + // + // If server side should reject client-initiated renegotiation, + // send an Alert.HANDSHAKE_FAILURE fatal alert, not a + // no_renegotiation warning alert (no_renegotiation must be a + // warning: RFC 2246). no_renegotiation might seem more + // natural at first, but warnings are not appropriate because + // the sending party does not know how the receiving party + // will behave. This state must be treated as a fatal server + // condition. + // + // This will not have any impact on server initiated renegotiation. + if (shc.conContext.isNegotiated) { + if (!shc.conContext.secureRenegotiation && + !HandshakeContext.allowUnsafeRenegotiation) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Unsafe renegotiation is not allowed"); + } + + if (ServerHandshakeContext.rejectClientInitiatedRenego && + !shc.kickstartMessageDelivered) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Client initiated renegotiation is not allowed"); + } + } + + // Is it an abbreviated handshake? + if (clientHello.sessionId.length() != 0) { + SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext + .engineGetServerSessionContext()) + .get(clientHello.sessionId.getId()); + + boolean resumingSession = + (previous != null) && previous.isRejoinable(); + if (!resumingSession) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "the existing session is not rejoinable"); + } + } + // Validate the negotiated protocol version. + if (resumingSession) { + ProtocolVersion sessionProtocol = + previous.getProtocolVersion(); + if (sessionProtocol != shc.negotiatedProtocol) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, not the same protocol version"); + } + } + } + + // Validate the required client authentication. + if (resumingSession && + (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) { + try { + previous.getPeerPrincipal(); + } catch (SSLPeerUnverifiedException e) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "client authentication is required"); + } + } + } + + // Validate that the cached cipher suite. + if (resumingSession) { + CipherSuite suite = previous.getSuite(); + if ((!shc.isNegotiable(suite)) || + (!clientHello.cipherSuites.contains(suite))) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "the session cipher suite is absent"); + } + } + } + + // So far so good. Note that the handshake extensions may reset + // the resuming options later. + shc.isResumption = resumingSession; + shc.resumingSession = resumingSession ? previous : null; + } + + // cache the client random number for further using + shc.clientHelloRandom = clientHello.clientRandom; + + // Check and launch ClientHello extensions. + SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO); + clientHello.extensions.consumeOnLoad(shc, extTypes); + + // + // update + // + if (!shc.conContext.isNegotiated) { + shc.conContext.protocolVersion = shc.negotiatedProtocol; + shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol); + } + + // update the responders + // + // Only need to ServerHello, which may add more responders later. + // Note that ServerHello and HelloRetryRequest share the same + // handshake type/id. The ServerHello producer may be replaced + // by HelloRetryRequest producer if needed. + shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id, + SSLHandshake.SERVER_HELLO); + + // + // produce + // + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + SSLHandshake.SERVER_HELLO, + + // full handshake messages + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_STATUS, + SSLHandshake.SERVER_KEY_EXCHANGE, + SSLHandshake.CERTIFICATE_REQUEST, + SSLHandshake.SERVER_HELLO_DONE, + + // abbreviated handshake messages + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + shc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(context, clientHello); + } + } + } + } + + /** + * The "ClientHello" handshake message consumer for TLS 1.3. + */ + private static final + class T13ClientHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private T13ClientHelloConsumer() { + // 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; + + // The client may send a dummy change_cipher_spec record + // immediately after the first ClientHello. + shc.conContext.consumers.putIfAbsent( + ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t13Consumer); + + // Is it a resumption? + // + // Check and launch the "psk_key_exchange_modes" and + // "pre_shared_key" extensions first, which will reset the + // resuming session, no matter the extensions present or not. + shc.isResumption = true; + SSLExtension[] extTypes = new SSLExtension[] { + SSLExtension.PSK_KEY_EXCHANGE_MODES, + SSLExtension.CH_PRE_SHARED_KEY + }; + clientHello.extensions.consumeOnLoad(shc, extTypes); + + // Check and launch ClientHello extensions other than + // "psk_key_exchange_modes", "pre_shared_key", "protocol_version" + // and "key_share" extensions. + // + // These extensions may discard session resumption, or ask for + // hello retry. + extTypes = shc.sslConfig.getExclusiveExtensions( + SSLHandshake.CLIENT_HELLO, + Arrays.asList( + SSLExtension.PSK_KEY_EXCHANGE_MODES, + SSLExtension.CH_PRE_SHARED_KEY, + SSLExtension.CH_SUPPORTED_VERSIONS)); + clientHello.extensions.consumeOnLoad(shc, extTypes); + + if (!shc.handshakeProducers.isEmpty()) { + // Should be HelloRetryRequest producer. + goHelloRetryRequest(shc, clientHello); + } else { + goServerHello(shc, clientHello); + } + } + + private void goHelloRetryRequest(ServerHandshakeContext shc, + ClientHelloMessage clientHello) throws IOException { + HandshakeProducer handshakeProducer = + shc.handshakeProducers.remove( + SSLHandshake.HELLO_RETRY_REQUEST.id); + if (handshakeProducer != null) { + handshakeProducer.produce(shc, clientHello); + } else { + // unlikely + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No HelloRetryRequest producer: " + shc.handshakeProducers); + } + + if (!shc.handshakeProducers.isEmpty()) { + // unlikely, but please double check. + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "unknown handshake producers: " + shc.handshakeProducers); + } + } + + private void goServerHello(ServerHandshakeContext shc, + ClientHelloMessage clientHello) throws IOException { + // + // validate + // + shc.clientHelloRandom = clientHello.clientRandom; + + // + // update + // + if (!shc.conContext.isNegotiated) { + shc.conContext.protocolVersion = shc.negotiatedProtocol; + shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol); + } + + // update the responders + // + // Only ServerHello/HelloRetryRequest producer, which adds + // more responders later. + shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id, + SSLHandshake.SERVER_HELLO); + + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + SSLHandshake.SERVER_HELLO, + + // full handshake messages + SSLHandshake.ENCRYPTED_EXTENSIONS, + SSLHandshake.CERTIFICATE_REQUEST, + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_VERIFY, + SSLHandshake.FINISHED + }; + + // + // produce + // + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + shc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(shc, clientHello); + } + } + } + } + + /** + * The "ClientHello" handshake message consumer for DTLS 1.2 and + * previous DTLS protocol versions. + */ + private static final + class D12ClientHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private D12ClientHelloConsumer() { + // 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; + + // + // validate + // + + // Reject client initiated renegotiation? + // + // If server side should reject client-initiated renegotiation, + // send an Alert.HANDSHAKE_FAILURE fatal alert, not a + // no_renegotiation warning alert (no_renegotiation must be a + // warning: RFC 2246). no_renegotiation might seem more + // natural at first, but warnings are not appropriate because + // the sending party does not know how the receiving party + // will behave. This state must be treated as a fatal server + // condition. + // + // This will not have any impact on server initiated renegotiation. + if (shc.conContext.isNegotiated) { + if (!shc.conContext.secureRenegotiation && + !HandshakeContext.allowUnsafeRenegotiation) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Unsafe renegotiation is not allowed"); + } + + if (ServerHandshakeContext.rejectClientInitiatedRenego && + !shc.kickstartMessageDelivered) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Client initiated renegotiation is not allowed"); + } + } + + // Is it an abbreviated handshake? + if (clientHello.sessionId.length() != 0) { + SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext + .engineGetServerSessionContext()) + .get(clientHello.sessionId.getId()); + + boolean resumingSession = + (previous != null) && previous.isRejoinable(); + if (!resumingSession) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "the existing session is not rejoinable"); + } + } + // Validate the negotiated protocol version. + if (resumingSession) { + ProtocolVersion sessionProtocol = + previous.getProtocolVersion(); + if (sessionProtocol != shc.negotiatedProtocol) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, not the same protocol version"); + } + } + } + + // Validate the required client authentication. + if (resumingSession && + (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) { + + try { + previous.getPeerPrincipal(); + } catch (SSLPeerUnverifiedException e) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "client authentication is required"); + } + } + } + + // Validate that the cached cipher suite. + if (resumingSession) { + CipherSuite suite = previous.getSuite(); + if ((!shc.isNegotiable(suite)) || + (!clientHello.cipherSuites.contains(suite))) { + resumingSession = false; + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Can't resume, " + + "the session cipher suite is absent"); + } + } + } + + // So far so good. Note that the handshake extensions may reset + // the resuming options later. + shc.isResumption = resumingSession; + shc.resumingSession = resumingSession ? previous : null; + } + + HelloCookieManager hcm = + shc.sslContext.getHelloCookieManager(ProtocolVersion.DTLS10); + if (!shc.isResumption && + !hcm.isCookieValid(shc, clientHello, clientHello.cookie)) { + // + // Perform cookie exchange for DTLS handshaking if no cookie + // or the cookie is invalid in the ClientHello message. + // + // update the responders + shc.handshakeProducers.put( + SSLHandshake.HELLO_VERIFY_REQUEST.id, + SSLHandshake.HELLO_VERIFY_REQUEST); + + // + // produce response handshake message + // + SSLHandshake.HELLO_VERIFY_REQUEST.produce(context, clientHello); + + return; + } + + // cache the client random number for further using + shc.clientHelloRandom = clientHello.clientRandom; + + // Check and launch ClientHello extensions. + SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO); + clientHello.extensions.consumeOnLoad(shc, extTypes); + + // + // update + // + if (!shc.conContext.isNegotiated) { + shc.conContext.protocolVersion = shc.negotiatedProtocol; + shc.conContext.outputRecord.setVersion(shc.negotiatedProtocol); + } + + // update the responders + // + // Only need to ServerHello, which may add more responders later. + shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id, + SSLHandshake.SERVER_HELLO); + + // + // produce + // + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + SSLHandshake.SERVER_HELLO, + + // full handshake messages + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_STATUS, + SSLHandshake.SERVER_KEY_EXCHANGE, + SSLHandshake.CERTIFICATE_REQUEST, + SSLHandshake.SERVER_HELLO_DONE, + + // abbreviated handshake messages + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + shc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(context, clientHello); + } + } + } + } + + /** + * The "ClientHello" handshake message consumer for DTLS 1.3. + */ + private static final + class D13ClientHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private D13ClientHelloConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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(); + + + /** + * The "ClientKeyExchange" handshake message producer. + */ + private static final + class ClientKeyExchangeProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private ClientKeyExchangeProducer() { + // blank + } - public ClientKeyExchange() { + @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 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; - } - - @Override - public abstract int messageLength(); + /** + * The "ClientKeyExchange" handshake message consumer. + */ + private static final + class ClientKeyExchangeConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ClientKeyExchangeConsumer() { + // blank + } - @Override - public abstract void send(HandshakeOutStream s) throws IOException; - - @Override - public abstract void print(PrintStream s) throws IOException; + @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 hc : + ke.getHandshakeConsumers(shc)) { + if (hc.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) { + hc.getValue().consume(context, message); + return; + } + } + } - public abstract SecretKey clientKeyExchange(); + // not consumer defined. + shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected ClientKeyExchange handshake message."); + } + } +} - public abstract Principal getPeerPrincipal(); - - public abstract Principal getLocalPrincipal(); -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java --- a/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 - providers = new HashMap<>(); - - static { - String path = GetPropertyAction.privilegedGetProperty("java.home"); - ServiceLoader sc = - AccessController.doPrivileged( - (PrivilegedAction>) - () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class), - null, - new FilePermission(new File(path, "-").toString(), "read")); - Iterator 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; -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ConnectionContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ConnectionContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +/** + * SSL/(D)TLS connection context. + */ +interface ConnectionContext { + // blank +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ContentType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ContentType.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 ""; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/CookieExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/CookieExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; - } + /** + * The DiffieHellman ClientKeyExchange handshake message. + * + * If the client has sent a certificate which contains a suitable + * DiffieHellman key (for fixed_dh client authentication), then the + * client public value is implicit and does not need to be sent again. + * In this case, the client key exchange message will be sent, but it + * MUST be empty. + * + * Currently, we don't support cipher suite that requires implicit public + * key of client. + */ + private static final + class DHClientKeyExchangeMessage extends HandshakeMessage { + private byte[] y; // 1 to 2^16 - 1 bytes - /* - * This value may be empty if it was included in the - * client's certificate ... - */ - private byte[] dh_Yc; // 1 to 2^16 -1 bytes + DHClientKeyExchangeMessage( + HandshakeContext handshakeContext) throws IOException { + super(handshakeContext); + // This happens in client side only. + ClientHandshakeContext chc = + (ClientHandshakeContext)handshakeContext; - BigInteger getClientPublicKey() { - return dh_Yc == null ? null : new BigInteger(1, dh_Yc); - } + DHEPossession dhePossession = null; + for (SSLPossession possession : chc.handshakePossessions) { + if (possession instanceof DHEPossession) { + dhePossession = (DHEPossession)possession; + break; + } + } - /* - * 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); - } + if (dhePossession == null) { + // unlikely + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No DHE credentials negotiated for client key exchange"); + } + + DHPublicKey publicKey = dhePossession.publicKey; + DHParameterSpec params = publicKey.getParams(); + this.y = Utilities.toByteArray(publicKey.getY()); + } - DHClientKeyExchange() { - dh_Yc = null; - } + 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"); + } + } - /* - * 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"); + @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 + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + DHEPossession dhePossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof DHEPossession) { + dhePossession = (DHEPossession)possession; + break; + } + } + + if (dhePossession == null) { + // unlikely + 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"); + } - @Override - void print(PrintStream s) throws IOException { - s.println("*** ClientKeyExchange, DH"); + 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"); + } - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "DH Public key", dh_Yc); + 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); + } } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DHCrypt.java --- a/src/java.base/share/classes/sun/security/ssl/DHCrypt.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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. - * - *

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; - } -} - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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. + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; - - int readEpoch; + private 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 @@ @Override public synchronized void close() throws IOException { if (!isClosed) { - prevReadCipher.dispose(); super.close(); } } @@ -87,14 +71,8 @@ @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; } @@ -108,6 +86,11 @@ } @Override + void finishHandshake() { + reassembler = null; + } + + @Override Plaintext acquirePlaintext() { if (reassembler != null) { return reassembler.acquirePlaintext(); @@ -116,16 +99,28 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ ((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 @@ @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 @@ } 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 @@ } 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 @@ 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 @@ // 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 @@ ((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 @@ } // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ // 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 @@ } 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 @@ resetHandshakeFlight(precedingFlight); if (expectCCSFlight && - (precedingFlight.flightEpoch == + (precedingFlight.handshakeType == HandshakeFlight.HF_UNKNOWN)) { expectCCSFlight = false; } @@ -1088,13 +1069,13 @@ } 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ // 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 @@ 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 @@ // 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 @@ // 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 @@ // // 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 @@ // 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 @@ 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 @@ // // 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 @@ // 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 @@ // a full handshake // List 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 @@ 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 @@ // 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 @@ } // 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 @@ 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 @@ 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 @@ // // 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ 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 @@ int prevWriteEpoch; Authenticator prevWriteAuthenticator; - CipherBox prevWriteCipher; + SSLWriteCipher prevWriteCipher; - private LinkedList alertMemos = new LinkedList<>(); + private final LinkedList 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 initHandshaker() { + // clean up + fragmenter = null; } @Override - void changeWriteCiphers(Authenticator writeAuthenticator, - CipherBox writeCipher) throws IOException { + void finishHandshake() { + // Nothing to do here currently. + } - encodeChangeCipherSpec(); + @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 @@ @Override void encodeHandshake(byte[] source, int offset, int length) throws IOException { - if (firstMessage) { firstMessage = false; } @@ -127,30 +129,53 @@ } @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 @@ 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 @@ 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) { @@ -274,12 +293,6 @@ } @Override - void initHandshaker() { - // clean up - fragmenter = null; - } - - @Override void launchRetransmission() { // Note: Please don't retransmit if there are handshake messages // or alerts waiting in the queue. @@ -295,8 +308,7 @@ byte majorVersion; byte minorVersion; int encodeEpoch; - CipherBox encodeCipher; - Authenticator encodeAuthenticator; + SSLWriteCipher encodeCipher; byte[] fragment; } @@ -308,7 +320,8 @@ } private final class DTLSFragmenter { - private LinkedList handshakeMemos = new LinkedList<>(); + private final LinkedList handshakeMemos = + new LinkedList<>(); private int acquireIndex = 0; private int messageSequence = 0; private boolean flightIsReady = false; @@ -336,12 +349,11 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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() { diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/DTLSRecord.java --- a/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -61,30 +61,6 @@ + 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 * suitable certificate is available. That is, the certificate_list diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Debug.java --- a/src/java.base/share/classes/sun/security/ssl/Debug.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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(); - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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(); + + 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); - @Override - int messageType() { - return ht_client_key_exchange; + // 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 = { + " " + }; + 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 + } + + @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"); + } - byte[] getEncodedPoint() { - return encodedPoint; + 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"); + } - ECDHClientKeyExchange(HandshakeInStream input) throws IOException { - encodedPoint = input.getBytes8(); + 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); + } + } } - @Override - int messageLength() { - return encodedPoint.length + 1; + /** + * 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 - void send(HandshakeOutStream s) throws IOException { - s.putBytes8(encodedPoint); - } + /** + * The ECDHE "ClientKeyExchange" handshake message consumer. + */ + private static final + class ECDHEClientKeyExchangeConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ECDHEClientKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + ECDHEPossession ecdhePossession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof ECDHEPossession) { + ecdhePossession = (ECDHEPossession)possession; + break; + } + } + if (ecdhePossession == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "No expected ECDHE possessions for client key exchange"); + return; // make the compiler happy + } + + 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 + } - @Override - void print(PrintStream s) throws IOException { - s.println("*** ECDHClientKeyExchange"); + // 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); - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "ECDH Public value", encodedPoint); + // 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); + } } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHCrypt.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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); - } - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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. + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 = { + "" + }; + 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. + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/EllipticPointFormatsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/EllipticPointFormatsExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 list = new ArrayList(); - for (byte format : formats) { - list.add(toString(format)); - } - return "Extension " + type + ", formats: " + list; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/EncryptedExtensions.java Mon Jun 25 13:41:39 2018 -0700 @@ -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. + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java --- a/src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/EphemeralKeyManager.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java --- a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 - * RFC 7627. - * - * @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(); + + static final HandshakeProducer shNetworkProducer = + new SHExtendedMasterSecretProducer(); + static final ExtensionConsumer shOnLoadConsumer = + new SHExtendedMasterSecretConsumer(); + static final HandshakeAbsence shOnLoadAbsence = + new SHExtendedMasterSecretAbsence(); + + static final SSLStringizer emsStringizer = + new ExtendedMasterSecretStringizer(); + + /** + * The "extended_master_secret" extension. + */ + static final class ExtendedMasterSecretSpec implements SSLExtensionSpec { + // A nominal object that does not holding any real renegotiation info. + static final ExtendedMasterSecretSpec NOMINAL = + new ExtendedMasterSecretSpec(); + + private ExtendedMasterSecretSpec() { + // blank + } + + private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException { + // Parse the extension. + if (m.hasRemaining()) { + throw new SSLProtocolException( + "Invalid extended_master_secret extension data: " + + "not empty"); + } + } + + @Override + public String toString() { + return ""; + } + } + + 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(); + } + } } - ExtendedMasterSecretExtension(HandshakeInStream s, - int len) throws IOException { - super(ExtensionType.EXT_EXTENDED_MASTER_SECRET); + /** + * 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; + } + } - if (len != 0) { - throw new SSLProtocolException("Invalid " + type + " extension"); + /** + * 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. } } - @Override - int length() { - return 4; // 4: extension type and length fields + /** + * 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"); + } + } + } + } + } } - @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 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; + } } - @Override - public String toString() { - return "Extension " + type; + /** + * 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"); + } + } + } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ExtensionType.java --- a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 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 -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Finished.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/Finished.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1077 @@ +/* + * 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.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.spec.AlgorithmParameterSpec; +import java.text.MessageFormat; +import java.util.Locale; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import sun.security.internal.spec.TlsPrfParameterSpec; +import sun.security.ssl.CipherSuite.HashAlg; +import static sun.security.ssl.CipherSuite.HashAlg.H_NONE; +import sun.security.ssl.SSLBasicKeyDerivation.SecretSizeSpec; +import sun.security.ssl.SSLCipher.SSLReadCipher; +import sun.security.ssl.SSLCipher.SSLWriteCipher; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.HexDumpEncoder; + +/** + * Pack of the Finished handshake message. + */ +final class Finished { + static final SSLConsumer t12HandshakeConsumer = + new T12FinishedConsumer(); + static final HandshakeProducer t12HandshakeProducer = + new T12FinishedProducer(); + + static final SSLConsumer t13HandshakeConsumer = + new T13FinishedConsumer(); + static final HandshakeProducer t13HandshakeProducer = + new T13FinishedProducer(); + + /** + * The Finished handshake message. + */ + private static final class FinishedMessage extends HandshakeMessage { + private final byte[] verifyData; + + FinishedMessage(HandshakeContext context) throws IOException { + super(context); + + VerifyDataScheme vds = + VerifyDataScheme.valueOf(context.negotiatedProtocol); + + byte[] vd = null; + try { + vd = vds.createVerifyData(context, false); + } catch (IOException ioe) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Failed to generate verify_data", ioe); + } + + this.verifyData = vd; + } + + FinishedMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + int verifyDataLen = 12; + if (context.negotiatedProtocol == ProtocolVersion.SSL30) { + verifyDataLen = 36; + } else if (context.negotiatedProtocol.useTLS13PlusSpec()) { + verifyDataLen = + context.negotiatedCipherSuite.hashAlg.hashLength; + } + + if (m.remaining() != verifyDataLen) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Inappropriate finished message: need " + verifyDataLen + + " but remaining " + m.remaining() + " bytes verify_data"); + } + + this.verifyData = new byte[verifyDataLen]; + m.get(verifyData); + + VerifyDataScheme vd = + VerifyDataScheme.valueOf(context.negotiatedProtocol); + byte[] myVerifyData; + try { + myVerifyData = vd.createVerifyData(context, true); + } catch (IOException ioe) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Failed to generate verify_data", ioe); + return; // make the compiler happy + } + if (!MessageDigest.isEqual(myVerifyData, verifyData)) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "The Finished message cannot be verified."); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.FINISHED; + } + + @Override + public int messageLength() { + return verifyData.length; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.write(verifyData); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"Finished\": '{'\n" + + " \"verify data\": '{'\n" + + "{0}\n" + + " '}'" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.indent(hexEncoder.encode(verifyData), " "), + }; + return messageFormat.format(messageFields); + } + } + + interface VerifyDataGenerator { + byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException; + } + + enum VerifyDataScheme { + SSL30 ("kdf_ssl30", new S30VerifyDataGenerator()), + TLS10 ("kdf_tls10", new T10VerifyDataGenerator()), + TLS12 ("kdf_tls12", new T12VerifyDataGenerator()), + TLS13 ("kdf_tls13", new T13VerifyDataGenerator()); + + final String name; + final VerifyDataGenerator generator; + + VerifyDataScheme(String name, VerifyDataGenerator verifyDataGenerator) { + this.name = name; + this.generator = verifyDataGenerator; + } + + static VerifyDataScheme valueOf(ProtocolVersion protocolVersion) { + switch (protocolVersion) { + case SSL30: + return VerifyDataScheme.SSL30; + case TLS10: + case TLS11: + case DTLS10: + return VerifyDataScheme.TLS10; + case TLS12: + case DTLS12: + return VerifyDataScheme.TLS12; + case TLS13: + return VerifyDataScheme.TLS13; + default: + return null; + } + } + + public byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException { + if (generator != null) { + return generator.createVerifyData(context, isValidation); + } + + throw new UnsupportedOperationException("Not supported yet."); + } + } + + // SSL 3.0 + private static final + class S30VerifyDataGenerator implements VerifyDataGenerator { + @Override + public byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException { + HandshakeHash handshakeHash = context.handshakeHash; + SecretKey masterSecretKey = + context.handshakeSession.getMasterSecret(); + + boolean useClientLabel = + (context.sslConfig.isClientMode && !isValidation) || + (!context.sslConfig.isClientMode && isValidation); + return handshakeHash.digest(useClientLabel, masterSecretKey); + } + } + + // TLS 1.0, TLS 1.1, DTLS 1.0 + private static final + class T10VerifyDataGenerator implements VerifyDataGenerator { + @Override + public byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException { + HandshakeHash handshakeHash = context.handshakeHash; + SecretKey masterSecretKey = + context.handshakeSession.getMasterSecret(); + + boolean useClientLabel = + (context.sslConfig.isClientMode && !isValidation) || + (!context.sslConfig.isClientMode && isValidation); + String tlsLabel; + if (useClientLabel) { + tlsLabel = "client finished"; + } else { + tlsLabel = "server finished"; + } + + try { + byte[] seed = handshakeHash.digest(); + String prfAlg = "SunTlsPrf"; + HashAlg hashAlg = H_NONE; + + /* + * RFC 5246/7.4.9 says that finished messages can + * be ciphersuite-specific in both length/PRF hash + * algorithm. If we ever run across a different + * length, this call will need to be updated. + */ + @SuppressWarnings("deprecation") + TlsPrfParameterSpec spec = new TlsPrfParameterSpec( + masterSecretKey, tlsLabel, seed, 12, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); + kg.init(spec); + SecretKey prfKey = kg.generateKey(); + if (!"RAW".equals(prfKey.getFormat())) { + throw new ProviderException( + "Invalid PRF output, format must be RAW. " + + "Format received: " + prfKey.getFormat()); + } + byte[] finished = prfKey.getEncoded(); + return finished; + } catch (GeneralSecurityException e) { + throw new RuntimeException("PRF failed", e); + } + } + } + + // TLS 1.2 + private static final + class T12VerifyDataGenerator implements VerifyDataGenerator { + @Override + public byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException { + CipherSuite cipherSuite = context.negotiatedCipherSuite; + HandshakeHash handshakeHash = context.handshakeHash; + SecretKey masterSecretKey = + context.handshakeSession.getMasterSecret(); + + boolean useClientLabel = + (context.sslConfig.isClientMode && !isValidation) || + (!context.sslConfig.isClientMode && isValidation); + String tlsLabel; + if (useClientLabel) { + tlsLabel = "client finished"; + } else { + tlsLabel = "server finished"; + } + + try { + byte[] seed = handshakeHash.digest(); + String prfAlg = "SunTls12Prf"; + HashAlg hashAlg = cipherSuite.hashAlg; + + /* + * RFC 5246/7.4.9 says that finished messages can + * be ciphersuite-specific in both length/PRF hash + * algorithm. If we ever run across a different + * length, this call will need to be updated. + */ + @SuppressWarnings("deprecation") + TlsPrfParameterSpec spec = new TlsPrfParameterSpec( + masterSecretKey, tlsLabel, seed, 12, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); + kg.init(spec); + SecretKey prfKey = kg.generateKey(); + if (!"RAW".equals(prfKey.getFormat())) { + throw new ProviderException( + "Invalid PRF output, format must be RAW. " + + "Format received: " + prfKey.getFormat()); + } + byte[] finished = prfKey.getEncoded(); + return finished; + } catch (GeneralSecurityException e) { + throw new RuntimeException("PRF failed", e); + } + } + } + + // TLS 1.2 + private static final + class T13VerifyDataGenerator implements VerifyDataGenerator { + private static final byte[] hkdfLabel = "tls13 finished".getBytes(); + private static final byte[] hkdfContext = new byte[0]; + + @Override + public byte[] createVerifyData(HandshakeContext context, + boolean isValidation) throws IOException { + // create finished secret key + HashAlg hashAlg = + context.negotiatedCipherSuite.hashAlg; + SecretKey secret = isValidation ? + context.baseReadSecret : context.baseWriteSecret; + SSLBasicKeyDerivation kdf = new SSLBasicKeyDerivation( + secret, hashAlg.name, + hkdfLabel, hkdfContext, hashAlg.hashLength); + AlgorithmParameterSpec keySpec = + new SecretSizeSpec(hashAlg.hashLength); + SecretKey finishedSecret = + kdf.deriveKey("TlsFinishedSecret", keySpec); + + String hmacAlg = + "Hmac" + hashAlg.name.replace("-", ""); + try { + Mac hmac = JsseJce.getMac(hmacAlg); + hmac.init(finishedSecret); + return hmac.doFinal(context.handshakeHash.digest()); + } catch (NoSuchAlgorithmException |InvalidKeyException ex) { + throw new ProviderException( + "Failed to generate verify_data", ex); + } + } + } + + /** + * The "Finished" handshake message producer. + */ + private static final + class T12FinishedProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T12FinishedProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + return onProduceFinished( + (ClientHandshakeContext)context, message); + } else { + return onProduceFinished( + (ServerHandshakeContext)context, message); + } + } + + private byte[] onProduceFinished(ClientHandshakeContext chc, + HandshakeMessage message) throws IOException { + // Refresh handshake hash + chc.handshakeHash.update(); + + FinishedMessage fm = new FinishedMessage(chc); + + // Change write cipher and delivery ChangeCipherSpec message. + ChangeCipherSpec.t10Producer.produce(chc, message); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced client Finished handshake message", fm); + } + + // Output the handshake message. + fm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + /* + * save server verify data for secure renegotiation + */ + if (chc.conContext.secureRenegotiation) { + chc.conContext.clientVerifyData = fm.verifyData; + } + + // update the consumers and producers + if (!chc.isResumption) { + chc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t10Consumer); + chc.handshakeConsumers.put( + SSLHandshake.FINISHED.id, SSLHandshake.FINISHED); + chc.conContext.inputRecord.expectingFinishFlight(); + } else { + if (chc.handshakeSession.isRejoinable()) { + ((SSLSessionContextImpl)chc.sslContext. + engineGetClientSessionContext()).put( + chc.handshakeSession); + } + chc.conContext.conSession = chc.handshakeSession.finish(); + chc.conContext.protocolVersion = chc.negotiatedProtocol; + + // handshake context cleanup. + chc.handshakeFinished = true; + + // May need to retransmit the last flight for DTLS. + if (!chc.sslContext.isDTLS()) { + chc.conContext.finishHandshake(); + } + } + + // The handshake message has been delivered. + return null; + } + + private byte[] onProduceFinished(ServerHandshakeContext shc, + HandshakeMessage message) throws IOException { + // Refresh handshake hash + shc.handshakeHash.update(); + + FinishedMessage fm = new FinishedMessage(shc); + + // Change write cipher and delivery ChangeCipherSpec message. + ChangeCipherSpec.t10Producer.produce(shc, message); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced server Finished handshake message", fm); + } + + // Output the handshake message. + fm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + /* + * save client verify data for secure renegotiation + */ + if (shc.conContext.secureRenegotiation) { + shc.conContext.serverVerifyData = fm.verifyData; + } + + // update the consumers and producers + if (shc.isResumption) { + shc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t10Consumer); + shc.handshakeConsumers.put( + SSLHandshake.FINISHED.id, SSLHandshake.FINISHED); + shc.conContext.inputRecord.expectingFinishFlight(); + } else { + if (shc.handshakeSession.isRejoinable()) { + ((SSLSessionContextImpl)shc.sslContext. + engineGetServerSessionContext()).put( + shc.handshakeSession); + } + shc.conContext.conSession = shc.handshakeSession.finish(); + shc.conContext.protocolVersion = shc.negotiatedProtocol; + + // handshake context cleanup. + shc.handshakeFinished = true; + + // May need to retransmit the last flight for DTLS. + if (!shc.sslContext.isDTLS()) { + shc.conContext.finishHandshake(); + } + } + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "Finished" handshake message consumer. + */ + private static final class T12FinishedConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T12FinishedConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // This consumer can be used only once. + hc.handshakeConsumers.remove(SSLHandshake.FINISHED.id); + + // We should not be processing finished messages unless + // we have received ChangeCipherSpec + if (hc.conContext.consumers.containsKey( + ContentType.CHANGE_CIPHER_SPEC.id)) { + hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Missing ChangeCipherSpec message"); + } + + if (hc.sslConfig.isClientMode) { + onConsumeFinished((ClientHandshakeContext)context, message); + } else { + onConsumeFinished((ServerHandshakeContext)context, message); + } + } + + private void onConsumeFinished(ClientHandshakeContext chc, + ByteBuffer message) throws IOException { + FinishedMessage fm = new FinishedMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming server Finished handshake message", fm); + } + + if (chc.conContext.secureRenegotiation) { + chc.conContext.serverVerifyData = fm.verifyData; + } + + if (!chc.isResumption) { + if (chc.handshakeSession.isRejoinable()) { + ((SSLSessionContextImpl)chc.sslContext. + engineGetClientSessionContext()).put( + chc.handshakeSession); + } + chc.conContext.conSession = chc.handshakeSession.finish(); + chc.conContext.protocolVersion = chc.negotiatedProtocol; + + // handshake context cleanup. + chc.handshakeFinished = true; + + // May need to retransmit the last flight for DTLS. + if (!chc.sslContext.isDTLS()) { + chc.conContext.finishHandshake(); + } + } else { + chc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + } + + // + // produce + // + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + chc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(chc, fm); + } + } + } + + private void onConsumeFinished(ServerHandshakeContext shc, + ByteBuffer message) throws IOException { + FinishedMessage fm = new FinishedMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming client Finished handshake message", fm); + } + + if (shc.conContext.secureRenegotiation) { + shc.conContext.clientVerifyData = fm.verifyData; + } + + if (shc.isResumption) { + if (shc.handshakeSession.isRejoinable()) { + ((SSLSessionContextImpl)shc.sslContext. + engineGetServerSessionContext()).put( + shc.handshakeSession); + } + shc.conContext.conSession = shc.handshakeSession.finish(); + shc.conContext.protocolVersion = shc.negotiatedProtocol; + + // handshake context cleanup. + shc.handshakeFinished = true; + + // May need to retransmit the last flight for DTLS. + if (!shc.sslContext.isDTLS()) { + shc.conContext.finishHandshake(); + } + } else { + shc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + } + + // + // produce + // + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + shc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(shc, fm); + } + } + } + } + + /** + * The "Finished" handshake message producer. + */ + private static final + class T13FinishedProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13FinishedProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + return onProduceFinished( + (ClientHandshakeContext)context, message); + } else { + return onProduceFinished( + (ServerHandshakeContext)context, message); + } + } + + private byte[] onProduceFinished(ClientHandshakeContext chc, + HandshakeMessage message) throws IOException { + // Refresh handshake hash + chc.handshakeHash.update(); + + FinishedMessage fm = new FinishedMessage(chc); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced client Finished handshake message", fm); + } + + // Output the handshake message. + fm.write(chc.handshakeOutput); + chc.handshakeOutput.flush(); + + // save server verify data for secure renegotiation + if (chc.conContext.secureRenegotiation) { + chc.conContext.clientVerifyData = fm.verifyData; + } + + // update the context + // Change client/server application traffic secrets. + SSLKeyDerivation kd = chc.handshakeKeyDerivation; + if (kd == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "no key derivation"); + return null; // make the compiler happy + } + + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kdg == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + return null; // make the compiler happy + } + + try { + // update the application traffic read keys. + SecretKey writeSecret = kd.deriveKey( + "TlsClientAppTrafficSecret", null); + + SSLKeyDerivation writeKD = + kdg.createKeyDerivation(chc, writeSecret); + SecretKey writeKey = writeKD.deriveKey( + "TlsKey", null); + SecretKey writeIvSecret = writeKD.deriveKey( + "TlsIv", null); + IvParameterSpec writeIv = + new IvParameterSpec(writeIvSecret.getEncoded()); + SSLWriteCipher writeCipher = + chc.negotiatedCipherSuite.bulkCipher.createWriteCipher( + Authenticator.valueOf(chc.negotiatedProtocol), + chc.negotiatedProtocol, writeKey, writeIv, + chc.sslContext.getSecureRandom()); + + chc.baseWriteSecret = writeSecret; + chc.conContext.outputRecord.changeWriteCiphers( + writeCipher, false); + + } catch (GeneralSecurityException gse) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failure to derive application secrets", gse); + return null; // make the compiler happy + } + + // The resumption master secret is stored in the session so + // it can be used after the handshake is completed. + SSLSecretDerivation sd = ((SSLSecretDerivation) kd).forContext(chc); + SecretKey resumptionMasterSecret = sd.deriveKey( + "TlsResumptionMasterSecret", null); + chc.handshakeSession.setResumptionMasterSecret(resumptionMasterSecret); + + chc.conContext.conSession = chc.handshakeSession.finish(); + chc.conContext.protocolVersion = chc.negotiatedProtocol; + + // handshake context cleanup. + chc.handshakeFinished = true; + chc.conContext.finishHandshake(); + + // The handshake message has been delivered. + return null; + } + + private byte[] onProduceFinished(ServerHandshakeContext shc, + HandshakeMessage message) throws IOException { + // Refresh handshake hash + shc.handshakeHash.update(); + + FinishedMessage fm = new FinishedMessage(shc); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced server Finished handshake message", fm); + } + + // Output the handshake message. + fm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // Change client/server application traffic secrets. + SSLKeyDerivation kd = shc.handshakeKeyDerivation; + if (kd == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "no key derivation"); + return null; // make the compiler happy + } + + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kdg == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + shc.negotiatedProtocol); + return null; // make the compiler happy + } + + // derive salt secret + try { + SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null); + + // derive application secrets + HashAlg hashAlg = shc.negotiatedCipherSuite.hashAlg; + HKDF hkdf = new HKDF(hashAlg.name); + byte[] zeros = new byte[hashAlg.hashLength]; + SecretKeySpec sharedSecret = + new SecretKeySpec(zeros, "TlsZeroSecret"); + SecretKey masterSecret = + hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret"); + + SSLKeyDerivation secretKD = + new SSLSecretDerivation(shc, masterSecret); + + // update the handshake traffic write keys. + SecretKey writeSecret = secretKD.deriveKey( + "TlsServerAppTrafficSecret", null); + SSLKeyDerivation writeKD = + kdg.createKeyDerivation(shc, writeSecret); + SecretKey writeKey = writeKD.deriveKey( + "TlsKey", null); + SecretKey writeIvSecret = writeKD.deriveKey( + "TlsIv", null); + IvParameterSpec writeIv = + new IvParameterSpec(writeIvSecret.getEncoded()); + SSLWriteCipher writeCipher = + shc.negotiatedCipherSuite.bulkCipher.createWriteCipher( + Authenticator.valueOf(shc.negotiatedProtocol), + shc.negotiatedProtocol, writeKey, writeIv, + shc.sslContext.getSecureRandom()); + + shc.baseWriteSecret = writeSecret; + shc.conContext.outputRecord.changeWriteCiphers( + writeCipher, false); + + // update the context for the following key derivation + shc.handshakeKeyDerivation = secretKD; + } catch (GeneralSecurityException gse) { + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failure to derive application secrets", gse); + return null; // make the compiler happy + } + + /* + * save client verify data for secure renegotiation + */ + if (shc.conContext.secureRenegotiation) { + shc.conContext.serverVerifyData = fm.verifyData; + } + + // update the context + shc.handshakeConsumers.put( + SSLHandshake.FINISHED.id, SSLHandshake.FINISHED); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "Finished" handshake message consumer. + */ + private static final class T13FinishedConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private T13FinishedConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + if (hc.sslConfig.isClientMode) { + onConsumeFinished( + (ClientHandshakeContext)context, message); + } else { + onConsumeFinished( + (ServerHandshakeContext)context, message); + } + } + + private void onConsumeFinished(ClientHandshakeContext chc, + ByteBuffer message) throws IOException { + FinishedMessage fm = new FinishedMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming server Finished handshake message", fm); + } + + // Save client verify data for secure renegotiation. + if (chc.conContext.secureRenegotiation) { + chc.conContext.serverVerifyData = fm.verifyData; + } + + // + // validate + // + // blank + + // + // update + // + // A change_cipher_spec record received after the peer's Finished + // message MUST be treated as an unexpected record type. + chc.conContext.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id); + + // Change client/server application traffic secrets. + // Refresh handshake hash + chc.handshakeHash.update(); + SSLKeyDerivation kd = chc.handshakeKeyDerivation; + if (kd == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "no key derivation"); + return; // make the compiler happy + } + + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kdg == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + return; // make the compiler happy + } + + // save the session + if (!chc.isResumption && chc.handshakeSession.isRejoinable()) { + SSLSessionContextImpl sessionContext = (SSLSessionContextImpl) + chc.sslContext.engineGetClientSessionContext(); + sessionContext.put(chc.handshakeSession); + } + + // derive salt secret + try { + SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null); + + // derive application secrets + HashAlg hashAlg = chc.negotiatedCipherSuite.hashAlg; + HKDF hkdf = new HKDF(hashAlg.name); + byte[] zeros = new byte[hashAlg.hashLength]; + SecretKeySpec sharedSecret = + new SecretKeySpec(zeros, "TlsZeroSecret"); + SecretKey masterSecret = + hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret"); + + SSLKeyDerivation secretKD = + new SSLSecretDerivation(chc, masterSecret); + + // update the handshake traffic read keys. + SecretKey readSecret = secretKD.deriveKey( + "TlsServerAppTrafficSecret", null); + SSLKeyDerivation writeKD = + kdg.createKeyDerivation(chc, readSecret); + SecretKey readKey = writeKD.deriveKey( + "TlsKey", null); + SecretKey readIvSecret = writeKD.deriveKey( + "TlsIv", null); + IvParameterSpec readIv = + new IvParameterSpec(readIvSecret.getEncoded()); + SSLReadCipher readCipher = + chc.negotiatedCipherSuite.bulkCipher.createReadCipher( + Authenticator.valueOf(chc.negotiatedProtocol), + chc.negotiatedProtocol, readKey, readIv, + chc.sslContext.getSecureRandom()); + + chc.baseReadSecret = readSecret; + chc.conContext.inputRecord.changeReadCiphers(readCipher); + + // update the context for the following key derivation + chc.handshakeKeyDerivation = secretKD; + } catch (GeneralSecurityException gse) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failure to derive application secrets", gse); + return; // make the compiler happy + } + + // + // produce + // + chc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + // full handshake messages + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_VERIFY, + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + chc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(chc, null); + } + } + } + + private void onConsumeFinished(ServerHandshakeContext shc, + ByteBuffer message) throws IOException { + FinishedMessage fm = new FinishedMessage(shc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming client Finished handshake message", fm); + } + + if (shc.conContext.secureRenegotiation) { + shc.conContext.clientVerifyData = fm.verifyData; + } + + // + // validate + // + // blank + + // + // update + // + // Change client/server application traffic secrets. + SSLKeyDerivation kd = shc.handshakeKeyDerivation; + if (kd == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "no key derivation"); + return; // make the compiler happy + } + + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kdg == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + shc.negotiatedProtocol); + return; // make the compiler happy + } + + // save the session + if (!shc.isResumption && shc.handshakeSession.isRejoinable()) { + SSLSessionContextImpl sessionContext = (SSLSessionContextImpl) + shc.sslContext.engineGetServerSessionContext(); + sessionContext.put(shc.handshakeSession); + } + + try { + // update the application traffic read keys. + SecretKey readSecret = kd.deriveKey( + "TlsClientAppTrafficSecret", null); + + SSLKeyDerivation readKD = + kdg.createKeyDerivation(shc, readSecret); + SecretKey readKey = readKD.deriveKey( + "TlsKey", null); + SecretKey readIvSecret = readKD.deriveKey( + "TlsIv", null); + IvParameterSpec readIv = + new IvParameterSpec(readIvSecret.getEncoded()); + SSLReadCipher readCipher = + shc.negotiatedCipherSuite.bulkCipher.createReadCipher( + Authenticator.valueOf(shc.negotiatedProtocol), + shc.negotiatedProtocol, readKey, readIv, + shc.sslContext.getSecureRandom()); + + shc.baseReadSecret = readSecret; + shc.conContext.inputRecord.changeReadCiphers(readCipher); + + // The resumption master secret is stored in the session so + // it can be used after the handshake is completed. + shc.handshakeHash.update(); + SSLSecretDerivation sd = ((SSLSecretDerivation)kd).forContext(shc); + SecretKey resumptionMasterSecret = sd.deriveKey( + "TlsResumptionMasterSecret", null); + shc.handshakeSession.setResumptionMasterSecret(resumptionMasterSecret); + } catch (GeneralSecurityException gse) { + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Failure to derive application secrets", gse); + return; // make the compiler happy + } + + // update connection context + shc.conContext.conSession = shc.handshakeSession.finish(); + shc.conContext.protocolVersion = shc.negotiatedProtocol; + + // handshake context cleanup. + shc.handshakeFinished = true; + + // May need to retransmit the last flight for DTLS. + if (!shc.sslContext.isDTLS()) { + shc.conContext.finishHandshake(); + } + + // + // produce + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Sending new session ticket"); + } + NewSessionTicket.kickstartProducer.produce(shc); + + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HKDF.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HKDF.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeAbsence.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeAbsence.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +/** + * Interface for handshake message or extension absence on handshake + * message processing. + * + * This is typically used after the SSLSession object created, so that the + * extension can update/impact the session object. + */ +interface HandshakeAbsence { + void absent(ConnectionContext context, + HandshakeMessage message) throws IOException; +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeConsumer.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 handshakeConsumers; + final HashMap handshakeProducers; + + // context + final SSLContextImpl sslContext; + final TransportContext conContext; + final SSLConfiguration sslConfig; + + // consolidated parameters + final List activeProtocols; + final List activeCipherSuites; + final AlgorithmConstraints algorithmConstraints; + final ProtocolVersion maximumActiveProtocol; + + // output stream + final HandshakeOutStream handshakeOutput; + + // handshake transcript hash + final HandshakeHash handshakeHash; + + // negotiated security parameters + SSLSessionImpl handshakeSession; + boolean handshakeFinished; + // boolean isInvalidated; + + boolean kickstartMessageDelivered; + + // Resumption + boolean isResumption; + SSLSessionImpl resumingSession; + + final Queue> delegatedActions; + volatile boolean taskDelegated = false; + volatile Exception delegatedThrown = null; + + ProtocolVersion negotiatedProtocol; + CipherSuite negotiatedCipherSuite; + final List handshakePossessions; + final List handshakeCredentials; + SSLKeyDerivation handshakeKeyDerivation; + SSLKeyExchange handshakeKeyExchange; + SecretKey baseReadSecret; + SecretKey baseWriteSecret; + + // protocol version being established + int clientHelloVersion; + String applicationProtocol; + + RandomCookie clientHelloRandom; + RandomCookie serverHelloRandom; + byte[] certRequestContext; + + //////////////////// + // Extensions + + // the extensions used in the handshake + final Map + handshakeExtensions; + + // MaxFragmentLength + int maxFragmentLength; + + // SignatureScheme + List localSupportedSignAlgs; + List peerRequestedSignatureSchemes; + List peerRequestedCertSignSchemes; + + // SupportedGroups + List clientRequestedNamedGroups; + + // HelloRetryRequest + NamedGroup serverSelectedNamedGroup; + + // if server name indicator is negotiated + // + // May need a public API for the indication in the future. + List requestedServerNames; + SNIServerName negotiatedServerName; + + // OCSP Stapling info + boolean staplingActive = false; + + protected HandshakeContext(SSLContextImpl sslContext, + TransportContext conContext) throws IOException { + this.sslContext = sslContext; + this.conContext = conContext; + this.sslConfig = (SSLConfiguration)conContext.sslConfig.clone(); + + this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols, + sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints); + if (activeProtocols.isEmpty()) { + throw new SSLHandshakeException( + "No appropriate protocol (protocol is disabled or " + + "cipher suites are inappropriate)"); + } + + ProtocolVersion maximumVersion = ProtocolVersion.NONE; + for (ProtocolVersion pv : this.activeProtocols) { + if (maximumVersion == ProtocolVersion.NONE || + pv.compare(maximumVersion) > 0) { + maximumVersion = pv; + } + } + this.maximumActiveProtocol = maximumVersion; + this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols, + sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints); + if (activeCipherSuites.isEmpty()) { + throw new SSLHandshakeException("No appropriate cipher suite"); + } + this.algorithmConstraints = + new SSLAlgorithmConstraints(sslConfig.algorithmConstraints); + + this.handshakeConsumers = new LinkedHashMap<>(); + this.handshakeProducers = new HashMap<>(); + this.handshakeHash = conContext.inputRecord.handshakeHash; + this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord); + + this.handshakeFinished = false; + this.kickstartMessageDelivered = false; + + this.delegatedActions = new LinkedList<>(); + this.handshakeExtensions = new HashMap<>(); + this.handshakePossessions = new LinkedList<>(); + this.handshakeCredentials = new LinkedList<>(); + this.requestedServerNames = null; + this.negotiatedServerName = null; + this.negotiatedCipherSuite = conContext.cipherSuite; + initialize(); + } + + /** + * Constructor for PostHandshakeContext + */ + HandshakeContext(TransportContext conContext) { + this.sslContext = conContext.sslContext; + this.conContext = conContext; + this.sslConfig = conContext.sslConfig; + + this.negotiatedProtocol = conContext.protocolVersion; + this.negotiatedCipherSuite = conContext.cipherSuite; + this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord); + this.delegatedActions = new LinkedList<>(); + + this.handshakeProducers = null; + this.handshakeHash = null; + this.activeProtocols = null; + this.activeCipherSuites = null; + this.algorithmConstraints = null; + this.maximumActiveProtocol = null; + this.handshakeExtensions = Collections.emptyMap(); // Not in TLS13 + this.handshakePossessions = null; + this.handshakeCredentials = null; + } + + // Initialize the non-final class variables. + private void initialize() { + ProtocolVersion inputHelloVersion; + ProtocolVersion outputHelloVersion; + if (conContext.isNegotiated) { + inputHelloVersion = conContext.protocolVersion; + outputHelloVersion = conContext.protocolVersion; + } else { + if (activeProtocols.contains(ProtocolVersion.SSL20Hello)) { + inputHelloVersion = ProtocolVersion.SSL20Hello; + + // Per TLS 1.3 protocol, implementation MUST NOT send an SSL + // version 2.0 compatible CLIENT-HELLO. + if (maximumActiveProtocol.useTLS13PlusSpec()) { + outputHelloVersion = maximumActiveProtocol; + } else { + outputHelloVersion = ProtocolVersion.SSL20Hello; + } + } else { + inputHelloVersion = maximumActiveProtocol; + outputHelloVersion = maximumActiveProtocol; + } + } + + conContext.inputRecord.setHelloVersion(inputHelloVersion); + conContext.outputRecord.setHelloVersion(outputHelloVersion); + + if (!conContext.isNegotiated) { + conContext.protocolVersion = maximumActiveProtocol; + } + conContext.outputRecord.setVersion(conContext.protocolVersion); + } + + private static List getActiveProtocols( + List enabledProtocols, + List enabledCipherSuites, + AlgorithmConstraints algorithmConstraints) { + boolean enabledSSL20Hello = false; + ArrayList protocols = new ArrayList<>(4); + for (ProtocolVersion protocol : enabledProtocols) { + if (!enabledSSL20Hello && protocol == ProtocolVersion.SSL20Hello) { + enabledSSL20Hello = true; + continue; + } + + if (!algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + protocol.name, null)) { + // Ignore disabled protocol. + continue; + } + + boolean found = false; + Map cachedStatus = + new EnumMap<>(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 getActiveCipherSuites( + List enabledProtocols, + List enabledCipherSuites, + AlgorithmConstraints algorithmConstraints) { + + List suites = new LinkedList<>(); + if (enabledProtocols != null && !enabledProtocols.isEmpty()) { + Map 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 proposed, CipherSuite cs) { + return proposed.contains(cs) && cs.isNegotiable(); + } + + /** + * Check if the given cipher suite is enabled and available within + * the proposed cipher suite list and specific protocol version. + * + * Does not check if the required server certificates are available. + */ + static final boolean isNegotiable(List proposed, + ProtocolVersion protocolVersion, CipherSuite cs) { + return proposed.contains(cs) && + cs.isNegotiable() && cs.supports(protocolVersion); + } + + /** + * Check if the given protocol version is enabled and available. + */ + boolean isNegotiable(ProtocolVersion protocolVersion) { + return activeProtocols.contains(protocolVersion); + } + + /** + * Set the active protocol version and propagate it to the SSLSocket + * and our handshake streams. Called from ClientHandshaker + * and ServerHandshaker with the negotiated protocol version. + */ + void setVersion(ProtocolVersion protocolVersion) { + this.conContext.protocolVersion = protocolVersion; + } + + private static boolean isActivatable(CipherSuite suite, + AlgorithmConstraints algorithmConstraints, + Map cachedStatus) { + + if (algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { + if (suite.keyExchange == null) { + // TLS 1.3, no definition of key exchange in cipher suite. + return true; + } + + 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 getRequestedServerNames() { + if (requestedServerNames == null) { + return Collections.emptyList(); + } + return requestedServerNames; + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeHash.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, 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,514 +23,623 @@ * questions. */ - package sun.security.ssl; import java.io.ByteArrayOutputStream; -import java.security.*; -import java.util.Locale; +import java.io.IOException; import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.LinkedList; +import javax.crypto.SecretKey; +import sun.security.util.MessageDigestSpi2; + +final class HandshakeHash { + private TranscriptHash transcriptHash; + private LinkedList reserves; // one handshake message per entry + private boolean hasBeenUsed; + + HandshakeHash() { + this.transcriptHash = new CacheOnlyHash(); + this.reserves = new LinkedList<>(); + this.hasBeenUsed = false; + } -/** - * Abstraction for the SSL/TLS hash of all handshake messages that is - * maintained to verify the integrity of the negotiation. Internally, - * it consists of an MD5 and an SHA1 digest. They are used in the client - * and server finished messages and in certificate verify messages (if sent). - * - * This class transparently deals with cloneable and non-cloneable digests. - * - * This class now supports TLS 1.2 also. The key difference for TLS 1.2 - * is that you cannot determine the hash algorithms for CertificateVerify - * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier) - * that there is no messy MD5+SHA1 digests. - * - * You need to obey these conventions when using this class: - * - * 1. protocolDetermined(version) should be called when the negotiated - * protocol version is determined. - * - * 2. Before protocolDetermined() is called, only update(), and reset() - * and setFinishedAlg() can be called. - * - * 3. After protocolDetermined() is called, reset() cannot be called. - * - * 4. After protocolDetermined() is called, if the version is pre-TLS 1.2, - * getFinishedHash() cannot be called. Otherwise, - * getMD5Clone() and getSHAClone() cannot be called. - * - * 5. getMD5Clone() and getSHAClone() can only be called after - * protocolDetermined() is called and version is pre-TLS 1.2. - * - * 6. getFinishedHash() can only be called after protocolDetermined() - * and setFinishedAlg() have been called and the version is TLS 1.2. - * - * Suggestion: Call protocolDetermined() and setFinishedAlg() - * as early as possible. - * - * Example: - *

- * HandshakeHash hh = new HandshakeHash(...)
- * hh.protocolDetermined(ProtocolVersion.TLS12);
- * hh.update(clientHelloBytes);
- * hh.setFinishedAlg("SHA-256");
- * hh.update(serverHelloBytes);
- * ...
- * hh.update(CertificateVerifyBytes);
- * ...
- * hh.update(finished1);
- * byte[] finDigest1 = hh.getFinishedHash();
- * hh.update(finished2);
- * byte[] finDigest2 = hh.getFinishedHash();
- * 
- */ -final class HandshakeHash { + // fix the negotiated protocol version and cipher suite + void determine(ProtocolVersion protocolVersion, + CipherSuite cipherSuite) { + if (!(transcriptHash instanceof CacheOnlyHash)) { + throw new IllegalStateException( + "Not expected instance of transcript hash"); + } - // Common - - // -1: unknown - // 1: <=TLS 1.1 - // 2: TLS 1.2 - private int version = -1; - private ByteArrayOutputStream data = new ByteArrayOutputStream(); - - // For TLS 1.1 - private MessageDigest md5, sha; - private final int clonesNeeded; // needs to be saved for later use - - // For TLS 1.2 - private MessageDigest finMD; - - // Cache for input record handshake hash computation - private ByteArrayOutputStream reserve = new ByteArrayOutputStream(); + CacheOnlyHash coh = (CacheOnlyHash)transcriptHash; + if (protocolVersion.useTLS13PlusSpec()) { + transcriptHash = new T13HandshakeHash(cipherSuite); + } else if (protocolVersion.useTLS12PlusSpec()) { + transcriptHash = new T12HandshakeHash(cipherSuite); + } else if (protocolVersion.useTLS10PlusSpec()) { + transcriptHash = new T10HandshakeHash(cipherSuite); + } else { + transcriptHash = new S30HandshakeHash(cipherSuite); + } - /** - * Create a new HandshakeHash. needCertificateVerify indicates whether - * a hash for the certificate verify message is required. - */ - HandshakeHash(boolean needCertificateVerify) { - // We may rework the code later, but for now we use hard-coded number - // of clones if the underlying MessageDigests are not cloneable. - // - // The number used here is based on the current handshake protocols and - // implementation. It may be changed if the handshake processe gets - // changed in the future, for example adding a new extension that - // requires handshake hash. Please be careful about the number of - // clones if additional handshak hash is required in the future. - // - // For the current implementation, the handshake hash is required for - // the following items: - // . CertificateVerify handshake message (optional) - // . client Finished handshake message - // . server Finished Handshake message - // . the extended Master Secret extension [RFC 7627] - // - // Note that a late call to server setNeedClientAuth dose not update - // the number of clones. We may address the issue later. - // - // Note for safety, we allocate one more clone for the current - // implementation. We may consider it more carefully in the future - // for the exact number or rework the code in a different way. - clonesNeeded = needCertificateVerify ? 5 : 4; + byte[] reserved = coh.baos.toByteArray(); + if (reserved.length != 0) { + transcriptHash.update(reserved, 0, reserved.length); + } } - void reserve(ByteBuffer input) { + HandshakeHash copy() { + if (transcriptHash instanceof CacheOnlyHash) { + HandshakeHash result = new HandshakeHash(); + result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy(); + result.reserves = new LinkedList<>(reserves); + result.hasBeenUsed = hasBeenUsed; + return result; + } else { + throw new IllegalStateException("Hash does not support copying"); + } + } + + void receive(byte[] input) { + reserves.add(Arrays.copyOf(input, input.length)); + } + + void receive(ByteBuffer input, int length) { if (input.hasArray()) { - reserve.write(input.array(), + int from = input.position() + input.arrayOffset(); + int to = from + length; + reserves.add(Arrays.copyOfRange(input.array(), from, to)); + } else { + int inPos = input.position(); + byte[] holder = new byte[length]; + input.get(holder); + input.position(inPos); + reserves.add(Arrays.copyOf(holder, holder.length)); + } + } + void receive(ByteBuffer input) { + receive(input, input.remaining()); + } + + // For HelloRetryRequest only! Please use this method very carefully! + void push(byte[] input) { + reserves.push(Arrays.copyOf(input, input.length)); + } + + // For PreSharedKey to modify the state of the PSK binder hash + byte[] removeLastReceived() { + return reserves.removeLast(); + } + + void deliver(byte[] input) { + update(); + transcriptHash.update(input, 0, input.length); + } + + void deliver(byte[] input, int offset, int length) { + update(); + transcriptHash.update(input, offset, length); + } + + void deliver(ByteBuffer input) { + update(); + if (input.hasArray()) { + transcriptHash.update(input.array(), input.position() + input.arrayOffset(), input.remaining()); } else { int inPos = input.position(); byte[] holder = new byte[input.remaining()]; input.get(holder); input.position(inPos); - reserve.write(holder, 0, holder.length); - } - } - - void reserve(byte[] b, int offset, int len) { - reserve.write(b, offset, len); - } - - void reload() { - if (reserve.size() != 0) { - byte[] bytes = reserve.toByteArray(); - reserve.reset(); - update(bytes, 0, bytes.length); + transcriptHash.update(holder, 0, holder.length); } } - void update(ByteBuffer input) { - - // reload if there are reserved messages. - reload(); - - int inPos = input.position(); - switch (version) { - case 1: - md5.update(input); - input.position(inPos); - - sha.update(input); - input.position(inPos); - - break; - default: - if (finMD != null) { - finMD.update(input); - input.position(inPos); - } - if (input.hasArray()) { - data.write(input.array(), - inPos + input.arrayOffset(), input.remaining()); - } else { - byte[] holder = new byte[input.remaining()]; - input.get(holder); - input.position(inPos); - data.write(holder, 0, holder.length); - } - break; + // Use one handshake message if it has not been used. + void utilize() { + if (hasBeenUsed) { + return; + } + if (reserves.size() != 0) { + byte[] holder = reserves.remove(); + transcriptHash.update(holder, 0, holder.length); + hasBeenUsed = true; } } - void update(byte handshakeType, byte[] handshakeBody) { - - // reload if there are reserved messages. - reload(); - - switch (version) { - case 1: - md5.update(handshakeType); - sha.update(handshakeType); - - md5.update((byte)((handshakeBody.length >> 16) & 0xFF)); - sha.update((byte)((handshakeBody.length >> 16) & 0xFF)); - md5.update((byte)((handshakeBody.length >> 8) & 0xFF)); - sha.update((byte)((handshakeBody.length >> 8) & 0xFF)); - md5.update((byte)(handshakeBody.length & 0xFF)); - sha.update((byte)(handshakeBody.length & 0xFF)); - - md5.update(handshakeBody); - sha.update(handshakeBody); - break; - default: - if (finMD != null) { - finMD.update(handshakeType); - finMD.update((byte)((handshakeBody.length >> 16) & 0xFF)); - finMD.update((byte)((handshakeBody.length >> 8) & 0xFF)); - finMD.update((byte)(handshakeBody.length & 0xFF)); - finMD.update(handshakeBody); - } - data.write(handshakeType); - data.write((byte)((handshakeBody.length >> 16) & 0xFF)); - data.write((byte)((handshakeBody.length >> 8) & 0xFF)); - data.write((byte)(handshakeBody.length & 0xFF)); - data.write(handshakeBody, 0, handshakeBody.length); - break; + // Consume one handshake message if it has not been consumed. + void consume() { + if (hasBeenUsed) { + hasBeenUsed = false; + return; + } + if (reserves.size() != 0) { + byte[] holder = reserves.remove(); + transcriptHash.update(holder, 0, holder.length); } } - void update(byte[] b, int offset, int len) { - - // reload if there are reserved messages. - reload(); + void update() { + while (reserves.size() != 0) { + byte[] holder = reserves.remove(); + transcriptHash.update(holder, 0, holder.length); + } + hasBeenUsed = false; + } - switch (version) { - case 1: - md5.update(b, offset, len); - sha.update(b, offset, len); - break; - default: - if (finMD != null) { - finMD.update(b, offset, len); - } - data.write(b, offset, len); - break; - } + byte[] digest() { + // Note that the reserve handshake message may be not a part of + // the expected digest. + return transcriptHash.digest(); + } + + void finish() { + this.transcriptHash = new CacheOnlyHash(); + this.reserves = new LinkedList<>(); + this.hasBeenUsed = false; + } + + // Optional + byte[] archived() { + // Note that the reserve handshake message may be not a part of + // the expected digest. + return transcriptHash.archived(); } - /** - * Reset the remaining digests. Note this does *not* reset the number of - * digest clones that can be obtained. Digests that have already been - * cloned and are gone remain gone. - */ - void reset() { - if (version != -1) { - throw new RuntimeException( - "reset() can be only be called before protocolDetermined"); - } - data.reset(); + // Optional, TLS 1.0/1.1 only + byte[] digest(String algorithm) { + T10HandshakeHash hh = (T10HandshakeHash)transcriptHash; + return hh.digest(algorithm); + } + + // Optional, SSL 3.0 only + byte[] digest(String algorithm, SecretKey masterSecret) { + S30HandshakeHash hh = (S30HandshakeHash)transcriptHash; + return hh.digest(algorithm, masterSecret); + } + + // Optional, SSL 3.0 only + byte[] digest(boolean useClientLabel, SecretKey masterSecret) { + S30HandshakeHash hh = (S30HandshakeHash)transcriptHash; + return hh.digest(useClientLabel, masterSecret); } - - void protocolDetermined(ProtocolVersion pv) { + public boolean isHashable(byte handshakeType) { + return handshakeType != SSLHandshake.HELLO_REQUEST.id && + handshakeType != SSLHandshake.HELLO_VERIFY_REQUEST.id; + } - // Do not set again, will ignore - if (version != -1) { - return; + interface TranscriptHash { + void update(byte[] input, int offset, int length); + byte[] digest(); + byte[] archived(); // optional + } + + // For cache only. + private static final class CacheOnlyHash implements TranscriptHash { + private final ByteArrayOutputStream baos; + + CacheOnlyHash() { + this.baos = new ByteArrayOutputStream(); } - if (pv.maybeDTLSProtocol()) { - version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1; - } else { - version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1; + @Override + public void update(byte[] input, int offset, int length) { + baos.write(input, offset, length); } - switch (version) { - case 1: - // initiate md5, sha and call update on saved array - try { - md5 = CloneableDigest.getDigest("MD5", clonesNeeded); - sha = CloneableDigest.getDigest("SHA", clonesNeeded); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException - ("Algorithm MD5 or SHA not available", e); - } - byte[] bytes = data.toByteArray(); - update(bytes, 0, bytes.length); - break; - case 2: - break; + + @Override + public byte[] digest() { + throw new IllegalStateException( + "Not expected call to handshake hash digest"); } - } - - ///////////////////////////////////////////////////////////// - // Below are old methods for pre-TLS 1.1 - ///////////////////////////////////////////////////////////// - /** - * Return a new MD5 digest updated with all data hashed so far. - */ - MessageDigest getMD5Clone() { - if (version != 1) { - throw new RuntimeException( - "getMD5Clone() can be only be called for TLS 1.1"); + @Override + public byte[] archived() { + return baos.toByteArray(); } - return cloneDigest(md5); - } - /** - * Return a new SHA digest updated with all data hashed so far. - */ - MessageDigest getSHAClone() { - if (version != 1) { - throw new RuntimeException( - "getSHAClone() can be only be called for TLS 1.1"); - } - return cloneDigest(sha); - } - - private static MessageDigest cloneDigest(MessageDigest digest) { - try { - return (MessageDigest)digest.clone(); - } catch (CloneNotSupportedException e) { - // cannot occur for digests generated via CloneableDigest - throw new RuntimeException("Could not clone digest", e); + CacheOnlyHash copy() { + CacheOnlyHash result = new CacheOnlyHash(); + try { + baos.writeTo(result.baos); + } catch (IOException ex) { + throw new RuntimeException("unable to to clone hash state"); + } + return result; } } - ///////////////////////////////////////////////////////////// - // Below are new methods for TLS 1.2 - ///////////////////////////////////////////////////////////// + static final class S30HandshakeHash implements TranscriptHash { + static final byte[] MD5_pad1 = genPad(0x36, 48); + static final byte[] MD5_pad2 = genPad(0x5c, 48); + + static final byte[] SHA_pad1 = genPad(0x36, 40); + static final byte[] SHA_pad2 = genPad(0x5c, 40); + + private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; + private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; + + private final MessageDigest mdMD5; + private final MessageDigest mdSHA; + private final TranscriptHash md5; + private final TranscriptHash sha; + private final ByteArrayOutputStream baos; + + S30HandshakeHash(CipherSuite cipherSuite) { + this.mdMD5 = JsseJce.getMessageDigest("MD5"); + this.mdSHA = JsseJce.getMessageDigest("SHA"); + + boolean hasArchived = false; + if (mdMD5 instanceof Cloneable) { + md5 = new CloneableHash(mdMD5); + } else { + hasArchived = true; + md5 = new NonCloneableHash(mdMD5); + } + if (mdSHA instanceof Cloneable) { + sha = new CloneableHash(mdSHA); + } else { + hasArchived = true; + sha = new NonCloneableHash(mdSHA); + } - private static String normalizeAlgName(String alg) { - alg = alg.toUpperCase(Locale.US); - if (alg.startsWith("SHA")) { - if (alg.length() == 3) { - return "SHA-1"; + if (hasArchived) { + this.baos = null; + } else { + this.baos = new ByteArrayOutputStream(); + } + } + + @Override + public void update(byte[] input, int offset, int length) { + md5.update(input, offset, length); + sha.update(input, offset, length); + if (baos != null) { + baos.write(input, offset, length); } - if (alg.charAt(3) != '-') { - return "SHA-" + alg.substring(3); + } + + @Override + public byte[] digest() { + byte[] digest = new byte[36]; + System.arraycopy(md5.digest(), 0, digest, 0, 16); + System.arraycopy(sha.digest(), 0, digest, 16, 20); + + return digest; + } + + @Override + public byte[] archived() { + if (baos != null) { + return baos.toByteArray(); + } else if (md5 instanceof NonCloneableHash) { + return md5.archived(); + } else { + return sha.archived(); } } - return alg; - } - /** - * Specifies the hash algorithm used in Finished. This should be called - * based in info in ServerHello. - * Can be called multiple times. - */ - void setFinishedAlg(String s) { - if (s == null) { - throw new RuntimeException( - "setFinishedAlg's argument cannot be null"); + + byte[] digest(boolean useClientLabel, SecretKey masterSecret) { + MessageDigest md5Clone = cloneMd5(); + MessageDigest shaClone = cloneSha(); + + if (useClientLabel) { + md5Clone.update(SSL_CLIENT); + shaClone.update(SSL_CLIENT); + } else { + md5Clone.update(SSL_SERVER); + shaClone.update(SSL_SERVER); + } + + updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret); + updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret); + + byte[] digest = new byte[36]; + System.arraycopy(md5Clone.digest(), 0, digest, 0, 16); + System.arraycopy(shaClone.digest(), 0, digest, 16, 20); + + return digest; + } + + byte[] digest(String algorithm, SecretKey masterSecret) { + if ("RSA".equalsIgnoreCase(algorithm)) { + MessageDigest md5Clone = cloneMd5(); + MessageDigest shaClone = cloneSha(); + updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret); + updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret); + + byte[] digest = new byte[36]; + System.arraycopy(md5Clone.digest(), 0, digest, 0, 16); + System.arraycopy(shaClone.digest(), 0, digest, 16, 20); + + return digest; + } else { + MessageDigest shaClone = cloneSha(); + updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret); + return shaClone.digest(); + } + } + + private static byte[] genPad(int b, int count) { + byte[] padding = new byte[count]; + Arrays.fill(padding, (byte)b); + return padding; + } + + private MessageDigest cloneMd5() { + MessageDigest md5Clone; + if (mdMD5 instanceof Cloneable) { + try { + md5Clone = (MessageDigest)mdMD5.clone(); + } catch (CloneNotSupportedException ex) { // unlikely + throw new RuntimeException( + "MessageDigest does no support clone operation"); + } + } else { + md5Clone = JsseJce.getMessageDigest("MD5"); + md5Clone.update(md5.archived()); + } + + return md5Clone; + } + + private MessageDigest cloneSha() { + MessageDigest shaClone; + if (mdSHA instanceof Cloneable) { + try { + shaClone = (MessageDigest)mdSHA.clone(); + } catch (CloneNotSupportedException ex) { // unlikely + throw new RuntimeException( + "MessageDigest does no support clone operation"); + } + } else { + shaClone = JsseJce.getMessageDigest("SHA"); + shaClone.update(sha.archived()); + } + + return shaClone; + } + + private static void updateDigest(MessageDigest md, + byte[] pad1, byte[] pad2, SecretKey masterSecret) { + byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) + ? masterSecret.getEncoded() : null; + if (keyBytes != null) { + md.update(keyBytes); + } else { + digestKey(md, masterSecret); + } + md.update(pad1); + byte[] temp = md.digest(); + + if (keyBytes != null) { + md.update(keyBytes); + } else { + digestKey(md, masterSecret); + } + md.update(pad2); + md.update(temp); } - // Can be called multiple times, but only set once - if (finMD != null) return; + private static void digestKey(MessageDigest md, SecretKey key) { + try { + if (md instanceof MessageDigestSpi2) { + ((MessageDigestSpi2)md).engineUpdate(key); + } else { + throw new Exception( + "Digest does not support implUpdate(SecretKey)"); + } + } catch (Exception e) { + throw new RuntimeException( + "Could not obtain encoded key and " + + "MessageDigest cannot digest key", e); + } + } + } + + // TLS 1.0 and TLS 1.1 + static final class T10HandshakeHash implements TranscriptHash { + private final TranscriptHash md5; + private final TranscriptHash sha; + private final ByteArrayOutputStream baos; + + T10HandshakeHash(CipherSuite cipherSuite) { + MessageDigest mdMD5 = JsseJce.getMessageDigest("MD5"); + MessageDigest mdSHA = JsseJce.getMessageDigest("SHA"); + + boolean hasArchived = false; + if (mdMD5 instanceof Cloneable) { + md5 = new CloneableHash(mdMD5); + } else { + hasArchived = true; + md5 = new NonCloneableHash(mdMD5); + } + if (mdSHA instanceof Cloneable) { + sha = new CloneableHash(mdSHA); + } else { + hasArchived = true; + sha = new NonCloneableHash(mdSHA); + } - try { - // See comment in the contructor. - finMD = CloneableDigest.getDigest(normalizeAlgName(s), 4); - } catch (NoSuchAlgorithmException e) { - throw new Error(e); + if (hasArchived) { + this.baos = null; + } else { + this.baos = new ByteArrayOutputStream(); + } + } + + @Override + public void update(byte[] input, int offset, int length) { + md5.update(input, offset, length); + sha.update(input, offset, length); + if (baos != null) { + baos.write(input, offset, length); + } } - finMD.update(data.toByteArray()); + + @Override + public byte[] digest() { + byte[] digest = new byte[36]; + System.arraycopy(md5.digest(), 0, digest, 0, 16); + System.arraycopy(sha.digest(), 0, digest, 16, 20); + + return digest; + } + + byte[] digest(String algorithm) { + if ("RSA".equalsIgnoreCase(algorithm)) { + return digest(); + } else { + return sha.digest(); + } + } + + @Override + public byte[] archived() { + if (baos != null) { + return baos.toByteArray(); + } else if (md5 instanceof NonCloneableHash) { + return md5.archived(); + } else { + return sha.archived(); + } + } } - byte[] getAllHandshakeMessages() { - return data.toByteArray(); + static final class T12HandshakeHash implements TranscriptHash { + private final TranscriptHash transcriptHash; + private final ByteArrayOutputStream baos; + + T12HandshakeHash(CipherSuite cipherSuite) { + MessageDigest md = + JsseJce.getMessageDigest(cipherSuite.hashAlg.name); + if (md instanceof Cloneable) { + transcriptHash = new CloneableHash(md); + this.baos = null; + } else { + transcriptHash = new NonCloneableHash(md); + this.baos = new ByteArrayOutputStream(); + } + } + + @Override + public void update(byte[] input, int offset, int length) { + transcriptHash.update(input, offset, length); + if (baos != null) { + baos.write(input, offset, length); + } + } + + @Override + public byte[] digest() { + return transcriptHash.digest(); + } + + @Override + public byte[] archived() { + if (baos != null) { + return baos.toByteArray(); + } else { + return transcriptHash.archived(); + } + } } - /** - * Calculates the hash in Finished. Must be called after setFinishedAlg(). - * This method can be called twice, for Finished messages of the server - * side and client side respectively. - */ - byte[] getFinishedHash() { - try { - return cloneDigest(finMD).digest(); - } catch (Exception e) { - throw new Error("Error during hash calculation", e); + static final class T13HandshakeHash implements TranscriptHash { + private final TranscriptHash transcriptHash; + private final ByteArrayOutputStream baos; + + T13HandshakeHash(CipherSuite cipherSuite) { + MessageDigest md = + JsseJce.getMessageDigest(cipherSuite.hashAlg.name); + if (md instanceof Cloneable) { + transcriptHash = new CloneableHash(md); + this.baos = null; + } else { + transcriptHash = new NonCloneableHash(md); + this.baos = new ByteArrayOutputStream(); + } + } + + @Override + public void update(byte[] input, int offset, int length) { + transcriptHash.update(input, offset, length); + if (baos != null) { + baos.write(input, offset, length); + } + } + + @Override + public byte[] digest() { + return transcriptHash.digest(); + } + + @Override + public byte[] archived() { + if (baos != null) { + return baos.toByteArray(); + } else { + return transcriptHash.archived(); + } + + // throw new UnsupportedOperationException("Not supported yet."); + } + } + + static final class CloneableHash implements TranscriptHash { + private final MessageDigest md; + + CloneableHash(MessageDigest md) { + this.md = md; + } + + @Override + public void update(byte[] input, int offset, int length) { + md.update(input, offset, length); + } + + @Override + public byte[] digest() { + try { + return ((MessageDigest)md.clone()).digest(); + } catch (CloneNotSupportedException ex) { + // unlikely + return new byte[0]; + } + } + + @Override + public byte[] archived() { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + static final class NonCloneableHash implements TranscriptHash { + private final MessageDigest md; + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + NonCloneableHash(MessageDigest md) { + this.md = md; + } + + @Override + public void update(byte[] input, int offset, int length) { + baos.write(input, offset, length); + } + + @Override + public byte[] digest() { + byte[] bytes = baos.toByteArray(); + md.reset(); + return md.digest(bytes); + } + + @Override + public byte[] archived() { + return baos.toByteArray(); } } } - -/** - * A wrapper for MessageDigests that simulates cloning of non-cloneable - * digests. It uses the standard MessageDigest API and therefore can be used - * transparently in place of a regular digest. - * - * Note that we extend the MessageDigest class directly rather than - * MessageDigestSpi. This works because MessageDigest was originally designed - * this way in the JDK 1.1 days which allows us to avoid creating an internal - * provider. - * - * It can be "cloned" a limited number of times, which is specified at - * construction time. This is achieved by internally maintaining n digests - * in parallel. Consequently, it is only 1/n-th times as fast as the original - * digest. - * - * Example: - * MessageDigest md = CloneableDigest.getDigest("SHA", 2); - * md.update(data1); - * MessageDigest md2 = (MessageDigest)md.clone(); - * md2.update(data2); - * byte[] d1 = md2.digest(); // digest of data1 || data2 - * md.update(data3); - * byte[] d2 = md.digest(); // digest of data1 || data3 - * - * This class is not thread safe. - * - */ -final class CloneableDigest extends MessageDigest implements Cloneable { - - /** - * The individual MessageDigests. Initially, all elements are non-null. - * When clone() is called, the non-null element with the maximum index is - * returned and the array element set to null. - * - * All non-null element are always in the same state. - */ - private final MessageDigest[] digests; - - private CloneableDigest(MessageDigest digest, int n, String algorithm) - throws NoSuchAlgorithmException { - super(algorithm); - digests = new MessageDigest[n]; - digests[0] = digest; - for (int i = 1; i < n; i++) { - digests[i] = JsseJce.getMessageDigest(algorithm); - } - } - - /** - * Return a MessageDigest for the given algorithm that can be cloned the - * specified number of times. If the default implementation supports - * cloning, it is returned. Otherwise, an instance of this class is - * returned. - */ - static MessageDigest getDigest(String algorithm, int n) - throws NoSuchAlgorithmException { - MessageDigest digest = JsseJce.getMessageDigest(algorithm); - try { - digest.clone(); - // already cloneable, use it - return digest; - } catch (CloneNotSupportedException e) { - return new CloneableDigest(digest, n, algorithm); - } - } - - /** - * Check if this object is still usable. If it has already been cloned the - * maximum number of times, there are no digests left and this object can no - * longer be used. - */ - private void checkState() { - // XXX handshaking currently doesn't stop updating hashes... - // if (digests[0] == null) { - // throw new IllegalStateException("no digests left"); - // } - } - - @Override - protected int engineGetDigestLength() { - checkState(); - return digests[0].getDigestLength(); - } - - @Override - protected void engineUpdate(byte b) { - checkState(); - for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { - digests[i].update(b); - } - } - - @Override - protected void engineUpdate(byte[] b, int offset, int len) { - checkState(); - for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { - digests[i].update(b, offset, len); - } - } - - @Override - protected byte[] engineDigest() { - checkState(); - byte[] digest = digests[0].digest(); - digestReset(); - return digest; - } - - @Override - protected int engineDigest(byte[] buf, int offset, int len) - throws DigestException { - checkState(); - int n = digests[0].digest(buf, offset, len); - digestReset(); - return n; - } - - /** - * Reset all digests after a digest() call. digests[0] has already been - * implicitly reset by the digest() call and does not need to be reset - * again. - */ - private void digestReset() { - for (int i = 1; (i < digests.length) && (digests[i] != null); i++) { - digests[i].reset(); - } - } - - @Override - protected void engineReset() { - checkState(); - for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { - digests[i].reset(); - } - } - - @Override - public Object clone() { - checkState(); - for (int i = digests.length - 1; i >= 0; i--) { - if (digests[i] != null) { - MessageDigest digest = digests[i]; - digests[i] = null; - return digest; - } - } - // cannot occur - throw new InternalError(); - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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"); - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2430 +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.io.*; -import java.math.BigInteger; -import java.security.*; -import java.security.interfaces.*; -import java.security.spec.*; -import java.security.cert.*; -import java.security.cert.Certificate; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import java.lang.reflect.*; - -import javax.security.auth.x500.X500Principal; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.spec.DHPublicKeySpec; - -import javax.net.ssl.*; - -import sun.security.internal.spec.TlsPrfParameterSpec; -import sun.security.ssl.CipherSuite.*; -import static sun.security.ssl.CipherSuite.PRF.*; -import sun.security.util.KeyUtil; -import sun.security.util.MessageDigestSpi2; -import sun.security.provider.certpath.OCSPResponse; - -/** - * Many data structures are involved in the handshake messages. These - * classes are used as structures, with public data members. They are - * not visible outside the SSL package. - * - * Handshake messages all have a common header format, and they are all - * encoded in a "handshake data" SSL record substream. The base class - * here (HandshakeMessage) provides a common framework and records the - * SSL record type of the particular handshake message. - * - * This file contains subclasses for all the basic handshake messages. - * All handshake messages know how to encode and decode themselves on - * SSL streams; this facilitates using the same code on SSL client and - * server sides, although they don't send and receive the same messages. - * - * Messages also know how to print themselves, which is quite handy - * for debugging. They always identify their type, and can optionally - * dump all of their content. - * - * @author David Brownell - */ -public abstract class HandshakeMessage { - - /* Class and subclass dynamic debugging support */ - public static final Debug debug = Debug.getInstance("ssl"); - - // enum HandshakeType: - // - // Please update the isUnsupported() method accordingly if the handshake - // types get updated in the future. - static final byte ht_hello_request = 0; // RFC 5246 - static final byte ht_client_hello = 1; // RFC 5246 - static final byte ht_server_hello = 2; // RFC 5246 - static final byte ht_hello_verify_request = 3; // RFC 6347 - static final byte ht_new_session_ticket = 4; // RFC 4507 - - static final byte ht_certificate = 11; // RFC 5246 - static final byte ht_server_key_exchange = 12; // RFC 5246 - static final byte ht_certificate_request = 13; // RFC 5246 - static final byte ht_server_hello_done = 14; // RFC 5246 - static final byte ht_certificate_verify = 15; // RFC 5246 - static final byte ht_client_key_exchange = 16; // RFC 5246 - - static final byte ht_finished = 20; // RFC 5246 - static final byte ht_certificate_url = 21; // RFC 6066 - static final byte ht_certificate_status = 22; // RFC 6066 - static final byte ht_supplemental_data = 23; // RFC 4680 - - static final byte ht_not_applicable = -1; // N/A - - /* - * SSL 3.0 MAC padding constants. - * Also used by CertificateVerify and Finished during the handshake. - */ - static final byte[] MD5_pad1 = genPad(0x36, 48); - static final byte[] MD5_pad2 = genPad(0x5c, 48); - - static final byte[] SHA_pad1 = genPad(0x36, 40); - static final byte[] SHA_pad2 = genPad(0x5c, 40); - - // default constructor - HandshakeMessage() { - } - - /** - * Utility method to convert a BigInteger to a byte array in unsigned - * format as needed in the handshake messages. BigInteger uses - * 2's complement format, i.e. it prepends an extra zero if the MSB - * is set. We remove that. - */ - static byte[] toByteArray(BigInteger bi) { - byte[] b = bi.toByteArray(); - if ((b.length > 1) && (b[0] == 0)) { - int n = b.length - 1; - byte[] newarray = new byte[n]; - System.arraycopy(b, 1, newarray, 0, n); - b = newarray; - } - return b; - } - - static boolean isUnsupported(byte handshakeType) { - return (handshakeType != ht_hello_request) && - (handshakeType != ht_client_hello) && - (handshakeType != ht_server_hello) && - (handshakeType != ht_hello_verify_request) && - (handshakeType != ht_new_session_ticket) && - (handshakeType != ht_certificate) && - (handshakeType != ht_server_key_exchange) && - (handshakeType != ht_certificate_request) && - (handshakeType != ht_server_hello_done) && - (handshakeType != ht_certificate_verify) && - (handshakeType != ht_client_key_exchange) && - (handshakeType != ht_finished) && - (handshakeType != ht_certificate_url) && - (handshakeType != ht_certificate_status) && - (handshakeType != ht_supplemental_data); - } - - private static byte[] genPad(int b, int count) { - byte[] padding = new byte[count]; - Arrays.fill(padding, (byte)b); - return padding; - } - - /* - * Write a handshake message on the (handshake) output stream. - * This is just a four byte header followed by the data. - * - * NOTE that huge messages -- notably, ones with huge cert - * chains -- are handled correctly. - */ - final void write(HandshakeOutStream s) throws IOException { - int len = messageLength(); - if (len >= Record.OVERFLOW_OF_INT24) { - throw new SSLException("Handshake message too big" - + ", type = " + messageType() + ", len = " + len); - } - s.write(messageType()); - s.putInt24(len); - send(s); - s.complete(); - } - - /* - * Subclasses implement these methods so those kinds of - * messages can be emitted. Base class delegates to subclass. - */ - abstract int messageType(); - abstract int messageLength(); - abstract void send(HandshakeOutStream s) throws IOException; - - /* - * Write a descriptive message on the output stream; for debugging. - */ - abstract void print(PrintStream p) throws IOException; - -// -// NOTE: the rest of these classes are nested within this one, and are -// imported by other classes in this package. There are a few other -// handshake message classes, not neatly nested here because of current -// licensing requirement for native (RSA) methods. They belong here, -// but those native methods complicate things a lot! -// - - -/* - * HelloRequest ... SERVER --> CLIENT - * - * Server can ask the client to initiate a new handshake, e.g. to change - * session parameters after a connection has been (re)established. - */ -static final class HelloRequest extends HandshakeMessage { - @Override - int messageType() { return ht_hello_request; } - - HelloRequest() { } - - HelloRequest(HandshakeInStream in) throws IOException - { - // nothing in this message - } - - @Override - int messageLength() { return 0; } - - @Override - void send(HandshakeOutStream out) throws IOException - { - // nothing in this messaage - } - - @Override - void print(PrintStream out) throws IOException - { - out.println("*** HelloRequest (empty)"); - } - -} - -/* - * HelloVerifyRequest ... SERVER --> CLIENT [DTLS only] - * - * The definition of HelloVerifyRequest is as follows: - * - * struct { - * ProtocolVersion server_version; - * opaque cookie<0..2^8-1>; - * } HelloVerifyRequest; - * - * For DTLS protocols, once the client has transmitted the ClientHello message, - * it expects to see a HelloVerifyRequest from the server. However, if the - * server's message is lost, the client knows that either the ClientHello or - * the HelloVerifyRequest has been lost and retransmits. [RFC 6347] - */ -static final class HelloVerifyRequest extends HandshakeMessage { - ProtocolVersion protocolVersion; - byte[] cookie; // 1 to 2^8 - 1 bytes - - HelloVerifyRequest(HelloCookieManager helloCookieManager, - ClientHello clientHelloMsg) { - - this.protocolVersion = clientHelloMsg.protocolVersion; - this.cookie = helloCookieManager.getCookie(clientHelloMsg); - } - - HelloVerifyRequest( - HandshakeInStream input, int messageLength) throws IOException { - - this.protocolVersion = - ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); - this.cookie = input.getBytes8(); - - // Is it a valid cookie? - HelloCookieManager.checkCookie(protocolVersion, cookie); - } - - @Override - int messageType() { - return ht_hello_verify_request; - } - - @Override - int messageLength() { - return 2 + cookie.length; // 2: the length of protocolVersion - } - - @Override - void send(HandshakeOutStream hos) throws IOException { - hos.putInt8(protocolVersion.major); - hos.putInt8(protocolVersion.minor); - hos.putBytes8(cookie); - } - - @Override - void print(PrintStream out) throws IOException { - out.println("*** HelloVerifyRequest"); - if (debug != null && Debug.isOn("verbose")) { - out.println("server_version: " + protocolVersion); - Debug.println(out, "cookie", cookie); - } - } -} - -/* - * ClientHello ... CLIENT --> SERVER - * - * Client initiates handshake by telling server what it wants, and what it - * can support (prioritized by what's first in the ciphe suite list). - * - * By RFC2246:7.4.1.2 it's explicitly anticipated that this message - * will have more data added at the end ... e.g. what CAs the client trusts. - * Until we know how to parse it, we will just read what we know - * about, and let our caller handle the jumps over unknown data. - */ -static final class ClientHello extends HandshakeMessage { - - ProtocolVersion protocolVersion; - RandomCookie clnt_random; - SessionId sessionId; - byte[] cookie; // DTLS only - private CipherSuiteList cipherSuites; - private final boolean isDTLS; - byte[] compression_methods; - - HelloExtensions extensions = new HelloExtensions(); - - private static final byte[] NULL_COMPRESSION = new byte[] {0}; - - ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, - SessionId sessionId, CipherSuiteList cipherSuites, - boolean isDTLS) { - - this.isDTLS = isDTLS; - this.protocolVersion = protocolVersion; - this.sessionId = sessionId; - this.cipherSuites = cipherSuites; - if (isDTLS) { - this.cookie = new byte[0]; - } else { - this.cookie = null; - } - - clnt_random = new RandomCookie(generator); - compression_methods = NULL_COMPRESSION; - } - - ClientHello(HandshakeInStream s, - int messageLength, boolean isDTLS) throws IOException { - - this.isDTLS = isDTLS; - - protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); - clnt_random = new RandomCookie(s); - sessionId = new SessionId(s.getBytes8()); - sessionId.checkLength(protocolVersion); - if (isDTLS) { - cookie = s.getBytes8(); - } else { - cookie = null; - } - - cipherSuites = new CipherSuiteList(s); - compression_methods = s.getBytes8(); - if (messageLength() != messageLength) { - extensions = new HelloExtensions(s); - } - } - - CipherSuiteList getCipherSuites() { - return cipherSuites; - } - - // add renegotiation_info extension - void addRenegotiationInfoExtension(byte[] clientVerifyData) { - HelloExtension renegotiationInfo = new RenegotiationInfoExtension( - clientVerifyData, new byte[0]); - extensions.add(renegotiationInfo); - } - - // add server_name extension - void addSNIExtension(List serverNames) { - try { - extensions.add(new ServerNameExtension(serverNames)); - } catch (IOException ioe) { - // ignore the exception and return - } - } - - // add signature_algorithm extension - void addSignatureAlgorithmsExtension( - Collection algorithms) { - HelloExtension signatureAlgorithm = - new SignatureAlgorithmsExtension(algorithms); - extensions.add(signatureAlgorithm); - } - - void addExtendedMasterSecretExtension() { - extensions.add(new ExtendedMasterSecretExtension()); - } - - void addMFLExtension(int maximumPacketSize) { - HelloExtension maxFragmentLength = - new MaxFragmentLengthExtension(maximumPacketSize); - extensions.add(maxFragmentLength); - } - - void updateHelloCookie(MessageDigest cookieDigest) { - // - // Just use HandshakeOutStream to compute the hello verify cookie. - // Not actually used to output handshake message records. - // - HandshakeOutStream hos = new HandshakeOutStream(null); - - try { - send(hos, false); // Do not count hello verify cookie. - } catch (IOException ioe) { - // unlikely to happen - } - - cookieDigest.update(hos.toByteArray()); - } - - // Add status_request extension type - void addCertStatusRequestExtension() { - extensions.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest())); - } - - // Add status_request_v2 extension type - void addCertStatusReqListV2Extension() { - // Create a default OCSPStatusRequest that we can use for both - // OCSP_MULTI and OCSP request list items. - OCSPStatusRequest osr = new OCSPStatusRequest(); - List itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - osr)); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); - extensions.add(new CertStatusReqListV2Extension(itemList)); - } - - // add application_layer_protocol_negotiation extension - void addALPNExtension(String[] applicationProtocols) throws SSLException { - extensions.add(new ALPNExtension(applicationProtocols)); - } - - @Override - int messageType() { return ht_client_hello; } - - @Override - int messageLength() { - /* - * Add fixed size parts of each field... - * version + random + session + cipher + compress - */ - return (2 + 32 + 1 + 2 + 1 - + sessionId.length() /* ... + variable parts */ - + (isDTLS ? (1 + cookie.length) : 0) - + (cipherSuites.size() * 2) - + compression_methods.length) - + extensions.length(); - } - - @Override - void send(HandshakeOutStream s) throws IOException { - send(s, true); // Count hello verify cookie. - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** ClientHello, " + protocolVersion); - - if (debug != null && Debug.isOn("verbose")) { - s.print("RandomCookie: "); - clnt_random.print(s); - - s.print("Session ID: "); - s.println(sessionId); - - if (isDTLS) { - Debug.println(s, "cookie", cookie); - } - - s.println("Cipher Suites: " + cipherSuites); - - Debug.println(s, "Compression Methods", compression_methods); - extensions.print(s); - s.println("***"); - } - } - - private void send(HandshakeOutStream s, - boolean computeCookie) throws IOException { - s.putInt8(protocolVersion.major); - s.putInt8(protocolVersion.minor); - clnt_random.send(s); - s.putBytes8(sessionId.getId()); - if (isDTLS && computeCookie) { - s.putBytes8(cookie); - } - cipherSuites.send(s); - s.putBytes8(compression_methods); - extensions.send(s); - } - -} - -/* - * ServerHello ... SERVER --> CLIENT - * - * Server chooses protocol options from among those it supports and the - * client supports. Then it sends the basic session descriptive parameters - * back to the client. - */ -static final -class ServerHello extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_hello; } - - ProtocolVersion protocolVersion; - RandomCookie svr_random; - SessionId sessionId; - CipherSuite cipherSuite; - byte compression_method; - HelloExtensions extensions = new HelloExtensions(); - - ServerHello() { - // empty - } - - ServerHello(HandshakeInStream input, int messageLength) - throws IOException { - protocolVersion = ProtocolVersion.valueOf(input.getInt8(), - input.getInt8()); - svr_random = new RandomCookie(input); - sessionId = new SessionId(input.getBytes8()); - sessionId.checkLength(protocolVersion); - cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8()); - compression_method = (byte)input.getInt8(); - if (messageLength() != messageLength) { - extensions = new HelloExtensions(input); - } - } - - @Override - int messageLength() - { - // almost fixed size, except session ID and extensions: - // major + minor = 2 - // random = 32 - // session ID len field = 1 - // cipher suite + compression = 3 - // extensions: if present, 2 + length of extensions - return 38 + sessionId.length() + extensions.length(); - } - - @Override - void send(HandshakeOutStream s) throws IOException - { - s.putInt8(protocolVersion.major); - s.putInt8(protocolVersion.minor); - svr_random.send(s); - s.putBytes8(sessionId.getId()); - s.putInt8(cipherSuite.id >> 8); - s.putInt8(cipherSuite.id & 0xff); - s.putInt8(compression_method); - extensions.send(s); - } - - @Override - void print(PrintStream s) throws IOException - { - s.println("*** ServerHello, " + protocolVersion); - - if (debug != null && Debug.isOn("verbose")) { - s.print("RandomCookie: "); - svr_random.print(s); - - s.print("Session ID: "); - s.println(sessionId); - - s.println("Cipher Suite: " + cipherSuite); - s.println("Compression Method: " + compression_method); - extensions.print(s); - s.println("***"); - } - } -} - - -/* - * CertificateMsg ... send by both CLIENT and SERVER - * - * Each end of a connection may need to pass its certificate chain to - * the other end. Such chains are intended to validate an identity with - * reference to some certifying authority. Examples include companies - * like Verisign, or financial institutions. There's some control over - * the certifying authorities which are sent. - * - * NOTE: that these messages might be huge, taking many handshake records. - * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 - * bytes each ... up to 2^32 records sent on the output stream. - */ -static final -class CertificateMsg extends HandshakeMessage -{ - @Override - int messageType() { return ht_certificate; } - - private X509Certificate[] chain; - - private List encodedChain; - - private int messageLength; - - CertificateMsg(X509Certificate[] certs) { - chain = certs; - } - - CertificateMsg(HandshakeInStream input) throws IOException { - int chainLen = input.getInt24(); - List v = new ArrayList<>(4); - - CertificateFactory cf = null; - while (chainLen > 0) { - byte[] cert = input.getBytes24(); - chainLen -= (3 + cert.length); - try { - if (cf == null) { - cf = CertificateFactory.getInstance("X.509"); - } - v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); - } catch (CertificateException e) { - throw (SSLProtocolException)new SSLProtocolException( - e.getMessage()).initCause(e); - } - } - - chain = v.toArray(new X509Certificate[v.size()]); - } - - @Override - int messageLength() { - if (encodedChain == null) { - messageLength = 3; - encodedChain = new ArrayList(chain.length); - try { - for (X509Certificate cert : chain) { - byte[] b = cert.getEncoded(); - encodedChain.add(b); - messageLength += b.length + 3; - } - } catch (CertificateEncodingException e) { - encodedChain = null; - throw new RuntimeException("Could not encode certificates", e); - } - } - return messageLength; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt24(messageLength() - 3); - for (byte[] b : encodedChain) { - s.putBytes24(b); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Certificate chain"); - - if (chain.length == 0) { - s.println(""); - } else if (debug != null && Debug.isOn("verbose")) { - for (int i = 0; i < chain.length; i++) { - s.println("chain [" + i + "] = " + chain[i]); - } - } - s.println("***"); - } - - X509Certificate[] getCertificateChain() { - return chain.clone(); - } -} - -/* - * CertificateStatus ... SERVER --> CLIENT - * - * When a ClientHello asserting the status_request or status_request_v2 - * extensions is accepted by the server, it will fetch and return one - * or more status responses in this handshake message. - * - * NOTE: Like the Certificate handshake message, this can potentially - * be a very large message both due to the size of multiple status - * responses and the certificate chains that are often attached to them. - * Up to 2^24 bytes of status responses may be sent, possibly fragmented - * over multiple TLS records. - */ -static final class CertificateStatus extends HandshakeMessage -{ - private final StatusRequestType statusType; - private int encodedResponsesLen; - private int messageLength = -1; - private List encodedResponses; - - @Override - int messageType() { return ht_certificate_status; } - - /** - * Create a CertificateStatus message from the certificates and their - * respective OCSP responses - * - * @param type an indication of the type of response (OCSP or OCSP_MULTI) - * @param responses a {@code List} of OCSP responses in DER-encoded form. - * For the OCSP type, only the first entry in the response list is - * used, and must correspond to the end-entity certificate sent to the - * peer. Zero-length or null values for the response data are not - * allowed for the OCSP type. For the OCSP_MULTI type, each entry in - * the list should match its corresponding certificate sent in the - * Server Certificate message. Where an OCSP response does not exist, - * either a zero-length array or a null value should be used. - * - * @throws SSLException if an unsupported StatusRequestType or invalid - * OCSP response data is provided. - */ - CertificateStatus(StatusRequestType type, X509Certificate[] chain, - Map responses) { - statusType = type; - encodedResponsesLen = 0; - encodedResponses = new ArrayList<>(chain.length); - - Objects.requireNonNull(chain, "Null chain not allowed"); - Objects.requireNonNull(responses, "Null responses not allowed"); - - if (statusType == StatusRequestType.OCSP) { - // Just get the response for the end-entity certificate - byte[] respDER = responses.get(chain[0]); - if (respDER != null && respDER.length > 0) { - encodedResponses.add(respDER); - encodedResponsesLen = 3 + respDER.length; - } else { - throw new IllegalArgumentException("Zero-length or null " + - "OCSP Response"); - } - } else if (statusType == StatusRequestType.OCSP_MULTI) { - for (X509Certificate cert : chain) { - byte[] respDER = responses.get(cert); - if (respDER != null) { - encodedResponses.add(respDER); - encodedResponsesLen += (respDER.length + 3); - } else { - // If we cannot find a response for a given certificate - // then use a zero-length placeholder. - encodedResponses.add(new byte[0]); - encodedResponsesLen += 3; - } - } - } else { - throw new IllegalArgumentException( - "Unsupported StatusResponseType: " + statusType); - } - } - - /** - * Decode the CertificateStatus handshake message coming from a - * {@code HandshakeInputStream}. - * - * @param input the {@code HandshakeInputStream} containing the - * CertificateStatus message bytes. - * - * @throws SSLHandshakeException if a zero-length response is found in the - * OCSP response type, or an unsupported response type is detected. - * @throws IOException if a decoding error occurs. - */ - CertificateStatus(HandshakeInStream input) throws IOException { - encodedResponsesLen = 0; - encodedResponses = new ArrayList<>(); - - statusType = StatusRequestType.get(input.getInt8()); - if (statusType == StatusRequestType.OCSP) { - byte[] respDER = input.getBytes24(); - // Convert the incoming bytes to a OCSPResponse strucutre - if (respDER.length > 0) { - encodedResponses.add(respDER); - encodedResponsesLen = 3 + respDER.length; - } else { - throw new SSLHandshakeException("Zero-length OCSP Response"); - } - } else if (statusType == StatusRequestType.OCSP_MULTI) { - int respListLen = input.getInt24(); - 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 = input.getBytes24(); - encodedResponses.add(respDER); - respListLen -= (respDER.length + 3); - } - - if (respListLen != 0) { - throw new SSLHandshakeException( - "Bad OCSP response list length"); - } - } else { - throw new SSLHandshakeException("Unsupported StatusResponseType: " + - statusType); - } - } - - /** - * Get the length of the CertificateStatus message. - * - * @return the length of the message in bytes. - */ - @Override - int messageLength() { - int len = 1; // Length + Status type - - if (messageLength == -1) { - if (statusType == StatusRequestType.OCSP) { - len += encodedResponsesLen; - } else if (statusType == StatusRequestType.OCSP_MULTI) { - len += 3 + encodedResponsesLen; - } - messageLength = len; - } - - return messageLength; - } - - /** - * Encode the CertificateStatus handshake message and place it on a - * {@code HandshakeOutputStream}. - * - * @param s the HandshakeOutputStream that will the message bytes. - * - * @throws IOException if an encoding error occurs. - */ - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt8(statusType.id); - if (statusType == StatusRequestType.OCSP) { - s.putBytes24(encodedResponses.get(0)); - } else if (statusType == StatusRequestType.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); - } - } - - /** - * Display a human-readable representation of the CertificateStatus message. - * - * @param s the PrintStream used to display the message data. - * - * @throws IOException if any errors occur while parsing the OCSP response - * bytes into a readable form. - */ - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateStatus"); - if (debug != null && Debug.isOn("verbose")) { - s.println("Type: " + statusType); - if (statusType == StatusRequestType.OCSP) { - OCSPResponse oResp = new OCSPResponse(encodedResponses.get(0)); - s.println(oResp); - } else if (statusType == StatusRequestType.OCSP_MULTI) { - int numResponses = encodedResponses.size(); - s.println(numResponses + - (numResponses == 1 ? " entry:" : " entries:")); - for (byte[] respDER : encodedResponses) { - if (respDER.length > 0) { - OCSPResponse oResp = new OCSPResponse(respDER); - s.println(oResp); - } else { - s.println(""); - } - } - } - } - } - - /** - * Get the type of CertificateStatus message - * - * @return the {@code StatusRequestType} for this CertificateStatus - * message. - */ - StatusRequestType getType() { - return statusType; - } - - /** - * Get the list of non-zero length OCSP responses. - * The responses returned in this list can be used to map to - * {@code X509Certificate} objects provided by the peer and - * provided to a {@code PKIXRevocationChecker}. - * - * @return an unmodifiable List of zero or more byte arrays, each one - * consisting of a single status response. - */ - List getResponses() { - return Collections.unmodifiableList(encodedResponses); - } -} - -/* - * ServerKeyExchange ... SERVER --> CLIENT - * - * The cipher suite selected, when combined with the certificate exchanged, - * implies one of several different kinds of key exchange. Most current - * cipher suites require the server to send more than its certificate. - * - * The primary exceptions are when a server sends an encryption-capable - * RSA public key in its cert, to be used with RSA (or RSA_export) key - * exchange; and when a server sends its Diffie-Hellman cert. Those kinds - * of key exchange do not require a ServerKeyExchange message. - * - * Key exchange can be viewed as having three modes, which are explicit - * for the Diffie-Hellman flavors and poorly specified for RSA ones: - * - * - "Ephemeral" keys. Here, a "temporary" key is allocated by the - * server, and signed. Diffie-Hellman keys signed using RSA or - * DSS are ephemeral (DHE flavor). RSA keys get used to do the same - * thing, to cut the key size down to 512 bits (export restrictions) - * or for signing-only RSA certificates. - * - * - Anonymity. Here no server certificate is sent, only the public - * key of the server. This case is subject to man-in-the-middle - * attacks. This can be done with Diffie-Hellman keys (DH_anon) or - * with RSA keys, but is only used in SSLv3 for DH_anon. - * - * - "Normal" case. Here a server certificate is sent, and the public - * key there is used directly in exchanging the premaster secret. - * For example, Diffie-Hellman "DH" flavor, and any RSA flavor with - * only 512 bit keys. - * - * If a server certificate is sent, there is no anonymity. However, - * when a certificate is sent, ephemeral keys may still be used to - * exchange the premaster secret. That's how RSA_EXPORT often works, - * as well as how the DHE_* flavors work. - */ -abstract static class ServerKeyExchange extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_key_exchange; } -} - - -/* - * Using RSA for Key Exchange: exchange a session key that's not as big - * as the signing-only key. Used for export applications, since exported - * RSA encryption keys can't be bigger than 512 bytes. - * - * This is never used when keys are 512 bits or smaller, and isn't used - * on "US Domestic" ciphers in any case. - */ -static final -class RSA_ServerKeyExchange extends ServerKeyExchange -{ - private byte[] rsa_modulus; // 1 to 2^16 - 1 bytes - private byte[] rsa_exponent; // 1 to 2^16 - 1 bytes - - private Signature signature; - private byte[] signatureBytes; - - /* - * Hash the nonces and the ephemeral RSA public key. - */ - private void updateSignature(byte[] clntNonce, byte[] svrNonce) - throws SignatureException { - int tmp; - - signature.update(clntNonce); - signature.update(svrNonce); - - tmp = rsa_modulus.length; - signature.update((byte)(tmp >> 8)); - signature.update((byte)(tmp & 0x0ff)); - signature.update(rsa_modulus); - - tmp = rsa_exponent.length; - signature.update((byte)(tmp >> 8)); - signature.update((byte)(tmp & 0x0ff)); - signature.update(rsa_exponent); - } - - - /* - * Construct an RSA server key exchange message, using data - * known _only_ to the server. - * - * The client knows the public key corresponding to this private - * key, from the Certificate message sent previously. To comply - * with US export regulations we use short RSA keys ... either - * long term ones in the server's X509 cert, or else ephemeral - * ones sent using this message. - */ - RSA_ServerKeyExchange(PublicKey ephemeralKey, PrivateKey privateKey, - RandomCookie clntNonce, RandomCookie svrNonce, SecureRandom sr) - throws GeneralSecurityException { - RSAPublicKeySpec rsaKey = JsseJce.getRSAPublicKeySpec(ephemeralKey); - rsa_modulus = toByteArray(rsaKey.getModulus()); - rsa_exponent = toByteArray(rsaKey.getPublicExponent()); - signature = RSASignature.getInstance(); - signature.initSign(privateKey, sr); - updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); - signatureBytes = signature.sign(); - } - - - /* - * Parse an RSA server key exchange message, using data known - * to the client (and, in some situations, eavesdroppers). - */ - RSA_ServerKeyExchange(HandshakeInStream input) - throws IOException, NoSuchAlgorithmException { - signature = RSASignature.getInstance(); - rsa_modulus = input.getBytes16(); - rsa_exponent = input.getBytes16(); - signatureBytes = input.getBytes16(); - } - - /* - * Get the ephemeral RSA public key that will be used in this - * SSL connection. - */ - PublicKey getPublicKey() { - try { - KeyFactory kfac = JsseJce.getKeyFactory("RSA"); - // modulus and exponent are always positive - RSAPublicKeySpec kspec = new RSAPublicKeySpec( - new BigInteger(1, rsa_modulus), - new BigInteger(1, rsa_exponent)); - return kfac.generatePublic(kspec); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /* - * Verify the signed temporary key using the hashes computed - * from it and the two nonces. This is called by clients - * with "exportable" RSA flavors. - */ - boolean verify(PublicKey certifiedKey, RandomCookie clntNonce, - RandomCookie svrNonce) throws GeneralSecurityException { - signature.initVerify(certifiedKey); - updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); - return signature.verify(signatureBytes); - } - - @Override - int messageLength() { - return 6 + rsa_modulus.length + rsa_exponent.length - + signatureBytes.length; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putBytes16(rsa_modulus); - s.putBytes16(rsa_exponent); - s.putBytes16(signatureBytes); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** RSA ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "RSA Modulus", rsa_modulus); - Debug.println(s, "RSA Public Exponent", rsa_exponent); - } - } -} - - -/* - * Using Diffie-Hellman algorithm for key exchange. All we really need to - * do is securely get Diffie-Hellman keys (using the same P, G parameters) - * to our peer, then we automatically have a shared secret without need - * to exchange any more data. (D-H only solutions, such as SKIP, could - * eliminate key exchange negotiations and get faster connection setup. - * But they still need a signature algorithm like DSS/DSA to support the - * trusted distribution of keys without relying on unscalable physical - * key distribution systems.) - * - * This class supports several DH-based key exchange algorithms, though - * perhaps eventually each deserves its own class. Notably, this has - * basic support for DH_anon and its DHE_DSS and DHE_RSA signed variants. - */ -static final -class DH_ServerKeyExchange extends ServerKeyExchange -{ - // Fix message encoding, see 4348279 - private static final boolean dhKeyExchangeFix = - Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true); - - private byte[] dh_p; // 1 to 2^16 - 1 bytes - private byte[] dh_g; // 1 to 2^16 - 1 bytes - private byte[] dh_Ys; // 1 to 2^16 - 1 bytes - - private byte[] signature; - - // protocol version being established using this ServerKeyExchange message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this ServerKeyExchange message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm; - - /* - * Construct from initialized DH key object, for DH_anon - * key exchange. - */ - DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - this.preferableSignatureAlgorithm = null; - - // The DH key has been validated in the constructor of DHCrypt. - setValues(obj); - signature = null; - } - - /* - * Construct from initialized DH key object and the key associated - * with the cert chain which was sent ... for DHE_DSS and DHE_RSA - * key exchange. (Constructor called by server.) - */ - DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte[] clntNonce, - byte[] svrNonce, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm, - ProtocolVersion protocolVersion) throws GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // The DH key has been validated in the constructor of DHCrypt. - setValues(obj); - - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - this.preferableSignatureAlgorithm = null; - if (key.getAlgorithm().equals("DSA")) { - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); - } else { - sig = RSASignature.getInstance(); - } - } - - sig.initSign(key, sr); - updateSignature(sig, clntNonce, svrNonce); - signature = sig.sign(); - } - - /* - * Construct a DH_ServerKeyExchange message from an input - * stream, as if sent from server to client for use with - * DH_anon key exchange - */ - DH_ServerKeyExchange(HandshakeInStream input, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - this.preferableSignatureAlgorithm = null; - - dh_p = input.getBytes16(); - dh_g = input.getBytes16(); - dh_Ys = input.getBytes16(); - KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), - new BigInteger(1, dh_p), - new BigInteger(1, dh_g))); - - signature = null; - } - - /* - * Construct a DH_ServerKeyExchange message from an input stream - * and a certificate, as if sent from server to client for use with - * DHE_DSS or DHE_RSA key exchange. (Called by client.) - */ - DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey, - byte[] clntNonce, byte[] svrNonce, int messageSize, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // read params: ServerDHParams - dh_p = input.getBytes16(); - dh_g = input.getBytes16(); - dh_Ys = input.getBytes16(); - KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), - new BigInteger(1, dh_p), - new BigInteger(1, dh_g))); - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "ServerKeyExchange message: " + - preferableSignatureAlgorithm); - } - } else { - this.preferableSignatureAlgorithm = null; - } - - // read the signature - byte[] signature; - if (dhKeyExchangeFix) { - signature = input.getBytes16(); - } else { - messageSize -= (dh_p.length + 2); - messageSize -= (dh_g.length + 2); - messageSize -= (dh_Ys.length + 2); - - signature = new byte[messageSize]; - input.read(signature); - } - - Signature sig; - String algorithm = publicKey.getAlgorithm(); - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - switch (algorithm) { - case "DSA": - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); - break; - case "RSA": - sig = RSASignature.getInstance(); - break; - default: - throw new SSLKeyException( - "neither an RSA or a DSA key: " + algorithm); - } - } - - sig.initVerify(publicKey); - updateSignature(sig, clntNonce, svrNonce); - - if (sig.verify(signature) == false ) { - throw new SSLKeyException("Server D-H key verification failed"); - } - } - - /* Return the Diffie-Hellman modulus */ - BigInteger getModulus() { - return new BigInteger(1, dh_p); - } - - /* Return the Diffie-Hellman base/generator */ - BigInteger getBase() { - return new BigInteger(1, dh_g); - } - - /* Return the server's Diffie-Hellman public key */ - BigInteger getServerPublicKey() { - return new BigInteger(1, dh_Ys); - } - - /* - * 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); - - tmp = dh_p.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_p); - - tmp = dh_g.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_g); - - tmp = dh_Ys.length; - sig.update((byte)(tmp >> 8)); - sig.update((byte)(tmp & 0x0ff)); - sig.update(dh_Ys); - } - - private void setValues(DHCrypt obj) { - dh_p = toByteArray(obj.getModulus()); - dh_g = toByteArray(obj.getBase()); - dh_Ys = toByteArray(obj.getPublicKey()); - } - - @Override - int messageLength() { - int temp = 6; // overhead for p, g, y(s) values. - - temp += dh_p.length; - temp += dh_g.length; - temp += dh_Ys.length; - - if (signature != null) { - if (protocolVersion.useTLS12PlusSpec()) { - temp += SignatureAndHashAlgorithm.sizeInRecord(); - } - - temp += signature.length; - if (dhKeyExchangeFix) { - temp += 2; - } - } - - return temp; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putBytes16(dh_p); - s.putBytes16(dh_g); - s.putBytes16(dh_Ys); - - if (signature != null) { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - if (dhKeyExchangeFix) { - s.putBytes16(signature); - } else { - s.write(signature); - } - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Diffie-Hellman ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "DH Modulus", dh_p); - Debug.println(s, "DH Base", dh_g); - Debug.println(s, "Server DH Public Key", dh_Ys); - - if (signature == null) { - s.println("Anonymous"); - } else { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - - s.println("Signed with a DSA or RSA public key"); - } - } - } -} - -/* - * ECDH server key exchange message. Sent by the server for ECDHE and ECDH_anon - * ciphersuites to communicate its ephemeral public key (including the - * EC domain parameters). - * - * We support named curves only, no explicitly encoded curves. - */ -static final -class ECDH_ServerKeyExchange extends ServerKeyExchange { - - // constants for ECCurveType - private static final int CURVE_EXPLICIT_PRIME = 1; - private static final int CURVE_EXPLICIT_CHAR2 = 2; - private static final int CURVE_NAMED_CURVE = 3; - - // id of the named group we are using - private int groupId; - - // encoded public point - private byte[] pointBytes; - - // signature bytes (or null if anonymous) - private byte[] signatureBytes; - - // public key object encapsulated in this message - private ECPublicKey publicKey; - - // protocol version being established using this ServerKeyExchange message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this ServerKeyExchange message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm; - - ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey, - byte[] clntNonce, byte[] svrNonce, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm, - ProtocolVersion protocolVersion) - throws SSLHandshakeException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - publicKey = (ECPublicKey)obj.getPublicKey(); - ECParameterSpec params = publicKey.getParams(); - ECPoint point = publicKey.getW(); - pointBytes = JsseJce.encodePoint(point, params.getCurve()); - - NamedGroup namedGroup = NamedGroup.valueOf(params); - if ((namedGroup == null) || (namedGroup.oid == null) ){ - // unlikely - throw new SSLHandshakeException( - "Unnamed EC parameter spec: " + params); - } - groupId = namedGroup.id; - - if (privateKey == null) { - // ECDH_anon - return; - } - - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(privateKey.getAlgorithm()); - } - sig.initSign(privateKey, sr); - - updateSignature(sig, clntNonce, svrNonce); - signatureBytes = sig.sign(); - } - - /* - * Parse an ECDH server key exchange message. - */ - ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey, - byte[] clntNonce, byte[] svrNonce, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) - throws IOException, GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - // read params: ServerECDHParams - int curveType = input.getInt8(); - ECParameterSpec parameters; - // These parsing errors should never occur as we negotiated - // the supported curves during the exchange of the Hello messages. - if (curveType == CURVE_NAMED_CURVE) { - groupId = input.getInt16(); - NamedGroup namedGroup = NamedGroup.valueOf(groupId); - if (namedGroup == null) { - throw new SSLHandshakeException( - "Unknown named group ID: " + groupId); - } - - if (!SupportedGroupsExtension.supports(namedGroup)) { - throw new SSLHandshakeException( - "Unsupported named group: " + namedGroup); - } - - if (namedGroup.oid == null) { - throw new SSLHandshakeException( - "Unknown named EC curve: " + namedGroup); - } - - parameters = JsseJce.getECParameterSpec(namedGroup.oid); - if (parameters == null) { - throw new SSLHandshakeException( - "No supported EC parameter for named group: " + namedGroup); - } - } else { - throw new SSLHandshakeException( - "Unsupported ECCurveType: " + curveType); - } - pointBytes = input.getBytes8(); - - ECPoint point = JsseJce.decodePoint(pointBytes, parameters.getCurve()); - KeyFactory factory = JsseJce.getKeyFactory("EC"); - publicKey = (ECPublicKey)factory.generatePublic( - new ECPublicKeySpec(point, parameters)); - - if (signingKey == null) { - // ECDH_anon - return; - } - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "ServerKeyExchange message: " + - preferableSignatureAlgorithm); - } - } - - // read the signature - signatureBytes = input.getBytes16(); - - // verify the signature - Signature sig; - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(signingKey.getAlgorithm()); - } - sig.initVerify(signingKey); - - updateSignature(sig, clntNonce, svrNonce); - - if (sig.verify(signatureBytes) == false ) { - throw new SSLKeyException( - "Invalid signature on ECDH server key exchange message"); - } - } - - /* - * Get the ephemeral EC public key encapsulated in this message. - */ - ECPublicKey getPublicKey() { - return publicKey; - } - - private static Signature getSignature(String keyAlgorithm) - throws NoSuchAlgorithmException { - switch (keyAlgorithm) { - case "EC": - return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA); - case "RSA": - return RSASignature.getInstance(); - default: - throw new NoSuchAlgorithmException( - "neither an RSA or a EC key : " + keyAlgorithm); - } - } - - private void updateSignature(Signature sig, byte[] clntNonce, - byte[] svrNonce) throws SignatureException { - sig.update(clntNonce); - sig.update(svrNonce); - - sig.update((byte)CURVE_NAMED_CURVE); - sig.update((byte)(groupId >> 8)); - sig.update((byte)groupId); - sig.update((byte)pointBytes.length); - sig.update(pointBytes); - } - - @Override - int messageLength() { - int sigLen = 0; - if (signatureBytes != null) { - sigLen = 2 + signatureBytes.length; - if (protocolVersion.useTLS12PlusSpec()) { - sigLen += SignatureAndHashAlgorithm.sizeInRecord(); - } - } - - return 4 + pointBytes.length + sigLen; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt8(CURVE_NAMED_CURVE); - s.putInt16(groupId); - s.putBytes8(pointBytes); - - if (signatureBytes != null) { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - s.putBytes16(signatureBytes); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** ECDH ServerKeyExchange"); - - if (debug != null && Debug.isOn("verbose")) { - if (signatureBytes == null) { - s.println("Anonymous"); - } else { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - } - - s.println("Server key: " + publicKey); - } - } -} - -static final class DistinguishedName { - - /* - * DER encoded distinguished name. - * TLS requires that its not longer than 65535 bytes. - */ - byte[] name; - - DistinguishedName(HandshakeInStream input) throws IOException { - name = input.getBytes16(); - } - - DistinguishedName(X500Principal dn) { - name = dn.getEncoded(); - } - - X500Principal getX500Principal() throws IOException { - try { - return new X500Principal(name); - } catch (IllegalArgumentException e) { - throw (SSLProtocolException)new SSLProtocolException( - e.getMessage()).initCause(e); - } - } - - int length() { - return 2 + name.length; - } - - void send(HandshakeOutStream output) throws IOException { - output.putBytes16(name); - } - - void print(PrintStream output) throws IOException { - X500Principal principal = new X500Principal(name); - output.println("<" + principal.toString() + ">"); - } -} - -/* - * CertificateRequest ... SERVER --> CLIENT - * - * Authenticated servers may ask clients to authenticate themselves - * in turn, using this message. - * - * Prior to TLS 1.2, the structure of the message is defined as: - * struct { - * ClientCertificateType certificate_types<1..2^8-1>; - * DistinguishedName certificate_authorities<0..2^16-1>; - * } CertificateRequest; - * - * In TLS 1.2, the structure is changed to: - * struct { - * ClientCertificateType certificate_types<1..2^8-1>; - * SignatureAndHashAlgorithm - * supported_signature_algorithms<2^16-1>; - * DistinguishedName certificate_authorities<0..2^16-1>; - * } CertificateRequest; - * - */ -static final -class CertificateRequest extends HandshakeMessage -{ - // enum ClientCertificateType - static final int cct_rsa_sign = 1; - static final int cct_dss_sign = 2; - static final int cct_rsa_fixed_dh = 3; - static final int cct_dss_fixed_dh = 4; - - // The existance of these two values is a bug in the SSL specification. - // They are never used in the protocol. - static final int cct_rsa_ephemeral_dh = 5; - static final int cct_dss_ephemeral_dh = 6; - - // From RFC 4492 (ECC) - static final int cct_ecdsa_sign = 64; - static final int cct_rsa_fixed_ecdh = 65; - static final int cct_ecdsa_fixed_ecdh = 66; - - private static final byte[] TYPES_NO_ECC = { cct_rsa_sign, cct_dss_sign }; - private static final byte[] TYPES_ECC = - { cct_rsa_sign, cct_dss_sign, cct_ecdsa_sign }; - - byte[] types; // 1 to 255 types - DistinguishedName[] authorities; // 3 to 2^16 - 1 - // ... "3" because that's the smallest DER-encoded X500 DN - - // protocol version being established using this CertificateRequest message - ProtocolVersion protocolVersion; - - // supported_signature_algorithms for TLS 1.2 or later - private Collection algorithms; - - // length of supported_signature_algorithms - private int algorithmsLen; - - CertificateRequest(X509Certificate[] ca, KeyExchange keyExchange, - Collection signAlgs, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // always use X500Principal - authorities = new DistinguishedName[ca.length]; - for (int i = 0; i < ca.length; i++) { - X500Principal x500Principal = ca[i].getSubjectX500Principal(); - authorities[i] = new DistinguishedName(x500Principal); - } - // we support RSA, DSS, and ECDSA client authentication and they - // can be used with all ciphersuites. If this changes, the code - // needs to be adapted to take keyExchange into account. - // We only request ECDSA client auth if we have ECC crypto available. - this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; - - // Use supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.useTLS12PlusSpec()) { - if (signAlgs == null || signAlgs.isEmpty()) { - throw new SSLProtocolException( - "No supported signature algorithms"); - } - - algorithms = new ArrayList(signAlgs); - algorithmsLen = - SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); - } else { - algorithms = new ArrayList(); - algorithmsLen = 0; - } - } - - CertificateRequest(HandshakeInStream input, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // Read the certificate_types. - types = input.getBytes8(); - - // Read the supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.useTLS12PlusSpec()) { - algorithmsLen = input.getInt16(); - if (algorithmsLen < 2) { - throw new SSLProtocolException( - "Invalid supported_signature_algorithms field: " + - algorithmsLen); - } - - algorithms = new ArrayList(); - int remains = algorithmsLen; - int sequence = 0; - while (remains > 1) { // needs at least two bytes - int hash = input.getInt8(); // hash algorithm - int signature = input.getInt8(); // signature algorithm - - SignatureAndHashAlgorithm algorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, - ++sequence); - algorithms.add(algorithm); - remains -= 2; // one byte for hash, one byte for signature - } - - if (remains != 0) { - throw new SSLProtocolException( - "Invalid supported_signature_algorithms field. remains: " + - remains); - } - } else { - algorithms = new ArrayList(); - algorithmsLen = 0; - } - - // read the certificate_authorities - int len = input.getInt16(); - ArrayList v = new ArrayList<>(); - while (len >= 3) { - DistinguishedName dn = new DistinguishedName(input); - v.add(dn); - len -= dn.length(); - } - - if (len != 0) { - throw new SSLProtocolException( - "Bad CertificateRequest DN length: " + len); - } - - authorities = v.toArray(new DistinguishedName[v.size()]); - } - - X500Principal[] getAuthorities() throws IOException { - X500Principal[] ret = new X500Principal[authorities.length]; - for (int i = 0; i < authorities.length; i++) { - ret[i] = authorities[i].getX500Principal(); - } - return ret; - } - - Collection getSignAlgorithms() { - return algorithms; - } - - @Override - int messageType() { - return ht_certificate_request; - } - - @Override - int messageLength() { - int len = 1 + types.length + 2; - - if (protocolVersion.useTLS12PlusSpec()) { - len += algorithmsLen + 2; - } - - for (int i = 0; i < authorities.length; i++) { - len += authorities[i].length(); - } - - return len; - } - - @Override - void send(HandshakeOutStream output) throws IOException { - // put certificate_types - output.putBytes8(types); - - // put supported_signature_algorithms - if (protocolVersion.useTLS12PlusSpec()) { - output.putInt16(algorithmsLen); - for (SignatureAndHashAlgorithm algorithm : algorithms) { - output.putInt8(algorithm.getHashValue()); // hash - output.putInt8(algorithm.getSignatureValue()); // signature - } - } - - // put certificate_authorities - int len = 0; - for (int i = 0; i < authorities.length; i++) { - len += authorities[i].length(); - } - - output.putInt16(len); - for (int i = 0; i < authorities.length; i++) { - authorities[i].send(output); - } - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateRequest"); - - if (debug != null && Debug.isOn("verbose")) { - s.print("Cert Types: "); - for (int i = 0; i < types.length; i++) { - switch (types[i]) { - case cct_rsa_sign: - s.print("RSA"); break; - case cct_dss_sign: - s.print("DSS"); break; - case cct_rsa_fixed_dh: - s.print("Fixed DH (RSA sig)"); break; - case cct_dss_fixed_dh: - s.print("Fixed DH (DSS sig)"); break; - case cct_rsa_ephemeral_dh: - s.print("Ephemeral DH (RSA sig)"); break; - case cct_dss_ephemeral_dh: - s.print("Ephemeral DH (DSS sig)"); break; - case cct_ecdsa_sign: - s.print("ECDSA"); break; - case cct_rsa_fixed_ecdh: - s.print("Fixed ECDH (RSA sig)"); break; - case cct_ecdsa_fixed_ecdh: - s.print("Fixed ECDH (ECDSA sig)"); break; - default: - s.print("Type-" + (types[i] & 0xff)); break; - } - if (i != types.length - 1) { - s.print(", "); - } - } - s.println(); - - if (protocolVersion.useTLS12PlusSpec()) { - StringBuilder sb = new StringBuilder(); - boolean opened = false; - for (SignatureAndHashAlgorithm signAlg : algorithms) { - if (opened) { - sb.append(", ").append(signAlg.getAlgorithmName()); - } else { - sb.append(signAlg.getAlgorithmName()); - opened = true; - } - } - s.println("Supported Signature Algorithms: " + sb); - } - - s.println("Cert Authorities:"); - if (authorities.length == 0) { - s.println(""); - } else { - for (int i = 0; i < authorities.length; i++) { - authorities[i].print(s); - } - } - } - } -} - - -/* - * ServerHelloDone ... SERVER --> CLIENT - * - * When server's done sending its messages in response to the client's - * "hello" (e.g. its own hello, certificate, key exchange message, perhaps - * client certificate request) it sends this message to flag that it's - * done that part of the handshake. - */ -static final -class ServerHelloDone extends HandshakeMessage -{ - @Override - int messageType() { return ht_server_hello_done; } - - ServerHelloDone() { } - - ServerHelloDone(HandshakeInStream input) - { - // nothing to do - } - - @Override - int messageLength() - { - return 0; - } - - @Override - void send(HandshakeOutStream s) throws IOException - { - // nothing to send - } - - @Override - void print(PrintStream s) throws IOException - { - s.println("*** ServerHelloDone"); - } -} - - -/* - * CertificateVerify ... CLIENT --> SERVER - * - * Sent after client sends signature-capable certificates (e.g. not - * Diffie-Hellman) to verify. - */ -static final class CertificateVerify extends HandshakeMessage { - - // the signature bytes - private byte[] signature; - - // protocol version being established using this CertificateVerify message - ProtocolVersion protocolVersion; - - // the preferable signature algorithm used by this CertificateVerify message - private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; - - /* - * Create an RSA or DSA signed certificate verify message. - */ - CertificateVerify(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, PrivateKey privateKey, - SecretKey masterSecret, SecureRandom sr, - SignatureAndHashAlgorithm signAlgorithm) - throws GeneralSecurityException { - - this.protocolVersion = protocolVersion; - - String algorithm = privateKey.getAlgorithm(); - Signature sig = null; - if (protocolVersion.useTLS12PlusSpec()) { - this.preferableSignatureAlgorithm = signAlgorithm; - sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(protocolVersion, algorithm); - } - sig.initSign(privateKey, sr); - updateSignature(sig, protocolVersion, handshakeHash, algorithm, - masterSecret); - signature = sig.sign(); - } - - // - // Unmarshal the signed data from the input stream. - // - CertificateVerify(HandshakeInStream input, - Collection localSupportedSignAlgs, - ProtocolVersion protocolVersion) throws IOException { - - this.protocolVersion = protocolVersion; - - // read the signature and hash algorithm - if (protocolVersion.useTLS12PlusSpec()) { - int hashAlg = input.getInt8(); // hash algorithm - int signAlg = input.getInt8(); // signature algorithm - - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0); - - // Is it a local supported signature algorithm? - if (!localSupportedSignAlgs.contains( - preferableSignatureAlgorithm)) { - throw new SSLHandshakeException( - "Unsupported SignatureAndHashAlgorithm in " + - "CertificateVerify message: " + preferableSignatureAlgorithm); - } - } - - // read the signature - signature = input.getBytes16(); - } - - /* - * Get the preferable signature algorithm used by this message - */ - SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() { - return preferableSignatureAlgorithm; - } - - /* - * Verify a certificate verify message. Return the result of verification, - * if there is a problem throw a GeneralSecurityException. - */ - boolean verify(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, PublicKey publicKey, - SecretKey masterSecret) throws GeneralSecurityException { - String algorithm = publicKey.getAlgorithm(); - Signature sig = null; - if (protocolVersion.useTLS12PlusSpec()) { - sig = JsseJce.getSignature( - preferableSignatureAlgorithm.getAlgorithmName()); - } else { - sig = getSignature(protocolVersion, algorithm); - } - sig.initVerify(publicKey); - updateSignature(sig, protocolVersion, handshakeHash, algorithm, - masterSecret); - return sig.verify(signature); - } - - /* - * Get the Signature object appropriate for verification using the - * given signature algorithm and protocol version. - */ - private static Signature getSignature(ProtocolVersion protocolVersion, - String algorithm) throws GeneralSecurityException { - switch (algorithm) { - case "RSA": - return RSASignature.getInternalInstance(); - case "DSA": - return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); - case "EC": - return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); - default: - throw new SignatureException("Unrecognized algorithm: " - + algorithm); - } - } - - /* - * Update the Signature with the data appropriate for the given - * signature algorithm and protocol version so that the object is - * ready for signing or verifying. - */ - private static void updateSignature(Signature sig, - ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) - throws SignatureException { - - if (algorithm.equals("RSA")) { - if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - - if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 - updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); - } - - // The signature must be an instance of RSASignature, need - // to use these hashes directly. - RSASignature.setHashes(sig, md5Clone, shaClone); - } else { // TLS1.2+ - sig.update(handshakeHash.getAllHandshakeMessages()); - } - } else { // DSA, ECDSA - if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- - MessageDigest shaClone = handshakeHash.getSHAClone(); - - if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); - } - - sig.update(shaClone.digest()); - } else { // TLS1.2+ - sig.update(handshakeHash.getAllHandshakeMessages()); - } - } - } - - /* - * Update the MessageDigest for SSLv3 certificate verify or finished - * message calculation. The digest must already have been updated with - * all preceding handshake messages. - * Used by the Finished class as well. - */ - private static void updateDigest(MessageDigest md, - byte[] pad1, byte[] pad2, - SecretKey masterSecret) { - // Digest the key bytes if available. - // Otherwise (sensitive key), try digesting the key directly. - // That is currently only implemented in SunPKCS11 using a private - // reflection API, so we avoid that if possible. - byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) - ? masterSecret.getEncoded() : null; - if (keyBytes != null) { - md.update(keyBytes); - } else { - digestKey(md, masterSecret); - } - md.update(pad1); - byte[] temp = md.digest(); - - if (keyBytes != null) { - md.update(keyBytes); - } else { - digestKey(md, masterSecret); - } - md.update(pad2); - md.update(temp); - } - - private static void digestKey(MessageDigest md, SecretKey key) { - try { - if (md instanceof MessageDigestSpi2) { - ((MessageDigestSpi2)md).engineUpdate(key); - } else { - throw new Exception( - "Digest does not support implUpdate(SecretKey)"); - } - } catch (Exception e) { - throw new RuntimeException( - "Could not obtain encoded key and " - + "MessageDigest cannot digest key", e); - } - } - - @Override - int messageType() { - return ht_certificate_verify; - } - - @Override - int messageLength() { - int temp = 2; - - if (protocolVersion.useTLS12PlusSpec()) { - temp += SignatureAndHashAlgorithm.sizeInRecord(); - } - - return temp + signature.length; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - if (protocolVersion.useTLS12PlusSpec()) { - s.putInt8(preferableSignatureAlgorithm.getHashValue()); - s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); - } - - s.putBytes16(signature); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** CertificateVerify"); - - if (debug != null && Debug.isOn("verbose")) { - if (protocolVersion.useTLS12PlusSpec()) { - s.println("Signature Algorithm " + - preferableSignatureAlgorithm.getAlgorithmName()); - } - } - } -} - - -/* - * FINISHED ... sent by both CLIENT and SERVER - * - * This is the FINISHED message as defined in the SSL and TLS protocols. - * Both protocols define this handshake message slightly differently. - * This class supports both formats. - * - * When handshaking is finished, each side sends a "change_cipher_spec" - * record, then immediately sends a "finished" handshake message prepared - * according to the newly adopted cipher spec. - * - * NOTE that until this is sent, no application data may be passed, unless - * some non-default cipher suite has already been set up on this connection - * connection (e.g. a previous handshake arranged one). - */ -static final class Finished extends HandshakeMessage { - - // constant for a Finished message sent by the client - static final int CLIENT = 1; - - // constant for a Finished message sent by the server - static final int SERVER = 2; - - // enum Sender: "CLNT" and "SRVR" - private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; - private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; - - /* - * Contents of the finished message ("checksum"). For TLS, it - * is 12 bytes long, for SSLv3 36 bytes. - */ - private byte[] verifyData; - - /* - * Current cipher suite we are negotiating. TLS 1.2 has - * ciphersuite-defined PRF algorithms. - */ - private ProtocolVersion protocolVersion; - private CipherSuite cipherSuite; - - /* - * Create a finished message to send to the remote peer. - */ - Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, - int sender, SecretKey master, CipherSuite cipherSuite) { - this.protocolVersion = protocolVersion; - this.cipherSuite = cipherSuite; - verifyData = getFinished(handshakeHash, sender, master); - } - - /* - * Constructor that reads FINISHED message from stream. - */ - Finished(ProtocolVersion protocolVersion, HandshakeInStream input, - CipherSuite cipherSuite) throws IOException { - this.protocolVersion = protocolVersion; - this.cipherSuite = cipherSuite; - int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36; - verifyData = new byte[msgLen]; - input.read(verifyData); - } - - /* - * Verify that the hashes here are what would have been produced - * according to a given set of inputs. This is used to ensure that - * both client and server are fully in sync, and that the handshake - * computations have been successful. - */ - boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) { - byte[] myFinished = getFinished(handshakeHash, sender, master); - return MessageDigest.isEqual(myFinished, verifyData); - } - - /* - * Perform the actual finished message calculation. - */ - private byte[] getFinished(HandshakeHash handshakeHash, - int sender, SecretKey masterKey) { - byte[] sslLabel; - String tlsLabel; - if (sender == CLIENT) { - sslLabel = SSL_CLIENT; - tlsLabel = "client finished"; - } else if (sender == SERVER) { - sslLabel = SSL_SERVER; - tlsLabel = "server finished"; - } else { - throw new RuntimeException("Invalid sender: " + sender); - } - - if (protocolVersion.useTLS10PlusSpec()) { - // TLS 1.0+ - try { - byte[] seed; - String prfAlg; - PRF prf; - - // Get the KeyGenerator alg and calculate the seed. - if (protocolVersion.useTLS12PlusSpec()) { - // TLS 1.2+ or DTLS 1.2+ - seed = handshakeHash.getFinishedHash(); - - prfAlg = "SunTls12Prf"; - prf = cipherSuite.prfAlg; - } else { - // TLS 1.0/1.1, DTLS 1.0 - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - seed = new byte[36]; - md5Clone.digest(seed, 0, 16); - shaClone.digest(seed, 16, 20); - - prfAlg = "SunTlsPrf"; - prf = P_NONE; - } - - String prfHashAlg = prf.getPRFHashAlg(); - int prfHashLength = prf.getPRFHashLength(); - int prfBlockSize = prf.getPRFBlockSize(); - - /* - * RFC 5246/7.4.9 says that finished messages can - * be ciphersuite-specific in both length/PRF hash - * algorithm. If we ever run across a different - * length, this call will need to be updated. - */ - @SuppressWarnings("deprecation") - TlsPrfParameterSpec spec = new TlsPrfParameterSpec( - masterKey, tlsLabel, seed, 12, - prfHashAlg, prfHashLength, prfBlockSize); - - KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); - kg.init(spec); - SecretKey prfKey = kg.generateKey(); - if ("RAW".equals(prfKey.getFormat()) == false) { - throw new ProviderException( - "Invalid PRF output, format must be RAW. " + - "Format received: " + prfKey.getFormat()); - } - byte[] finished = prfKey.getEncoded(); - return finished; - } catch (GeneralSecurityException e) { - throw new RuntimeException("PRF failed", e); - } - } else { - // SSLv3 - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey); - updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey); - byte[] finished = new byte[36]; - try { - md5Clone.digest(finished, 0, 16); - shaClone.digest(finished, 16, 20); - } catch (DigestException e) { - // cannot occur - throw new RuntimeException("Digest failed", e); - } - return finished; - } - } - - /* - * Update the MessageDigest for SSLv3 finished message calculation. - * The digest must already have been updated with all preceding handshake - * messages. This operation is almost identical to the certificate verify - * hash, reuse that code. - */ - private static void updateDigest(MessageDigest md, byte[] sender, - byte[] pad1, byte[] pad2, SecretKey masterSecret) { - md.update(sender); - CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); - } - - // get the verify_data of the finished message - byte[] getVerifyData() { - return verifyData; - } - - @Override - int messageType() { return ht_finished; } - - @Override - int messageLength() { - return verifyData.length; - } - - @Override - void send(HandshakeOutStream out) throws IOException { - out.write(verifyData); - } - - @Override - void print(PrintStream s) throws IOException { - s.println("*** Finished"); - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "verify_data", verifyData); - s.println("***"); - } - } -} - -// -// END of nested classes -// - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ 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 @@ @Override public void flush() throws IOException { - outputRecord.flush(); + if (outputRecord != null) { + outputRecord.flush(); + } } // @@ -106,6 +109,13 @@ 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. diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeProducer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeProducer.java Mon Jun 25 13:41:39 2018 -0700 @@ -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; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 upcomingStates; - private LinkedList alternatives; - - private boolean isDTLS; - - private static final boolean debugIsOn; - - private static final HashMap 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 check(byte handshakeType) throws SSLProtocolException { - List 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 + "]"); - } -} - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Handshaker.java --- a/src/java.base/share/classes/sun/security/ssl/Handshaker.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1623 +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.io.*; -import java.util.*; -import java.security.*; -import java.nio.ByteBuffer; -import java.util.function.BiFunction; - -import javax.crypto.*; -import javax.crypto.spec.*; - -import javax.net.ssl.*; -import sun.security.util.HexDumpEncoder; - -import sun.security.internal.spec.*; -import sun.security.internal.interfaces.TlsMasterSecret; - -import sun.security.ssl.HandshakeMessage.*; -import sun.security.ssl.CipherSuite.*; - -import static sun.security.ssl.CipherSuite.PRF.*; -import static sun.security.ssl.CipherSuite.CipherType.*; -import static sun.security.ssl.NamedGroupType.*; - -/** - * Handshaker ... processes handshake records from an SSL V3.0 - * data stream, handling all the details of the handshake protocol. - * - * Note that the real protocol work is done in two subclasses, the base - * class just provides the control flow and key generation framework. - * - * @author David Brownell - */ -abstract class Handshaker { - - // protocol version being established using this Handshaker - ProtocolVersion protocolVersion; - - // the currently active protocol version during a renegotiation - ProtocolVersion activeProtocolVersion; - - // security parameters for secure renegotiation. - boolean secureRenegotiation; - byte[] clientVerifyData; - byte[] serverVerifyData; - - // Is it an initial negotiation or a renegotiation? - boolean isInitialHandshake; - - // List of enabled protocols - private ProtocolList enabledProtocols; - - // List of enabled CipherSuites - private CipherSuiteList enabledCipherSuites; - - // The endpoint identification protocol - String identificationProtocol; - - // The cryptographic algorithm constraints - AlgorithmConstraints algorithmConstraints = null; - - // Local supported signature and algorithms - private Collection localSupportedSignAlgs; - - // Peer supported signature and algorithms - Collection peerSupportedSignAlgs; - - /* - * List of active protocols - * - * Active protocols is a subset of enabled protocols, and will - * contain only those protocols that have vaild cipher suites - * enabled. - */ - private ProtocolList activeProtocols; - - /* - * List of active cipher suites - * - * Active cipher suites is a subset of enabled cipher suites, and will - * contain only those cipher suites available for the active protocols. - */ - private CipherSuiteList activeCipherSuites; - - // The server name indication and matchers - List serverNames = Collections.emptyList(); - Collection sniMatchers = Collections.emptyList(); - - // List of local ApplicationProtocols - String[] localApl = null; - - // Negotiated ALPN value - String applicationProtocol = null; - - // Application protocol callback function (for SSLEngine) - BiFunction,String> - appProtocolSelectorSSLEngine = null; - - // Application protocol callback function (for SSLSocket) - BiFunction,String> - appProtocolSelectorSSLSocket = null; - - // The maximum expected network packet size for SSL/TLS/DTLS records. - int maximumPacketSize = 0; - - private boolean isClient; - private boolean needCertVerify; - - SSLSocketImpl conn = null; - SSLEngineImpl engine = null; - - HandshakeHash handshakeHash; - HandshakeInStream input; - HandshakeOutStream output; - SSLContextImpl sslContext; - RandomCookie clnt_random, svr_random; - SSLSessionImpl session; - - HandshakeStateManager handshakeState; - boolean clientHelloDelivered; - boolean serverHelloRequested; - boolean handshakeActivated; - boolean handshakeFinished; - - // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL - CipherSuite cipherSuite; - - // current key exchange. Never null, initially K_NULL - KeyExchange keyExchange; - - // True if this session is being resumed (fast handshake) - boolean resumingSession; - - // True if it's OK to start a new SSL session - boolean enableNewSession; - - // Whether local cipher suites preference should be honored during - // handshaking? - // - // Note that in this provider, this option only applies to server side. - // Local cipher suites preference is always honored in client side in - // this provider. - boolean preferLocalCipherSuites = false; - - // Temporary storage for the individual keys. Set by - // calculateConnectionKeys() and cleared once the ciphers are - // activated. - private SecretKey clntWriteKey, svrWriteKey; - private IvParameterSpec clntWriteIV, svrWriteIV; - private SecretKey clntMacSecret, svrMacSecret; - - /* - * Delegated task subsystem data structures. - * - * If thrown is set, we need to propagate this back immediately - * on entry into processMessage(). - * - * Data is protected by the SSLEngine.this lock. - */ - private volatile boolean taskDelegated = false; - private volatile DelegatedTask delegatedTask = null; - private volatile Exception thrown = null; - - // Could probably use a java.util.concurrent.atomic.AtomicReference - // here instead of using this lock. Consider changing. - private Object thrownLock = new Object(); - - /* Class and subclass dynamic debugging support */ - static final Debug debug = Debug.getInstance("ssl"); - - // By default, disable the unsafe legacy session renegotiation - static final boolean allowUnsafeRenegotiation = Debug.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 = Debug.getBooleanProperty( - "sun.security.ssl.allowLegacyHelloMessages", true); - - // To prevent the TLS renegotiation issues, by setting system property - // "jdk.tls.rejectClientInitiatedRenegotiation" to true, applications in - // server side can disable all client initiated SSL renegotiations - // regardless of the support of TLS protocols. - // - // By default, allow client initiated renegotiations. - static final boolean rejectClientInitiatedRenego = - Debug.getBooleanProperty( - "jdk.tls.rejectClientInitiatedRenegotiation", false); - - // To switch off the extended_master_secret extension. - static final boolean useExtendedMasterSecret; - - // Allow session resumption without Extended Master Secret extension. - static final boolean allowLegacyResumption = - Debug.getBooleanProperty("jdk.tls.allowLegacyResumption", true); - - // Allow full handshake without Extended Master Secret extension. - static final boolean allowLegacyMasterSecret = - Debug.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true); - - // Is it requested to use extended master secret extension? - boolean requestedToUseEMS = false; - - // need to dispose the object when it is invalidated - boolean invalidated; - - /* - * Is this an instance for Datagram Transport Layer Security (DTLS)? - */ - final boolean isDTLS; - - // Is the extended_master_secret extension supported? - static { - boolean supportExtendedMasterSecret = true; - try { - KeyGenerator kg = - JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret"); - } catch (NoSuchAlgorithmException nae) { - supportExtendedMasterSecret = false; - } - - if (supportExtendedMasterSecret) { - useExtendedMasterSecret = Debug.getBooleanProperty( - "jdk.tls.useExtendedMasterSecret", true); - } else { - useExtendedMasterSecret = false; - } - } - - Handshaker(SSLSocketImpl c, SSLContextImpl context, - ProtocolList enabledProtocols, boolean needCertVerify, - boolean isClient, ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { - this.conn = c; - this.isDTLS = false; - init(context, enabledProtocols, needCertVerify, isClient, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); - } - - Handshaker(SSLEngineImpl engine, SSLContextImpl context, - ProtocolList enabledProtocols, boolean needCertVerify, - boolean isClient, ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData, - boolean isDTLS) { - this.engine = engine; - this.isDTLS = isDTLS; - init(context, enabledProtocols, needCertVerify, isClient, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); - } - - private void init(SSLContextImpl context, ProtocolList enabledProtocols, - boolean needCertVerify, boolean isClient, - ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { - - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Allow unsafe renegotiation: " + allowUnsafeRenegotiation + - "\nAllow legacy hello messages: " + allowLegacyHelloMessages + - "\nIs initial handshake: " + isInitialHandshake + - "\nIs secure renegotiation: " + secureRenegotiation); - } - - this.sslContext = context; - this.isClient = isClient; - this.needCertVerify = needCertVerify; - this.activeProtocolVersion = activeProtocolVersion; - this.isInitialHandshake = isInitialHandshake; - this.secureRenegotiation = secureRenegotiation; - this.clientVerifyData = clientVerifyData; - this.serverVerifyData = serverVerifyData; - this.enableNewSession = true; - this.invalidated = false; - this.handshakeState = new HandshakeStateManager(isDTLS); - this.clientHelloDelivered = false; - this.serverHelloRequested = false; - this.handshakeActivated = false; - this.handshakeFinished = false; - - setCipherSuite(CipherSuite.C_NULL); - setEnabledProtocols(enabledProtocols); - - if (conn != null) { - algorithmConstraints = new SSLAlgorithmConstraints(conn, true); - } else { // engine != null - algorithmConstraints = new SSLAlgorithmConstraints(engine, true); - } - } - - /* - * Reroutes calls to the SSLSocket or SSLEngine (*SE). - * - * We could have also done it by extra classes - * and letting them override, but this seemed much - * less involved. - */ - void fatalSE(byte b, String diagnostic) throws IOException { - fatalSE(b, diagnostic, null); - } - - void fatalSE(byte b, Throwable cause) throws IOException { - fatalSE(b, null, cause); - } - - void fatalSE(byte b, String diagnostic, Throwable cause) - throws IOException { - if (conn != null) { - conn.fatal(b, diagnostic, cause); - } else { - engine.fatal(b, diagnostic, cause); - } - } - - void warningSE(byte b) { - if (conn != null) { - conn.warning(b); - } else { - engine.warning(b); - } - } - - // ONLY used by ClientHandshaker to setup the peer host in SSLSession. - String getHostSE() { - if (conn != null) { - return conn.getHost(); - } else { - return engine.getPeerHost(); - } - } - - // ONLY used by ServerHandshaker to setup the peer host in SSLSession. - String getHostAddressSE() { - if (conn != null) { - return conn.getInetAddress().getHostAddress(); - } else { - /* - * This is for caching only, doesn't matter that's is really - * a hostname. The main thing is that it doesn't do - * a reverse DNS lookup, potentially slowing things down. - */ - return engine.getPeerHost(); - } - } - - int getPortSE() { - if (conn != null) { - return conn.getPort(); - } else { - return engine.getPeerPort(); - } - } - - int getLocalPortSE() { - if (conn != null) { - return conn.getLocalPort(); - } else { - return -1; - } - } - - AccessControlContext getAccSE() { - if (conn != null) { - return conn.getAcc(); - } else { - return engine.getAcc(); - } - } - - String getEndpointIdentificationAlgorithmSE() { - SSLParameters paras; - if (conn != null) { - paras = conn.getSSLParameters(); - } else { - paras = engine.getSSLParameters(); - } - - return paras.getEndpointIdentificationAlgorithm(); - } - - private void setVersionSE(ProtocolVersion protocolVersion) { - if (conn != null) { - conn.setVersion(protocolVersion); - } else { - engine.setVersion(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.protocolVersion = protocolVersion; - setVersionSE(protocolVersion); - } - - /** - * Set the enabled protocols. Called from the constructor or - * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the - * handshake is not yet in progress). - */ - void setEnabledProtocols(ProtocolList enabledProtocols) { - activeCipherSuites = null; - activeProtocols = null; - - this.enabledProtocols = enabledProtocols; - } - - /** - * Set the enabled cipher suites. Called from - * SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites() (if the - * handshake is not yet in progress). - */ - void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) { - activeCipherSuites = null; - activeProtocols = null; - this.enabledCipherSuites = enabledCipherSuites; - } - - /** - * Set the algorithm constraints. Called from the constructor or - * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the - * handshake is not yet in progress). - */ - void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { - activeCipherSuites = null; - activeProtocols = null; - - this.algorithmConstraints = - new SSLAlgorithmConstraints(algorithmConstraints); - this.localSupportedSignAlgs = null; - } - - Collection getLocalSupportedSignAlgs() { - if (localSupportedSignAlgs == null) { - localSupportedSignAlgs = - SignatureAndHashAlgorithm.getSupportedAlgorithms( - algorithmConstraints); - } - - return localSupportedSignAlgs; - } - - void setPeerSupportedSignAlgs( - Collection algorithms) { - peerSupportedSignAlgs = - new ArrayList(algorithms); - } - - Collection getPeerSupportedSignAlgs() { - return peerSupportedSignAlgs; - } - - - /** - * Set the identification protocol. Called from the constructor or - * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the - * handshake is not yet in progress). - */ - void setIdentificationProtocol(String protocol) { - this.identificationProtocol = protocol; - } - - /** - * Sets the server name indication of the handshake. - */ - void setSNIServerNames(List serverNames) { - // The serverNames parameter is unmodifiable. - this.serverNames = serverNames; - } - - /** - * Sets the server name matchers of the handshaking. - */ - void setSNIMatchers(Collection sniMatchers) { - // The sniMatchers parameter is unmodifiable. - this.sniMatchers = sniMatchers; - } - - /** - * Sets the maximum packet size of the handshaking. - */ - void setMaximumPacketSize(int maximumPacketSize) { - this.maximumPacketSize = maximumPacketSize; - } - - /** - * Sets the Application Protocol list. - */ - void setApplicationProtocols(String[] apl) { - this.localApl = apl; - } - - /** - * Gets the "negotiated" ALPN value. - */ - String getHandshakeApplicationProtocol() { - return applicationProtocol; - } - - /** - * Sets the Application Protocol selector function for SSLEngine. - */ - void setApplicationProtocolSelectorSSLEngine( - BiFunction,String> selector) { - this.appProtocolSelectorSSLEngine = selector; - } - - /** - * Sets the Application Protocol selector function for SSLSocket. - */ - void setApplicationProtocolSelectorSSLSocket( - BiFunction,String> selector) { - this.appProtocolSelectorSSLSocket = selector; - } - - /** - * Sets the cipher suites preference. - */ - void setUseCipherSuitesOrder(boolean on) { - this.preferLocalCipherSuites = on; - } - - /** - * Prior to handshaking, activate the handshake and initialize the version, - * input stream and output stream. - */ - void activate(ProtocolVersion helloVersion) throws IOException { - if (activeProtocols == null) { - activeProtocols = getActiveProtocols(); - } - - if (activeProtocols.collection().isEmpty() || - activeProtocols.max.v == ProtocolVersion.NONE.v) { - throw new SSLHandshakeException( - "No appropriate protocol (protocol is disabled or " + - "cipher suites are inappropriate)"); - } - - if (activeCipherSuites == null) { - activeCipherSuites = getActiveCipherSuites(); - } - - if (activeCipherSuites.collection().isEmpty()) { - throw new SSLHandshakeException("No appropriate cipher suite"); - } - - // temporary protocol version until the actual protocol version - // is negotiated in the Hello exchange. This affects the record - // version we sent with the ClientHello. - if (!isInitialHandshake) { - protocolVersion = activeProtocolVersion; - } else { - protocolVersion = activeProtocols.max; - } - - if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) { - helloVersion = activeProtocols.helloVersion; - } - - // We accumulate digests of the handshake messages so that - // we can read/write CertificateVerify and Finished messages, - // getting assurance against some particular active attacks. - handshakeHash = new HandshakeHash(needCertVerify); - - // Generate handshake input/output stream. - if (conn != null) { - input = new HandshakeInStream(); - output = new HandshakeOutStream(conn.outputRecord); - - conn.inputRecord.setHandshakeHash(handshakeHash); - conn.inputRecord.setHelloVersion(helloVersion); - - conn.outputRecord.setHandshakeHash(handshakeHash); - conn.outputRecord.setHelloVersion(helloVersion); - conn.outputRecord.setVersion(protocolVersion); - } else if (engine != null) { - input = new HandshakeInStream(); - output = new HandshakeOutStream(engine.outputRecord); - - engine.inputRecord.setHandshakeHash(handshakeHash); - engine.inputRecord.setHelloVersion(helloVersion); - - engine.outputRecord.setHandshakeHash(handshakeHash); - engine.outputRecord.setHelloVersion(helloVersion); - engine.outputRecord.setVersion(protocolVersion); - } - - handshakeActivated = true; - } - - /** - * Set cipherSuite and keyExchange to the given CipherSuite. - * Does not perform any verification that this is a valid selection, - * this must be done before calling this method. - */ - void setCipherSuite(CipherSuite s) { - this.cipherSuite = s; - this.keyExchange = s.keyExchange; - } - - /** - * Check if the given ciphersuite is enabled and available within the - * current active cipher suites. - * - * Does not check if the required server certificates are available. - */ - boolean isNegotiable(CipherSuite s) { - if (activeCipherSuites == null) { - activeCipherSuites = getActiveCipherSuites(); - } - - return isNegotiable(activeCipherSuites, s); - } - - /** - * Check if the given ciphersuite is enabled and available within the - * proposed cipher suite list. - * - * Does not check if the required server certificates are available. - */ - static final boolean isNegotiable(CipherSuiteList proposed, CipherSuite s) { - return proposed.contains(s) && s.isNegotiable(); - } - - /** - * Check if the given protocol version is enabled and available. - */ - boolean isNegotiable(ProtocolVersion protocolVersion) { - if (activeProtocols == null) { - activeProtocols = getActiveProtocols(); - } - - return activeProtocols.contains(protocolVersion); - } - - /** - * Select a protocol version from the list. Called from - * ServerHandshaker to negotiate protocol version. - * - * Return the lower of the protocol version suggested in the - * clien hello and the highest supported by the server. - */ - ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { - if (activeProtocols == null) { - activeProtocols = getActiveProtocols(); - } - - return activeProtocols.selectProtocolVersion(protocolVersion); - } - - /** - * Get the active cipher suites. - * - * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, - * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT - * negotiate these cipher suites in TLS 1.1 or later mode. - * - * Therefore, when the active protocols only include TLS 1.1 or later, - * the client cannot request to negotiate those obsoleted cipher - * suites. That is, the obsoleted suites should not be included in the - * client hello. So we need to create a subset of the enabled cipher - * suites, the active cipher suites, which does not contain obsoleted - * cipher suites of the minimum active protocol. - * - * Return empty list instead of null if no active cipher suites. - */ - CipherSuiteList getActiveCipherSuites() { - if (activeCipherSuites == null) { - if (activeProtocols == null) { - activeProtocols = getActiveProtocols(); - } - - ArrayList suites = new ArrayList<>(); - if (!(activeProtocols.collection().isEmpty()) && - activeProtocols.min.v != ProtocolVersion.NONE.v) { - Map cachedStatus = - new EnumMap<>(NamedGroupType.class); - for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.isAvailable() && - (!activeProtocols.min.obsoletes(suite)) && - activeProtocols.max.supports(suite)) { - if (isActivatable(suite, cachedStatus)) { - suites.add(suite); - } - } else if (debug != null && Debug.isOn("verbose")) { - if (activeProtocols.min.obsoletes(suite)) { - System.out.println( - "Ignoring obsoleted cipher suite: " + suite); - } else { - System.out.println( - "Ignoring unsupported cipher suite: " + suite); - } - } - } - } - activeCipherSuites = new CipherSuiteList(suites); - } - - return activeCipherSuites; - } - - /* - * Get the active protocol versions. - * - * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, - * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT - * negotiate these cipher suites in TLS 1.1 or later mode. - * - * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the - * only enabled cipher suite, the client cannot request TLS 1.1 or - * later, even though TLS 1.1 or later is enabled. We need to create a - * subset of the enabled protocols, called the active protocols, which - * contains protocols appropriate to the list of enabled Ciphersuites. - * - * Return empty list instead of null if no active protocol versions. - */ - ProtocolList getActiveProtocols() { - if (activeProtocols == null) { - boolean enabledSSL20Hello = false; - boolean checkedCurves = false; - boolean hasCurves = false; - ArrayList protocols = new ArrayList<>(4); - for (ProtocolVersion protocol : enabledProtocols.collection()) { - // Need not to check the SSL20Hello protocol. - if (protocol.v == ProtocolVersion.SSL20Hello.v) { - enabledSSL20Hello = true; - continue; - } - - if (!algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - protocol.name, null)) { - if (debug != null && Debug.isOn("verbose")) { - System.out.println( - "Ignoring disabled protocol: " + protocol); - } - - continue; - } - - boolean found = false; - Map cachedStatus = - new EnumMap<>(NamedGroupType.class); - for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.isAvailable() && (!protocol.obsoletes(suite)) && - protocol.supports(suite)) { - if (isActivatable(suite, cachedStatus)) { - protocols.add(protocol); - found = true; - break; - } - } else if (debug != null && Debug.isOn("verbose")) { - System.out.println( - "Ignoring unsupported cipher suite: " + suite + - " for " + protocol); - } - } - - if (!found && (debug != null) && Debug.isOn("handshake")) { - System.out.println( - "No available cipher suite for " + protocol); - } - } - - if (!protocols.isEmpty() && enabledSSL20Hello) { - protocols.add(ProtocolVersion.SSL20Hello); - } - - activeProtocols = new ProtocolList(protocols); - } - - return activeProtocols; - } - - private boolean isActivatable(CipherSuite suite, - Map cachedStatus) { - - if (algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { - boolean available = true; - NamedGroupType groupType = suite.keyExchange.groupType; - if (groupType != NAMED_GROUP_NONE) { - Boolean checkedStatus = cachedStatus.get(groupType); - if (checkedStatus == null) { - available = SupportedGroupsExtension.isActivatable( - algorithmConstraints, groupType); - cachedStatus.put(groupType, available); - - if (!available && debug != null && Debug.isOn("verbose")) { - System.out.println("No activated named group"); - } - } else { - available = checkedStatus.booleanValue(); - } - - if (!available && debug != null && Debug.isOn("verbose")) { - System.out.println( - "No active named group, ignore " + suite); - } - - return available; - } else { - return true; - } - } else if (debug != null && Debug.isOn("verbose")) { - System.out.println("Ignoring disabled cipher suite: " + suite); - } - - return false; - } - - /** - * As long as handshaking has not activated, we can - * change whether session creations are allowed. - * - * Callers should do their own checking if handshaking - * has activated. - */ - void setEnableSessionCreation(boolean newSessions) { - enableNewSession = newSessions; - } - - /** - * Create a new read cipher and return it to caller. - */ - CipherBox newReadCipher() throws NoSuchAlgorithmException { - BulkCipher cipher = cipherSuite.cipher; - CipherBox box; - if (isClient) { - box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, - sslContext.getSecureRandom(), false); - svrWriteKey = null; - svrWriteIV = null; - } else { - box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, - sslContext.getSecureRandom(), false); - clntWriteKey = null; - clntWriteIV = null; - } - return box; - } - - /** - * Create a new write cipher and return it to caller. - */ - CipherBox newWriteCipher() throws NoSuchAlgorithmException { - BulkCipher cipher = cipherSuite.cipher; - CipherBox box; - if (isClient) { - box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, - sslContext.getSecureRandom(), true); - clntWriteKey = null; - clntWriteIV = null; - } else { - box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, - sslContext.getSecureRandom(), true); - svrWriteKey = null; - svrWriteIV = null; - } - return box; - } - - /** - * Create a new read MAC and return it to caller. - */ - Authenticator newReadAuthenticator() - throws NoSuchAlgorithmException, InvalidKeyException { - - Authenticator authenticator = null; - if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { - authenticator = new Authenticator(protocolVersion); - } else { - MacAlg macAlg = cipherSuite.macAlg; - if (isClient) { - authenticator = macAlg.newMac(protocolVersion, svrMacSecret); - svrMacSecret = null; - } else { - authenticator = macAlg.newMac(protocolVersion, clntMacSecret); - clntMacSecret = null; - } - } - - return authenticator; - } - - /** - * Create a new write MAC and return it to caller. - */ - Authenticator newWriteAuthenticator() - throws NoSuchAlgorithmException, InvalidKeyException { - - Authenticator authenticator = null; - if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { - authenticator = new Authenticator(protocolVersion); - } else { - MacAlg macAlg = cipherSuite.macAlg; - if (isClient) { - authenticator = macAlg.newMac(protocolVersion, clntMacSecret); - clntMacSecret = null; - } else { - authenticator = macAlg.newMac(protocolVersion, svrMacSecret); - svrMacSecret = null; - } - } - - return authenticator; - } - - /* - * Returns true iff the handshake sequence is done, so that - * this freshly created session can become the current one. - */ - boolean isDone() { - return started() && handshakeState.isEmpty() && handshakeFinished; - } - - - /* - * Returns the session which was created through this - * handshake sequence ... should be called after isDone() - * returns true. - */ - SSLSessionImpl getSession() { - return session; - } - - /* - * Set the handshake session - */ - void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { - if (conn != null) { - conn.setHandshakeSession(handshakeSession); - } else { - engine.setHandshakeSession(handshakeSession); - } - } - - void expectingFinishFlightSE() { - if (conn != null) { - conn.expectingFinishFlight(); - } else { - engine.expectingFinishFlight(); - } - } - - /* - * Returns true if renegotiation is in use for this connection. - */ - boolean isSecureRenegotiation() { - return secureRenegotiation; - } - - /* - * Returns the verify_data from the Finished message sent by the client. - */ - byte[] getClientVerifyData() { - return clientVerifyData; - } - - /* - * Returns the verify_data from the Finished message sent by the server. - */ - byte[] getServerVerifyData() { - return serverVerifyData; - } - - /* - * This routine is fed SSL handshake records when they become available, - * and processes messages found therein. - */ - void processRecord(ByteBuffer record, - boolean expectingFinished) throws IOException { - - checkThrown(); - - /* - * Store the incoming handshake data, then see if we can - * now process any completed handshake messages - */ - input.incomingRecord(record); - - /* - * We don't need to create a separate delegatable task - * for finished messages. - */ - if ((conn != null) || expectingFinished) { - processLoop(); - } else { - delegateTask(new PrivilegedExceptionAction() { - @Override - public Void run() throws Exception { - processLoop(); - return null; - } - }); - } - } - - /* - * On input, we hash messages one at a time since servers may need - * to access an intermediate hash to validate a CertificateVerify - * message. - * - * Note that many handshake messages can come in one record (and often - * do, to reduce network resource utilization), and one message can also - * require multiple records (e.g. very large Certificate messages). - */ - void processLoop() throws IOException { - - // need to read off 4 bytes at least to get the handshake - // message type and length. - while (input.available() >= 4) { - byte messageType; - int messageLen; - - /* - * See if we can read the handshake message header, and - * then the entire handshake message. If not, wait till - * we can read and process an entire message. - */ - input.mark(4); - - messageType = (byte)input.getInt8(); - if (HandshakeMessage.isUnsupported(messageType)) { - throw new SSLProtocolException( - "Received unsupported or unknown handshake message: " + - messageType); - } - - messageLen = input.getInt24(); - - if (input.available() < messageLen) { - input.reset(); - return; - } - - // Set the flags in the message receiving side. - if (messageType == HandshakeMessage.ht_client_hello) { - clientHelloDelivered = true; - } else if (messageType == HandshakeMessage.ht_hello_request) { - serverHelloRequested = true; - } - - /* - * Process the message. We require - * that processMessage() consumes the entire message. In - * lieu of explicit error checks (how?!) we assume that the - * data will look like garbage on encoding/processing errors, - * and that other protocol code will detect such errors. - * - * Note that digesting is normally deferred till after the - * message has been processed, though to process at least the - * client's Finished message (i.e. send the server's) we need - * to acccelerate that digesting. - * - * Also, note that hello request messages are never hashed; - * that includes the hello request header, too. - */ - processMessage(messageType, messageLen); - - // Reload if this message has been reserved. - // - // Note: in the implementation, only certificate_verify and - // finished messages are reserved. - if ((messageType == HandshakeMessage.ht_finished) || - (messageType == HandshakeMessage.ht_certificate_verify)) { - - handshakeHash.reload(); - } - } - } - - - /** - * Returns true iff the handshaker has been activated. - * - * In activated state, the handshaker may not send any messages out. - */ - boolean activated() { - return handshakeActivated; - } - - /** - * Returns true iff the handshaker has sent any messages. - */ - boolean started() { - return (serverHelloRequested || clientHelloDelivered); - } - - /* - * Used to kickstart the negotiation ... either writing a - * ClientHello or a HelloRequest as appropriate, whichever - * the subclass returns. NOP if handshaking's already started. - */ - void kickstart() throws IOException { - if ((isClient && clientHelloDelivered) || - (!isClient && serverHelloRequested)) { - return; - } - - HandshakeMessage m = getKickstartMessage(); - handshakeState.update(m, resumingSession); - - if (debug != null && Debug.isOn("handshake")) { - m.print(System.out); - } - m.write(output); - output.flush(); - - // Set the flags in the message delivering side. - int handshakeType = m.messageType(); - if (handshakeType == HandshakeMessage.ht_hello_request) { - serverHelloRequested = true; - } else { // HandshakeMessage.ht_client_hello - clientHelloDelivered = true; - } - } - - /** - * Both client and server modes can start handshaking; but the - * message they send to do so is different. - */ - abstract HandshakeMessage getKickstartMessage() throws SSLException; - - /* - * Client and Server side protocols are each driven though this - * call, which processes a single message and drives the appropriate - * side of the protocol state machine (depending on the subclass). - */ - abstract void processMessage(byte messageType, int messageLen) - throws IOException; - - /* - * Most alerts in the protocol relate to handshaking problems. - * Alerts are detected as the connection reads data. - */ - abstract void handshakeAlert(byte description) throws SSLProtocolException; - - /* - * Sends a change cipher spec message and updates the write side - * cipher state so that future messages use the just-negotiated spec. - */ - void sendChangeCipherSpec(Finished mesg, boolean lastMessage) - throws IOException { - - output.flush(); // i.e. handshake data - - /* - * The write cipher state is protected by the connection write lock - * so we must grab it while making the change. We also - * make sure no writes occur between sending the ChangeCipherSpec - * message, installing the new cipher state, and sending the - * Finished message. - * - * We already hold SSLEngine/SSLSocket "this" by virtue - * of this being called from the readRecord code. - */ - if (conn != null) { - conn.writeLock.lock(); - try { - handshakeState.changeCipherSpec(false, isClient); - conn.changeWriteCiphers(); - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - handshakeState.update(mesg, resumingSession); - mesg.write(output); - output.flush(); - } finally { - conn.writeLock.unlock(); - } - } else { - synchronized (engine.writeLock) { - handshakeState.changeCipherSpec(false, isClient); - engine.changeWriteCiphers(); - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - handshakeState.update(mesg, resumingSession); - mesg.write(output); - output.flush(); - } - } - - if (lastMessage) { - handshakeFinished = true; - } - } - - void receiveChangeCipherSpec() throws IOException { - handshakeState.changeCipherSpec(true, isClient); - } - - /* - * Single access point to key calculation logic. Given the - * pre-master secret and the nonces from client and server, - * produce all the keying material to be used. - */ - void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) { - SecretKey master = calculateMasterSecret(preMasterSecret, version); - session.setMasterSecret(master); - calculateConnectionKeys(master); - } - - /* - * Calculate the master secret from its various components. This is - * used for key exchange by all cipher suites. - * - * The master secret is the catenation of three MD5 hashes, each - * consisting of the pre-master secret and a SHA1 hash. Those three - * SHA1 hashes are of (different) constant strings, the pre-master - * secret, and the nonces provided by the client and the server. - */ - @SuppressWarnings("deprecation") - private SecretKey calculateMasterSecret(SecretKey preMasterSecret, - ProtocolVersion requestedVersion) { - - if (debug != null && Debug.isOn("keygen")) { - HexDumpEncoder dump = new HexDumpEncoder(); - - System.out.println("SESSION KEYGEN:"); - - System.out.println("PreMaster Secret:"); - printHex(dump, preMasterSecret.getEncoded()); - - // Nonces are dumped with connection keygen, no - // benefit to doing it twice - } - - // What algs/params do we need to use? - String masterAlg; - PRF prf; - - byte majorVersion = protocolVersion.major; - byte minorVersion = protocolVersion.minor; - if (protocolVersion.isDTLSProtocol()) { - // Use TLS version number for DTLS key calculation - if (protocolVersion.v == ProtocolVersion.DTLS10.v) { - majorVersion = ProtocolVersion.TLS11.major; - minorVersion = ProtocolVersion.TLS11.minor; - - masterAlg = "SunTlsMasterSecret"; - prf = P_NONE; - } else { // DTLS 1.2 - majorVersion = ProtocolVersion.TLS12.major; - minorVersion = ProtocolVersion.TLS12.minor; - - masterAlg = "SunTls12MasterSecret"; - prf = cipherSuite.prfAlg; - } - } else { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { - masterAlg = "SunTls12MasterSecret"; - prf = cipherSuite.prfAlg; - } else { - masterAlg = "SunTlsMasterSecret"; - prf = P_NONE; - } - } - - String prfHashAlg = prf.getPRFHashAlg(); - int prfHashLength = prf.getPRFHashLength(); - int prfBlockSize = prf.getPRFBlockSize(); - - TlsMasterSecretParameterSpec spec; - if (session.getUseExtendedMasterSecret()) { - // reset to use the extended master secret algorithm - masterAlg = "SunTlsExtendedMasterSecret"; - - byte[] sessionHash = null; - if (protocolVersion.useTLS12PlusSpec()) { - sessionHash = handshakeHash.getFinishedHash(); - } else { - // TLS 1.0/1.1, DTLS 1.0 - sessionHash = new byte[36]; - try { - handshakeHash.getMD5Clone().digest(sessionHash, 0, 16); - handshakeHash.getSHAClone().digest(sessionHash, 16, 20); - } catch (DigestException de) { - throw new ProviderException(de); - } - } - - spec = new TlsMasterSecretParameterSpec( - preMasterSecret, - (majorVersion & 0xFF), (minorVersion & 0xFF), - sessionHash, - prfHashAlg, prfHashLength, prfBlockSize); - } else { - spec = new TlsMasterSecretParameterSpec( - preMasterSecret, - (majorVersion & 0xFF), (minorVersion & 0xFF), - clnt_random.random_bytes, svr_random.random_bytes, - prfHashAlg, prfHashLength, prfBlockSize); - } - - try { - KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); - kg.init(spec); - return kg.generateKey(); - } catch (InvalidAlgorithmParameterException | - NoSuchAlgorithmException iae) { - // unlikely to happen, otherwise, must be a provider exception - // - // For RSA premaster secrets, do not signal a protocol error - // due to the Bleichenbacher attack. See comments further down. - if (debug != null && Debug.isOn("handshake")) { - System.out.println("RSA master secret generation error:"); - iae.printStackTrace(System.out); - } - throw new ProviderException(iae); - - } - } - - /* - * Calculate the keys needed for this connection, once the session's - * master secret has been calculated. Uses the master key and nonces; - * the amount of keying material generated is a function of the cipher - * suite that's been negotiated. - * - * This gets called both on the "full handshake" (where we exchanged - * a premaster secret and started a new session) as well as on the - * "fast handshake" (where we just resumed a pre-existing session). - */ - @SuppressWarnings("deprecation") - void calculateConnectionKeys(SecretKey masterKey) { - /* - * For both the read and write sides of the protocol, we use the - * master to generate MAC secrets and cipher keying material. Block - * ciphers need initialization vectors, which we also generate. - * - * First we figure out how much keying material is needed. - */ - int hashSize = cipherSuite.macAlg.size; - boolean is_exportable = cipherSuite.exportable; - BulkCipher cipher = cipherSuite.cipher; - int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; - - // Which algs/params do we need to use? - String keyMaterialAlg; - PRF prf; - - byte majorVersion = protocolVersion.major; - byte minorVersion = protocolVersion.minor; - if (protocolVersion.isDTLSProtocol()) { - // Use TLS version number for DTLS key calculation - if (protocolVersion.v == ProtocolVersion.DTLS10.v) { - majorVersion = ProtocolVersion.TLS11.major; - minorVersion = ProtocolVersion.TLS11.minor; - - keyMaterialAlg = "SunTlsKeyMaterial"; - prf = P_NONE; - } else { // DTLS 1.2+ - majorVersion = ProtocolVersion.TLS12.major; - minorVersion = ProtocolVersion.TLS12.minor; - - keyMaterialAlg = "SunTls12KeyMaterial"; - prf = cipherSuite.prfAlg; - } - } else { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { - keyMaterialAlg = "SunTls12KeyMaterial"; - prf = cipherSuite.prfAlg; - } else { - keyMaterialAlg = "SunTlsKeyMaterial"; - prf = P_NONE; - } - } - - String prfHashAlg = prf.getPRFHashAlg(); - int prfHashLength = prf.getPRFHashLength(); - int prfBlockSize = prf.getPRFBlockSize(); - - // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to - // protect against the CBC attacks. AEAD/GCM cipher suites in TLS - // v1.2 or later use a fixed IV as the implicit part of the partially - // implicit nonce technique described in RFC 5116. - int ivSize = cipher.ivSize; - if (cipher.cipherType == AEAD_CIPHER) { - ivSize = cipher.fixedIvSize; - } else if ((cipher.cipherType == BLOCK_CIPHER) && - protocolVersion.useTLS11PlusSpec()) { - ivSize = 0; - } - - TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( - masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF), - clnt_random.random_bytes, svr_random.random_bytes, - cipher.algorithm, cipher.keySize, expandedKeySize, - ivSize, hashSize, - prfHashAlg, prfHashLength, prfBlockSize); - - try { - KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); - kg.init(spec); - TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec)kg.generateKey(); - - // Return null if cipher keys are not supposed to be generated. - clntWriteKey = keySpec.getClientCipherKey(); - svrWriteKey = keySpec.getServerCipherKey(); - - // Return null if IVs are not supposed to be generated. - clntWriteIV = keySpec.getClientIv(); - svrWriteIV = keySpec.getServerIv(); - - // Return null if MAC keys are not supposed to be generated. - clntMacSecret = keySpec.getClientMacKey(); - svrMacSecret = keySpec.getServerMacKey(); - } catch (GeneralSecurityException e) { - throw new ProviderException(e); - } - - // - // Dump the connection keys as they're generated. - // - if (debug != null && Debug.isOn("keygen")) { - synchronized (System.out) { - HexDumpEncoder dump = new HexDumpEncoder(); - - System.out.println("CONNECTION KEYGEN:"); - - // Inputs: - System.out.println("Client Nonce:"); - printHex(dump, clnt_random.random_bytes); - System.out.println("Server Nonce:"); - printHex(dump, svr_random.random_bytes); - System.out.println("Master Secret:"); - printHex(dump, masterKey.getEncoded()); - - // Outputs: - if (clntMacSecret != null) { - System.out.println("Client MAC write Secret:"); - printHex(dump, clntMacSecret.getEncoded()); - System.out.println("Server MAC write Secret:"); - printHex(dump, svrMacSecret.getEncoded()); - } else { - System.out.println("... no MAC keys used for this cipher"); - } - - if (clntWriteKey != null) { - System.out.println("Client write key:"); - printHex(dump, clntWriteKey.getEncoded()); - System.out.println("Server write key:"); - printHex(dump, svrWriteKey.getEncoded()); - } else { - System.out.println("... no encryption keys used"); - } - - if (clntWriteIV != null) { - System.out.println("Client write IV:"); - printHex(dump, clntWriteIV.getIV()); - System.out.println("Server write IV:"); - printHex(dump, svrWriteIV.getIV()); - } else { - if (protocolVersion.useTLS11PlusSpec()) { - System.out.println( - "... no IV derived for this protocol"); - } else { - System.out.println("... no IV used for this cipher"); - } - } - System.out.flush(); - } - } - } - - private static void printHex(HexDumpEncoder dump, byte[] bytes) { - if (bytes == null) { - System.out.println("(key bytes not available)"); - } else { - try { - dump.encodeBuffer(bytes, System.out); - } catch (IOException e) { - // just for debugging, ignore this - } - } - } - - /* - * Implement a simple task delegator. - * - * We are currently implementing this as a single delegator, may - * try for parallel tasks later. Client Authentication could - * benefit from this, where ClientKeyExchange/CertificateVerify - * could be carried out in parallel. - */ - class DelegatedTask implements Runnable { - - private PrivilegedExceptionAction pea; - - DelegatedTask(PrivilegedExceptionAction pea) { - this.pea = pea; - } - - public void run() { - synchronized (engine) { - try { - AccessController.doPrivileged(pea, engine.getAcc()); - } catch (PrivilegedActionException pae) { - thrown = pae.getException(); - } catch (RuntimeException rte) { - thrown = rte; - } - delegatedTask = null; - taskDelegated = false; - } - } - } - - private void delegateTask(PrivilegedExceptionAction pea) { - delegatedTask = new DelegatedTask(pea); - taskDelegated = false; - thrown = null; - } - - DelegatedTask getTask() { - if (!taskDelegated) { - taskDelegated = true; - return delegatedTask; - } else { - return null; - } - } - - /* - * See if there are any tasks which need to be delegated - * - * Locked by SSLEngine.this. - */ - boolean taskOutstanding() { - return (delegatedTask != null); - } - - /* - * The previous caller failed for some reason, report back the - * Exception. We won't worry about Error's. - * - * Locked by SSLEngine.this. - */ - void checkThrown() throws SSLException { - synchronized (thrownLock) { - if (thrown != null) { - - String msg = thrown.getMessage(); - - if (msg == null) { - msg = "Delegated task threw Exception/Error"; - } - - /* - * See what the underlying type of exception is. We should - * throw the same thing. Chain thrown to the new exception. - */ - Exception e = thrown; - thrown = null; - - if (e instanceof RuntimeException) { - throw new RuntimeException(msg, e); - } else if (e instanceof SSLHandshakeException) { - throw (SSLHandshakeException) - new SSLHandshakeException(msg).initCause(e); - } else if (e instanceof SSLKeyException) { - throw (SSLKeyException) - new SSLKeyException(msg).initCause(e); - } else if (e instanceof SSLPeerUnverifiedException) { - throw (SSLPeerUnverifiedException) - new SSLPeerUnverifiedException(msg).initCause(e); - } else if (e instanceof SSLProtocolException) { - throw (SSLProtocolException) - new SSLProtocolException(msg).initCause(e); - } else { - /* - * If it's SSLException or any other Exception, - * we'll wrap it in an SSLException. - */ - throw new SSLException(msg, e); - } - } - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java --- a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 sun.security.ssl.HandshakeMessage.ClientHello; +import java.util.Arrays; +import static sun.security.ssl.ClientHello.ClientHelloMessage; -/* - * 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 { + + static class Builder { + + final SecureRandom secureRandom; + + private volatile D10HelloCookieManager d10HelloCookieManager; + private volatile D13HelloCookieManager d13HelloCookieManager; + private volatile T13HelloCookieManager t13HelloCookieManager; + + Builder(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + } - private final SecureRandom secureRandom; - private final MessageDigest cookieDigest; + HelloCookieManager valueOf(ProtocolVersion protocolVersion) { + if (protocolVersion.isDTLS) { + if (protocolVersion.useTLS13PlusSpec()) { + if (d13HelloCookieManager != null) { + return d13HelloCookieManager; + } + + synchronized (this) { + if (d13HelloCookieManager == null) { + d13HelloCookieManager = + new D13HelloCookieManager(secureRandom); + } + } - private int cookieVersion; // allow to wrap - private long secretLifetime; - private byte[] cookieSecret; + return d13HelloCookieManager; + } else { + if (d10HelloCookieManager != null) { + return d10HelloCookieManager; + } - private int prevCookieVersion; - private byte[] prevCookieSecret; + synchronized (this) { + if (d10HelloCookieManager == null) { + d10HelloCookieManager = + new D10HelloCookieManager(secureRandom); + } + } - HelloCookieManager(SecureRandom secureRandom) { - this.secureRandom = secureRandom; - this.cookieDigest = JsseJce.getMessageDigest("SHA-256"); + return d10HelloCookieManager; + } + } else { + if (protocolVersion.useTLS13PlusSpec()) { + if (t13HelloCookieManager != null) { + return t13HelloCookieManager; + } - this.cookieVersion = secureRandom.nextInt(); - this.secretLifetime = 0; - this.cookieSecret = null; + synchronized (this) { + if (t13HelloCookieManager == null) { + t13HelloCookieManager = + new T13HelloCookieManager(secureRandom); + } + } - this.prevCookieVersion = 0; - this.prevCookieSecret = null; + return t13HelloCookieManager; + } + } + + return null; + } } - // 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(); - } else { - cookieSecret = new byte[32]; + abstract byte[] createCookie(ServerHandshakeContext context, + ClientHelloMessage clientHello) throws IOException; + + 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); + } + + @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, 32); + secureRandom.nextBytes(cookieSecret); + } + + cookieVersion++; } - cookieVersion++; - secureRandom.nextBytes(cookieSecret); - secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW; + 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[] 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); + @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 cookie; + 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 server side to check the cookie in ClientHello message. - synchronized boolean isValid(ClientHello clientHelloMsg) { - byte[] cookie = clientHelloMsg.cookie; + private static final + class D13HelloCookieManager extends HelloCookieManager { + D13HelloCookieManager(SecureRandom secureRandom) { + } + + @Override + byte[] createCookie(ServerHandshakeContext context, + ClientHelloMessage clientHello) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } - // no cookie exchange or not a valid cookie length - if ((cookie == null) || (cookie.length != 32)) { - return false; + @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); } - 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; + + 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); - byte[] secret; - if (version == cookieVersion) { - secret = cookieSecret; - } else if (version == prevCookieVersion) { - secret = prevCookieSecret; - } else { - return false; // may be out of the timing window - } + // hash of ClientHello handshake message + context.handshakeHash.update(); + byte[] clientHelloHash = context.handshakeHash.digest(); - clientHelloMsg.updateHelloCookie(cookieDigest); - byte[] target = cookieDigest.digest(secret); // 32 bytes + // 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) + }; - for (int i = 4; i < 32; i++) { - if (cookie[i] != target[i]) { - return false; - } + 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; } - return true; - } + @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; + } - // 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; + 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 + } } - if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) { - throw new SSLProtocolException( - "Invalid HelloVerifyRequest.cookie (length = " + - cookie.length + " bytes)"); + 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) - // Otherwise, no cookie exchange. + // 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; + } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HelloExtension.java --- a/src/java.base/share/classes/sun/security/ssl/HelloExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +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; - -abstract class HelloExtension { - - final ExtensionType type; - - HelloExtension(ExtensionType type) { - this.type = type; - } - - // Length of the encoded extension, including the type and length fields - abstract int length(); - - abstract void send(HandshakeOutStream s) throws IOException; - - @Override - public abstract String toString(); - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HelloExtensions.java --- a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 extensions; - private int encodedLength; - - HelloExtensions() { - extensions = Collections.emptyList(); - } - - HelloExtensions(HandshakeInStream s) throws IOException { - int len = s.getInt16(); - extensions = new ArrayList(); - 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 list() { - return extensions; - } - - void add(HelloExtension ext) { - if (extensions.isEmpty()) { - extensions = new ArrayList(); - } - 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()); - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HelloRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HelloRequest.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 ""; + } + } + + /** + * 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"); + } + } + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/HelloVerifyRequest.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/InputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/InputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/InputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ * * @author David Brownell */ -class InputRecord implements Record, Closeable { - - /* Class and subclass dynamic debugging support */ - static final Debug debug = Debug.getInstance("ssl"); +abstract class InputRecord implements Record, Closeable { + SSLReadCipher readCipher; + // Needed for KeyUpdate, used after Handshake.Finished + TransportContext tc; - 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 @@ // 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 @@ 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 @@ // 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 @@ } } + 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 @@ */ readCipher.dispose(); - this.readAuthenticator = readAuthenticator; this.readCipher = readCipher; } @@ -160,24 +131,22 @@ * @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 @@ // 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 @@ // 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 @@ // 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 @@ * 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 @@ 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 @@ return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size } - static ByteBuffer decrypt(Authenticator authenticator, CipherBox box, - byte contentType, ByteBuffer bb) throws BadPaddingException { - - 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; - } - } + // Extract an SSL/(D)TLS record from the specified source buffers. + static ByteBuffer extract( + ByteBuffer[] buffers, int offset, int length, int headerSize) { - // 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"); + 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; } - - // 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"); - } - } - - // 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); - - // 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); } } - // Is it a failover? - if (reservedBPE != null) { - throw reservedBPE; - } - - return bb.slice(); - } - - /* - * 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"); + if (!hasFullHeader) { + throw new BufferUnderflowException(); } - 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 + int packetLen = headerSize + contentLen; + int remains = 0; + for (int i = offset; i < offset + length; i++) { + remains += buffers[i].remaining(); + if (remains >= packetLen) { + 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"); + if (remains < packetLen) { + throw new BufferUnderflowException(); } - 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) { + 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; + } - // 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 + if (packetSpaces <= 0) { + break; } } - 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); } } - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/JsseJce.java --- a/src/java.base/share/classes/sun/security/ssl/JsseJce.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/JsseJce.java Mon Jun 25 13:41:39 2018 -0700 @@ -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.ProviderList; import sun.security.jca.Providers; -import sun.security.jca.ProviderList; - +import static sun.security.ssl.SunJSSE.cryptoProvider; import sun.security.util.ECUtil; - -import static sun.security.ssl.SunJSSE.cryptoProvider; import static sun.security.util.SecurityConstants.PROVIDER_VER; /** @@ -53,18 +44,11 @@ * @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 @@ * 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 @@ * 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 @@ return EcAvailability.isAvailable; } - static boolean isKerberosAvailable() { - return kerberosAvailable; - } - /** * Return an JCE cipher implementation for the specified algorithm. */ @@ -299,7 +290,8 @@ 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 @@ // 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; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java --- a/src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/KeyManagerFactoryImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ keyManager = new X509KeyManagerImpl( Collections.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 @@ "Parameters must be instance of KeyStoreBuilderParameters"); } if (SunJSSE.isFIPS()) { - // XXX should be fixed throw new InvalidAlgorithmParameterException ("FIPS mode: KeyStoreBuilderParameters not supported"); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 clientShares; + + private CHKeyShareSpec(List 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 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 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 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 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 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); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/KeyUpdate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/KeyUpdate.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 ""; + } + } + + 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; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/MAC.java --- a/src/java.base/share/classes/sun/security/ssl/MAC.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/MaxFragExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java --- a/src/java.base/share/classes/sun/security/ssl/MaxFragmentLengthExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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) + ")"; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/NamedGroup.java --- a/src/java.base/share/classes/sun/security/ssl/NamedGroup.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/NamedGroupType.java --- a/src/java.base/share/classes/sun/security/ssl/NamedGroupType.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +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; - -enum NamedGroupType { - NAMED_GROUP_ECDHE, // Elliptic Curve Groups (ECDHE) - NAMED_GROUP_FFDHE, // Finite Field Groups (DHE) - NAMED_GROUP_NONE // No predefined named group -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java Mon Jun 25 13:41:39 2018 -0700 @@ -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, + "", //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 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 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(); + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java --- a/src/java.base/share/classes/sun/security/ssl/OCSPStatusRequest.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 responderIds; - private final List 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 respIds, List 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(" "); - } else { - for (ResponderId rid : responderIds) { - sb.append("\n ").append(rid.toString()); - } - } - - sb.append("\n").append(" Extensions:"); - if (extensions.isEmpty()) { - sb.append(" "); - } 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 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 getExtensions() { - return Collections.unmodifiableList(extensions); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/OutputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ * 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. } @@ -100,15 +99,6 @@ } /* - * 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 @@ } boolean seqNumIsHuge() { - return (writeAuthenticator != null) && - writeAuthenticator.seqNumIsHuge(); + return (writeCipher.authenticator != null) && + writeCipher.authenticator.seqNumIsHuge(); } // SSLEngine and SSLSocket @@ -132,8 +122,10 @@ 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 @@ } // 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 @@ 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 @@ */ writeCipher.dispose(); - this.writeAuthenticator = writeAuthenticator; this.writeCipher = writeCipher; this.isFirstAppOutputRecord = true; } @@ -195,6 +183,11 @@ } // apply to DTLS SSLEngine + void finishHandshake() { + // blank + } + + // apply to DTLS SSLEngine void launchRetransmission() { // blank } @@ -207,6 +200,10 @@ } } + synchronized boolean isClosed() { + return isClosed; + } + // // shared helpers // @@ -216,72 +213,47 @@ // 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 (protocolVersion.useTLS13PlusSpec()) { + return d13Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); + } else { + return d10Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); + } + } else { + if (protocolVersion.useTLS13PlusSpec()) { + return t13Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); + } else { + return t10Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); } } + } - 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 + static long d13Encrypt( + SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, + int headerOffset, int dstLim, int headerSize, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException("Not supported yet."); + } - // 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; - } - } else { - destination.position(destination.limit()); - } + 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,30 +261,83 @@ 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]); + + // 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); + + // 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); + } - // fragment length - destination.put(headerOffset + 11, (byte)(fragLen >> 8)); - destination.put(headerOffset + 12, (byte)fragLen); + // 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); - // Increase the sequence number for next use if it is not shared. - if (!sharedSequenceNumber) { - authenticator.increaseSequenceNumber(); - } - } + // 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,55 +349,78 @@ // // Uses the internal expandable buf variable and the current // protocolVersion variable. - void encrypt(Authenticator authenticator, - CipherBox encCipher, byte contentType, int headerSize) { + long encrypt( + SSLWriteCipher encCipher, byte contentType, int headerSize) { + if (protocolVersion.useTLS13PlusSpec()) { + return t13Encrypt(encCipher, contentType, headerSize); + } else { + return t10Encrypt(encCipher, contentType, headerSize); + } + } - int position = headerSize + writeCipher.getExplicitNonceSize(); + private static final class T13PaddingHolder { + private static final byte[] zeros = new byte[16]; + } - // "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); + long t13Encrypt( + SSLWriteCipher encCipher, byte contentType, int headerSize) { + if (!encCipher.isNullCipher()) { + // inner plaintext + write(contentType); + write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length); + } - write(hash, 0, hash.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()) { - // Requires explicit IV/nonce for CBC/AEAD cipher suites for - // TLS 1.1 or later. - if (protocolVersion.useTLS11PlusSpec() && - (encCipher.isCBCMode() || encCipher.isAEADMode())) { + pv = ProtocolVersion.TLS12; + contentType = ContentType.APPLICATION_DATA.id; + } else { + pv = ProtocolVersion.TLS12; + } - byte[] nonce = encCipher.createExplicitNonce( - authenticator, contentType, (count - position)); - int noncePos = position - nonce.length; - System.arraycopy(nonce, 0, buf, noncePos, nonce.length); - } + 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; - 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 + 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); + } - // 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; - } + long t10Encrypt( + SSLWriteCipher encCipher, byte contentType, int headerSize) { + byte[] sequenceNumber = encCipher.authenticator.sequenceNumber(); + int position = headerSize + writeCipher.getExplicitNonceSize(); + int contentLen = count - position; - // Encrypt may pad, so again the count may be changed. - count = position + - encCipher.encrypt(buf, position, (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; @@ -381,11 +429,12 @@ 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 @@ 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 diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Plaintext.java --- a/src/java.base/share/classes/sun/security/ssl/Plaintext.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/Plaintext.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ 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 @@ 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 @@ this.handshakeStatus = null; } + @Override public String toString() { return "contentType: " + contentType + "/" + "majorVersion: " + majorVersion + "/" + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 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); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 identities; + final List binders; + + CHPreSharedKeySpec(List identities, List 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 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 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 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 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 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 identities) { + List 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(); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java --- a/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/PredefinedDHParameterSpecs.java Mon Jun 25 13:41:39 2018 -0700 @@ -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.math.BigInteger; import java.security.*; -import java.math.BigInteger; -import java.util.regex.Pattern; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; -import java.util.Map; -import java.util.HashMap; -import java.util.Collections; +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 @@ // 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 @@ "\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}"); // cache of predefined default DH ephemeral parameters - final static Map definedParams; + static final Map definedParams; // cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE) - final static Map ffdheParams; + static final Map ffdheParams; static { String property = AccessController.doPrivileged( @@ -252,8 +250,9 @@ 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 @@ 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 @@ 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"); } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ProtocolList.java --- a/src/java.base/share/classes/sun/security/ssl/ProtocolList.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 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 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 convert(String[] names) { - if (names == null) { - throw new IllegalArgumentException("Protocols may not be null"); - } - - ArrayList 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 collection() { - return protocols; - } - - /** - * Select a protocol version from the list. - * - * Return the lower of the protocol version of that suggested by - * the protocolVersion 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(); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java --- a/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/ProtocolVersion.java Mon Jun 25 13:41:39 2018 -0700 @@ -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: - * - *
{@code
- * if (protocolVersion.v >= ProtocolVersion.TLS10) {
- *   // TLS 1.0 code goes here
- * } else {
- *   // SSL 3.0 code here
- * }
- * }
+ * Enum for an SSL/TLS/DTLS protocol version. * * @author Andreas Sterbenz * @since 1.4.1 */ -public final class ProtocolVersion implements Comparable { +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 @@ // 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(); - - // minimum version we implement (SSL 3.0) - static final ProtocolVersion MIN = FIPS ? TLS10 : SSL30; + // (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 + }; - // maximum version we implement (TLS 1.2) - static final ProtocolVersion MAX = TLS12; + // (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 + }; - // SSL/TLS ProtocolVersion to use by default (TLS 1.2) - static final ProtocolVersion DEFAULT_TLS = 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 + }; - // DTLS ProtocolVersion to use by default (TLS 1.2) - static final ProtocolVersion DEFAULT_DTLS = DTLS12; + // (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 + }; - // Default version for hello messages (SSLv2Hello) - static final ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30; + // TLS ProtocolVersion array for TLS 1.2 and previous versions. + static final ProtocolVersion[] PROTOCOLS_TO_TLS12 = new ProtocolVersion[] { + TLS12, TLS11, TLS10, SSL30 + }; - // Available protocols - // - // Including all supported protocols except the disabled ones. - static final Set availableProtocols; + // TLS ProtocolVersion array for TLS 1.1 and previous versions. + static final ProtocolVersion[] PROTOCOLS_TO_TLS11 = new ProtocolVersion[] { + TLS11, 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; + // TLS ProtocolVersion array for TLS 1.0 and previous versions. + static final ProtocolVersion[] PROTOCOLS_TO_TLS10 = new ProtocolVersion[] { + TLS10, SSL30 + }; - // major and minor version - public final byte major, minor; - - // name used in JSSE (e.g. TLSv1 for TLS 1.0) - final String name; + // Empty ProtocolVersion array + static final ProtocolVersion[] PROTOCOLS_EMPTY = new ProtocolVersion[0]; - // Initialize the available protocols. - static { - Set protocols = new HashSet<>(7); + private ProtocolVersion(int id, String name, boolean isDTLS) { + this.id = id; + this.name = name; + this.isDTLS = isDTLS; + this.major = (byte)((id >>> 8) & 0xFF); + this.minor = (byte)(id & 0xFF); - ProtocolVersion[] pvs = new ProtocolVersion[] { - SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12}; - EnumSet cryptoPrimitives = - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT); - for (ProtocolVersion p : pvs) { - if (SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits( - cryptoPrimitives, p.name, null)) { - protocols.add(p); + this.isAvailable = SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + name, null); + } + + /** + * Return a ProtocolVersion with the specified major and minor + * version numbers. + */ + static ProtocolVersion valueOf(byte major, byte minor) { + for (ProtocolVersion pv : ProtocolVersion.values()) { + if ((pv.major == major) && (pv.minor == minor)) { + return pv; } } - availableProtocols = - Collections.unmodifiableSet(protocols); + return null; } - // private - private ProtocolVersion(int v, String name) { - this.v = v; - this.name = name; - major = (byte)(v >>> 8); - minor = (byte)(v & 0xFF); + /** + * 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; } - // 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; + /** + * 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. + * + * Used to filter out SSLv2Hello and protocol numbers less than the + * minimal supported protocol versions. + */ + 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 { - int major = (v >>> 8) & 0xFF; - int minor = v & 0xFF; - return new ProtocolVersion(v, "Unknown-" + major + "." + minor); + if (v < SSL30.id) { + if (!allowSSL20Hello || (v != SSL20Hello.id)) { + return false; + } + } + return true; } } /** - * Return a ProtocolVersion with the specified major and minor version - * numbers. Never throws exceptions. + * Get names of a list of ProtocolVersion objects. */ - public static ProtocolVersion valueOf(int major, int minor) { - return valueOf(((major & 0xFF) << 8) | (minor & 0xFF)); + static String[] toStringArray(List protocolVersions) { + if ((protocolVersions != null) && !protocolVersions.isEmpty()) { + String[] protocolNames = new String[protocolVersions.size()]; + int i = 0; + for (ProtocolVersion pv : protocolVersions) { + protocolNames[i++] = pv.name; + } + + return protocolNames; + } + + return new String[0]; + } + + /** + * Get names of a list of protocol version identifiers. + */ + static String[] toStringArray(int[] protocolVersions) { + if ((protocolVersions != null) && protocolVersions.length != 0) { + String[] protocolNames = new String[protocolVersions.length]; + int i = 0; + for (int pv : protocolVersions) { + protocolNames[i++] = ProtocolVersion.nameOf(pv); + } + + return protocolNames; + } + + return new String[0]; } /** - * Return a ProtocolVersion for the given name. - * - * @exception IllegalArgumentException if name is null or does not - * identify a supported protocol + * Get a list of ProtocolVersion objects of an array protocol + * version names. */ - 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"); + static List namesOf(String[] protocolNames) { + if (protocolNames == null || protocolNames.length == 0) { + return Collections.emptyList(); } - 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; - } else { - throw new IllegalArgumentException(name); + List 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); } - } - @Override - public String toString() { - return name; + return Collections.unmodifiableList(pvs); } /** - * Compares this object with the specified object for order. + * Return true if the specific protocol version name is + * of (D)TLS 1.2 or newer version. */ - @Override - public int compareTo(ProtocolVersion protocolVersion) { - if (maybeDTLSProtocol()) { - if (!protocolVersion.maybeDTLSProtocol()) { - throw new IllegalArgumentException("Not DTLS protocol"); - } + 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; + } - return protocolVersion.v - this.v; + /** + * 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 { - if (protocolVersion.maybeDTLSProtocol()) { - throw new IllegalArgumentException("Not TLS protocol"); - } - - return this.v - protocolVersion.v; + 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 useTLS13PlusSpec() { + return isDTLS ? (this.id < DTLS12.id) : (this.id >= TLS13.id); + } + + /** + * Return true if this ProtocolVersion object is of (D)TLS 1.2 or + * newer version. */ - boolean isDTLSProtocol() { - return this.v == DTLS12.v || this.v == DTLS10.v; + boolean useTLS12PlusSpec() { + 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 isDTLS ? true : (this.id >= TLS11.id); + } + + /** + * Return true if this ProtocolVersion object is of TLS 1.0 or + * newer version. + */ + boolean useTLS10PlusSpec() { + return isDTLS ? true : (this.id >= TLS10.id); } /** - * Returns true if a ProtocolVersion may represent a DTLS protocol. + * Return true if this ProtocolVersion object is of TLS 1.0 or + * newer version. */ - boolean maybeDTLSProtocol() { - return (this.major & 0x80) != 0; + static boolean useTLS10PlusSpec(int id, boolean isDTLS) { + return isDTLS ? true : (id >= TLS10.id); } - boolean useTLS12PlusSpec() { - return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v); - } - - boolean useTLS11PlusSpec() { - return maybeDTLSProtocol() ? true : (this.v >= TLS11.v); - } - - boolean useTLS10PlusSpec() { - return maybeDTLSProtocol() ? true : (this.v >= TLS10.v); + /** + * 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); } - 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); - } - - 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); - } - - // 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 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; } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/PskKeyExchangeModesExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 ""; + } + } + + 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 = { + "" + }; + 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"); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/RSAClientKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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. - */ - private ProtocolVersion protocolVersion; // preMaster [0,1] - SecretKey preMaster; - private byte[] encrypted; // same size as public modulus - - /* - * 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. + /** + * The RSA ClientKeyExchange handshake message. */ - @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()); + private static final + class RSAClientKeyExchangeMessage extends HandshakeMessage { + final int protocolVersion; + final boolean useTLS10PlusSpec; + final byte[] encrypted; + + RSAClientKeyExchangeMessage(HandshakeContext context, + RSAPremasterSecret premaster, + PublicKey publicKey) throws GeneralSecurityException { + super(context); + this.protocolVersion = context.clientHelloVersion; + this.encrypted = premaster.getEncoded( + publicKey, context.sslContext.getSecureRandom()); + this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec( + protocolVersion, 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(); - 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); - } - } + RSAClientKeyExchangeMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + if (m.remaining() < 2) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid RSA ClientKeyExchange message: insufficient data"); + } - /* - * 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()); + 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); } } - 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()); + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.CLIENT_KEY_EXCHANGE; + } + + @Override + public int messageLength() { + if (useTLS10PlusSpec) { + return encrypted.length + 2; + } else { + return encrypted.length; } } - 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"); + @Override + public void send(HandshakeOutStream hos) throws IOException { + if (useTLS10PlusSpec) { + hos.putBytes16(encrypted); + } else { + hos.write(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); - - // 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); + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"RSA ClientKeyExchange\": '{'\n" + + " \"client_version\": {0}\n" + + " \"encncrypted\": '{'\n" + + "{1}\n" + + " '}'\n" + + "'}'", + Locale.ENGLISH); - // 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); - } else { - // the cipher should have been initialized - preMaster = (SecretKey)cipher.unwrap(encrypted, - "TlsRsaPremasterSecret", Cipher.SECRET_KEY); - } - } 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); - } - throw new RuntimeException("Could not generate dummy secret", e); + 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; + } + } + } + + 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"); } - throw new RuntimeException("Could not generate premaster secret", iae); + + 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; - } + /** + * The RSA "ClientKeyExchange" handshake message consumer. + */ + private static final + class RSAClientKeyExchangeConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private RSAClientKeyExchangeConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + EphemeralRSAPossession rsaPossession = null; + X509Possession x509Possession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof EphemeralRSAPossession) { + rsaPossession = (EphemeralRSAPossession)possession; + break; + } else if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + if (rsaPossession != null) { + break; + } + } + } + + if (rsaPossession == null && x509Possession == null) { // unlikely + 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"); + } - @Override - int messageLength() { - if (protocolVersion.useTLS10PlusSpec()) { - return encrypted.length + 2; - } else { - return encrypted.length; + 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); + } + } } } - - @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; - } - - s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + version); - } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/RSAKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/RSAServerKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -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. + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RSASignature.java --- a/src/java.base/share/classes/sun/security/ssl/RSASignature.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/RSASignature.java Mon Jun 25 13:41:39 2018 -0700 @@ -23,7 +23,6 @@ * questions. */ - package sun.security.ssl; import java.security.*; @@ -46,71 +45,38 @@ * 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 @@ @Override protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { - checkNull(privateKey); - reset(); + if (privateKey == null) { + throw new InvalidKeyException("Private key must not be null"); + } + mdMD5.reset(); + mdSHA.reset(); rawRsa.initSign(privateKey, random); } - // lazily initialize the MessageDigests - private void initDigests() { - if (md5 == null) { - md5 = JsseJce.getMD5(); - sha = JsseJce.getSHA(); - } - } - @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 @@ @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 @@ @Override @SuppressWarnings("deprecation") - protected Object engineGetParameter(String param) - throws InvalidParameterException { + protected Object engineGetParameter( + String param) throws InvalidParameterException { throw new InvalidParameterException("Parameters not supported"); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RandomCookie.java --- a/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/RandomCookie.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ * @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); - } - - RandomCookie(HandshakeInStream m) throws IOException { - random_bytes = new byte[32]; - m.read(random_bytes, 0, 32); + generator.nextBytes(randomBytes); } - void send(HandshakeOutStream out) throws IOException { - out.write(random_bytes, 0, 32); + // Used for server random generation with version downgrade protection. + RandomCookie(HandshakeContext context) { + SecureRandom generator = context.sslContext.getSecureRandom(); + generator.nextBytes(randomBytes); + + // TLS 1.3 has a downgrade protection mechanism embedded in the + // server's random value. TLS 1.3 servers which negotiate TLS 1.2 + // or below in response to a ClientHello MUST set the last eight + // bytes of their Random value specially. + byte[] protection = null; + if (context.maximumActiveProtocol.useTLS13PlusSpec()) { + if (!context.negotiatedProtocol.useTLS13PlusSpec()) { + if (context.negotiatedProtocol.useTLS12PlusSpec()) { + protection = t12Protection; + } else { + protection = t11Protection; + } + } + } else if (context.maximumActiveProtocol.useTLS12PlusSpec()) { + if (!context.negotiatedProtocol.useTLS12PlusSpec()) { + protection = t11Protection; + } + } + + if (protection != null) { + System.arraycopy(protection, 0, randomBytes, + randomBytes.length - protection.length, protection.length); + } + } + + RandomCookie(ByteBuffer m) throws IOException { + m.get(randomBytes); } - 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(' '); + 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(); } - s.print(Utilities.hexDigits[k >>> 4]); - s.print(Utilities.hexDigits[k & 0xf]); + } else if (context.maximumActiveProtocol.useTLS12PlusSpec()) { + if (!context.negotiatedProtocol.useTLS12PlusSpec()) { + return isT11Downgrade(); + } } - s.println("}"); + + 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); } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Record.java --- a/src/java.base/share/classes/sun/security/ssl/Record.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/Record.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ * 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); + + /* + * Read 8, 16, 24, and 32 bit integer data types, encoded + * in standard big-endian form. + */ + static int getInt8(ByteBuffer m) throws IOException { + verifyLength(m, 1); + return (m.get() & 0xFF); + } - /** - * Return a description for the given content type. + 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 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 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."); + } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/RenegoInfoExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 = { + "" + }; + 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"); + } + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/RenegotiationInfoExtension.java --- a/src/java.base/share/classes/sun/security/ssl/RenegotiationInfoExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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 ? "" : - Debug.toString(renegotiated_connection)); - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Mon Jun 25 13:41:39 2018 -0700 @@ -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.AlgorithmParameters; import java.security.CryptoPrimitive; -import java.security.AlgorithmParameters; - +import java.security.Key; +import java.util.Set; import javax.net.ssl.*; - -import java.security.Key; - -import java.util.Set; - 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 @@ 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 @@ 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(); + HandshakeContext hc = + ((SSLSocketImpl)socket).conContext.handshakeContext; + if (hc != null) { + configuredConstraints = hc.sslConfig.algorithmConstraints; + } else { + configuredConstraints = null; + } } - - if (!withDefaultCertPathConstraints) { - enabledX509DisabledAlgConstraints = false; - } + this.userSpecifiedConstraints = configuredConstraints; + this.peerSpecifiedConstraints = null; + this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints; } SSLAlgorithmConstraints(SSLEngine engine, boolean withDefaultCertPathConstraints) { + AlgorithmConstraints configuredConstraints = null; if (engine != null) { - userAlgConstraints = - engine.getSSLParameters().getAlgorithmConstraints(); + HandshakeContext hc = + ((SSLEngineImpl)engine).conContext.handshakeContext; + if (hc != null) { + configuredConstraints = hc.sslConfig.algorithmConstraints; + } else { + configuredConstraints = null; + } } - - if (!withDefaultCertPathConstraints) { - enabledX509DisabledAlgConstraints = false; - } + 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 @@ 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 @@ 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 @@ 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); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ import java.util.HashSet; import java.util.Set; +import sun.security.ssl.CipherSuite.HashAlg; +import sun.security.ssl.CipherSuite.KeyExchange; +import static sun.security.ssl.CipherSuite.KeyExchange.*; +import sun.security.ssl.CipherSuite.MacAlg; +import static sun.security.ssl.SSLCipher.*; import sun.security.util.AlgorithmDecomposer; -import static sun.security.ssl.CipherSuite.*; -import static sun.security.ssl.CipherSuite.KeyExchange.*; /** * The class decomposes standard SSL/TLS cipher suites into sub-elements. @@ -126,18 +129,13 @@ } break; default: - if (ClientKeyExchangeService.find(keyExchange.name) != null) { - if (!onlyX509) { - components.add(keyExchange.name); - } - } // otherwise ignore } return components; } - private Set decomposes(CipherSuite.BulkCipher bulkCipher) { + private Set decomposes(SSLCipher bulkCipher) { Set components = new HashSet<>(); if (bulkCipher.transformation != null) { @@ -185,7 +183,7 @@ } private Set decomposes(CipherSuite.MacAlg macAlg, - BulkCipher cipher) { + SSLCipher cipher) { Set components = new HashSet<>(); if (macAlg == CipherSuite.MacAlg.M_NULL @@ -211,8 +209,26 @@ return components; } - private Set decompose(KeyExchange keyExchange, BulkCipher cipher, - MacAlg macAlg) { + private Set decomposes(CipherSuite.HashAlg hashAlg) { + Set 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 decompose(KeyExchange keyExchange, + SSLCipher cipher, + MacAlg macAlg, + HashAlg hashAlg) { Set components = new HashSet<>(); if (keyExchange != null) { @@ -233,6 +249,10 @@ components.addAll(decomposes(macAlg, cipher)); } + if (hashAlg != null) { + components.addAll(decomposes(hashAlg)); + } + return components; } @@ -241,18 +261,19 @@ 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); } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLAuthentication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLAuthentication.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLBasicKeyDerivation.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; + +final class SSLBasicKeyDerivation implements SSLKeyDerivation { + private final String hashAlg; + private final SecretKey secret; + private final byte[] hkdfInfo; + + SSLBasicKeyDerivation(SecretKey secret, String hashAlg, + byte[] label, byte[] context, int length) { + this.hashAlg = hashAlg.replace("-", ""); + this.secret = secret; + this.hkdfInfo = createHkdfInfo(label, context, length); + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec keySpec) throws IOException { + try { + HKDF hkdf = new HKDF(hashAlg); + return hkdf.expand(secret, hkdfInfo, + ((SecretSizeSpec)keySpec).length, algorithm); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } + + private static byte[] createHkdfInfo( + byte[] label, byte[] context, int length) { + byte[] info = new byte[4 + label.length + context.length]; + ByteBuffer m = ByteBuffer.wrap(info); + try { + Record.putInt16(m, length); + Record.putBytes8(m, label); + Record.putBytes8(m, context); + } catch (IOException ioe) { + // unlikely + } + return info; + } + + static class SecretSizeSpec implements AlgorithmParameterSpec { + final int length; + + SecretSizeSpec(int length) { + this.length = length; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLCipher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLCipher.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,2369 @@ +/* + * 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.nio.ByteBuffer; +import java.security.AccessController; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivilegedAction; +import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import sun.security.ssl.Authenticator.MAC; +import static sun.security.ssl.CipherType.*; +import static sun.security.ssl.JsseJce.*; + +enum SSLCipher { + // exportable ciphers + @SuppressWarnings({"unchecked", "rawtypes"}) + B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true, true, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new NullReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_NONE + ), + new SimpleImmutableEntry( + new NullReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new NullWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_NONE + ), + new SimpleImmutableEntry( + new NullWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true, true, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false, true, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true, true, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ) + })), + + // domestic strength ciphers + @SuppressWarnings({"unchecked", "rawtypes"}) + B_RC4_128(CIPHER_RC4, STREAM_CIPHER, 16, 16, 0, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new StreamWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_DES(CIPHER_DES, BLOCK_CIPHER, 8, 8, 8, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_11 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_11 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_3DES(CIPHER_3DES, BLOCK_CIPHER, 24, 24, 8, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_IDEA("IDEA", BLOCK_CIPHER, 16, 16, 8, 0, false, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + null, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + null, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_128(CIPHER_AES, BLOCK_CIPHER, 16, 16, 16, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_256(CIPHER_AES, BLOCK_CIPHER, 32, 32, 16, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T10BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_TO_10 + ), + new SimpleImmutableEntry( + new T11BlockWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_11_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 4, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 32, 12, 4, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_12 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T12GcmWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_128_GCM_IV(CIPHER_AES_GCM, AEAD_CIPHER, 16, 16, 12, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T13GcmReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T13GcmWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + B_AES_256_GCM_IV(CIPHER_AES_GCM, AEAD_CIPHER, 32, 32, 12, 0, true, false, + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T13GcmReadCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry( + new T13GcmWriteCipherGenerator(), + ProtocolVersion.PROTOCOLS_OF_13 + ) + })); + + // descriptive name including key size, e.g. AES/128 + final String description; + + // JCE cipher transformation string, e.g. AES/CBC/NoPadding + final String transformation; + + // algorithm name, e.g. AES + final String algorithm; + + // supported and compile time enabled. Also see isAvailable() + final boolean allowed; + + // number of bytes of entropy in the key + final int keySize; + + // length of the actual cipher key in bytes. + // for non-exportable ciphers, this is the same as keySize + final int expandedKeySize; + + // size of the IV + final int ivSize; + + // size of fixed IV + // + // record_iv_length = ivSize - fixedIvSize + final int fixedIvSize; + + // exportable under 512/40 bit rules + final boolean exportable; + + // Is the cipher algorithm of Cipher Block Chaining (CBC) mode? + final CipherType cipherType; + + // size of the authentication tag, only applicable to cipher suites in + // Galois Counter Mode (GCM) + // + // As far as we know, all supported GCM cipher suites use 128-bits + // authentication tags. + final int tagSize = 16; + + // runtime availability + private final boolean isAvailable; + + private final Map.Entry[] readCipherGenerators; + private final Map.Entry[] writeCipherGenerators; + + // Map of Ciphers listed in jdk.tls.KeyLimit + private static final HashMap cipherLimits = new HashMap<>(); + + // Keywords found on the jdk.tls.KeyLimit security property. + final static String tag[] = {"KEYUPDATE"}; + + static { + final long max = 4611686018427387904L; // 2^62 + String prop = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return Security.getProperty("jdk.tls.keyLimits"); + } + }); + + if (prop != null) { + String propvalue[] = prop.split(","); + + for (String entry : propvalue) { + int index; + // If this is not a UsageLimit, goto to next entry. + String values[] = entry.trim().toUpperCase().split(" "); + + if (values[1].contains(tag[0])) { + index = 0; + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("jdk.net.keyLimits: Unknown action: " + + entry); + } + continue; + } + + long size; + int i = values[2].indexOf("^"); + try { + if (i >= 0) { + size = (long) Math.pow(2, + Integer.parseInt(values[2].substring(i + 1))); + } else { + size = Long.parseLong(values[2]); + } + if (size < 1 || size > max) { + throw new NumberFormatException("Length exceeded limits"); + } + } catch (NumberFormatException e) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("jdk.net.keyLimits: " + e.getMessage() + + ": " + entry); + } + continue; + } + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("jdk.net.keyLimits: entry = " + entry + + ". " + values[0] + ":" + tag[index] + " = " + size); + } + cipherLimits.put(values[0] + ":" + tag[index], size); + } + } + } + + private SSLCipher(String transformation, + CipherType cipherType, int keySize, + int expandedKeySize, int ivSize, + int fixedIvSize, boolean allowed, boolean exportable, + Map.Entry[] readCipherGenerators, + Map.Entry[] writeCipherGenerators) { + this.transformation = transformation; + String[] splits = transformation.split("/"); + this.algorithm = splits[0]; + this.cipherType = cipherType; + this.description = this.algorithm + "/" + (keySize << 3); + this.keySize = keySize; + this.ivSize = ivSize; + this.fixedIvSize = fixedIvSize; + this.allowed = allowed; + + this.expandedKeySize = expandedKeySize; + this.exportable = exportable; + + // availability of this bulk cipher + // + // We assume all supported ciphers are always available since they are + // shipped with the SunJCE provider. However, AES/256 is unavailable + // when the default JCE policy jurisdiction files are installed because + // of key length restrictions. + this.isAvailable = allowed && isUnlimited(keySize, transformation); + + this.readCipherGenerators = readCipherGenerators; + this.writeCipherGenerators = writeCipherGenerators; + } + + SSLReadCipher createReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SecretKey key, IvParameterSpec iv, + SecureRandom random) throws GeneralSecurityException { + if (readCipherGenerators.length == 0) { + return null; + } + + ReadCipherGenerator rcg = null; + for (Map.Entry me : readCipherGenerators) { + for (ProtocolVersion pv : me.getValue()) { + if (protocolVersion == pv) { + rcg = me.getKey(); + } + } + } + + if (rcg != null) { + return rcg.createCipher(this, authenticator, + protocolVersion, transformation, key, iv, random); + } + return null; + } + + SSLWriteCipher createWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SecretKey key, IvParameterSpec iv, + SecureRandom random) throws GeneralSecurityException { + if (readCipherGenerators.length == 0) { + return null; + } + + WriteCipherGenerator rcg = null; + for (Map.Entry me : writeCipherGenerators) { + for (ProtocolVersion pv : me.getValue()) { + if (protocolVersion == pv) { + rcg = me.getKey(); + } + } + } + + if (rcg != null) { + return rcg.createCipher(this, authenticator, + protocolVersion, transformation, key, iv, random); + } + return null; + } + + /** + * Test if this bulk cipher is available. For use by CipherSuite. + */ + boolean isAvailable() { + return this.isAvailable; + } + + private static boolean isUnlimited(int keySize, String transformation) { + int keySizeInBits = keySize * 8; + if (keySizeInBits > 128) { // need the JCE unlimited + // strength jurisdiction policy + try { + if (Cipher.getMaxAllowedKeyLength( + transformation) < keySizeInBits) { + return false; + } + } catch (Exception e) { + return false; + } + } + + return true; + } + + @Override + public String toString() { + return description; + } + + interface ReadCipherGenerator { + SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException; + } + + abstract static class SSLReadCipher { + final Authenticator authenticator; + final ProtocolVersion protocolVersion; + boolean keyLimitEnabled = false; + long keyLimitCountdown = 0; + SecretKey baseSecret; + + SSLReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion) { + this.authenticator = authenticator; + this.protocolVersion = protocolVersion; + } + + static final SSLReadCipher nullTlsReadCipher() { + try { + return B_NULL.createReadCipher( + Authenticator.nullTlsMac(), + ProtocolVersion.NONE, null, null, null); + } catch (GeneralSecurityException gse) { + // unlikely + throw new RuntimeException("Cannot create NULL SSLCipher", gse); + } + } + + static final SSLReadCipher nullDTlsReadCipher() { + try { + return B_NULL.createReadCipher( + Authenticator.nullDtlsMac(), + ProtocolVersion.NONE, null, null, null); + } catch (GeneralSecurityException gse) { + // unlikely + throw new RuntimeException("Cannot create NULL SSLCipher", gse); + } + } + + abstract Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException; + + void dispose() { + // blank + } + + abstract int estimateFragmentSize(int packetSize, int headerSize); + + boolean isNullCipher() { + return false; + } + + /** + * Check if processed bytes have reached the key usage limit. + * If key usage limit is not be monitored, return false. + */ + public boolean atKeyLimit() { + if (keyLimitCountdown >= 0) { + return false; + } + + // Turn off limit checking as KeyUpdate will be occurring + keyLimitEnabled = false; + return true; + } + } + + interface WriteCipherGenerator { + SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException; + } + + abstract static class SSLWriteCipher { + final Authenticator authenticator; + final ProtocolVersion protocolVersion; + boolean keyLimitEnabled = false; + long keyLimitCountdown = 0; + SecretKey baseSecret; + + SSLWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion) { + this.authenticator = authenticator; + this.protocolVersion = protocolVersion; + } + + abstract int encrypt(byte contentType, ByteBuffer bb); + + static final SSLWriteCipher nullTlsWriteCipher() { + try { + return B_NULL.createWriteCipher( + Authenticator.nullTlsMac(), + ProtocolVersion.NONE, null, null, null); + } catch (GeneralSecurityException gse) { + // unlikely + throw new RuntimeException( + "Cannot create NULL SSL write Cipher", gse); + } + } + + static final SSLWriteCipher nullDTlsWriteCipher() { + try { + return B_NULL.createWriteCipher( + Authenticator.nullDtlsMac(), + ProtocolVersion.NONE, null, null, null); + } catch (GeneralSecurityException gse) { + // unlikely + throw new RuntimeException( + "Cannot create NULL SSL write Cipher", gse); + } + } + + void dispose() { + // blank + } + + abstract int getExplicitNonceSize(); + abstract int calculateFragmentSize(int packetLimit, int headerSize); + abstract int calculatePacketSize(int fragmentSize, int headerSize); + + boolean isCBCMode() { + return false; + } + + boolean isNullCipher() { + return false; + } + + /** + * Check if processed bytes have reached the key usage limit. + * If key usage limit is not be monitored, return false. + */ + public boolean atKeyLimit() { + if (keyLimitCountdown >= 0) { + return false; + } + + // Turn off limit checking as KeyUpdate will be occurring + keyLimitEnabled = false; + return true; + } + } + + private static final + class NullReadCipherGenerator implements ReadCipherGenerator { + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new NullReadCipher(authenticator, protocolVersion); + } + + static final class NullReadCipher extends SSLReadCipher { + NullReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion) { + super(authenticator, protocolVersion); + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + checkStreamMac(signer, bb, contentType, sequence); + } else { + authenticator.increaseSequenceNumber(); + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return packetSize - headerSize - macLen; + } + + @Override + boolean isNullCipher() { + return true; + } + } + } + + private static final + class NullWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new NullWriteCipher(authenticator, protocolVersion); + } + + static final class NullWriteCipher extends SSLWriteCipher { + NullWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion) { + super(authenticator, protocolVersion); + } + + @Override + public int encrypt(byte contentType, ByteBuffer bb) { + // add message authentication code + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + addMac(signer, bb, contentType); + } else { + authenticator.increaseSequenceNumber(); + } + + int len = bb.remaining(); + bb.position(bb.limit()); + return len; + } + + + @Override + int getExplicitNonceSize() { + return 0; + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return packetLimit - headerSize - macLen; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return fragmentSize + headerSize + macLen; + } + + @Override + boolean isNullCipher() { + return true; + } + } + } + + private static final + class StreamReadCipherGenerator implements ReadCipherGenerator { + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new StreamReadCipher(authenticator, protocolVersion, + algorithm, key, params, random); + } + + static final class StreamReadCipher extends SSLReadCipher { + private final Cipher cipher; + + StreamReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, params, random); + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + int len = bb.remaining(); + int pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + bb.position(pos); + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Plaintext after DECRYPTION", bb.duplicate()); + } + + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + checkStreamMac(signer, bb, contentType, sequence); + } else { + authenticator.increaseSequenceNumber(); + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return packetSize - headerSize - macLen; + } + } + } + + private static final + class StreamWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new StreamWriteCipher(authenticator, + protocolVersion, algorithm, key, params, random); + } + + static final class StreamWriteCipher extends SSLWriteCipher { + private final Cipher cipher; + + StreamWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, params, random); + } + + @Override + public int encrypt(byte contentType, ByteBuffer bb) { + // add message authentication code + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + addMac(signer, bb, contentType); + } else { + authenticator.increaseSequenceNumber(); + } + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.finest( + "Padded plaintext before ENCRYPTION", bb.duplicate()); + } + + int len = bb.remaining(); + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + + return len; + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int getExplicitNonceSize() { + return 0; + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return packetLimit - headerSize - macLen; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + return fragmentSize + headerSize + macLen; + } + } + } + + private static final + class T10BlockReadCipherGenerator implements ReadCipherGenerator { + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new BlockReadCipher(authenticator, + protocolVersion, algorithm, key, params, random); + } + + static final class BlockReadCipher extends SSLReadCipher { + private final Cipher cipher; + + BlockReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, params, random); + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + BadPaddingException reservedBPE = null; + + // sanity check length of the ciphertext + MAC signer = (MAC)authenticator; + int cipheredLength = bb.remaining(); + int tagLen = signer.macAlg().size; + if (tagLen != 0) { + if (!sanityCheck(tagLen, bb.remaining())) { + reservedBPE = new BadPaddingException( + "ciphertext sanity check failed"); + } + } + // decryption + int len = bb.remaining(); + int pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Padded plaintext after DECRYPTION", + bb.duplicate().position(pos)); + } + + // remove the block padding + int blockSize = cipher.getBlockSize(); + bb.position(pos); + try { + removePadding(bb, tagLen, blockSize, protocolVersion); + } catch (BadPaddingException bpe) { + if (reservedBPE == null) { + reservedBPE = bpe; + } + } + + // Requires message authentication code for null, stream and + // block cipher suites. + try { + if (tagLen != 0) { + checkCBCMac(signer, bb, + contentType, cipheredLength, sequence); + } else { + authenticator.increaseSequenceNumber(); + } + } catch (BadPaddingException bpe) { + if (reservedBPE == null) { + reservedBPE = bpe; + } + } + + // Is it a failover? + if (reservedBPE != null) { + throw reservedBPE; + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + + // No padding for a maximum fragment. + // + // 1 byte padding length field: 0x00 + return packetSize - headerSize - macLen - 1; + } + + /** + * Sanity check the length of a fragment before decryption. + * + * In CBC mode, check that the fragment length is one or multiple + * times of the block size of the cipher suite, and is at least + * one (one is the smallest size of padding in CBC mode) bigger + * than the tag size of the MAC algorithm except the explicit IV + * size for TLS 1.1 or later. + * + * In non-CBC mode, check that the fragment length is not less than + * the tag size of the MAC algorithm. + * + * @return true if the length of a fragment matches above + * requirements + */ + private boolean sanityCheck(int tagLen, int fragmentLen) { + int blockSize = cipher.getBlockSize(); + if ((fragmentLen % blockSize) == 0) { + int minimal = tagLen + 1; + minimal = (minimal >= blockSize) ? minimal : blockSize; + + return (fragmentLen >= minimal); + } + + return false; + } + } + } + + private static final + class T10BlockWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new BlockWriteCipher(authenticator, + protocolVersion, algorithm, key, params, random); + } + + static final class BlockWriteCipher extends SSLWriteCipher { + private final Cipher cipher; + + BlockWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, params, random); + } + + @Override + public int encrypt(byte contentType, ByteBuffer bb) { + int pos = bb.position(); + + // add message authentication code + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + addMac(signer, bb, contentType); + } else { + authenticator.increaseSequenceNumber(); + } + + int blockSize = cipher.getBlockSize(); + int len = addPadding(bb, blockSize); + bb.position(pos); + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Padded plaintext before ENCRYPTION", + bb.duplicate()); + } + + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + + return len; + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int getExplicitNonceSize() { + return 0; + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + int blockSize = cipher.getBlockSize(); + int fragLen = packetLimit - headerSize; + fragLen -= (fragLen % blockSize); // cannot hold a block + // No padding for a maximum fragment. + fragLen -= 1; // 1 byte padding length field: 0x00 + fragLen -= macLen; + return fragLen; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + int blockSize = cipher.getBlockSize(); + int paddedLen = fragmentSize + macLen + 1; + if ((paddedLen % blockSize) != 0) { + paddedLen += blockSize - 1; + paddedLen -= paddedLen % blockSize; + } + + return headerSize + paddedLen; + } + + @Override + boolean isCBCMode() { + return true; + } + } + } + + // For TLS 1.1 and 1.2 + private static final + class T11BlockReadCipherGenerator implements ReadCipherGenerator { + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, ProtocolVersion protocolVersion, + String algorithm, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new BlockReadCipher(authenticator, protocolVersion, + sslCipher, algorithm, key, params, random); + } + + static final class BlockReadCipher extends SSLReadCipher { + private final Cipher cipher; + + BlockReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + if (params == null) { + params = new IvParameterSpec(new byte[sslCipher.ivSize]); + } + cipher.init(Cipher.DECRYPT_MODE, key, params, random); + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + BadPaddingException reservedBPE = null; + + // sanity check length of the ciphertext + MAC signer = (MAC)authenticator; + int cipheredLength = bb.remaining(); + int tagLen = signer.macAlg().size; + if (tagLen != 0) { + if (!sanityCheck(tagLen, bb.remaining())) { + reservedBPE = new BadPaddingException( + "ciphertext sanity check failed"); + } + } + + // decryption + int len = bb.remaining(); + int pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Padded plaintext after DECRYPTION", + bb.duplicate().position(pos)); + } + + // Ignore the explicit nonce. + bb.position(pos + cipher.getBlockSize()); + pos = bb.position(); + + // remove the block padding + int blockSize = cipher.getBlockSize(); + bb.position(pos); + try { + removePadding(bb, tagLen, blockSize, protocolVersion); + } catch (BadPaddingException bpe) { + if (reservedBPE == null) { + reservedBPE = bpe; + } + } + + // Requires message authentication code for null, stream and + // block cipher suites. + try { + if (tagLen != 0) { + checkCBCMac(signer, bb, + contentType, cipheredLength, sequence); + } else { + authenticator.increaseSequenceNumber(); + } + } catch (BadPaddingException bpe) { + if (reservedBPE == null) { + reservedBPE = bpe; + } + } + + // Is it a failover? + if (reservedBPE != null) { + throw reservedBPE; + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + + // No padding for a maximum fragment. + // + // 1 byte padding length field: 0x00 + int nonceSize = cipher.getBlockSize(); + return packetSize - headerSize - nonceSize - macLen - 1; + } + + /** + * Sanity check the length of a fragment before decryption. + * + * In CBC mode, check that the fragment length is one or multiple + * times of the block size of the cipher suite, and is at least + * one (one is the smallest size of padding in CBC mode) bigger + * than the tag size of the MAC algorithm except the explicit IV + * size for TLS 1.1 or later. + * + * In non-CBC mode, check that the fragment length is not less than + * the tag size of the MAC algorithm. + * + * @return true if the length of a fragment matches above + * requirements + */ + private boolean sanityCheck(int tagLen, int fragmentLen) { + int blockSize = cipher.getBlockSize(); + if ((fragmentLen % blockSize) == 0) { + int minimal = tagLen + 1; + minimal = (minimal >= blockSize) ? minimal : blockSize; + minimal += blockSize; + + return (fragmentLen >= minimal); + } + + return false; + } + } + } + + // For TLS 1.1 and 1.2 + private static final + class T11BlockWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, ProtocolVersion protocolVersion, + String algorithm, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new BlockWriteCipher(authenticator, protocolVersion, + sslCipher, algorithm, key, params, random); + } + + static final class BlockWriteCipher extends SSLWriteCipher { + private final Cipher cipher; + private final SecureRandom random; + + BlockWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + this.random = random; + if (params == null) { + params = new IvParameterSpec(new byte[sslCipher.ivSize]); + } + cipher.init(Cipher.ENCRYPT_MODE, key, params, random); + } + + @Override + public int encrypt(byte contentType, ByteBuffer bb) { + // To be unique and aware of overflow-wrap, sequence number + // is used as the nonce_explicit of block cipher suites. + int pos = bb.position(); + + // add message authentication code + MAC signer = (MAC)authenticator; + if (signer.macAlg().size != 0) { + addMac(signer, bb, contentType); + } else { + authenticator.increaseSequenceNumber(); + } + + // DON'T WORRY, the nonce spaces are considered already. + byte[] nonce = new byte[cipher.getBlockSize()]; + random.nextBytes(nonce); + pos = pos - nonce.length; + bb.position(pos); + bb.put(nonce); + bb.position(pos); + + int blockSize = cipher.getBlockSize(); + int len = addPadding(bb, blockSize); + bb.position(pos); + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Padded plaintext before ENCRYPTION", + bb.duplicate()); + } + + ByteBuffer dup = bb.duplicate(); + try { + if (len != cipher.update(dup, bb)) { + // catch BouncyCastle buffering error + throw new RuntimeException( + "Unexpected number of plaintext bytes"); + } + + if (bb.position() != dup.position()) { + throw new RuntimeException( + "Unexpected ByteBuffer position"); + } + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + + return len; + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int getExplicitNonceSize() { + return cipher.getBlockSize(); + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + int blockSize = cipher.getBlockSize(); + int fragLen = packetLimit - headerSize - blockSize; + fragLen -= (fragLen % blockSize); // cannot hold a block + // No padding for a maximum fragment. + fragLen -= 1; // 1 byte padding length field: 0x00 + fragLen -= macLen; + return fragLen; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + int macLen = ((MAC)authenticator).macAlg().size; + int blockSize = cipher.getBlockSize(); + int paddedLen = fragmentSize + macLen + 1; + if ((paddedLen % blockSize) != 0) { + paddedLen += blockSize - 1; + paddedLen -= paddedLen % blockSize; + } + + return headerSize + blockSize + paddedLen; + } + + @Override + boolean isCBCMode() { + return true; + } + } + } + + private static final + class T12GcmReadCipherGenerator implements ReadCipherGenerator { + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new GcmReadCipher(authenticator, protocolVersion, sslCipher, + algorithm, key, params, random); + } + + static final class GcmReadCipher extends SSLReadCipher { + private final Cipher cipher; + private final int tagSize; + private final Key key; + private final byte[] fixedIv; + private final int recordIvSize; + private final SecureRandom random; + + GcmReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + this.tagSize = sslCipher.tagSize; + this.key = key; + this.fixedIv = ((IvParameterSpec)params).getIV(); + this.recordIvSize = sslCipher.ivSize - sslCipher.fixedIvSize; + this.random = random; + + // DON'T initialize the cipher for AEAD! + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + if (bb.remaining() < (recordIvSize + tagSize)) { + throw new BadPaddingException( + "Insufficient buffer remaining for AEAD cipher " + + "fragment (" + bb.remaining() + "). Needs to be " + + "more than or equal to IV size (" + recordIvSize + + ") + tag size (" + tagSize + ")"); + } + + // initialize the AEAD cipher for the unique IV + byte[] iv = Arrays.copyOf(fixedIv, + fixedIv.length + recordIvSize); + bb.get(iv, fixedIv.length, recordIvSize); + GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); + try { + cipher.init(Cipher.DECRYPT_MODE, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // update the additional authentication data + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, bb.remaining() - tagSize, + sequence); + cipher.updateAAD(aad); + + // DON'T decrypt the nonce_explicit for AEAD mode. The buffer + // position has moved out of the nonce_explicit range. + int len, pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + try { + len = cipher.doFinal(dup, bb); + } catch (IllegalBlockSizeException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode \"" + ibse.getMessage() + + " \"in JCE provider " + cipher.getProvider().getName()); + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + // reset the limit to the end of the decrypted data + bb.position(pos); + bb.limit(pos + len); + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Plaintext after DECRYPTION", bb.duplicate()); + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + return packetSize - headerSize - recordIvSize - tagSize; + } + } + } + + private static final + class T12GcmWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, + ProtocolVersion protocolVersion, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new GcmWriteCipher(authenticator, protocolVersion, sslCipher, + algorithm, key, params, random); + } + + private static final class GcmWriteCipher extends SSLWriteCipher { + private final Cipher cipher; + private final int tagSize; + private final Key key; + private final byte[] fixedIv; + private final int recordIvSize; + private final SecureRandom random; + + GcmWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + this.tagSize = sslCipher.tagSize; + this.key = key; + this.fixedIv = ((IvParameterSpec)params).getIV(); + this.recordIvSize = sslCipher.ivSize - sslCipher.fixedIvSize; + this.random = random; + + // DON'T initialize the cipher for AEAD! + } + + @Override + public int encrypt(byte contentType, + ByteBuffer bb) { + // To be unique and aware of overflow-wrap, sequence number + // is used as the nonce_explicit of AEAD cipher suites. + byte[] nonce = authenticator.sequenceNumber(); + + // initialize the AEAD cipher for the unique IV + byte[] iv = Arrays.copyOf(fixedIv, + fixedIv.length + nonce.length); + System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); + + GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); + try { + cipher.init(Cipher.ENCRYPT_MODE, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // Update the additional authentication data, using the + // implicit sequence number of the authenticator. + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, bb.remaining(), null); + cipher.updateAAD(aad); + + // DON'T WORRY, the nonce spaces are considered already. + bb.position(bb.position() - nonce.length); + bb.put(nonce); + + // DON'T encrypt the nonce for AEAD mode. + int len, pos = bb.position(); + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Plaintext before ENCRYPTION", + bb.duplicate()); + } + + ByteBuffer dup = bb.duplicate(); + int outputSize = cipher.getOutputSize(dup.remaining()); + if (outputSize > bb.remaining()) { + // Need to expand the limit of the output buffer for + // the authentication tag. + // + // DON'T worry about the buffer's capacity, we have + // reserved space for the authentication tag. + bb.limit(pos + outputSize); + } + + try { + len = cipher.doFinal(dup, bb); + } catch (IllegalBlockSizeException | + BadPaddingException | ShortBufferException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode in JCE provider " + + cipher.getProvider().getName(), ibse); + } + + if (len != outputSize) { + throw new RuntimeException( + "Cipher buffering error in JCE provider " + + cipher.getProvider().getName()); + } + + return len + nonce.length; + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int getExplicitNonceSize() { + return recordIvSize; + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + return packetLimit - headerSize - recordIvSize - tagSize; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + return fragmentSize + headerSize + recordIvSize + tagSize; + } + } + } + + private static final + class T13GcmReadCipherGenerator implements ReadCipherGenerator { + + @Override + public SSLReadCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, ProtocolVersion protocolVersion, + String algorithm, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new GcmReadCipher(authenticator, protocolVersion, sslCipher, + algorithm, key, params, random); + } + + static final class GcmReadCipher extends SSLReadCipher { + private final Cipher cipher; + private final int tagSize; + private final Key key; + private final byte[] iv; + private final SecureRandom random; + + GcmReadCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + this.tagSize = sslCipher.tagSize; + this.key = key; + this.iv = ((IvParameterSpec)params).getIV(); + this.random = random; + + keyLimitCountdown = cipherLimits.getOrDefault( + algorithm.toUpperCase() + ":" + tag[0], 0L); + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyLimit read side: algorithm = " + + algorithm.toUpperCase() + ":" + tag[0] + + "\ncountdown value = " + keyLimitCountdown); + } + if (keyLimitCountdown > 0) { + keyLimitEnabled = true; + } + // DON'T initialize the cipher for AEAD! + } + + @Override + public Plaintext decrypt(byte contentType, ByteBuffer bb, + byte[] sequence) throws GeneralSecurityException { + // 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. + if (contentType == ContentType.CHANGE_CIPHER_SPEC.id) { + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + if (bb.remaining() <= tagSize) { + throw new BadPaddingException( + "Insufficient buffer remaining for AEAD cipher " + + "fragment (" + bb.remaining() + "). Needs to be " + + "more than tag size (" + tagSize + ")"); + } + + byte[] sn = sequence; + if (sn == null) { + sn = authenticator.sequenceNumber(); + } + byte[] nonce = iv.clone(); + int offset = nonce.length - sn.length; + for (int i = 0; i < sn.length; i++) { + nonce[offset + i] ^= sn[i]; + } + + // initialize the AEAD cipher for the unique IV + GCMParameterSpec spec = + new GCMParameterSpec(tagSize * 8, nonce); + try { + cipher.init(Cipher.DECRYPT_MODE, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // Update the additional authentication data, using the + // implicit sequence number of the authenticator. + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, bb.remaining(), sn); + cipher.updateAAD(aad); + + int len, pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + try { + len = cipher.doFinal(dup, bb); + } catch (IllegalBlockSizeException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode \"" + ibse.getMessage() + + " \"in JCE provider " + cipher.getProvider().getName()); + } catch (ShortBufferException sbe) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error in " + + "JCE provider " + cipher.getProvider().getName(), sbe); + } + // reset the limit to the end of the decrypted data + bb.position(pos); + bb.limit(pos + len); + + // remove inner plaintext padding + int i = bb.limit() - 1; + for (; i > 0 && bb.get(i) == 0; i--) { + // blank + } + if (i < (pos + 1)) { + throw new BadPaddingException( + "Incorrect inner plaintext: no content type"); + } + contentType = bb.get(i); + bb.limit(i); + + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Plaintext after DECRYPTION", bb.duplicate()); + } + if (keyLimitEnabled) { + keyLimitCountdown -= len; + } + + return new Plaintext(contentType, + ProtocolVersion.NONE.major, ProtocolVersion.NONE.minor, + -1, -1L, bb.slice()); + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int estimateFragmentSize(int packetSize, int headerSize) { + return packetSize - headerSize - tagSize; + } + } + } + + private static final + class T13GcmWriteCipherGenerator implements WriteCipherGenerator { + @Override + public SSLWriteCipher createCipher(SSLCipher sslCipher, + Authenticator authenticator, ProtocolVersion protocolVersion, + String algorithm, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + return new GcmWriteCipher(authenticator, protocolVersion, sslCipher, + algorithm, key, params, random); + } + + private static final class GcmWriteCipher extends SSLWriteCipher { + private final Cipher cipher; + private final int tagSize; + private final Key key; + private final byte[] iv; + private final SecureRandom random; + + GcmWriteCipher(Authenticator authenticator, + ProtocolVersion protocolVersion, + SSLCipher sslCipher, String algorithm, + Key key, AlgorithmParameterSpec params, + SecureRandom random) throws GeneralSecurityException { + super(authenticator, protocolVersion); + this.cipher = JsseJce.getCipher(algorithm); + this.tagSize = sslCipher.tagSize; + this.key = key; + this.iv = ((IvParameterSpec)params).getIV(); + this.random = random; + + keyLimitCountdown = cipherLimits.getOrDefault( + algorithm.toUpperCase() + ":" + tag[0], 0L); + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyLimit write side: algorithm = " + + algorithm.toUpperCase() + ":" + tag[0] + + "\ncountdown value = " + keyLimitCountdown); + } + if (keyLimitCountdown > 0) { + keyLimitEnabled = true; + } + + // DON'T initialize the cipher for AEAD! + } + + @Override + public int encrypt(byte contentType, + ByteBuffer bb) { + byte[] sn = authenticator.sequenceNumber(); + byte[] nonce = iv.clone(); + int offset = nonce.length - sn.length; + for (int i = 0; i < sn.length; i++) { + nonce[offset + i] ^= sn[i]; + } + + // initialize the AEAD cipher for the unique IV + GCMParameterSpec spec = + new GCMParameterSpec(tagSize * 8, nonce); + try { + cipher.init(Cipher.ENCRYPT_MODE, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // Update the additional authentication data, using the + // implicit sequence number of the authenticator. + int outputSize = cipher.getOutputSize(bb.remaining()); + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, outputSize, sn); + cipher.updateAAD(aad); + + int len, pos = bb.position(); + if (SSLLogger.isOn && SSLLogger.isOn("plaintext")) { + SSLLogger.fine( + "Plaintext before ENCRYPTION", + bb.duplicate()); + } + + ByteBuffer dup = bb.duplicate(); + if (outputSize > bb.remaining()) { + // Need to expand the limit of the output buffer for + // the authentication tag. + // + // DON'T worry about the buffer's capacity, we have + // reserved space for the authentication tag. + bb.limit(pos + outputSize); + } + + try { + len = cipher.doFinal(dup, bb); + } catch (IllegalBlockSizeException | + BadPaddingException | ShortBufferException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode in JCE provider " + + cipher.getProvider().getName(), ibse); + } + + if (len != outputSize) { + throw new RuntimeException( + "Cipher buffering error in JCE provider " + + cipher.getProvider().getName()); + } + + if (keyLimitEnabled) { + keyLimitCountdown -= len; + } + return len; + } + + @Override + void dispose() { + if (cipher != null) { + try { + cipher.doFinal(); + } catch (Exception e) { + // swallow all types of exceptions. + } + } + } + + @Override + int getExplicitNonceSize() { + return 0; + } + + @Override + int calculateFragmentSize(int packetLimit, int headerSize) { + return packetLimit - headerSize - tagSize; + } + + @Override + int calculatePacketSize(int fragmentSize, int headerSize) { + return fragmentSize + headerSize + tagSize; + } + } + } + + private static void addMac(MAC signer, + ByteBuffer destination, byte contentType) { + if (signer.macAlg().size != 0) { + int dstContent = destination.position(); + 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.position(dstContent); + } + } + + // for null and stream cipher + private static void checkStreamMac(MAC signer, ByteBuffer bb, + byte contentType, byte[] sequence) throws BadPaddingException { + int tagLen = signer.macAlg().size; + + // Requires message authentication code for null, stream and + // block cipher suites. + if (tagLen != 0) { + int contentLen = bb.remaining() - tagLen; + if (contentLen < 0) { + throw new BadPaddingException("bad record"); + } + + // Run MAC computation and comparison on the payload. + // + // MAC data would be stripped off during the check. + if (checkMacTags(contentType, bb, signer, sequence, false)) { + throw new BadPaddingException("bad record MAC"); + } + } + } + + // for CBC cipher + private static void checkCBCMac(MAC signer, ByteBuffer bb, + byte contentType, int cipheredLength, + byte[] sequence) throws BadPaddingException { + BadPaddingException reservedBPE = null; + int tagLen = signer.macAlg().size; + int pos = bb.position(); + + if (tagLen != 0) { + int contentLen = bb.remaining() - tagLen; + if (contentLen < 0) { + reservedBPE = new BadPaddingException("bad record"); + + // set offset of the dummy MAC + contentLen = cipheredLength - tagLen; + bb.limit(pos + 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"); + } + } + + // Run MAC computation and comparison on the remainder. + int remainingLen = calculateRemainingLen( + signer, cipheredLength, contentLen); + + // 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.macAlg().size; + 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); + } + + // Is it a failover? + if (reservedBPE != null) { + throw reservedBPE; + } + } + + /* + * Run MAC computation and comparison + */ + private static boolean checkMacTags(byte contentType, ByteBuffer bb, + MAC signer, byte[] sequence, boolean isSimulated) { + int tagLen = signer.macAlg().size; + 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 (byte t : tag) { + if (bb.get() != t) { + 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.macAlg().hashBlockSize; + int minimalPaddingLen = signer.macAlg().minimalPaddingSize; + + // (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; + } + + private static int addPadding(ByteBuffer bb, int blockSize) { + + int len = bb.remaining(); + int offset = bb.position(); + + int newlen = len + 1; + byte pad; + int i; + + if ((newlen % blockSize) != 0) { + newlen += blockSize - 1; + newlen -= newlen % blockSize; + } + pad = (byte) (newlen - len); + + /* + * Update the limit to what will be padded. + */ + bb.limit(newlen + offset); + + /* + * TLS version of the padding works for both SSLv3 and TLSv1 + */ + for (i = 0, offset += len; i < pad; i++) { + bb.put(offset++, (byte) (pad - 1)); + } + + bb.position(offset); + bb.limit(offset); + + return newlen; + } + + private static int removePadding(ByteBuffer bb, + int tagLen, int blockSize, + ProtocolVersion protocolVersion) throws BadPaddingException { + int len = bb.remaining(); + int offset = bb.position(); + + // last byte is length byte (i.e. actual padding length - 1) + int padOffset = offset + len - 1; + int padLen = bb.get(padOffset) & 0xFF; + + int newLen = len - (padLen + 1); + if ((newLen - tagLen) < 0) { + // If the buffer is not long enough to contain the padding plus + // a MAC tag, do a dummy constant-time padding check. + // + // Note that it is a dummy check, so we won't care about what is + // the actual padding data. + checkPadding(bb.duplicate(), (byte)(padLen & 0xFF)); + + throw new BadPaddingException("Invalid Padding length: " + padLen); + } + + // The padding data should be filled with the padding length value. + int[] results = checkPadding( + bb.duplicate().position(offset + newLen), + (byte)(padLen & 0xFF)); + if (protocolVersion.useTLS10PlusSpec()) { + if (results[0] != 0) { // padding data has invalid bytes + throw new BadPaddingException("Invalid TLS padding data"); + } + } else { // SSLv3 + // SSLv3 requires 0 <= length byte < block size + // some implementations do 1 <= length byte <= block size, + // so accept that as well + // v3 does not require any particular value for the other bytes + if (padLen > blockSize) { + throw new BadPaddingException("Padding length (" + + padLen + ") of SSLv3 message should not be bigger " + + "than the block size (" + blockSize + ")"); + } + } + + // Reset buffer limit to remove padding. + bb.limit(offset + newLen); + + return newLen; + } + + /* + * A constant-time check of the padding. + * + * NOTE that we are checking both the padding and the padLen bytes here. + * + * The caller MUST ensure that the bb parameter has remaining. + */ + private static int[] checkPadding(ByteBuffer bb, byte pad) { + if (!bb.hasRemaining()) { + throw new RuntimeException("hasRemaining() must be positive"); + } + + // An array of hits is used to prevent Hotspot optimization for + // the purpose of a constant-time check. + int[] results = {0, 0}; // {missed #, matched #} + bb.mark(); + for (int i = 0; i <= 256; bb.reset()) { + for (; bb.hasRemaining() && i <= 256; i++) { + if (bb.get() != pad) { + results[0]++; // mismatched padding data + } else { + results[1]++; // matched padding data + } + } + } + + return results; + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,420 @@ +/* + * 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.AccessControlContext; +import java.security.AccessController; +import java.security.AlgorithmConstraints; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.function.BiFunction; +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; +import sun.security.ssl.SSLExtension.ClientExtensions; +import sun.security.ssl.SSLExtension.ServerExtensions; + +/** + * SSL/(D)TLS configuration. + */ +final class SSLConfiguration implements Cloneable { + // configurations with SSLParameters + AlgorithmConstraints algorithmConstraints; + List enabledProtocols; + List enabledCipherSuites; + ClientAuthType clientAuthType; + String identificationProtocol; + List serverNames; + Collection sniMatchers; + String[] applicationProtocols; + boolean preferLocalCipherSuites; + boolean enableRetransmissions; + int maximumPacketSize; + + // the maximum protocol version of enabled protocols + ProtocolVersion maximumProtocolVersion; + + // Configurations per SSLSocket or SSLEngine instance. + boolean isClientMode; + boolean enableSessionCreation; + + // the application layer protocol negotiation configuration + BiFunction, String> socketAPSelector; + BiFunction, String> engineAPSelector; + + HashMap + handshakeListeners; + + boolean noSniExtension; + boolean noSniMatcher; + + // To switch off the extended_master_secret extension. + static final boolean useExtendedMasterSecret; + + // Allow session resumption without Extended Master Secret extension. + static final boolean allowLegacyResumption = + Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true); + + // Allow full handshake without Extended Master Secret extension. + static final boolean allowLegacyMasterSecret = + Utilities.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true); + + // Allow full handshake without Extended Master Secret extension. + static final boolean useCompatibilityMode = Utilities.getBooleanProperty( + "jdk.tls.client.useCompatibilityMode", true); + +// TODO: Please remove after TLS 1.3 draft interop testing +// delete me +static int tls13VN; + + // Is the extended_master_secret extension supported? + static { + boolean supportExtendedMasterSecret = Utilities.getBooleanProperty( + "jdk.tls.useExtendedMasterSecret", true); + if (supportExtendedMasterSecret) { + try { + JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret"); + } catch (NoSuchAlgorithmException nae) { + supportExtendedMasterSecret = false; + } + } + useExtendedMasterSecret = supportExtendedMasterSecret; + +// delete me +try { + tls13VN = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Integer run() throws Exception { + return Integer.parseInt( + System.getProperty("jdk.tls13.version", "0304"), 16); + } + }); +} catch (PrivilegedActionException ex) { + // blank +} + } + + SSLConfiguration(SSLContextImpl sslContext, boolean isClientMode) { + + // Configurations with SSLParameters, default values. + this.algorithmConstraints = SSLAlgorithmConstraints.DEFAULT; + this.enabledProtocols = + sslContext.getDefaultProtocolVersions(!isClientMode); + this.enabledCipherSuites = + sslContext.getDefaultCipherSuites(!isClientMode); + this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE; + + this.identificationProtocol = null; + this.serverNames = Collections.emptyList(); + this.sniMatchers = Collections.emptyList(); + this.preferLocalCipherSuites = false; + + this.applicationProtocols = new String[0]; + this.enableRetransmissions = sslContext.isDTLS(); + this.maximumPacketSize = 0; // please reset it explicitly later + + this.maximumProtocolVersion = ProtocolVersion.NONE; + for (ProtocolVersion pv : enabledProtocols) { + if (pv.compareTo(maximumProtocolVersion) > 0) { + this.maximumProtocolVersion = pv; + } + } + + // Configurations per SSLSocket or SSLEngine instance. + this.isClientMode = isClientMode; + this.enableSessionCreation = true; + this.socketAPSelector = null; + this.engineAPSelector = null; + + this.handshakeListeners = null; + this.noSniExtension = false; + this.noSniMatcher = false; + } + + SSLParameters getSSLParameters() { + SSLParameters params = new SSLParameters(); + + params.setAlgorithmConstraints(this.algorithmConstraints); + params.setProtocols(ProtocolVersion.toStringArray(enabledProtocols)); + params.setCipherSuites(CipherSuite.namesOf(enabledCipherSuites)); + switch (this.clientAuthType) { + case CLIENT_AUTH_REQUIRED: + params.setNeedClientAuth(true); + break; + case CLIENT_AUTH_REQUESTED: + params.setWantClientAuth(true); + break; + default: + params.setWantClientAuth(false); + } + params.setEndpointIdentificationAlgorithm(this.identificationProtocol); + + if (serverNames.isEmpty() && !noSniExtension) { + // 'null' indicates none has been set + params.setServerNames(null); + } else { + params.setServerNames(this.serverNames); + } + + if (sniMatchers.isEmpty() && !noSniMatcher) { + // 'null' indicates none has been set + params.setSNIMatchers(null); + } else { + params.setSNIMatchers(this.sniMatchers); + } + + params.setApplicationProtocols(this.applicationProtocols); + params.setUseCipherSuitesOrder(this.preferLocalCipherSuites); + params.setEnableRetransmissions(this.enableRetransmissions); + params.setMaximumPacketSize(this.maximumPacketSize); + + return params; + } + + void setSSLParameters(SSLParameters params) { + AlgorithmConstraints ac = params.getAlgorithmConstraints(); + if (ac != null) { + this.algorithmConstraints = ac; + } // otherwise, use the default value + + String[] sa = params.getCipherSuites(); + if (sa != null) { + this.enabledCipherSuites = CipherSuite.validValuesOf(sa); + } // otherwise, use the default values + + sa = params.getProtocols(); + if (sa != null) { + this.enabledProtocols = ProtocolVersion.namesOf(sa); + + this.maximumProtocolVersion = ProtocolVersion.NONE; + for (ProtocolVersion pv : enabledProtocols) { + if (pv.compareTo(maximumProtocolVersion) > 0) { + this.maximumProtocolVersion = pv; + } + } + } // otherwise, use the default values + + if (params.getNeedClientAuth()) { + this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUIRED; + } else if (params.getWantClientAuth()) { + this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUESTED; + } else { + this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE; + } + + String s = params.getEndpointIdentificationAlgorithm(); + if (s != null) { + this.identificationProtocol = s; + } // otherwise, use the default value + + List sniNames = params.getServerNames(); + if (sniNames != null) { + this.noSniExtension = sniNames.isEmpty(); + this.serverNames = sniNames; + } // null if none has been set + + Collection matchers = params.getSNIMatchers(); + if (matchers != null) { + this.noSniMatcher = matchers.isEmpty(); + this.sniMatchers = matchers; + } // null if none has been set + + sa = params.getApplicationProtocols(); + if (sa != null) { + this.applicationProtocols = sa; + } // otherwise, use the default values + + this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); + this.enableRetransmissions = params.getEnableRetransmissions(); + this.maximumPacketSize = params.getMaximumPacketSize(); + } + + // SSLSocket only + void addHandshakeCompletedListener( + HandshakeCompletedListener listener) { + + if (handshakeListeners == null) { + handshakeListeners = new HashMap<>(4); + } + + handshakeListeners.put(listener, AccessController.getContext()); + } + + // SSLSocket only + void removeHandshakeCompletedListener( + HandshakeCompletedListener listener) { + + if (handshakeListeners == null) { + throw new IllegalArgumentException("no listeners"); + } + + if (handshakeListeners.remove(listener) == null) { + throw new IllegalArgumentException("listener not registered"); + } + + if (handshakeListeners.isEmpty()) { + handshakeListeners = null; + } + } + + /** + * Return true if the extension is available. + */ + boolean isAvailable(SSLExtension extension) { + for (ProtocolVersion protocolVersion : enabledProtocols) { + if (extension.isAvailable(protocolVersion)) { + if (isClientMode ? + ClientExtensions.defaults.contains(extension) : + ServerExtensions.defaults.contains(extension)) { + return true; + } + } + } + + return false; + } + + /** + * Return true if the extension is available for the specific protocol. + */ + boolean isAvailable(SSLExtension extension, + ProtocolVersion protocolVersion) { + return extension.isAvailable(protocolVersion) && + (isClientMode ? ClientExtensions.defaults.contains(extension) : + ServerExtensions.defaults.contains(extension)); + } + + /** + * Get the enabled extensions for the specific handshake message. + * + * Used to consume handshake extensions. + */ + SSLExtension[] getEnabledExtensions(SSLHandshake handshakeType) { + List extensions = new ArrayList<>(); + for (SSLExtension extension : SSLExtension.values()) { + if (extension.handshakeType == handshakeType) { + if (isAvailable(extension)) { + extensions.add(extension); + } + } + } + + return extensions.toArray(new SSLExtension[0]); + } + + /** + * Get the enabled extensions for the specific handshake message, excluding + * the specified extensions. + * + * Used to consume handshake extensions. + */ + SSLExtension[] getExclusiveExtensions(SSLHandshake handshakeType, + List excluded) { + List extensions = new ArrayList<>(); + for (SSLExtension extension : SSLExtension.values()) { + if (extension.handshakeType == handshakeType) { + if (isAvailable(extension) && !excluded.contains(extension)) { + extensions.add(extension); + } + } + } + + return extensions.toArray(new SSLExtension[0]); + } + + /** + * Get the enabled extensions for the specific handshake message + * and the specific protocol version. + * + * Used to produce handshake extensions after handshake protocol + * version negotiation. + */ + SSLExtension[] getEnabledExtensions( + SSLHandshake handshakeType, ProtocolVersion protocolVersion) { + return getEnabledExtensions( + handshakeType, Arrays.asList(protocolVersion)); + } + + /** + * Get the enabled extensions for the specific handshake message + * and the specific protocol versions. + * + * Used to produce ClientHello extensions before handshake protocol + * version negotiation. + */ + SSLExtension[] getEnabledExtensions( + SSLHandshake handshakeType, List activeProtocols) { + List extensions = new ArrayList<>(); + for (SSLExtension extension : SSLExtension.values()) { + if (extension.handshakeType == handshakeType) { + if (!isAvailable(extension)) { + continue; + } + + for (ProtocolVersion protocolVersion : activeProtocols) { + if (extension.isAvailable(protocolVersion)) { + extensions.add(extension); + break; + } + } + } + } + + return extensions.toArray(new SSLExtension[0]); + } + + @Override + @SuppressWarnings({"unchecked", "CloneDeclaresCloneNotSupported"}) + public Object clone() { + // Note that only references to the configurations are copied. + try { + SSLConfiguration config = (SSLConfiguration)super.clone(); + if (handshakeListeners != null) { + config.handshakeListeners = + (HashMap) + handshakeListeners.clone(); + } + + return config; + } catch (CloneNotSupportedException cnse) { + // unlikely + } + + return null; // unlikely + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLConsumer.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +interface SSLConsumer { + void consume(ConnectionContext context, + ByteBuffer message) throws IOException; +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, 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 @@ -25,23 +25,25 @@ package sun.security.ssl; +import java.io.*; import java.net.Socket; - -import java.io.*; -import java.util.*; import java.security.*; import java.security.cert.*; -import java.security.cert.Certificate; - +import java.util.*; import javax.net.ssl.*; - +import sun.security.action.GetPropertyAction; import sun.security.provider.certpath.AlgorithmChecker; -import sun.security.action.GetPropertyAction; import sun.security.validator.Validator; -public abstract class SSLContextImpl extends SSLContextSpi { +/** + * Implementation of an SSLContext. + * + * Implementation note: Instances of this class and the child classes are + * immutable, except that the context initialization (SSLContext.init()) may + * reset the key, trust managers and source of secure random. + */ - private static final Debug debug = Debug.getInstance("ssl"); +public abstract class SSLContextImpl extends SSLContextSpi { private final EphemeralKeyManager ephemeralKeyManager; private final SSLSessionContextImpl clientCache; @@ -54,15 +56,15 @@ private SecureRandom secureRandom; // DTLS cookie exchange manager - private volatile HelloCookieManager helloCookieManager; + private volatile HelloCookieManager.Builder helloCookieManagerBuilder; - private final boolean clientEnableStapling = Debug.getBooleanProperty( + private final boolean clientEnableStapling = Utilities.getBooleanProperty( "jdk.tls.client.enableStatusRequestExtension", true); - private final boolean serverEnableStapling = Debug.getBooleanProperty( + private final boolean serverEnableStapling = Utilities.getBooleanProperty( "jdk.tls.server.enableStatusRequestExtension", false); - private final static Collection clientCustomizedCipherSuites = + private static final Collection clientCustomizedCipherSuites = getCustomizedCipherSuites("jdk.tls.client.cipherSuites"); - private final static Collection serverCustomizedCipherSuites = + private static final Collection serverCustomizedCipherSuites = getCustomizedCipherSuites("jdk.tls.server.cipherSuites"); private volatile StatusResponseManager statusResponseManager; @@ -109,12 +111,12 @@ * first connection to timeout and fail. Make sure it is * primed and ready by getting some initial output from it. */ - if (debug != null && Debug.isOn("sslctx")) { - System.out.println("trigger seeding of SecureRandom"); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.finest("trigger seeding of SecureRandom"); } secureRandom.nextInt(); - if (debug != null && Debug.isOn("sslctx")) { - System.out.println("done seeding SecureRandom"); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.finest("done seeding of SecureRandom"); } isInitialized = true; @@ -168,10 +170,10 @@ if (km instanceof X509ExtendedKeyManager) { return (X509ExtendedKeyManager)km; } - if (debug != null && Debug.isOn("sslctx")) { - System.out.println( - "X509KeyManager passed to " + - "SSLContext.init(): need an " + + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.warning( + "X509KeyManager passed to SSLContext.init(): need an " + "X509ExtendedKeyManager for SSLEngine use"); } return new AbstractKeyManagerWrapper((X509KeyManager)km); @@ -242,36 +244,26 @@ return ephemeralKeyManager; } - // Used for DTLS in server mode only, see ServerHandshaker. - HelloCookieManager getHelloCookieManager() { - if (!isInitialized) { - throw new IllegalStateException("SSLContext is not initialized"); - } - - if (helloCookieManager != null) { - return helloCookieManager; - } - - synchronized (this) { - if (helloCookieManager == null) { - helloCookieManager = getHelloCookieManager(secureRandom); + // Used for DTLS in server mode only. + HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) { + if (helloCookieManagerBuilder == null) { + synchronized (this) { + if (helloCookieManagerBuilder == null) { + helloCookieManagerBuilder = + new HelloCookieManager.Builder(secureRandom); + } } } - return helloCookieManager; - } - - HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) { - throw new UnsupportedOperationException( - "Cookie exchange applies to DTLS only"); + return helloCookieManagerBuilder.valueOf(protocolVersion); } StatusResponseManager getStatusResponseManager() { if (serverEnableStapling && statusResponseManager == null) { synchronized (this) { if (statusResponseManager == null) { - if (debug != null && Debug.isOn("sslctx")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.finest( "Initializing StatusResponseManager"); } statusResponseManager = new StatusResponseManager(); @@ -282,52 +274,55 @@ return statusResponseManager; } - // Get supported ProtocolList. - abstract ProtocolList getSuportedProtocolList(); + // Get supported protocols. + abstract List getSupportedProtocolVersions(); + + // Get default protocols for server mode. + abstract List getServerDefaultProtocolVersions(); - // Get default ProtocolList for server mode. - abstract ProtocolList getServerDefaultProtocolList(); + // Get default protocols for client mode. + abstract List getClientDefaultProtocolVersions(); - // Get default ProtocolList for client mode. - abstract ProtocolList getClientDefaultProtocolList(); + // Get supported CipherSuite list. + abstract List getSupportedCipherSuites(); - // Get supported CipherSuiteList. - abstract CipherSuiteList getSupportedCipherSuiteList(); + // Get default CipherSuite list for server mode. + abstract List getServerDefaultCipherSuites(); - // Get default CipherSuiteList for server mode. - abstract CipherSuiteList getServerDefaultCipherSuiteList(); + // Get default CipherSuite list for client mode. + abstract List getClientDefaultCipherSuites(); - // Get default CipherSuiteList for client mode. - abstract CipherSuiteList getClientDefaultCipherSuiteList(); + // Is the context for DTLS protocols? + abstract boolean isDTLS(); - // Get default ProtocolList. - ProtocolList getDefaultProtocolList(boolean roleIsServer) { - return roleIsServer ? getServerDefaultProtocolList() - : getClientDefaultProtocolList(); + // Get default protocols. + List getDefaultProtocolVersions(boolean roleIsServer) { + return roleIsServer ? getServerDefaultProtocolVersions() + : getClientDefaultProtocolVersions(); } - // Get default CipherSuiteList. - CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { - return roleIsServer ? getServerDefaultCipherSuiteList() - : getClientDefaultCipherSuiteList(); + // Get default CipherSuite list. + List getDefaultCipherSuites(boolean roleIsServer) { + return roleIsServer ? getServerDefaultCipherSuites() + : getClientDefaultCipherSuites(); } /** * Return whether a protocol list is the original default enabled * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ - boolean isDefaultProtocolList(ProtocolList protocols) { - return (protocols == getServerDefaultProtocolList()) || - (protocols == getClientDefaultProtocolList()); + boolean isDefaultProtocolVesions(List protocols) { + return (protocols == getServerDefaultProtocolVersions()) || + (protocols == getClientDefaultProtocolVersions()); } /** * Return whether a protocol list is the original default enabled * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ - boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) { - return (cipherSuites == getServerDefaultCipherSuiteList()) || - (cipherSuites == getClientDefaultCipherSuiteList()); + boolean isDefaultCipherSuiteList(List cipherSuites) { + return (cipherSuites == getServerDefaultCipherSuites()) || + (cipherSuites == getClientDefaultCipherSuites()); } /** @@ -342,93 +337,83 @@ return isClient ? clientEnableStapling : serverEnableStapling; } - /* * Return the list of all available CipherSuites that are supported * using currently installed providers. */ - private static CipherSuiteList getApplicableSupportedCipherSuiteList( - ProtocolList protocols) { + private static List getApplicableSupportedCipherSuites( + List protocols) { - return getApplicableCipherSuiteList( - CipherSuite.allowedCipherSuites(), - protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); + return getApplicableCipherSuites( + CipherSuite.allowedCipherSuites(), protocols); } /* * Return the list of all available CipherSuites that are default enabled * in client or server side. */ - private static CipherSuiteList getApplicableEnabledCipherSuiteList( - ProtocolList protocols, boolean isClient) { + private static List getApplicableEnabledCipherSuites( + List protocols, boolean isClient) { if (isClient) { if (!clientCustomizedCipherSuites.isEmpty()) { - return getApplicableCipherSuiteList( - clientCustomizedCipherSuites, - protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); + return getApplicableCipherSuites( + clientCustomizedCipherSuites, protocols); } } else { if (!serverCustomizedCipherSuites.isEmpty()) { - return getApplicableCipherSuiteList( - serverCustomizedCipherSuites, - protocols, CipherSuite.SUPPORTED_SUITES_PRIORITY); + return getApplicableCipherSuites( + serverCustomizedCipherSuites, protocols); } } - return getApplicableCipherSuiteList( - CipherSuite.allowedCipherSuites(), - protocols, CipherSuite.DEFAULT_SUITES_PRIORITY); + return getApplicableCipherSuites( + CipherSuite.defaultCipherSuites(), protocols); } /* * Return the list of available CipherSuites which are applicable to * the specified protocols. */ - private static CipherSuiteList getApplicableCipherSuiteList( + private static List getApplicableCipherSuites( Collection allowedCipherSuites, - ProtocolList protocols, int minPriority) { - + List protocols) { TreeSet suites = new TreeSet<>(); - if (!(protocols.collection().isEmpty()) && - protocols.min.v != ProtocolVersion.NONE.v) { + if (protocols != null && (!protocols.isEmpty())) { for (CipherSuite suite : allowedCipherSuites) { - if (!suite.allowed || suite.priority < minPriority) { + if (!suite.isAvailable()) { continue; } - if (suite.isAvailable() && - !protocols.min.obsoletes(suite) && - protocols.max.supports(suite)) { + boolean isSupported = false; + for (ProtocolVersion protocol : protocols) { + if (!suite.supports(protocol)) { + continue; + } + if (SSLAlgorithmConstraints.DEFAULT.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); - } else { - if (debug != null && Debug.isOn("sslctx") && - Debug.isOn("verbose")) { - System.out.println( - "Ignoring disabled cipher suite: " + - suite.name); - } + isSupported = true; + } else if (SSLLogger.isOn && + SSLLogger.isOn("ssl,sslctx,verbose")) { + SSLLogger.fine( + "Ignore disabled cipher suite: " + suite.name); } - } else if (debug != null && - Debug.isOn("sslctx") && Debug.isOn("verbose")) { - if (protocols.min.obsoletes(suite)) { - System.out.println( - "Ignoring obsoleted cipher suite: " + suite); - } else if (!protocols.max.supports(suite)) { - System.out.println( - "Ignoring unsupported cipher suite: " + suite); - } else { - System.out.println( - "Ignoring unavailable cipher suite: " + suite); - } + + break; + } + + if (!isSupported && SSLLogger.isOn && + SSLLogger.isOn("ssl,sslctx,verbose")) { + SSLLogger.finest( + "Ignore unsupported cipher suite: " + suite); } } } - return new CipherSuiteList(suites); + return new ArrayList<>(suites); } /* @@ -438,8 +423,8 @@ String propertyName) { String property = GetPropertyAction.privilegedGetProperty(propertyName); - if (debug != null && Debug.isOn("sslctx")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.fine( "System property " + propertyName + " is set to '" + property + "'"); } @@ -463,10 +448,10 @@ CipherSuite suite; try { - suite = CipherSuite.valueOf(cipherSuiteNames[i]); + suite = CipherSuite.nameOf(cipherSuiteNames[i]); } catch (IllegalArgumentException iae) { - if (debug != null && Debug.isOn("sslctx")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.fine( "Unknown or unsupported cipher suite name: " + cipherSuiteNames[i]); } @@ -474,11 +459,11 @@ continue; } - if (suite.isAvailable()) { + if (suite != null && suite.isAvailable()) { cipherSuites.add(suite); } else { - if (debug != null && Debug.isOn("sslctx")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) { + SSLLogger.fine( "The current installed providers do not " + "support cipher suite: " + cipherSuiteNames[i]); } @@ -492,23 +477,23 @@ } - private static String[] getAvailableProtocols( + private static List getAvailableProtocols( ProtocolVersion[] protocolCandidates) { - List availableProtocols = Collections.emptyList(); - if (protocolCandidates != null && protocolCandidates.length != 0) { + List availableProtocols = + Collections.emptyList(); + if (protocolCandidates != null && protocolCandidates.length != 0) { availableProtocols = new ArrayList<>(protocolCandidates.length); for (ProtocolVersion p : protocolCandidates) { - if (ProtocolVersion.availableProtocols.contains(p)) { - availableProtocols.add(p.name); + if (p.isAvailable) { + availableProtocols.add(p); } } } - return availableProtocols.toArray(new String[0]); + return availableProtocols; } - /* * The SSLContext implementation for SSL/(D)TLS algorithm * @@ -548,79 +533,108 @@ * @see SSLContext */ private abstract static class AbstractTLSContext extends SSLContextImpl { - private static final ProtocolList supportedProtocolList; - private static final ProtocolList serverDefaultProtocolList; + private static final List supportedProtocols; + private static final List serverDefaultProtocols; - private static final CipherSuiteList supportedCipherSuiteList; - private static final CipherSuiteList serverDefaultCipherSuiteList; + private static final List supportedCipherSuites; + private static final List serverDefaultCipherSuites; static { if (SunJSSE.isFIPS()) { - supportedProtocolList = new ProtocolList(new String[] { - ProtocolVersion.TLS10.name, - ProtocolVersion.TLS11.name, - ProtocolVersion.TLS12.name - }); + supportedProtocols = Arrays.asList( + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + ); - serverDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.TLS10, + serverDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - })); + ProtocolVersion.TLS10 + }); } else { - supportedProtocolList = new ProtocolList(new String[] { - ProtocolVersion.SSL20Hello.name, - ProtocolVersion.SSL30.name, - ProtocolVersion.TLS10.name, - ProtocolVersion.TLS11.name, - ProtocolVersion.TLS12.name - }); + supportedProtocols = Arrays.asList( + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10, + ProtocolVersion.SSL30, + ProtocolVersion.SSL20Hello + ); - serverDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.SSL20Hello, + serverDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10, ProtocolVersion.SSL30, - ProtocolVersion.TLS10, - ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - })); + ProtocolVersion.SSL20Hello + }); } - supportedCipherSuiteList = getApplicableSupportedCipherSuiteList( - supportedProtocolList); - serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - serverDefaultProtocolList, false); + supportedCipherSuites = getApplicableSupportedCipherSuites( + supportedProtocols); + serverDefaultCipherSuites = getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); } @Override - ProtocolList getSuportedProtocolList() { - return supportedProtocolList; + List getSupportedProtocolVersions() { + return supportedProtocols; } @Override - CipherSuiteList getSupportedCipherSuiteList() { - return supportedCipherSuiteList; + List getSupportedCipherSuites() { + return supportedCipherSuites; } @Override - ProtocolList getServerDefaultProtocolList() { - return serverDefaultProtocolList; + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; } @Override - CipherSuiteList getServerDefaultCipherSuiteList() { - return serverDefaultCipherSuiteList; + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; } @Override SSLEngine createSSLEngineImpl() { - return new SSLEngineImpl(this, false); + return new SSLEngineImpl(this); } @Override SSLEngine createSSLEngineImpl(String host, int port) { - return new SSLEngineImpl(this, host, port, false); + return new SSLEngineImpl(this, host, port); + } + + @Override + boolean isDTLS() { + return false; + } + + static ProtocolVersion[] getSupportedProtocols() { + if (SunJSSE.isFIPS()) { + return new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + }; + } else { + return new ProtocolVersion[]{ + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10, + ProtocolVersion.SSL30, + ProtocolVersion.SSL20Hello + }; + } } } @@ -630,35 +644,35 @@ * @see SSLContext */ public static final class TLS10Context extends AbstractTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; static { if (SunJSSE.isFIPS()) { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { ProtocolVersion.TLS10 - })); + }); } else { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.SSL30, - ProtocolVersion.TLS10 - })); + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS10, + ProtocolVersion.SSL30 + }); } - clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } } @@ -668,38 +682,38 @@ * @see SSLContext */ public static final class TLS11Context extends AbstractTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; static { if (SunJSSE.isFIPS()) { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.TLS10, - ProtocolVersion.TLS11 - })); + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + }); } else { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.SSL30, + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS11, ProtocolVersion.TLS10, - ProtocolVersion.TLS11 - })); + ProtocolVersion.SSL30 + }); } - clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } } @@ -709,39 +723,83 @@ * @see SSLContext */ public static final class TLS12Context extends AbstractTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; static { if (SunJSSE.isFIPS()) { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.TLS10, + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS12, ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - })); + ProtocolVersion.TLS10 + }); } else { - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.SSL30, + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, ProtocolVersion.TLS10, - ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - })); + ProtocolVersion.SSL30 + }); } - clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); + } + + @Override + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; + } + } + + /* + * The SSLContext implementation for TLS1.3 algorithm + * + * @see SSLContext + */ + public static final class TLS13Context extends AbstractTLSContext { + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; + + static { + if (SunJSSE.isFIPS()) { + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + }); + } else { + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10, + ProtocolVersion.SSL30 + }); + } + + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; + } + + @Override + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } } @@ -751,10 +809,15 @@ * @see SSLContext */ private static class CustomizedSSLProtocols { - private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; + private static final String JDK_TLS_CLIENT_PROTOCOLS = + "jdk.tls.client.protocols"; + private static final String JDK_TLS_SERVER_PROTOCOLS = + "jdk.tls.server.protocols"; static IllegalArgumentException reservedException = null; - static ArrayList - customizedProtocols = new ArrayList<>(); + static final ArrayList customizedClientProtocols = + new ArrayList<>(); + static final ArrayList customizedServerProtocols = + new ArrayList<>(); // Don't want a java.lang.LinkageError for illegal system property. // @@ -763,9 +826,18 @@ // the provider service. Instead, please handle the initialization // exception in the caller's constructor. static { - String property = GetPropertyAction - .privilegedGetProperty(PROPERTY_NAME); - if (property != null && property.length() != 0) { + populate(JDK_TLS_CLIENT_PROTOCOLS, customizedClientProtocols); + populate(JDK_TLS_SERVER_PROTOCOLS, customizedServerProtocols); + } + + private static void populate(String propname, + ArrayList arrayList) { + String property = GetPropertyAction.privilegedGetProperty(propname); + if (property == null) { + return; + } + + if (property.length() != 0) { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { @@ -773,33 +845,32 @@ } } - if (property != null && property.length() != 0) { + if (property.length() != 0) { String[] protocols = property.split(","); for (int i = 0; i < protocols.length; i++) { protocols[i] = protocols[i].trim(); // Is it a supported protocol name? - try { - ProtocolVersion pro = - ProtocolVersion.valueOf(protocols[i]); - - if (SunJSSE.isFIPS() && - ((pro.v == ProtocolVersion.SSL30.v) || - (pro.v == ProtocolVersion.SSL20Hello.v))) { - reservedException = new IllegalArgumentException( - PROPERTY_NAME + ": " + pro + - " is not FIPS compliant"); + ProtocolVersion pv = + ProtocolVersion.nameOf(protocols[i]); + if (pv == null) { + reservedException = new IllegalArgumentException( + propname + ": " + protocols[i] + + " is not a supported SSL protocol name"); + } - break; - } + if (SunJSSE.isFIPS() && + ((pv == ProtocolVersion.SSL30) || + (pv == ProtocolVersion.SSL20Hello))) { + reservedException = new IllegalArgumentException( + propname + ": " + pv + + " is not FIPS compliant"); - // ignore duplicated protocols - if (!customizedProtocols.contains(pro)) { - customizedProtocols.add(pro); - } - } catch (IllegalArgumentException iae) { - reservedException = new IllegalArgumentException( - PROPERTY_NAME + ": " + protocols[i] + - " is not a standard SSL protocol name", iae); + break; + } + + // ignore duplicated protocols + if (!arrayList.contains(pv)) { + arrayList.add(pv); } } } @@ -813,10 +884,11 @@ */ private static class CustomizedTLSContext extends AbstractTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; - - private static IllegalArgumentException reservedException = null; + private static final List clientDefaultProtocols; + private static final List serverDefaultProtocols; + private static final List clientDefaultCipherSuites; + private static final List serverDefaultCipherSuites; + private static final IllegalArgumentException reservedException; // Don't want a java.lang.LinkageError for illegal system property. // @@ -827,49 +899,69 @@ static { reservedException = CustomizedSSLProtocols.reservedException; if (reservedException == null) { - ArrayList - customizedTLSProtocols = new ArrayList<>(); - for (ProtocolVersion protocol : - CustomizedSSLProtocols.customizedProtocols) { - if (!protocol.isDTLSProtocol()) { - customizedTLSProtocols.add(protocol); - } + clientDefaultProtocols = customizedProtocols(true, + CustomizedSSLProtocols.customizedClientProtocols); + serverDefaultProtocols = customizedProtocols(false, + CustomizedSSLProtocols.customizedServerProtocols); + + clientDefaultCipherSuites = + getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); + serverDefaultCipherSuites = + getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); + + } else { + // unlikely to be used + clientDefaultProtocols = null; + serverDefaultProtocols = null; + clientDefaultCipherSuites = null; + serverDefaultCipherSuites = null; + } + } + + private static List customizedProtocols( + boolean client, List customized) { + List refactored = new ArrayList<>(); + for (ProtocolVersion pv : customized) { + if (!pv.isDTLS) { + refactored.add(pv); } + } - // candidates for available protocols - ProtocolVersion[] candidates; - if (customizedTLSProtocols.isEmpty()) { - // Use the default enabled client protocols if no - // customized TLS protocols. - if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { - ProtocolVersion.TLS10, - ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - }; - } else { - candidates = new ProtocolVersion[] { - ProtocolVersion.SSL30, - ProtocolVersion.TLS10, - ProtocolVersion.TLS11, - ProtocolVersion.TLS12 - }; - } + // Use the default enabled protocols if no customization + ProtocolVersion[] candidates; + if (refactored.isEmpty()) { + if (client) { + candidates = getProtocols(); } else { - // Use the customized TLS protocols. - candidates = - new ProtocolVersion[customizedTLSProtocols.size()]; - candidates = customizedTLSProtocols.toArray(candidates); + candidates = getSupportedProtocols(); } + } else { + // Use the customized TLS protocols. + candidates = + refactored.toArray(new ProtocolVersion[refactored.size()]); + } + + return getAvailableProtocols(candidates); + } - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(candidates)); - clientDefaultCipherSuiteList = - getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + static ProtocolVersion[] getProtocols() { + if (SunJSSE.isFIPS()) { + return new ProtocolVersion[]{ + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10 + }; } else { - clientDefaultProtocolList = null; // unlikely to be used - clientDefaultCipherSuiteList = null; // unlikely to be used + return new ProtocolVersion[]{ + ProtocolVersion.TLS13, + ProtocolVersion.TLS12, + ProtocolVersion.TLS11, + ProtocolVersion.TLS10, + ProtocolVersion.SSL30 + }; } } @@ -880,14 +972,26 @@ } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; + } + + @Override + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } + + @Override + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; + } + + } /* @@ -909,30 +1013,33 @@ private static final TrustManager[] trustManagers; private static final KeyManager[] keyManagers; - static Exception reservedException = null; + private static final Exception reservedException; static { + Exception reserved = null; TrustManager[] tmMediator; try { tmMediator = getTrustManagers(); } catch (Exception e) { - reservedException = e; + reserved = e; tmMediator = new TrustManager[0]; } trustManagers = tmMediator; - if (reservedException == null) { + if (reserved == null) { KeyManager[] kmMediator; try { kmMediator = getKeyManagers(); } catch (Exception e) { - reservedException = e; + reserved = e; kmMediator = new KeyManager[0]; } keyManagers = kmMediator; } else { keyManagers = new KeyManager[0]; } + + reservedException = reserved; } private static TrustManager[] getTrustManagers() throws Exception { @@ -976,11 +1083,11 @@ final String defaultKeyStore = props.get("keyStore"); String defaultKeyStoreType = props.get("keyStoreType"); String defaultKeyStoreProvider = props.get("keyStoreProvider"); - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("keyStore is : " + defaultKeyStore); - System.out.println("keyStore type is : " + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + SSLLogger.fine("keyStore is : " + defaultKeyStore); + SSLLogger.fine("keyStore type is : " + defaultKeyStoreType); - System.out.println("keyStore provider is : " + + SSLLogger.fine("keyStore provider is : " + defaultKeyStoreProvider); } @@ -1014,8 +1121,8 @@ * Try to initialize key store. */ if ((defaultKeyStoreType.length()) != 0) { - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("init keystore"); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + SSLLogger.finest("init keystore"); } if (defaultKeyStoreProvider.length() == 0) { ks = KeyStore.getInstance(defaultKeyStoreType); @@ -1037,8 +1144,8 @@ /* * Try to initialize key manager. */ - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("init keymanager of type " + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + SSLLogger.fine("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance( @@ -1095,8 +1202,8 @@ super.engineInit(DefaultManagersHolder.keyManagers, DefaultManagersHolder.trustManagers, null); } catch (Exception e) { - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("default context init failed: " + e); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,defaultctx")) { + SSLLogger.fine("default context init failed: ", e); } throw e; } @@ -1128,65 +1235,65 @@ * @see SSLContext */ private abstract static class AbstractDTLSContext extends SSLContextImpl { - private static final ProtocolList supportedProtocolList; - private static final ProtocolList serverDefaultProtocolList; + private static final List supportedProtocols; + private static final List serverDefaultProtocols; - private static final CipherSuiteList supportedCipherSuiteList; - private static final CipherSuiteList serverDefaultCipherSuiteList; + private static final List supportedCipherSuites; + private static final List serverDefaultCipherSuites; static { // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode. - supportedProtocolList = new ProtocolList(new String[] { - ProtocolVersion.DTLS10.name, - ProtocolVersion.DTLS12.name - }); + supportedProtocols = Arrays.asList( + ProtocolVersion.DTLS12, + ProtocolVersion.DTLS10 + ); // available protocols for server mode - serverDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.DTLS10, - ProtocolVersion.DTLS12 - })); + serverDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.DTLS12, + ProtocolVersion.DTLS10 + }); - supportedCipherSuiteList = getApplicableSupportedCipherSuiteList( - supportedProtocolList); - serverDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - serverDefaultProtocolList, false); + supportedCipherSuites = getApplicableSupportedCipherSuites( + supportedProtocols); + serverDefaultCipherSuites = getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); } @Override - ProtocolList getSuportedProtocolList() { - return supportedProtocolList; + List getSupportedProtocolVersions() { + return supportedProtocols; } @Override - CipherSuiteList getSupportedCipherSuiteList() { - return supportedCipherSuiteList; + List getSupportedCipherSuites() { + return supportedCipherSuites; } @Override - ProtocolList getServerDefaultProtocolList() { - return serverDefaultProtocolList; + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; } @Override - CipherSuiteList getServerDefaultCipherSuiteList() { - return serverDefaultCipherSuiteList; + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; } @Override SSLEngine createSSLEngineImpl() { - return new SSLEngineImpl(this, true); + return new SSLEngineImpl(this); } @Override SSLEngine createSSLEngineImpl(String host, int port) { - return new SSLEngineImpl(this, host, port, true); + return new SSLEngineImpl(this, host, port); } @Override - HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) { - return new HelloCookieManager(secureRandom); + boolean isDTLS() { + return true; } } @@ -1196,28 +1303,28 @@ * @see SSLContext */ public static final class DTLS10Context extends AbstractDTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; static { // available protocols for client mode - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { ProtocolVersion.DTLS10 - })); + }); - clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } } @@ -1227,29 +1334,29 @@ * @see SSLContext */ public static final class DTLS12Context extends AbstractDTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List clientDefaultCipherSuites; static { // available protocols for client mode - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(new ProtocolVersion[] { - ProtocolVersion.DTLS10, - ProtocolVersion.DTLS12 - })); + clientDefaultProtocols = getAvailableProtocols( + new ProtocolVersion[] { + ProtocolVersion.DTLS12, + ProtocolVersion.DTLS10 + }); - clientDefaultCipherSuiteList = getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); + clientDefaultCipherSuites = getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; } } @@ -1259,8 +1366,10 @@ * @see SSLContext */ private static class CustomizedDTLSContext extends AbstractDTLSContext { - private static final ProtocolList clientDefaultProtocolList; - private static final CipherSuiteList clientDefaultCipherSuiteList; + private static final List clientDefaultProtocols; + private static final List serverDefaultProtocols; + private static final List clientDefaultCipherSuites; + private static final List serverDefaultCipherSuites; private static IllegalArgumentException reservedException = null; @@ -1273,43 +1382,53 @@ static { reservedException = CustomizedSSLProtocols.reservedException; if (reservedException == null) { - ArrayList - customizedDTLSProtocols = new ArrayList<>(); - for (ProtocolVersion protocol : - CustomizedSSLProtocols.customizedProtocols) { - if (protocol.isDTLSProtocol()) { - customizedDTLSProtocols.add(protocol); - } - } + clientDefaultProtocols = customizedProtocols(true, + CustomizedSSLProtocols.customizedClientProtocols); + serverDefaultProtocols = customizedProtocols(false, + CustomizedSSLProtocols.customizedServerProtocols); + + clientDefaultCipherSuites = + getApplicableEnabledCipherSuites( + clientDefaultProtocols, true); + serverDefaultCipherSuites = + getApplicableEnabledCipherSuites( + serverDefaultProtocols, false); + + } else { + // unlikely to be used + clientDefaultProtocols = null; + serverDefaultProtocols = null; + clientDefaultCipherSuites = null; + serverDefaultCipherSuites = null; + } + } - // candidates for available protocols - ProtocolVersion[] candidates; - if (customizedDTLSProtocols.isEmpty()) { - // Use the default enabled client protocols if no - // customized TLS protocols. - // - // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode. - candidates = new ProtocolVersion[] { - ProtocolVersion.DTLS10, - ProtocolVersion.DTLS12 - }; + private static List customizedProtocols(boolean client, + List customized) { + List refactored = new ArrayList<>(); + for (ProtocolVersion pv : customized) { + if (pv.isDTLS) { + refactored.add(pv); + } + } - } else { - // Use the customized TLS protocols. - candidates = - new ProtocolVersion[customizedDTLSProtocols.size()]; - candidates = customizedDTLSProtocols.toArray(candidates); - } + ProtocolVersion[] candidates; + // Use the default enabled protocols if no customization + if (refactored.isEmpty()) { + candidates = new ProtocolVersion[]{ + ProtocolVersion.DTLS12, + ProtocolVersion.DTLS10 + }; + if (!client) + return Arrays.asList(candidates); + } else { + // Use the customized TLS protocols. + candidates = + new ProtocolVersion[customized.size()]; + candidates = customized.toArray(candidates); + } - clientDefaultProtocolList = new ProtocolList( - getAvailableProtocols(candidates)); - clientDefaultCipherSuiteList = - getApplicableEnabledCipherSuiteList( - clientDefaultProtocolList, true); - } else { - clientDefaultProtocolList = null; // unlikely to be used - clientDefaultCipherSuiteList = null; // unlikely to be used - } + return getAvailableProtocols(candidates); } protected CustomizedDTLSContext() { @@ -1319,13 +1438,23 @@ } @Override - ProtocolList getClientDefaultProtocolList() { - return clientDefaultProtocolList; + List getClientDefaultProtocolVersions() { + return clientDefaultProtocols; } @Override - CipherSuiteList getClientDefaultCipherSuiteList() { - return clientDefaultCipherSuiteList; + List getServerDefaultProtocolVersions() { + return serverDefaultProtocols; + } + + @Override + List getClientDefaultCipherSuites() { + return clientDefaultCipherSuites; + } + + @Override + List getServerDefaultCipherSuites() { + return serverDefaultCipherSuites; } } @@ -1340,7 +1469,6 @@ } - final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager implements X509TrustManager { @@ -1417,10 +1545,8 @@ } // try the best to check the algorithm constraints - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); - AlgorithmConstraints constraints = null; - if (protocolVersion.useTLS12PlusSpec()) { + AlgorithmConstraints constraints; + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; @@ -1459,10 +1585,8 @@ } // try the best to check the algorithm constraints - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); - AlgorithmConstraints constraints = null; - if (protocolVersion.useTLS12PlusSpec()) { + AlgorithmConstraints constraints; + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; @@ -1484,8 +1608,8 @@ } private void checkAlgorithmConstraints(X509Certificate[] chain, - AlgorithmConstraints constraints, boolean isClient) throws CertificateException { - + AlgorithmConstraints constraints, + boolean isClient) throws CertificateException { try { // Does the certificate chain end with a trusted certificate? int checkedLength = chain.length - 1; @@ -1503,11 +1627,12 @@ // A forward checker, need to check from trust to target if (checkedLength >= 0) { AlgorithmChecker checker = - new AlgorithmChecker(constraints, null, - (isClient ? Validator.VAR_TLS_CLIENT : Validator.VAR_TLS_SERVER)); + new AlgorithmChecker(constraints, null, + (isClient ? Validator.VAR_TLS_CLIENT : + Validator.VAR_TLS_SERVER)); checker.init(false); for (int i = checkedLength; i >= 0; i--) { - Certificate cert = chain[i]; + X509Certificate cert = chain[i]; // We don't care about the unresolved critical extensions. checker.check(cert, Collections.emptySet()); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLCredentials.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLCredentials.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +interface SSLCredentials { +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 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 @@ -25,761 +25,468 @@ package sun.security.ssl; -import java.io.*; -import java.nio.*; -import java.security.*; -import java.util.*; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ReadOnlyBufferException; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.List; +import java.util.Map; import java.util.function.BiFunction; - -import javax.crypto.BadPaddingException; - -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLKeyException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.SSLSession; /** * Implementation of an non-blocking SSLEngine. * - * *Currently*, the SSLEngine code exists in parallel with the current - * SSLSocket. As such, the current implementation is using legacy code - * with many of the same abstractions. However, it varies in many - * areas, most dramatically in the IO handling. - * - * There are three main I/O threads that can be existing in parallel: - * wrap(), unwrap(), and beginHandshake(). We are encouraging users to - * not call multiple instances of wrap or unwrap, because the data could - * appear to flow out of the SSLEngine in a non-sequential order. We - * take all steps we can to at least make sure the ordering remains - * consistent, but once the calls returns, anything can happen. For - * example, thread1 and thread2 both call wrap, thread1 gets the first - * packet, thread2 gets the second packet, but thread2 gets control back - * before thread1, and sends the data. The receiving side would see an - * out-of-order error. - * * @author Brad Wetmore */ -public final class SSLEngineImpl extends SSLEngine { - - // - // Fields and global comments - // - - /* - * There's a state machine associated with each connection, which - * among other roles serves to negotiate session changes. - * - * - START with constructor, until the TCP connection's around. - * - HANDSHAKE picks session parameters before allowing traffic. - * There are many substates due to sequencing requirements - * for handshake messages. - * - DATA may be transmitted. - * - RENEGOTIATE state allows concurrent data and handshaking - * traffic ("same" substates as HANDSHAKE), and terminates - * in selection of new session (and connection) parameters - * - ERROR state immediately precedes abortive disconnect. - * - CLOSED when one side closes down, used to start the shutdown - * process. SSL connection objects are not reused. - * - * State affects what SSL record types may legally be sent: - * - * - Handshake ... only in HANDSHAKE and RENEGOTIATE states - * - App Data ... only in DATA and RENEGOTIATE states - * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE - * - * Re what may be received: same as what may be sent, except that - * HandshakeRequest handshaking messages can come from servers even - * in the application data state, to request entry to RENEGOTIATE. - * - * The state machine within HANDSHAKE and RENEGOTIATE states controls - * the pending session, not the connection state, until the change - * cipher spec and "Finished" handshake messages are processed and - * make the "new" session become the current one. - * - * NOTE: details of the SMs always need to be nailed down better. - * The text above illustrates the core ideas. - * - * +---->-------+------>--------->-------+ - * | | | - * <-----< ^ ^ <-----< | - *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE | - * v v v | - * | | | | - * +------------+---------------+ | - * | | - * v | - * ERROR>------>----->CLOSED<--------<----+ - * - * ALSO, note that the purpose of handshaking (renegotiation is - * included) is to assign a different, and perhaps new, session to - * the connection. The SSLv3 spec is a bit confusing on that new - * protocol feature. - */ - private int connectionState; - - private static final int cs_START = 0; - private static final int cs_HANDSHAKE = 1; - private static final int cs_DATA = 2; - private static final int cs_RENEGOTIATE = 3; - private static final int cs_ERROR = 4; - private static final int cs_CLOSED = 6; - - /* - * Once we're in state cs_CLOSED, we can continue to - * wrap/unwrap until we finish sending/receiving the messages - * for close_notify. - */ - private boolean inboundDone = false; - private boolean outboundDone = false; - - /* - * The authentication context holds all information used to establish - * who this end of the connection is (certificate chains, private keys, - * etc) and who is trusted (e.g. as CAs or websites). - */ - private SSLContextImpl sslContext; - - /* - * This connection is one of (potentially) many associated with - * any given session. The output of the handshake protocol is a - * new session ... although all the protocol description talks - * about changing the cipher spec (and it does change), in fact - * that's incidental since it's done by changing everything that - * is associated with a session at the same time. (TLS/IETF may - * change that to add client authentication w/o new key exchg.) - */ - private Handshaker handshaker; - private SSLSessionImpl sess; - private volatile SSLSessionImpl handshakeSession; - - /* - * Flag indicating if the next record we receive MUST be a Finished - * message. Temporarily set during the handshake to ensure that - * a change cipher spec message is followed by a finished message. - */ - private boolean expectingFinished; - - - /* - * If someone tries to closeInbound() (say at End-Of-Stream) - * our engine having received a close_notify, we need to - * notify the app that we may have a truncation attack underway. - */ - private boolean recvCN; - - /* - * For improved diagnostics, we detail connection closure - * If the engine is closed (connectionState >= cs_ERROR), - * closeReason != null indicates if the engine was closed - * because of an error or because or normal shutdown. - */ - private SSLException closeReason; - - /* - * Per-connection private state that doesn't change when the - * session is changed. - */ - private ClientAuthType doClientAuth = - ClientAuthType.CLIENT_AUTH_NONE; - private boolean enableSessionCreation = true; - InputRecord inputRecord; - OutputRecord outputRecord; - private AccessControlContext acc; - - // The cipher suites enabled for use on this connection. - private CipherSuiteList enabledCipherSuites; - - // the endpoint identification protocol - private String identificationProtocol = null; - - // The cryptographic algorithm constraints - private AlgorithmConstraints algorithmConstraints = null; - - // The server name indication and matchers - List serverNames = - Collections.emptyList(); - Collection sniMatchers = - Collections.emptyList(); - - // Configured application protocol values - String[] applicationProtocols = new String[0]; - - // Negotiated application protocol value. - // - // The value under negotiation will be obtained from handshaker. - String applicationProtocol = null; - - // Callback function that selects the application protocol value during - // the SSL/TLS handshake. - BiFunction, String> applicationProtocolSelector; - - // Have we been told whether we're client or server? - private boolean serverModeSet = false; - private boolean roleIsServer; - - /* - * The protocol versions enabled for use on this connection. - * - * Note: we support a pseudo protocol called SSLv2Hello which when - * set will result in an SSL v2 Hello being sent with SSL (version 3.0) - * or TLS (version 3.1, 3.2, etc.) version info. - */ - private ProtocolList enabledProtocols; - - /* - * The SSL version associated with this connection. - */ - private ProtocolVersion protocolVersion; - - /* - * security parameters for secure renegotiation. - */ - private boolean secureRenegotiation; - private byte[] clientVerifyData; - private byte[] serverVerifyData; - - /* - * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * - * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES. - * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * - * - * There are several locks here. - * - * The primary lock is the per-instance lock used by - * synchronized(this) and the synchronized methods. It controls all - * access to things such as the connection state and variables which - * affect handshaking. If we are inside a synchronized method, we - * can access the state directly, otherwise, we must use the - * synchronized equivalents. - * - * Note that we must never acquire the this lock after - * writeLock or run the risk of deadlock. - * - * Grab some coffee, and be careful with any code changes. - */ - private Object wrapLock; - private Object unwrapLock; - Object writeLock; - - /* - * Whether local cipher suites preference in server side should be - * honored during handshaking? - */ - private boolean preferLocalCipherSuites = false; - - /* - * whether DTLS handshake retransmissions should be enabled? - */ - private boolean enableRetransmissions = false; - - /* - * The maximum expected network packet size for SSL/TLS/DTLS records. - */ - private int maximumPacketSize = 0; - - /* - * Is this an instance for Datagram Transport Layer Security (DTLS)? - */ - private final boolean isDTLS; - - /* - * Class and subclass dynamic debugging support - */ - private static final Debug debug = Debug.getInstance("ssl"); - - // - // Initialization/Constructors - // +final class SSLEngineImpl extends SSLEngine implements SSLTransport { + private final SSLContextImpl sslContext; + final TransportContext conContext; /** * Constructor for an SSLEngine from SSLContext, without - * host/port hints. This Engine will not be able to cache - * sessions, but must renegotiate everything by hand. + * host/port hints. + * + * This Engine will not be able to cache sessions, but must renegotiate + * everything by hand. */ - SSLEngineImpl(SSLContextImpl ctx, boolean isDTLS) { - super(); - this.isDTLS = isDTLS; - init(ctx, isDTLS); + SSLEngineImpl(SSLContextImpl sslContext) { + this(sslContext, null, -1); } /** * Constructor for an SSLEngine from SSLContext. */ - SSLEngineImpl(SSLContextImpl ctx, String host, int port, boolean isDTLS) { + SSLEngineImpl(SSLContextImpl sslContext, + String host, int port) { super(host, port); - this.isDTLS = isDTLS; - init(ctx, isDTLS); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + if (sslContext.isDTLS()) { + this.conContext = new TransportContext(sslContext, this, + new DTLSInputRecord(handshakeHash), + new DTLSOutputRecord(handshakeHash)); + } else { + this.conContext = new TransportContext(sslContext, this, + new SSLEngineInputRecord(handshakeHash), + new SSLEngineOutputRecord(handshakeHash)); + } + + // Server name indication is a connection scope extension. + if (host != null) { + this.conContext.sslConfig.serverNames = + Utilities.addToSNIServerNameList( + conContext.sslConfig.serverNames, host); + } + } + + @Override + public synchronized void beginHandshake() throws SSLException { + if (conContext.isUnsureMode) { + throw new IllegalStateException( + "Client/Server mode has not yet been set."); + } + + try { + conContext.kickstart(); + } catch (IOException ioe) { + conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", ioe); + } catch (Exception ex) { // including RuntimeException + conContext.fatal(Alert.INTERNAL_ERROR, + "Fail to begin handshake", ex); + } + } + + @Override + public synchronized SSLEngineResult wrap(ByteBuffer[] appData, + int offset, int length, ByteBuffer netData) throws SSLException { + return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1); } - /** - * Initializes the Engine - */ - private void init(SSLContextImpl ctx, boolean isDTLS) { - if (debug != null && Debug.isOn("ssl")) { - System.out.println("Using SSLEngineImpl."); + // @Override + public synchronized SSLEngineResult wrap( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException { + + if (conContext.isUnsureMode) { + throw new IllegalStateException( + "Client/Server mode has not yet been set."); + } + + // See if the handshaker needs to report back some SSLException. + if (conContext.outputRecord.isEmpty()) { + checkTaskThrown(); + } // Otherwise, deliver cached records before throwing task exception. + + // check parameters + checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); + + try { + return writeRecord( + srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); + } catch (SSLProtocolException spe) { + // may be an unexpected handshake message + conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe); + } catch (IOException ioe) { + conContext.fatal(Alert.INTERNAL_ERROR, + "problem wrapping app data", ioe); + } catch (Exception ex) { // including RuntimeException + conContext.fatal(Alert.INTERNAL_ERROR, + "Fail to wrap application data", ex); } - sslContext = ctx; - sess = SSLSessionImpl.nullSession; - handshakeSession = null; - protocolVersion = isDTLS ? - ProtocolVersion.DEFAULT_DTLS : ProtocolVersion.DEFAULT_TLS; + return null; // make compiler happy + } + + private SSLEngineResult writeRecord( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { + + if (isOutboundDone()) { + return new SSLEngineResult( + Status.CLOSED, getHandshakeStatus(), 0, 0); + } + + HandshakeContext hc = conContext.handshakeContext; + HandshakeStatus hsStatus = null; + if (!conContext.isNegotiated && + !conContext.isClosed() && !conContext.isBroken) { + conContext.kickstart(); + + hsStatus = getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_UNWRAP) { + /* + * For DTLS, if the handshake state is + * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap() + * means that the previous handshake packets (if delivered) + * get lost, and need retransmit the handshake messages. + */ + if (!sslContext.isDTLS() || hc == null || + !hc.sslConfig.enableRetransmissions || + conContext.outputRecord.firstMessage) { + + return new SSLEngineResult(Status.OK, hsStatus, 0, 0); + } // otherwise, need retransmission + } + } + + if (hsStatus == null) { + hsStatus = getHandshakeStatus(); + } /* - * State is cs_START until we initialize the handshaker. - * - * Apps using SSLEngine are probably going to be server. - * Somewhat arbitrary choice. + * If we have a task outstanding, this *MUST* be done before + * doing any more wrapping, because we could be in the middle + * of receiving a handshake message, for example, a finished + * message which would change the ciphers. */ - roleIsServer = true; - connectionState = cs_START; + if (hsStatus == HandshakeStatus.NEED_TASK) { + return new SSLEngineResult(Status.OK, hsStatus, 0, 0); + } + + int dstsRemains = 0; + for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) { + dstsRemains += dsts[i].remaining(); + } - // default server name indication - serverNames = - Utilities.addToSNIServerNameList(serverNames, getPeerHost()); + // Check destination buffer size. + // + // We can be smarter about using smaller buffer sizes later. For + // now, force it to be large enough to handle any valid record. + if (dstsRemains < conContext.conSession.getPacketBufferSize()) { + return new SSLEngineResult( + Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); + } - // default security parameters for secure renegotiation - secureRenegotiation = false; - clientVerifyData = new byte[0]; - serverVerifyData = new byte[0]; + int srcsRemains = 0; + for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) { + srcsRemains += srcs[i].remaining(); + } - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(roleIsServer); - enabledProtocols = - sslContext.getDefaultProtocolList(roleIsServer); + Ciphertext ciphertext = null; + try { + // Acquire the buffered to-be-delivered records or retransmissions. + // + // May have buffered records, or need retransmission if handshaking. + if (!conContext.outputRecord.isEmpty() || (hc != null && + hc.sslConfig.enableRetransmissions && + hc.sslContext.isDTLS() && + hsStatus == HandshakeStatus.NEED_UNWRAP)) { + ciphertext = encode(null, 0, 0, + dsts, dstsOffset, dstsLength); + } - wrapLock = new Object(); - unwrapLock = new Object(); - writeLock = new Object(); + if (ciphertext == null && srcsRemains != 0) { + ciphertext = encode(srcs, srcsOffset, srcsLength, + dsts, dstsOffset, dstsLength); + } + } catch (IOException ioe) { + if (ioe instanceof SSLException) { + throw ioe; + } else { + throw new SSLException("Write problems", ioe); + } + } /* - * Save the Access Control Context. This will be used later - * for a couple of things, including providing a context to - * run tasks in, and for determining which credentials - * to use for Subject based (JAAS) decisions + * Check for status. */ - acc = AccessController.getContext(); + Status status = (isOutboundDone() ? Status.CLOSED : Status.OK); + if (ciphertext != null && ciphertext.handshakeStatus != null) { + hsStatus = ciphertext.handshakeStatus; + } else { + hsStatus = getHandshakeStatus(); + } + + int deltaSrcs = srcsRemains; + for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) { + deltaSrcs -= srcs[i].remaining(); + } + + int deltaDsts = dstsRemains; + for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) { + deltaDsts -= dsts[i].remaining(); + } - /* - * All outbound application data goes through this OutputRecord, - * other data goes through their respective records created - * elsewhere. All inbound data goes through this one - * input record. - */ - if (isDTLS) { - enableRetransmissions = true; + return new SSLEngineResult(status, hsStatus, deltaSrcs, deltaDsts, + ciphertext != null ? ciphertext.recordSN : -1L); + } + + private Ciphertext encode( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { - // SSLEngine needs no record local buffer - outputRecord = new DTLSOutputRecord(); - inputRecord = new DTLSInputRecord(); + Ciphertext ciphertext = null; + try { + ciphertext = conContext.outputRecord.encode( + srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); + } catch (SSLHandshakeException she) { + // may be record sequence number overflow + conContext.fatal(Alert.HANDSHAKE_FAILURE, she); + } catch (IOException e) { + conContext.fatal(Alert.UNEXPECTED_MESSAGE, e); + } - } else { - outputRecord = new SSLEngineOutputRecord(); - inputRecord = new SSLEngineInputRecord(); + if (ciphertext == null) { + return Ciphertext.CIPHERTEXT_NULL; } - maximumPacketSize = outputRecord.getMaxPacketSize(); + // Is the handshake completed? + boolean needRetransmission = + conContext.sslContext.isDTLS() && + conContext.handshakeContext != null && + conContext.handshakeContext.sslConfig.enableRetransmissions; + HandshakeStatus hsStatus = + tryToFinishHandshake(ciphertext.contentType); + if (needRetransmission && + hsStatus == HandshakeStatus.FINISHED && + conContext.sslContext.isDTLS() && + ciphertext.handshakeType == SSLHandshake.FINISHED.id) { + // Retransmit the last flight for DTLS. + // + // The application data transactions may begin immediately + // after the last flight. If the last flight get lost, the + // application data may be discarded accordingly. As could + // be an issue for some applications. This impact can be + // mitigated by sending the last fligth twice. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + SSLLogger.finest("retransmit the last flight messages"); + } + + conContext.outputRecord.launchRetransmission(); + hsStatus = HandshakeStatus.NEED_WRAP; + } + + if (hsStatus == null) { + hsStatus = conContext.getHandshakeStatus(); + } + + // Is the sequence number is nearly overflow? + if (conContext.outputRecord.seqNumIsHuge()) { + hsStatus = tryKeyUpdate(hsStatus); + } + + // update context status + ciphertext.handshakeStatus = hsStatus; + + return ciphertext; + } + + private HandshakeStatus tryToFinishHandshake(byte contentType) { + HandshakeStatus hsStatus = null; + if ((contentType == ContentType.HANDSHAKE.id) && + conContext.outputRecord.isEmpty()) { + if (conContext.handshakeContext == null) { + hsStatus = HandshakeStatus.FINISHED; + } else if (conContext.isPostHandshakeContext()) { + // unlikely, but just in case. + hsStatus = conContext.finishPostHandshake(); + } else if (conContext.handshakeContext.handshakeFinished) { + hsStatus = conContext.finishHandshake(); + } + } // Otherwise, the followed call to getHSStatus() will help. + + return hsStatus; } /** - * Initialize the handshaker object. This means: - * - * . if a handshake is already in progress (state is cs_HANDSHAKE - * or cs_RENEGOTIATE), do nothing and return - * - * . if the engine is already closed, throw an Exception (internal error) + * Try renegotiation or key update for sequence number wrap. * - * . otherwise (cs_START or cs_DATA), create the appropriate handshaker - * object and advance the connection state (to cs_HANDSHAKE or - * cs_RENEGOTIATE, respectively). - * - * This method is called right after a new engine is created, when - * starting renegotiation, or when changing client/server mode of the - * engine. + * Note that in order to maintain the handshake status properly, we check + * the sequence number after the last record reading/writing process. As + * we request renegotiation or close the connection for wrapped sequence + * number when there is enough sequence number space left to handle a few + * more records, so the sequence number of the last record cannot be + * wrapped. */ - private void initHandshaker() { - switch (connectionState) { - - // - // Starting a new handshake. - // - case cs_START: - case cs_DATA: - break; - - // - // We're already in the middle of a handshake. - // - case cs_HANDSHAKE: - case cs_RENEGOTIATE: - return; - - // - // Anyone allowed to call this routine is required to - // do so ONLY if the connection state is reasonable... - // - default: - throw new IllegalStateException("Internal error"); - } - - // state is either cs_START or cs_DATA - if (connectionState == cs_START) { - connectionState = cs_HANDSHAKE; - } else { // cs_DATA - connectionState = cs_RENEGOTIATE; + private HandshakeStatus tryKeyUpdate( + HandshakeStatus currentHandshakeStatus) throws IOException { + // Don't bother to kickstart the renegotiation or key update when the + // local is asking for it. + if ((conContext.handshakeContext == null) && + !conContext.isClosed() && !conContext.isBroken) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("key update to wrap sequence number"); + } + conContext.keyUpdate(); + return conContext.getHandshakeStatus(); } - if (roleIsServer) { - handshaker = new ServerHandshaker(this, sslContext, - enabledProtocols, doClientAuth, - protocolVersion, connectionState == cs_HANDSHAKE, - secureRenegotiation, clientVerifyData, serverVerifyData, - isDTLS); - handshaker.setSNIMatchers(sniMatchers); - handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); - } else { - handshaker = new ClientHandshaker(this, sslContext, - enabledProtocols, - protocolVersion, connectionState == cs_HANDSHAKE, - secureRenegotiation, clientVerifyData, serverVerifyData, - isDTLS); - handshaker.setSNIServerNames(serverNames); - } - handshaker.setMaximumPacketSize(maximumPacketSize); - handshaker.setEnabledCipherSuites(enabledCipherSuites); - handshaker.setEnableSessionCreation(enableSessionCreation); - handshaker.setApplicationProtocols(applicationProtocols); - handshaker.setApplicationProtocolSelectorSSLEngine( - applicationProtocolSelector); - - outputRecord.initHandshaker(); - } - - /* - * Report the current status of the Handshaker - */ - private HandshakeStatus getHSStatus(HandshakeStatus hss) { - - if (hss != null) { - return hss; - } - - synchronized (this) { - if (!outputRecord.isEmpty()) { - // If no handshaking, special case to wrap alters. - return HandshakeStatus.NEED_WRAP; - } else if (handshaker != null) { - if (handshaker.taskOutstanding()) { - return HandshakeStatus.NEED_TASK; - } else if (isDTLS && !inputRecord.isEmpty()) { - return HandshakeStatus.NEED_UNWRAP_AGAIN; - } else { - return HandshakeStatus.NEED_UNWRAP; - } - } else if (connectionState == cs_CLOSED) { - /* - * Special case where we're closing, but - * still need the close_notify before we - * can officially be closed. - * - * Note isOutboundDone is taken care of by - * hasOutboundData() above. - */ - if (!isInboundDone()) { - return HandshakeStatus.NEED_UNWRAP; - } // else not handshaking - } - - return HandshakeStatus.NOT_HANDSHAKING; - } - } - - private synchronized void checkTaskThrown() throws SSLException { - if (handshaker != null) { - handshaker.checkThrown(); - } - } - - // - // Handshaking and connection state code - // - - /* - * Provides "this" synchronization for connection state. - * Otherwise, you can access it directly. - */ - private synchronized int getConnectionState() { - return connectionState; + return currentHandshakeStatus; } - private synchronized void setConnectionState(int state) { - connectionState = state; - } - - /* - * Get the Access Control Context. - * - * Used for a known context to - * run tasks in, and for determining which credentials - * to use for Subject-based (JAAS) decisions. - */ - AccessControlContext getAcc() { - return acc; - } + private static void checkParams( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) { - /* - * Is a handshake currently underway? - */ - @Override - public SSLEngineResult.HandshakeStatus getHandshakeStatus() { - return getHSStatus(null); - } + if ((srcs == null) || (dsts == null)) { + throw new IllegalArgumentException( + "source or destination buffer is null"); + } - /* - * used by Handshaker to change the active write cipher, follows - * the output of the CCS message. - * - * Also synchronized on "this" from readRecord/delegatedTask. - */ - void changeWriteCiphers() throws IOException { - - Authenticator writeAuthenticator; - CipherBox writeCipher; - try { - writeCipher = handshaker.newWriteCipher(); - writeAuthenticator = handshaker.newWriteAuthenticator(); - } catch (GeneralSecurityException e) { - // "can't happen" - throw new SSLException("Algorithm missing: ", e); + if ((dstsOffset < 0) || (dstsLength < 0) || + (dstsOffset > dsts.length - dstsLength)) { + throw new IndexOutOfBoundsException( + "index out of bound of the destination buffers"); } - outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher); - } - - /* - * Updates the SSL version associated with this connection. - * Called from Handshaker once it has determined the negotiated version. - */ - synchronized void setVersion(ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - outputRecord.setVersion(protocolVersion); - } - + if ((srcsOffset < 0) || (srcsLength < 0) || + (srcsOffset > srcs.length - srcsLength)) { + throw new IndexOutOfBoundsException( + "index out of bound of the source buffers"); + } - /** - * Kickstart the handshake if it is not already in progress. - * This means: - * - * . if handshaking is already underway, do nothing and return - * - * . if the engine is not connected or already closed, throw an - * Exception. - * - * . otherwise, call initHandshake() to initialize the handshaker - * object and progress the state. Then, send the initial - * handshaking message if appropriate (always on clients and - * on servers when renegotiating). - */ - private synchronized void kickstartHandshake() throws IOException { - switch (connectionState) { - - case cs_START: - if (!serverModeSet) { - throw new IllegalStateException( - "Client/Server mode not yet set."); - } - initHandshaker(); - break; - - case cs_HANDSHAKE: - // handshaker already setup, proceed - break; - - case cs_DATA: - if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { - throw new SSLHandshakeException( - "Insecure renegotiation is not allowed"); + for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) { + if (dsts[i] == null) { + throw new IllegalArgumentException( + "destination buffer[" + i + "] == null"); } - if (!secureRenegotiation) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning: Using insecure renegotiation"); - } + /* + * Make sure the destination bufffers are writable. + */ + if (dsts[i].isReadOnly()) { + throw new ReadOnlyBufferException(); } - - // initialize the handshaker, move to cs_RENEGOTIATE - initHandshaker(); - break; - - case cs_RENEGOTIATE: - // handshaking already in progress, return - return; - - default: - // cs_ERROR/cs_CLOSED - throw new SSLException("SSLEngine is closing/closed"); } - // - // Kickstart handshake state machine if we need to ... - // - if (!handshaker.activated()) { - // prior to handshaking, activate the handshake - if (connectionState == cs_RENEGOTIATE) { - // don't use SSLv2Hello when renegotiating - handshaker.activate(protocolVersion); - } else { - handshaker.activate(null); - } - - if (handshaker instanceof ClientHandshaker) { - // send client hello - handshaker.kickstart(); - } else { // instanceof ServerHandshaker - if (connectionState == cs_HANDSHAKE) { - // initial handshake, no kickstart message to send - } else { - // we want to renegotiate, send hello request - handshaker.kickstart(); - } + for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) { + if (srcs[i] == null) { + throw new IllegalArgumentException( + "source buffer[" + i + "] == null"); } } } - /* - * Start a SSLEngine handshake - */ @Override - public void beginHandshake() throws SSLException { - try { - kickstartHandshake(); - } catch (Exception e) { - fatal(Alerts.alert_handshake_failure, - "Couldn't kickstart handshaking", e); - } + public synchronized SSLEngineResult unwrap(ByteBuffer src, + ByteBuffer[] dsts, int offset, int length) throws SSLException { + return unwrap( + new ByteBuffer[]{src}, 0, 1, dsts, offset, length); } - - // - // Read/unwrap side - // - + // @Override + public synchronized SSLEngineResult unwrap( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException { - /** - * Unwraps a buffer. Does a variety of checks before grabbing - * the unwrapLock, which blocks multiple unwraps from occurring. - */ - @Override - public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer[] appData, - int offset, int length) throws SSLException { + if (conContext.isUnsureMode) { + throw new IllegalStateException( + "Client/Server mode has not yet been set."); + } - // check engine parameters - checkEngineParas(netData, appData, offset, length, false); + // See if the handshaker needs to report back some SSLException. + checkTaskThrown(); + + // check parameters + checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); try { - synchronized (unwrapLock) { - return readNetRecord(netData, appData, offset, length); - } + return readRecord( + srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength); } catch (SSLProtocolException spe) { // may be an unexpected handshake message - fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe); - return null; // make compiler happy - } catch (Exception e) { + conContext.fatal(Alert.UNEXPECTED_MESSAGE, + spe.getMessage(), spe); + } catch (IOException ioe) { /* * Don't reset position so it looks like we didn't * consume anything. We did consume something, and it * got us into this situation, so report that much back. * Our days of consuming are now over anyway. */ - fatal(Alerts.alert_internal_error, - "problem unwrapping net record", e); - return null; // make compiler happy - } - } - - private static void checkEngineParas(ByteBuffer netData, - ByteBuffer[] appData, int offset, int len, boolean isForWrap) { - - if ((netData == null) || (appData == null)) { - throw new IllegalArgumentException("src/dst is null"); - } - - if ((offset < 0) || (len < 0) || (offset > appData.length - len)) { - throw new IndexOutOfBoundsException(); - } - - /* - * If wrapping, make sure the destination bufffer is writable. - */ - if (isForWrap && netData.isReadOnly()) { - throw new ReadOnlyBufferException(); + conContext.fatal(Alert.INTERNAL_ERROR, + "problem unwrapping net record", ioe); + } catch (Exception ex) { // including RuntimeException + conContext.fatal(Alert.INTERNAL_ERROR, + "Fail to unwrap network record", ex); } - for (int i = offset; i < offset + len; i++) { - if (appData[i] == null) { - throw new IllegalArgumentException( - "appData[" + i + "] == null"); - } - - /* - * If unwrapping, make sure the destination bufffers are writable. - */ - if (!isForWrap && appData[i].isReadOnly()) { - throw new ReadOnlyBufferException(); - } - } + return null; // make compiler happy } - /* - * Makes additional checks for unwrap, but this time more - * specific to this packet and the current state of the machine. - */ - private SSLEngineResult readNetRecord(ByteBuffer netData, - ByteBuffer[] appData, int offset, int length) throws IOException { - - Status status = null; - HandshakeStatus hsStatus = null; - - /* - * See if the handshaker needs to report back some SSLException. - */ - checkTaskThrown(); + private SSLEngineResult readRecord( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { /* * Check if we are closing/closed. */ if (isInboundDone()) { - return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); + return new SSLEngineResult( + Status.CLOSED, getHandshakeStatus(), 0, 0); } - /* - * If we're still in cs_HANDSHAKE, make sure it's been - * started. - */ - synchronized (this) { - if ((connectionState == cs_HANDSHAKE) || - (connectionState == cs_START)) { - kickstartHandshake(); + HandshakeStatus hsStatus = null; + if (!conContext.isNegotiated && + !conContext.isClosed() && !conContext.isBroken) { + conContext.kickstart(); - /* - * If there's still outbound data to flush, we - * can return without trying to unwrap anything. - */ - hsStatus = getHSStatus(null); - - if (hsStatus == HandshakeStatus.NEED_WRAP) { - return new SSLEngineResult(Status.OK, hsStatus, 0, 0); - } + /* + * If there's still outbound data to flush, we + * can return without trying to unwrap anything. + */ + hsStatus = getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_WRAP) { + return new SSLEngineResult(Status.OK, hsStatus, 0, 0); } } - /* - * Grab a copy of this if it doesn't already exist, - * and we can use it several places before anything major - * happens on this side. Races aren't critical - * here. - */ if (hsStatus == null) { - hsStatus = getHSStatus(null); + hsStatus = getHandshakeStatus(); } /* @@ -795,42 +502,61 @@ if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) { Plaintext plainText = null; try { - plainText = readRecord(null, null, 0, 0); - } catch (SSLException e) { - throw e; - } catch (IOException e) { - throw new SSLException("readRecord", e); + plainText = decode(null, 0, 0, + dsts, dstsOffset, dstsLength); + } catch (IOException ioe) { + if (ioe instanceof SSLException) { + throw ioe; + } else { + throw new SSLException("readRecord", ioe); + } } - status = (isInboundDone() ? Status.CLOSED : Status.OK); - hsStatus = getHSStatus(plainText.handshakeStatus); + Status status = (isInboundDone() ? Status.CLOSED : Status.OK); + if (plainText.handshakeStatus != null) { + hsStatus = plainText.handshakeStatus; + } else { + hsStatus = getHandshakeStatus(); + } return new SSLEngineResult( status, hsStatus, 0, 0, plainText.recordSN); } + int srcsRemains = 0; + for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) { + srcsRemains += srcs[i].remaining(); + } + + if (srcsRemains == 0) { + return new SSLEngineResult( + Status.BUFFER_UNDERFLOW, hsStatus, 0, 0); + } + /* * Check the packet to make sure enough is here. * This will also indirectly check for 0 len packets. */ int packetLen = 0; try { - packetLen = inputRecord.bytesInCompletePacket(netData); + packetLen = conContext.inputRecord.bytesInCompletePacket( + srcs, srcsOffset, srcsLength); } catch (SSLException ssle) { // Need to discard invalid records for DTLS protocols. - if (isDTLS) { - if (debug != null && Debug.isOn("ssl")) { - System.out.println( - Thread.currentThread().getName() + - " discard invalid record: " + ssle); + if (sslContext.isDTLS()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + SSLLogger.finest("Discard invalid DTLS records", ssle); } // invalid, discard the entire data [section 4.1.2.7, RFC 6347] - int deltaNet = netData.remaining(); - netData.position(netData.limit()); + int deltaNet = 0; + // int deltaNet = netData.remaining(); + // netData.position(netData.limit()); - status = (isInboundDone() ? Status.CLOSED : Status.OK); - hsStatus = getHSStatus(hsStatus); + Status status = (isInboundDone() ? Status.CLOSED : Status.OK); + if (hsStatus == null) { + hsStatus = getHandshakeStatus(); + } return new SSLEngineResult(status, hsStatus, deltaNet, 0, -1L); } else { @@ -839,10 +565,10 @@ } // Is this packet bigger than SSL/TLS normally allows? - if (packetLen > sess.getPacketBufferSize()) { - int largestRecordSize = isDTLS ? + if (packetLen > conContext.conSession.getPacketBufferSize()) { + int largestRecordSize = sslContext.isDTLS() ? DTLSRecord.maxRecordSize : SSLRecord.maxLargeRecordSize; - if ((packetLen <= largestRecordSize) && !isDTLS) { + if ((packetLen <= largestRecordSize) && !sslContext.isDTLS()) { // Expand the expected maximum packet/application buffer // sizes. // @@ -850,11 +576,11 @@ // Old behavior: shall we honor the System Property // "jsse.SSLEngine.acceptLargeFragments" if it is "false"? - sess.expandBufferSizes(); + conContext.conSession.expandBufferSizes(); } // check the packet again - largestRecordSize = sess.getPacketBufferSize(); + largestRecordSize = conContext.conSession.getPacketBufferSize(); if (packetLen > largestRecordSize) { throw new SSLProtocolException( "Input record too big: max = " + @@ -862,35 +588,28 @@ } } - int netPos = netData.position(); - int appRemains = 0; - for (int i = offset; i < offset + length; i++) { - if (appData[i] == null) { - throw new IllegalArgumentException( - "appData[" + i + "] == null"); - } - appRemains += appData[i].remaining(); - } - /* * Check for OVERFLOW. * * Delay enforcing the application buffer free space requirement * until after the initial handshaking. */ - // synchronize connectionState? - if ((connectionState == cs_DATA) || - (connectionState == cs_RENEGOTIATE)) { + int dstsRemains = 0; + for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) { + dstsRemains += dsts[i].remaining(); + } - int FragLen = inputRecord.estimateFragmentSize(packetLen); - if (FragLen > appRemains) { + if (conContext.isNegotiated) { + int FragLen = + conContext.inputRecord.estimateFragmentSize(packetLen); + if (FragLen > dstsRemains) { return new SSLEngineResult( Status.BUFFER_OVERFLOW, hsStatus, 0, 0); } } // check for UNDERFLOW. - if ((packetLen == -1) || (netData.remaining() < packetLen)) { + if ((packetLen == -1) || (srcsRemains < packetLen)) { return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0); } @@ -899,11 +618,14 @@ */ Plaintext plainText = null; try { - plainText = readRecord(netData, appData, offset, length); - } catch (SSLException e) { - throw e; - } catch (IOException e) { - throw new SSLException("readRecord", e); + plainText = decode(srcs, srcsOffset, srcsLength, + dsts, dstsOffset, dstsLength); + } catch (IOException ioe) { + if (ioe instanceof SSLException) { + throw ioe; + } else { + throw new SSLException("readRecord", ioe); + } } /* @@ -916,1395 +638,348 @@ * * status above should cover: FINISHED, NEED_TASK */ - status = (isInboundDone() ? Status.CLOSED : Status.OK); - hsStatus = getHSStatus(plainText.handshakeStatus); + Status status = (isInboundDone() ? Status.CLOSED : Status.OK); + if (plainText.handshakeStatus != null) { + hsStatus = plainText.handshakeStatus; + } else { + hsStatus = getHandshakeStatus(); + } - int deltaNet = netData.position() - netPos; - int deltaApp = appRemains; - for (int i = offset; i < offset + length; i++) { - deltaApp -= appData[i].remaining(); + int deltaNet = srcsRemains; + for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) { + deltaNet -= srcs[i].remaining(); + } + + int deltaApp = dstsRemains; + for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) { + deltaApp -= dsts[i].remaining(); } return new SSLEngineResult( status, hsStatus, deltaNet, deltaApp, plainText.recordSN); } - // the caller have synchronized readLock - void expectingFinishFlight() { - inputRecord.expectingFinishFlight(); - } + private Plaintext decode( + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { - /* - * Actually do the read record processing. - * - * Returns a Status if it can make specific determinations - * of the engine state. In particular, we need to signal - * that a handshake just completed. - * - * It would be nice to be symmetrical with the write side and move - * the majority of this to SSLInputRecord, but there's too much - * SSLEngine state to do that cleanly. It must still live here. - */ - private Plaintext readRecord(ByteBuffer netData, - ByteBuffer[] appData, int offset, int length) throws IOException { + Plaintext pt = SSLTransport.decode(conContext, + srcs, srcsOffset, srcsLength, + dsts, dstsOffset, dstsLength); - /* - * The various operations will return new sliced BB's, - * this will avoid having to worry about positions and - * limits in the netBB. - */ - Plaintext plainText = null; - - if (getConnectionState() == cs_ERROR) { - return Plaintext.PLAINTEXT_NULL; - } - - /* - * Read a record ... maybe emitting an alert if we get a - * comprehensible but unsupported "hello" message during - * format checking (e.g. V2). - */ - try { - if (isDTLS) { - // Don't process the incoming record until all of the - // buffered records get handled. - plainText = inputRecord.acquirePlaintext(); - } - - if ((!isDTLS || plainText == null) && netData != null) { - plainText = inputRecord.decode(netData); - } - } catch (UnsupportedOperationException unsoe) { // SSLv2Hello - // Hack code to deliver SSLv2 error message for SSL/TLS connections. - if (!isDTLS) { - outputRecord.encodeV2NoCipher(); + // Is the handshake completed? + if (pt != Plaintext.PLAINTEXT_NULL) { + HandshakeStatus hsStatus = tryToFinishHandshake(pt.contentType); + if (hsStatus == null) { + pt.handshakeStatus = conContext.getHandshakeStatus(); + } else { + pt.handshakeStatus = hsStatus; } - fatal(Alerts.alert_unexpected_message, unsoe); - } catch (BadPaddingException e) { - /* - * The basic SSLv3 record protection involves (optional) - * encryption for privacy, and an integrity check ensuring - * data origin authentication. We do them both here, and - * throw a fatal alert if the integrity check fails. - */ - byte alertType = (connectionState != cs_DATA) ? - Alerts.alert_handshake_failure : - Alerts.alert_bad_record_mac; - fatal(alertType, e.getMessage(), e); - } catch (SSLHandshakeException she) { - // may be record sequence number overflow - fatal(Alerts.alert_handshake_failure, she); - } catch (IOException ioe) { - fatal(Alerts.alert_unexpected_message, ioe); - } - - // plainText should never be null for TLS protocols - HandshakeStatus hsStatus = null; - if (plainText == Plaintext.PLAINTEXT_NULL) { - // Only happens for DTLS protocols. - // - // Received a retransmitted flight, and need to retransmit the - // previous delivered handshake flight messages. - if (enableRetransmissions) { - if (debug != null && Debug.isOn("verbose")) { - Debug.log( - "Retransmit the previous handshake flight messages."); - } - - synchronized (this) { - outputRecord.launchRetransmission(); - } - } // Otherwise, discard the retransmitted flight. - } else if (!isDTLS || plainText != null) { - hsStatus = processInputRecord(plainText, appData, offset, length); - } - - if (hsStatus == null) { - hsStatus = getHSStatus(null); - } - - if (plainText == null) { - plainText = Plaintext.PLAINTEXT_NULL; - } - plainText.handshakeStatus = hsStatus; - - return plainText; - } - - /* - * Process the record. - */ - private synchronized HandshakeStatus processInputRecord( - Plaintext plainText, - ByteBuffer[] appData, int offset, int length) throws IOException { - - HandshakeStatus hsStatus = null; - switch (plainText.contentType) { - case Record.ct_handshake: - /* - * Handshake messages always go to a pending session - * handshaker ... if there isn't one, create one. This - * must work asynchronously, for renegotiation. - * - * NOTE that handshaking will either resume a session - * which was in the cache (and which might have other - * connections in it already), or else will start a new - * session (new keys exchanged) with just this connection - * in it. - */ - initHandshaker(); - if (!handshaker.activated()) { - // prior to handshaking, activate the handshake - if (connectionState == cs_RENEGOTIATE) { - // don't use SSLv2Hello when renegotiating - handshaker.activate(protocolVersion); - } else { - handshaker.activate(null); - } - } - - /* - * process the handshake record ... may contain just - * a partial handshake message or multiple messages. - * - * The handshaker state machine will ensure that it's - * a finished message. - */ - handshaker.processRecord(plainText.fragment, expectingFinished); - expectingFinished = false; - - if (handshaker.invalidated) { - finishHandshake(); - - // if state is cs_RENEGOTIATE, revert it to cs_DATA - if (connectionState == cs_RENEGOTIATE) { - connectionState = cs_DATA; - } - } else if (handshaker.isDone()) { - // reset the parameters for secure renegotiation. - secureRenegotiation = - handshaker.isSecureRenegotiation(); - clientVerifyData = handshaker.getClientVerifyData(); - serverVerifyData = handshaker.getServerVerifyData(); - // set connection ALPN value - applicationProtocol = - handshaker.getHandshakeApplicationProtocol(); - - sess = handshaker.getSession(); - handshakeSession = null; - if (outputRecord.isEmpty()) { - hsStatus = finishHandshake(); - connectionState = cs_DATA; - } - - // No handshakeListeners here. That's a - // SSLSocket thing. - } else if (handshaker.taskOutstanding()) { - hsStatus = HandshakeStatus.NEED_TASK; - } - break; - - case Record.ct_application_data: - // Pass this right back up to the application. - if ((connectionState != cs_DATA) - && (connectionState != cs_RENEGOTIATE) - && (connectionState != cs_CLOSED)) { - throw new SSLProtocolException( - "Data received in non-data state: " + - connectionState); - } - - if (expectingFinished) { - throw new SSLProtocolException - ("Expecting finished message, received data"); - } - - if (!inboundDone) { - ByteBuffer fragment = plainText.fragment; - int remains = fragment.remaining(); - - // Should have enough room in appData. - for (int i = offset; - ((i < (offset + length)) && (remains > 0)); i++) { - int amount = Math.min(appData[i].remaining(), remains); - fragment.limit(fragment.position() + amount); - appData[i].put(fragment); - remains -= amount; - } - } - - break; - - case Record.ct_alert: - recvAlert(plainText.fragment); - break; - - case Record.ct_change_cipher_spec: - if ((connectionState != cs_HANDSHAKE - && connectionState != cs_RENEGOTIATE)) { - // For the CCS message arriving in the wrong state - fatal(Alerts.alert_unexpected_message, - "illegal change cipher spec msg, conn state = " - + connectionState); - } else if (plainText.fragment.remaining() != 1 - || plainText.fragment.get() != 1) { - // For structural/content issues with the CCS - fatal(Alerts.alert_unexpected_message, - "Malformed change cipher spec msg"); - } - - // - // The first message after a change_cipher_spec - // record MUST be a "Finished" handshake record, - // else it's a protocol violation. We force this - // to be checked by a minor tweak to the state - // machine. - // - handshaker.receiveChangeCipherSpec(); - - CipherBox readCipher; - Authenticator readAuthenticator; - try { - readCipher = handshaker.newReadCipher(); - readAuthenticator = handshaker.newReadAuthenticator(); - } catch (GeneralSecurityException e) { - // can't happen - throw new SSLException("Algorithm missing: ", e); - } - inputRecord.changeReadCiphers(readAuthenticator, readCipher); - - // next message MUST be a finished message - expectingFinished = true; - break; - - default: - // - // TLS requires that unrecognized records be ignored. - // - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", Received record type: " + plainText.contentType); - } - break; - } // switch - - /* - * We only need to check the sequence number state for - * non-handshaking record. - * - * Note that in order to maintain the handshake status - * properly, we check the sequence number after the last - * record reading process. As we request renegotiation - * or close the connection for wrapped sequence number - * when there is enough sequence number space left to - * handle a few more records, so the sequence number - * of the last record cannot be wrapped. - */ - hsStatus = getHSStatus(hsStatus); - if (connectionState < cs_ERROR && !isInboundDone() && - (hsStatus == HandshakeStatus.NOT_HANDSHAKING) && - (inputRecord.seqNumIsHuge())) { - /* - * Ask for renegotiation when need to renew sequence number. - * - * Don't bother to kickstart the renegotiation when the local is - * asking for it. - */ - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", request renegotiation " + - "to avoid sequence number overflow"); - } - - beginHandshake(); - - hsStatus = getHSStatus(null); - } - - return hsStatus; - } - - - // - // write/wrap side - // - - - /** - * Wraps a buffer. Does a variety of checks before grabbing - * the wrapLock, which blocks multiple wraps from occurring. - */ - @Override - public SSLEngineResult wrap(ByteBuffer[] appData, - int offset, int length, ByteBuffer netData) throws SSLException { - - // check engine parameters - checkEngineParas(netData, appData, offset, length, true); - - /* - * We can be smarter about using smaller buffer sizes later. - * For now, force it to be large enough to handle any valid record. - */ - if (netData.remaining() < sess.getPacketBufferSize()) { - return new SSLEngineResult( - Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0); - } - - try { - synchronized (wrapLock) { - return writeAppRecord(appData, offset, length, netData); - } - } catch (SSLProtocolException spe) { - // may be an unexpected handshake message - fatal(Alerts.alert_unexpected_message, spe.getMessage(), spe); - return null; // make compiler happy - } catch (Exception e) { - fatal(Alerts.alert_internal_error, - "problem wrapping app data", e); - return null; // make compiler happy - } - } - - /* - * Makes additional checks for unwrap, but this time more - * specific to this packet and the current state of the machine. - */ - private SSLEngineResult writeAppRecord(ByteBuffer[] appData, - int offset, int length, ByteBuffer netData) throws IOException { - - Status status = null; - HandshakeStatus hsStatus = null; - - /* - * See if the handshaker needs to report back some SSLException. - */ - checkTaskThrown(); - - /* - * short circuit if we're closed/closing. - */ - if (isOutboundDone()) { - return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); - } - - /* - * If we're still in cs_HANDSHAKE, make sure it's been - * started. - */ - synchronized (this) { - if ((connectionState == cs_HANDSHAKE) || - (connectionState == cs_START)) { - - kickstartHandshake(); - - /* - * If there's no HS data available to write, we can return - * without trying to wrap anything. - */ - hsStatus = getHSStatus(null); - if (hsStatus == HandshakeStatus.NEED_UNWRAP) { - /* - * For DTLS, if the handshake state is - * HandshakeStatus.NEED_UNWRAP, a call to SSLEngine.wrap() - * means that the previous handshake packets (if delivered) - * get lost, and need retransmit the handshake messages. - */ - if (!isDTLS || !enableRetransmissions || - (handshaker == null) || outputRecord.firstMessage) { - - return new SSLEngineResult(Status.OK, hsStatus, 0, 0); - } // otherwise, need retransmission - } + // Is the sequence number is nearly overflow? + if (conContext.inputRecord.seqNumIsHuge()) { + pt.handshakeStatus = + tryKeyUpdate(pt.handshakeStatus); } } - /* - * Grab a copy of this if it doesn't already exist, - * and we can use it several places before anything major - * happens on this side. Races aren't critical - * here. - */ - if (hsStatus == null) { - hsStatus = getHSStatus(null); - } - - /* - * If we have a task outstanding, this *MUST* be done before - * doing any more wrapping, because we could be in the middle - * of receiving a handshake message, for example, a finished - * message which would change the ciphers. - */ - if (hsStatus == HandshakeStatus.NEED_TASK) { - return new SSLEngineResult(Status.OK, hsStatus, 0, 0); - } - - /* - * This will obtain any waiting outbound data, or will - * process the outbound appData. - */ - int netPos = netData.position(); - int appRemains = 0; - for (int i = offset; i < offset + length; i++) { - if (appData[i] == null) { - throw new IllegalArgumentException( - "appData[" + i + "] == null"); - } - appRemains += appData[i].remaining(); - } - - Ciphertext ciphertext = null; - try { - if (appRemains != 0) { - synchronized (writeLock) { - ciphertext = writeRecord(appData, offset, length, netData); - } - } else { - synchronized (writeLock) { - ciphertext = writeRecord(null, 0, 0, netData); - } - } - } catch (SSLException e) { - throw e; - } catch (IOException e) { - throw new SSLException("Write problems", e); - } - - /* - * writeRecord might have reported some status. - * Now check for the remaining cases. - * - * status above should cover: NEED_WRAP/FINISHED - */ - status = (isOutboundDone() ? Status.CLOSED : Status.OK); - hsStatus = getHSStatus(ciphertext.handshakeStatus); - - int deltaNet = netData.position() - netPos; - int deltaApp = appRemains; - for (int i = offset; i < offset + length; i++) { - deltaApp -= appData[i].remaining(); - } - - return new SSLEngineResult( - status, hsStatus, deltaApp, deltaNet, ciphertext.recordSN); + return pt; } - /* - * Central point to write/get all of the outgoing data. - */ - private Ciphertext writeRecord(ByteBuffer[] appData, - int offset, int length, ByteBuffer netData) throws IOException { - - Ciphertext ciphertext = null; - try { - // Acquire the buffered to-be-delivered records or retransmissions. - // - // May have buffered records, or need retransmission if handshaking. - if (!outputRecord.isEmpty() || - (enableRetransmissions && handshaker != null)) { - ciphertext = outputRecord.acquireCiphertext(netData); - } - - if ((ciphertext == null) && (appData != null)) { - ciphertext = outputRecord.encode( - appData, offset, length, netData); - } - } catch (SSLHandshakeException she) { - // may be record sequence number overflow - fatal(Alerts.alert_handshake_failure, she); - - return Ciphertext.CIPHERTEXT_NULL; // make the complier happy - } catch (IOException e) { - fatal(Alerts.alert_unexpected_message, e); - - return Ciphertext.CIPHERTEXT_NULL; // make the complier happy - } - - if (ciphertext == null) { - return Ciphertext.CIPHERTEXT_NULL; + @Override + public synchronized Runnable getDelegatedTask() { + if (conContext.handshakeContext != null && // PRE or POST handshake + !conContext.handshakeContext.taskDelegated && + !conContext.handshakeContext.delegatedActions.isEmpty()) { + conContext.handshakeContext.taskDelegated = true; + return new DelegatedTask(this); } - HandshakeStatus hsStatus = null; - Ciphertext.RecordType recordType = ciphertext.recordType; - if ((recordType.contentType == Record.ct_handshake) && - (recordType.handshakeType == HandshakeMessage.ht_finished) && - outputRecord.isEmpty()) { - - if (handshaker == null) { - hsStatus = HandshakeStatus.FINISHED; - } else if (handshaker.isDone()) { - hsStatus = finishHandshake(); - connectionState = cs_DATA; - - // Retransmit the last flight twice. - // - // The application data transactions may begin immediately - // after the last flight. If the last flight get lost, the - // application data may be discarded accordingly. As could - // be an issue for some applications. This impact can be - // mitigated by sending the last fligth twice. - if (isDTLS && enableRetransmissions) { - if (debug != null && Debug.isOn("verbose")) { - Debug.log( - "Retransmit the last flight messages."); - } - - synchronized (this) { - outputRecord.launchRetransmission(); - } - - hsStatus = HandshakeStatus.NEED_WRAP; - } - } - } // Otherwise, the followed call to getHSStatus() will help. - - /* - * We only need to check the sequence number state for - * non-handshaking record. - * - * Note that in order to maintain the handshake status - * properly, we check the sequence number after the last - * record writing process. As we request renegotiation - * or close the connection for wrapped sequence number - * when there is enough sequence number space left to - * handle a few more records, so the sequence number - * of the last record cannot be wrapped. - */ - hsStatus = getHSStatus(hsStatus); - if (connectionState < cs_ERROR && !isOutboundDone() && - (hsStatus == HandshakeStatus.NOT_HANDSHAKING) && - (outputRecord.seqNumIsHuge())) { - /* - * Ask for renegotiation when need to renew sequence number. - * - * Don't bother to kickstart the renegotiation when the local is - * asking for it. - */ - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", request renegotiation " + - "to avoid sequence number overflow"); - } - - beginHandshake(); - - hsStatus = getHSStatus(null); - } - ciphertext.handshakeStatus = hsStatus; - - return ciphertext; + return null; } - private HandshakeStatus finishHandshake() { - handshaker = null; - inputRecord.setHandshakeHash(null); - outputRecord.setHandshakeHash(null); - connectionState = cs_DATA; - - return HandshakeStatus.FINISHED; - } - - // - // Close code - // - - /** - * Signals that no more outbound application data will be sent - * on this SSLEngine. - */ - private void closeOutboundInternal() { - - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", closeOutboundInternal()"); - } - - /* - * Already closed, ignore - */ - if (outboundDone) { - return; - } - - switch (connectionState) { + @Override + public synchronized void closeInbound() throws SSLException { + conContext.closeInbound(); + } - /* - * If we haven't even started yet, don't bother reading inbound. - */ - case cs_START: - try { - outputRecord.close(); - } catch (IOException ioe) { - // ignore - } - outboundDone = true; - - try { - inputRecord.close(); - } catch (IOException ioe) { - // ignore - } - inboundDone = true; - break; - - case cs_ERROR: - case cs_CLOSED: - break; - - /* - * Otherwise we indicate clean termination. - */ - // case cs_HANDSHAKE: - // case cs_DATA: - // case cs_RENEGOTIATE: - default: - warning(Alerts.alert_close_notify); - try { - outputRecord.close(); - } catch (IOException ioe) { - // ignore - } - outboundDone = true; - break; - } - - connectionState = cs_CLOSED; + @Override + public synchronized boolean isInboundDone() { + return conContext.isInboundDone(); } @Override public synchronized void closeOutbound() { - /* - * Dump out a close_notify to the remote side - */ - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called closeOutbound()"); - } - - closeOutboundInternal(); + conContext.closeOutbound(); } - /** - * Returns the outbound application data closure state - */ @Override - public boolean isOutboundDone() { - return outboundDone && outputRecord.isEmpty(); + public synchronized boolean isOutboundDone() { + return conContext.isOutboundDone(); } - /** - * Signals that no more inbound network data will be sent - * to this SSLEngine. - */ - private void closeInboundInternal() { - - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", closeInboundInternal()"); - } + @Override + public String[] getSupportedCipherSuites() { + return CipherSuite.namesOf(sslContext.getSupportedCipherSuites()); + } - /* - * Already closed, ignore - */ - if (inboundDone) { - return; - } - - closeOutboundInternal(); + @Override + public synchronized String[] getEnabledCipherSuites() { + return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites); + } - try { - inputRecord.close(); - } catch (IOException ioe) { - // ignore - } - inboundDone = true; - - connectionState = cs_CLOSED; + @Override + public synchronized void setEnabledCipherSuites(String[] suites) { + conContext.sslConfig.enabledCipherSuites = + CipherSuite.validValuesOf(suites); } - /* - * Close the inbound side of the connection. We grab the - * lock here, and do the real work in the internal verison. - * We do check for truncation attacks. - */ + @Override + public String[] getSupportedProtocols() { + return ProtocolVersion.toStringArray( + sslContext.getSupportedProtocolVersions()); + } + @Override - public synchronized void closeInbound() throws SSLException { - /* - * Currently closes the outbound side as well. The IETF TLS - * working group has expressed the opinion that 1/2 open - * connections are not allowed by the spec. May change - * someday in the future. - */ - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called closeInbound()"); + public synchronized String[] getEnabledProtocols() { + return ProtocolVersion.toStringArray( + conContext.sslConfig.enabledProtocols); + } + + @Override + public synchronized void setEnabledProtocols(String[] protocols) { + if (protocols == null) { + throw new IllegalArgumentException("Protocols cannot be null"); } - /* - * No need to throw an Exception if we haven't even started yet. - */ - if ((connectionState != cs_START) && !recvCN) { - recvCN = true; // Only receive the Exception once - fatal(Alerts.alert_internal_error, - "Inbound closed before receiving peer's close_notify: " + - "possible truncation attack?"); - } else { - /* - * Currently, this is a no-op, but in case we change - * the close inbound code later. - */ - closeInboundInternal(); - } + conContext.sslConfig.enabledProtocols = + ProtocolVersion.namesOf(protocols); } - /** - * Returns the network inbound data closure state - */ - @Override - public synchronized boolean isInboundDone() { - return inboundDone; - } - - - // - // Misc stuff - // - - - /** - * Returns the current SSLSession for this - * SSLEngine - *

- * These can be long lived, and frequently correspond to an - * entire login session for some user. - */ @Override public synchronized SSLSession getSession() { - return sess; + return conContext.conSession; } @Override public synchronized SSLSession getHandshakeSession() { - return handshakeSession; - } - - synchronized void setHandshakeSession(SSLSessionImpl session) { - // update the fragment size, which may be negotiated during handshaking - inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize()); - outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize()); - - handshakeSession = session; - } - - /** - * Returns a delegated Runnable task for - * this SSLEngine. - */ - @Override - public synchronized Runnable getDelegatedTask() { - if (handshaker != null) { - return handshaker.getTask(); - } - return null; + return conContext.handshakeContext == null ? + null : conContext.handshakeContext.handshakeSession; } - - // - // EXCEPTION AND ALERT HANDLING - // - - /* - * Send a warning alert. - */ - void warning(byte description) { - sendAlert(Alerts.alert_warning, description); - } - - synchronized void fatal(byte description, String diagnostic) - throws SSLException { - fatal(description, diagnostic, null, false); - } - - synchronized void fatal(byte description, Throwable cause) - throws SSLException { - fatal(description, null, cause, false); - } - - synchronized void fatal(byte description, String diagnostic, - Throwable cause) throws SSLException { - fatal(description, diagnostic, cause, false); + @Override + public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() { + return conContext.getHandshakeStatus(); } - /* - * We've got a fatal error here, so start the shutdown process. - * - * Because of the way the code was written, we have some code - * calling fatal directly when the "description" is known - * and some throwing Exceptions which are then caught by higher - * levels which then call here. This code needs to determine - * if one of the lower levels has already started the process. - * - * We won't worry about Errors, if we have one of those, - * we're in worse trouble. Note: the networking code doesn't - * deal with Errors either. - */ - synchronized void fatal(byte description, String diagnostic, - Throwable cause, boolean recvFatalAlert) throws SSLException { - - /* - * If we have no further information, make a general-purpose - * message for folks to see. We generally have one or the other. - */ - if (diagnostic == null) { - diagnostic = "General SSLEngine problem"; - } - if (cause == null) { - cause = Alerts.getSSLException(description, cause, diagnostic); - } - - /* - * If we've already shutdown because of an error, - * there is nothing we can do except rethrow the exception. - * - * Most exceptions seen here will be SSLExceptions. - * We may find the occasional Exception which hasn't been - * converted to a SSLException, so we'll do it here. - */ - if (closeReason != null) { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", fatal: engine already closed. Rethrowing " + - cause.toString()); - } - if (cause instanceof RuntimeException) { - throw (RuntimeException)cause; - } else if (cause instanceof SSLException) { - throw (SSLException)cause; - } else if (cause instanceof Exception) { - throw new SSLException("fatal SSLEngine condition", cause); - } - } - - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() - + ", fatal error: " + description + - ": " + diagnostic + "\n" + cause.toString()); - } - - /* - * Ok, this engine's going down. - */ - int oldState = connectionState; - connectionState = cs_ERROR; - - try { - inputRecord.close(); - } catch (IOException ioe) { - // ignore - } - inboundDone = true; - - sess.invalidate(); - if (handshakeSession != null) { - handshakeSession.invalidate(); - } - - /* - * If we haven't even started handshaking yet, or we are the - * recipient of a fatal alert, no need to generate a fatal close - * alert. - */ - if (oldState != cs_START && !recvFatalAlert) { - sendAlert(Alerts.alert_fatal, description); - } - - if (cause instanceof SSLException) { // only true if != null - closeReason = (SSLException)cause; - } else { - /* - * Including RuntimeExceptions, but we'll throw those - * down below. The closeReason isn't used again, - * except for null checks. - */ - closeReason = - Alerts.getSSLException(description, cause, diagnostic); - } - - try { - outputRecord.close(); - } catch (IOException ioe) { - // ignore - } - outboundDone = true; - - connectionState = cs_CLOSED; - - if (cause instanceof RuntimeException) { - throw (RuntimeException)cause; - } else { - throw closeReason; - } + @Override + public synchronized void setUseClientMode(boolean mode) { + conContext.setUseClientMode(mode); } - /* - * Process an incoming alert ... caller must already have synchronized - * access to "this". - */ - private void recvAlert(ByteBuffer fragment) throws IOException { - byte level = fragment.get(); - byte description = fragment.get(); - - if (debug != null && (Debug.isOn("record") || - Debug.isOn("handshake"))) { - synchronized (System.out) { - System.out.print(Thread.currentThread().getName()); - System.out.print(", RECV " + protocolVersion + " ALERT: "); - if (level == Alerts.alert_fatal) { - System.out.print("fatal, "); - } else if (level == Alerts.alert_warning) { - System.out.print("warning, "); - } else { - System.out.print(", "); - } - System.out.println(Alerts.alertDescription(description)); - } - } - - if (level == Alerts.alert_warning) { - if (description == -1) { // check for short message - fatal(Alerts.alert_illegal_parameter, "Short alert message"); - } else if (description == Alerts.alert_close_notify) { - if (connectionState == cs_HANDSHAKE) { - fatal(Alerts.alert_unexpected_message, - "Received close_notify during handshake"); - } else { - recvCN = true; - closeInboundInternal(); // reply to close - } - } else { - - // - // The other legal warnings relate to certificates, - // e.g. no_certificate, bad_certificate, etc; these - // are important to the handshaking code, which can - // also handle illegal protocol alerts if needed. - // - if (handshaker != null) { - handshaker.handshakeAlert(description); - } - } - } else { // fatal or unknown level - String reason = "Received fatal alert: " - + Alerts.alertDescription(description); - - // The inbound and outbound queues will be closed as part of - // the call to fatal. The handhaker to needs to be set to null - // so subsequent calls to getHandshakeStatus will return - // NOT_HANDSHAKING. - handshaker = null; - Throwable cause = Alerts.getSSLException(description, reason); - fatal(description, null, cause, true); - } + @Override + public synchronized boolean getUseClientMode() { + return conContext.sslConfig.isClientMode; } - - /* - * Emit alerts. Caller must have synchronized with "this". - */ - private void sendAlert(byte level, byte description) { - // the connectionState cannot be cs_START - if (connectionState >= cs_CLOSED) { - return; - } - - // For initial handshaking, don't send alert message to peer if - // handshaker has not started. - // - // Shall we send an fatal alter to terminate the connection gracefully? - if (connectionState <= cs_HANDSHAKE && - (handshaker == null || !handshaker.started() || - !handshaker.activated())) { - return; - } - - try { - outputRecord.encodeAlert(level, description); - } catch (IOException ioe) { - // ignore - } - } - - - // - // VARIOUS OTHER METHODS (COMMON TO SSLSocket) - // - - - /** - * Controls whether new connections may cause creation of new SSL - * sessions. - * - * As long as handshaking has not started, we can change - * whether we enable session creations. Otherwise, - * we will need to wait for the next handshake. - */ @Override - public synchronized void setEnableSessionCreation(boolean flag) { - enableSessionCreation = flag; - - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnableSessionCreation(enableSessionCreation); - } - } - - /** - * Returns true if new connections may cause creation of new SSL - * sessions. - */ - @Override - public synchronized boolean getEnableSessionCreation() { - return enableSessionCreation; - } - - - /** - * Sets the flag controlling whether a server mode engine - * *REQUIRES* SSL client authentication. - * - * As long as handshaking has not started, we can change - * whether client authentication is needed. Otherwise, - * we will need to wait for the next handshake. - */ - @Override - public synchronized void setNeedClientAuth(boolean flag) { - doClientAuth = (flag ? - ClientAuthType.CLIENT_AUTH_REQUIRED : - ClientAuthType.CLIENT_AUTH_NONE); - - if ((handshaker != null) && - (handshaker instanceof ServerHandshaker) && - !handshaker.activated()) { - ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); - } + public synchronized void setNeedClientAuth(boolean need) { + conContext.sslConfig.clientAuthType = + (need ? ClientAuthType.CLIENT_AUTH_REQUIRED : + ClientAuthType.CLIENT_AUTH_NONE); } @Override public synchronized boolean getNeedClientAuth() { - return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED); + return (conContext.sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUIRED); } - /** - * Sets the flag controlling whether a server mode engine - * *REQUESTS* SSL client authentication. - * - * As long as handshaking has not started, we can change - * whether client authentication is requested. Otherwise, - * we will need to wait for the next handshake. - */ @Override - public synchronized void setWantClientAuth(boolean flag) { - doClientAuth = (flag ? - ClientAuthType.CLIENT_AUTH_REQUESTED : - ClientAuthType.CLIENT_AUTH_NONE); - - if ((handshaker != null) && - (handshaker instanceof ServerHandshaker) && - !handshaker.activated()) { - ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); - } + public synchronized void setWantClientAuth(boolean want) { + conContext.sslConfig.clientAuthType = + (want ? ClientAuthType.CLIENT_AUTH_REQUESTED : + ClientAuthType.CLIENT_AUTH_NONE); } @Override public synchronized boolean getWantClientAuth() { - return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED); + return (conContext.sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUESTED); } - - /** - * Sets the flag controlling whether the engine is in SSL - * client or server mode. Must be called before any SSL - * traffic has started. - */ @Override - @SuppressWarnings("fallthrough") - public synchronized void setUseClientMode(boolean flag) { - switch (connectionState) { - - case cs_START: - /* - * If we need to change the socket mode and the enabled - * protocols and cipher suites haven't specifically been - * set by the user, change them to the corresponding - * default ones. - */ - if (roleIsServer != (!flag)) { - if (sslContext.isDefaultProtocolList(enabledProtocols)) { - enabledProtocols = - sslContext.getDefaultProtocolList(!flag); - } - - if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) { - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(!flag); - } - } - - roleIsServer = !flag; - serverModeSet = true; - break; - - case cs_HANDSHAKE: - /* - * If we have a handshaker, but haven't started - * SSL traffic, we can throw away our current - * handshaker, and start from scratch. Don't - * need to call doneConnect() again, we already - * have the streams. - */ - assert(handshaker != null); - if (!handshaker.activated()) { - /* - * If we need to change the socket mode and the enabled - * protocols and cipher suites haven't specifically been - * set by the user, change them to the corresponding - * default ones. - */ - if (roleIsServer != (!flag)) { - if (sslContext.isDefaultProtocolList(enabledProtocols)) { - enabledProtocols = - sslContext.getDefaultProtocolList(!flag); - } - - if (sslContext.isDefaultCipherSuiteList( - enabledCipherSuites)) { - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(!flag); - } - } - - roleIsServer = !flag; - connectionState = cs_START; - initHandshaker(); - break; - } - - // If handshake has started, that's an error. Fall through... - - default: - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", setUseClientMode() invoked in state = " + - connectionState); - } - - /* - * We can let them continue if they catch this correctly, - * we don't need to shut this down. - */ - throw new IllegalArgumentException( - "Cannot change mode after SSL traffic has started"); - } + public synchronized void setEnableSessionCreation(boolean flag) { + conContext.sslConfig.enableSessionCreation = flag; } @Override - public synchronized boolean getUseClientMode() { - return !roleIsServer; - } - - - /** - * Returns the names of the cipher suites which could be enabled for use - * on an SSL connection. Normally, only a subset of these will actually - * be enabled by default, since this list may include cipher suites which - * do not support the mutual authentication of servers and clients, or - * which do not protect data confidentiality. Servers may also need - * certain kinds of certificates to use certain cipher suites. - * - * @return an array of cipher suite names - */ - @Override - public String[] getSupportedCipherSuites() { - return sslContext.getSupportedCipherSuiteList().toStringArray(); - } - - /** - * Controls which particular cipher suites are enabled for use on - * this connection. The cipher suites must have been listed by - * getCipherSuites() as being supported. Even if a suite has been - * enabled, it might never be used if no peer supports it or the - * requisite certificates (and private keys) are not available. - * - * @param suites Names of all the cipher suites to enable. - */ - @Override - public synchronized void setEnabledCipherSuites(String[] suites) { - enabledCipherSuites = new CipherSuiteList(suites); - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnabledCipherSuites(enabledCipherSuites); - } - } - - /** - * Returns the names of the SSL cipher suites which are currently enabled - * for use on this connection. When an SSL engine is first created, - * all enabled cipher suites (a) protect data confidentiality, - * by traffic encryption, and (b) can mutually authenticate - * both clients and servers. Thus, in some environments, this value - * might be empty. - * - * @return an array of cipher suite names - */ - @Override - public synchronized String[] getEnabledCipherSuites() { - return enabledCipherSuites.toStringArray(); - } - - - /** - * Returns the protocols that are supported by this implementation. - * A subset of the supported protocols may be enabled for this connection - * @return an array of protocol names. - */ - @Override - public String[] getSupportedProtocols() { - return sslContext.getSuportedProtocolList().toStringArray(); - } - - /** - * Controls which protocols are enabled for use on - * this connection. The protocols must have been listed by - * getSupportedProtocols() as being supported. - * - * @param protocols protocols to enable. - * @exception IllegalArgumentException when one of the protocols - * named by the parameter is not supported. - */ - @Override - public synchronized void setEnabledProtocols(String[] protocols) { - enabledProtocols = new ProtocolList(protocols); - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnabledProtocols(enabledProtocols); - } + public synchronized boolean getEnableSessionCreation() { + return conContext.sslConfig.enableSessionCreation; } @Override - public synchronized String[] getEnabledProtocols() { - return enabledProtocols.toStringArray(); + public synchronized SSLParameters getSSLParameters() { + return conContext.sslConfig.getSSLParameters(); } - /** - * Returns the SSLParameters in effect for this SSLEngine. - */ - @Override - public synchronized SSLParameters getSSLParameters() { - SSLParameters params = super.getSSLParameters(); - - // the super implementation does not handle the following parameters - params.setEndpointIdentificationAlgorithm(identificationProtocol); - params.setAlgorithmConstraints(algorithmConstraints); - params.setSNIMatchers(sniMatchers); - params.setServerNames(serverNames); - params.setUseCipherSuitesOrder(preferLocalCipherSuites); - params.setEnableRetransmissions(enableRetransmissions); - params.setMaximumPacketSize(maximumPacketSize); - params.setApplicationProtocols(applicationProtocols); - - return params; - } - - /** - * Applies SSLParameters to this engine. - */ @Override public synchronized void setSSLParameters(SSLParameters params) { - super.setSSLParameters(params); - - // the super implementation does not handle the following parameters - identificationProtocol = params.getEndpointIdentificationAlgorithm(); - algorithmConstraints = params.getAlgorithmConstraints(); - preferLocalCipherSuites = params.getUseCipherSuitesOrder(); - enableRetransmissions = params.getEnableRetransmissions(); - maximumPacketSize = params.getMaximumPacketSize(); - - if (maximumPacketSize != 0) { - outputRecord.changePacketSize(maximumPacketSize); - } else { - // use the implicit maximum packet size. - maximumPacketSize = outputRecord.getMaxPacketSize(); - } + conContext.sslConfig.setSSLParameters(params); - List sniNames = params.getServerNames(); - if (sniNames != null) { - serverNames = sniNames; - } - - Collection matchers = params.getSNIMatchers(); - if (matchers != null) { - sniMatchers = matchers; - } - applicationProtocols = params.getApplicationProtocols(); - - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setIdentificationProtocol(identificationProtocol); - handshaker.setAlgorithmConstraints(algorithmConstraints); - handshaker.setMaximumPacketSize(maximumPacketSize); - handshaker.setApplicationProtocols(applicationProtocols); - if (roleIsServer) { - handshaker.setSNIMatchers(sniMatchers); - handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); - } else { - handshaker.setSNIServerNames(serverNames); - } + if (conContext.sslConfig.maximumPacketSize != 0) { + conContext.outputRecord.changePacketSize( + conContext.sslConfig.maximumPacketSize); } } @Override public synchronized String getApplicationProtocol() { - return applicationProtocol; + return conContext.applicationProtocol; } @Override public synchronized String getHandshakeApplicationProtocol() { - if ((handshaker != null) && handshaker.started()) { - return handshaker.getHandshakeApplicationProtocol(); - } - return null; + return conContext.handshakeContext == null ? + null : conContext.handshakeContext.applicationProtocol; } @Override public synchronized void setHandshakeApplicationProtocolSelector( - BiFunction, String> selector) { - applicationProtocolSelector = selector; - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setApplicationProtocolSelectorSSLEngine(selector); - } + BiFunction, String> selector) { + conContext.sslConfig.engineAPSelector = selector; } @Override public synchronized BiFunction, String> - getHandshakeApplicationProtocolSelector() { - return this.applicationProtocolSelector; + getHandshakeApplicationProtocolSelector() { + return conContext.sslConfig.engineAPSelector; + } + + @Override + public boolean useDelegatedTask() { + return true; + } + + private synchronized void checkTaskThrown() throws SSLException { + HandshakeContext hc = conContext.handshakeContext; + if (hc != null && hc.delegatedThrown != null) { + try { + throw getTaskThrown(hc.delegatedThrown); + } finally { + hc.delegatedThrown = null; + } + } + + if (conContext.isBroken && conContext.closeReason != null) { + throw getTaskThrown(conContext.closeReason); + } + } + + private static SSLException getTaskThrown(Exception taskThrown) { + String msg = taskThrown.getMessage(); + + if (msg == null) { + msg = "Delegated task threw Exception or Error"; + } + + if (taskThrown instanceof RuntimeException) { + throw new RuntimeException(msg, taskThrown); + } else if (taskThrown instanceof SSLHandshakeException) { + return (SSLHandshakeException) + new SSLHandshakeException(msg).initCause(taskThrown); + } else if (taskThrown instanceof SSLKeyException) { + return (SSLKeyException) + new SSLKeyException(msg).initCause(taskThrown); + } else if (taskThrown instanceof SSLPeerUnverifiedException) { + return (SSLPeerUnverifiedException) + new SSLPeerUnverifiedException(msg).initCause(taskThrown); + } else if (taskThrown instanceof SSLProtocolException) { + return (SSLProtocolException) + new SSLProtocolException(msg).initCause(taskThrown); + } else if (taskThrown instanceof SSLException) { + return (SSLException)taskThrown; + } else { + return new SSLException(msg, taskThrown); + } } /** - * Returns a printable representation of this end of the connection. + * Implement a simple task delegator. */ - @Override - public String toString() { - StringBuilder retval = new StringBuilder(80); + private static class DelegatedTask implements Runnable { + private final SSLEngineImpl engine; + + DelegatedTask(SSLEngineImpl engineInstance) { + this.engine = engineInstance; + } + + @Override + public void run() { + synchronized (engine) { + HandshakeContext hc = engine.conContext.handshakeContext; + if (hc == null || hc.delegatedActions.isEmpty()) { + return; + } - retval.append(Integer.toHexString(hashCode())); - retval.append("["); - retval.append("SSLEngine[hostname="); - String host = getPeerHost(); - retval.append((host == null) ? "null" : host); - retval.append(" port="); - retval.append(Integer.toString(getPeerPort())); - retval.append(" role=" + (roleIsServer ? "Server" : "Client")); - retval.append("] "); - retval.append(getSession().getCipherSuite()); - retval.append("]"); + try { + AccessController.doPrivileged( + new DelegatedAction(hc), engine.conContext.acc); + } catch (PrivilegedActionException pae) { + // Get the handshake context again in case the + // handshaking has completed. + hc = engine.conContext.handshakeContext; + if (hc != null) { + hc.delegatedThrown = pae.getException(); + } else if (engine.conContext.closeReason != null) { + engine.conContext.closeReason = + getTaskThrown(pae.getException()); + } + } catch (RuntimeException rte) { + // Get the handshake context again in case the + // handshaking has completed. + hc = engine.conContext.handshakeContext; + if (hc != null) { + hc.delegatedThrown = rte; + } else if (engine.conContext.closeReason != null) { + engine.conContext.closeReason = rte; + } + } - return retval.toString(); + // Get the handshake context again in case the + // handshaking has completed. + hc = engine.conContext.handshakeContext; + if (hc != null) { + hc.taskDelegated = false; + } + } + } + + private static class DelegatedAction + implements PrivilegedExceptionAction { + final HandshakeContext context; + DelegatedAction(HandshakeContext context) { + this.context = context; + } + + @Override + public Void run() throws Exception { + while (!context.delegatedActions.isEmpty()) { + // Report back the task SSLException + if (context.delegatedThrown != null) { + Exception delegatedThrown = context.delegatedThrown; + context.delegatedThrown = null; + throw getTaskThrown(delegatedThrown); + } + + Map.Entry me = + context.delegatedActions.poll(); + if (me != null) { + context.dispatch(me.getKey(), me.getValue()); + } + } + return null; + } + } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineInputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,48 +25,48 @@ package sun.security.ssl; -import java.io.*; -import java.nio.*; - +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.ArrayList; import javax.crypto.BadPaddingException; - -import javax.net.ssl.*; - -import sun.security.util.HexDumpEncoder; - +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLProtocolException; +import sun.security.ssl.SSLCipher.SSLReadCipher; +import sun.security.ssl.KeyUpdate.KeyUpdateMessage; +import sun.security.ssl.KeyUpdate.KeyUpdateRequest; /** * {@code InputRecord} implementation for {@code SSLEngine}. */ final class SSLEngineInputRecord extends InputRecord implements SSLRecord { - // used by handshake hash computation for handshake fragment - private byte prevType = -1; - private int hsMsgOff = 0; - private int hsMsgLen = 0; - private boolean formatVerified = false; // SSLv2 ruled out? - SSLEngineInputRecord() { - this.readAuthenticator = MAC.TLS_NULL; + // Cache for incomplete handshake messages. + private ByteBuffer handshakeBuffer = null; + + SSLEngineInputRecord(HandshakeHash handshakeHash) { + super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); } @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; } } @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 { /* * SSLv2 length field is in bytes 0/1 * SSLv3/TLS length field is in bytes 3/4 @@ -83,19 +83,23 @@ /* * If we have already verified previous packets, we can * ignore the verifications steps, and jump right to the - * determination. Otherwise, try one last hueristic to + * determination. Otherwise, try one last heuristic to * see if it's SSL/TLS. */ if (formatVerified || - (byteZero == ct_handshake) || (byteZero == ct_alert)) { + (byteZero == ContentType.HANDSHAKE.id) || + (byteZero == ContentType.ALERT.id)) { /* * Last sanity check that it's not a wild record */ - ProtocolVersion recordVersion = ProtocolVersion.valueOf( - packet.get(pos + 1), packet.get(pos + 2)); - - // check the record version - checkRecordVersion(recordVersion, false); + byte majorVersion = packet.get(pos + 1); + byte minorVersion = packet.get(pos + 2); + if (!ProtocolVersion.isNegotiable( + majorVersion, minorVersion, false, false)) { + throw new SSLException("Unrecognized record version " + + ProtocolVersion.nameOf(majorVersion, minorVersion) + + " , plaintext connection?"); + } /* * Reasonably sure this is a V3, disable further checks. @@ -123,11 +127,14 @@ if (isShort && ((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) { - ProtocolVersion recordVersion = ProtocolVersion.valueOf( - packet.get(pos + 3), packet.get(pos + 4)); - - // check the record version - checkRecordVersion(recordVersion, true); + byte majorVersion = packet.get(pos + 3); + byte minorVersion = packet.get(pos + 4); + if (!ProtocolVersion.isNegotiable( + majorVersion, minorVersion, false, false)) { + throw new SSLException("Unrecognized record version " + + ProtocolVersion.nameOf(majorVersion, minorVersion) + + " , plaintext connection?"); + } /* * Client or Server Hello @@ -147,37 +154,29 @@ } @Override - void checkRecordVersion(ProtocolVersion recordVersion, - boolean allowSSL20Hello) throws SSLException { - - if (recordVersion.maybeDTLSProtocol()) { - throw new SSLException( - "Unrecognized record version " + recordVersion + - " , DTLS packet?"); - } + Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, + int srcsLength) throws IOException, BadPaddingException { + if (srcs == null || srcs.length == 0 || srcsLength == 0) { + return new Plaintext[0]; + } else if (srcsLength == 1) { + return decode(srcs[srcsOffset]); + } else { + ByteBuffer packet = extract(srcs, + srcsOffset, srcsLength, SSLRecord.headerSize); - // Check if the record version is too old. - if ((recordVersion.v < ProtocolVersion.MIN.v)) { - // if it's not SSLv2, we're out of here. - if (!allowSSL20Hello || - (recordVersion.v != ProtocolVersion.SSL20Hello.v)) { - throw new SSLException( - "Unsupported record version " + recordVersion); - } + return decode(packet); } } - @Override - Plaintext decode(ByteBuffer packet) + private Plaintext[] decode(ByteBuffer packet) throws IOException, BadPaddingException { 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. @@ -191,7 +190,8 @@ */ int pos = packet.position(); byte byteZero = packet.get(pos); - if (byteZero != ct_handshake && byteZero != ct_alert) { + if (byteZero != ContentType.HANDSHAKE.id && + byteZero != ContentType.ALERT.id) { return handleUnknownRecord(packet); } } @@ -199,27 +199,24 @@ return decodeInputRecord(packet); } - private Plaintext decodeInputRecord(ByteBuffer packet) + private Plaintext[] decodeInputRecord(ByteBuffer packet) throws IOException, BadPaddingException { - // // The packet should be a complete record, or more. // - int srcPos = packet.position(); int srcLim = packet.limit(); byte contentType = packet.get(); // pos: 0 byte majorVersion = packet.get(); // pos: 1 byte minorVersion = packet.get(); // pos: 2 - int contentLen = ((packet.get() & 0xFF) << 8) + - (packet.get() & 0xFF); // pos: 3, 4 + int contentLen = Record.getInt16(packet); // pos: 3, 4 - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", 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); } @@ -233,106 +230,131 @@ } // - // check for handshake fragment - // - if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) { - throw new SSLProtocolException( - "Expected to get a handshake fragment"); - } - - // // Decrypt the fragment // int recLim = srcPos + SSLRecord.headerSize + contentLen; packet.limit(recLim); packet.position(srcPos + SSLRecord.headerSize); - ByteBuffer plaintext; + ByteBuffer fragment; try { - plaintext = - decrypt(readAuthenticator, readCipher, contentType, packet); + Plaintext plaintext = + readCipher.decrypt(contentType, packet, null); + fragment = plaintext.fragment; + contentType = plaintext.contentType; + } catch (BadPaddingException bpe) { + throw bpe; + } catch (GeneralSecurityException gse) { + throw (SSLProtocolException)(new SSLProtocolException( + "Unexpected exception")).initCause(gse); } finally { - // comsume a complete record + // consume a complete record packet.limit(srcLim); packet.position(recLim); } // - // handshake hashing + // check for handshake fragment // - if (contentType == ct_handshake) { - int pltPos = plaintext.position(); - int pltLim = plaintext.limit(); - int frgPos = pltPos; - for (int remains = plaintext.remaining(); remains > 0;) { - int howmuch; - byte handshakeType; - if (hsMsgOff < hsMsgLen) { - // a fragment of the handshake message - howmuch = Math.min((hsMsgLen - hsMsgOff), remains); - handshakeType = prevType; + if (contentType != ContentType.HANDSHAKE.id && + handshakeBuffer != null && handshakeBuffer.hasRemaining()) { + throw new SSLProtocolException( + "Expecting a handshake fragment, but received " + + ContentType.nameOf(contentType)); + } - hsMsgOff += howmuch; - if (hsMsgOff == hsMsgLen) { - // Now is a complete handshake message. - hsMsgOff = 0; - hsMsgLen = 0; - } - } else { // hsMsgOff == hsMsgLen, a new handshake message - handshakeType = plaintext.get(); - int handshakeLen = ((plaintext.get() & 0xFF) << 16) | - ((plaintext.get() & 0xFF) << 8) | - (plaintext.get() & 0xFF); - plaintext.position(frgPos); - if (remains < (handshakeLen + 4)) { // 4: handshake header - // This handshake message is fragmented. - prevType = handshakeType; - hsMsgOff = remains - 4; // 4: handshake header - hsMsgLen = handshakeLen; - } + // + // parse handshake messages + // + if (contentType == ContentType.HANDSHAKE.id) { + ByteBuffer handshakeFrag = fragment; + if ((handshakeBuffer != null) && + (handshakeBuffer.remaining() != 0)) { + ByteBuffer bb = ByteBuffer.wrap(new byte[ + handshakeBuffer.remaining() + fragment.remaining()]); + bb.put(handshakeBuffer); + bb.put(fragment); + handshakeFrag = bb.rewind(); + handshakeBuffer = null; + } - howmuch = Math.min(handshakeLen + 4, remains); + ArrayList

plaintexts = new ArrayList<>(5); + while (handshakeFrag.hasRemaining()) { + int remaining = handshakeFrag.remaining(); + if (remaining < handshakeHeaderSize) { + handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); + handshakeBuffer.put(handshakeFrag); + handshakeBuffer.rewind(); + break; } - plaintext.limit(frgPos + howmuch); + handshakeFrag.mark(); + // skip the first byte: handshake type + byte handshakeType = handshakeFrag.get(); + int handshakeBodyLen = Record.getInt24(handshakeFrag); + handshakeFrag.reset(); + int handshakeMessageLen = + handshakeHeaderSize + handshakeBodyLen; + if (remaining < handshakeMessageLen) { + handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); + handshakeBuffer.put(handshakeFrag); + handshakeBuffer.rewind(); + break; + } if (remaining == handshakeMessageLen) { + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.receive(handshakeFrag); + } - if (handshakeType == HandshakeMessage.ht_hello_request) { - // omitted from handshake hash computation - } else if ((handshakeType != HandshakeMessage.ht_finished) && - (handshakeType != HandshakeMessage.ht_certificate_verify)) { - - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); - } - handshakeHash.update(plaintext); + plaintexts.add( + new Plaintext(contentType, + majorVersion, minorVersion, -1, -1L, handshakeFrag) + ); + break; } else { - // Reserve until this handshake message has been processed. - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); + int fragPos = handshakeFrag.position(); + int fragLim = handshakeFrag.limit(); + int nextPos = fragPos + handshakeMessageLen; + handshakeFrag.limit(nextPos); + + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.receive(handshakeFrag); } - handshakeHash.reserve(plaintext); + + plaintexts.add( + new Plaintext(contentType, majorVersion, minorVersion, + -1, -1L, handshakeFrag.slice()) + ); + + handshakeFrag.position(nextPos); + handshakeFrag.limit(fragLim); } - - plaintext.position(frgPos + howmuch); - plaintext.limit(pltLim); - - frgPos += howmuch; - remains -= howmuch; } - plaintext.position(pltPos); + return plaintexts.toArray(new Plaintext[0]); } - return new Plaintext(contentType, - majorVersion, minorVersion, -1, -1L, plaintext); - // recordEpoch, recordSeq, plaintext); + // KeyLimit check during application data. + // atKeyLimit() inactive when limits not checked, tc set when limits + // are active. + + if (readCipher.atKeyLimit()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyUpdate: triggered, read side."); + } + + PostHandshakeContext p = new PostHandshakeContext(tc); + KeyUpdate.handshakeProducer.produce(p, + new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); + } + + return new Plaintext[] { + new Plaintext(contentType, + majorVersion, minorVersion, -1, -1L, fragment) + }; } - private Plaintext handleUnknownRecord(ByteBuffer packet) + private Plaintext[] handleUnknownRecord(ByteBuffer packet) throws IOException, BadPaddingException { - // // The packet should be a complete record. // @@ -363,8 +385,8 @@ * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); } @@ -380,23 +402,20 @@ * V3 ClientHello message, and pass it up. */ packet.position(srcPos + 2); // exclude the header - - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); - } - handshakeHash.update(packet); + handshakeHash.receive(packet); packet.position(srcPos); ByteBuffer converted = convertToClientHello(packet); - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine( "[Converted] ClientHello", converted); } - return new Plaintext(ct_handshake, - majorVersion, minorVersion, -1, -1L, converted); + return new Plaintext[] { + new Plaintext(ContentType.HANDSHAKE.id, + majorVersion, minorVersion, -1, -1L, converted) + }; } else { if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { throw new SSLException("SSL V2.0 servers are not supported."); @@ -405,5 +424,4 @@ throw new SSLException("Unsupported or unrecognized SSL message"); } } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,14 +25,14 @@ package sun.security.ssl; -import java.io.*; -import java.nio.*; -import java.util.*; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import sun.security.util.HexDumpEncoder; -import static sun.security.ssl.Ciphertext.RecordType; +import sun.security.ssl.SSLCipher.SSLWriteCipher; +import sun.security.ssl.KeyUpdate.KeyUpdateMessage; +import sun.security.ssl.KeyUpdate.KeyUpdateRequest; /** * {@code OutputRecord} implementation for {@code SSLEngine}. @@ -40,23 +40,22 @@ final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord { private HandshakeFragment fragmenter = null; - private LinkedList<RecordMemo> alertMemos = new LinkedList<>(); private boolean isTalkingToV2 = false; // SSLv2Hello private ByteBuffer v2ClientHello = null; // SSLv2Hello private boolean isCloseWaiting = false; - SSLEngineOutputRecord() { - this.writeAuthenticator = MAC.TLS_NULL; + SSLEngineOutputRecord(HandshakeHash handshakeHash) { + super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher()); this.packetSize = SSLRecord.maxRecordSize; - this.protocolVersion = ProtocolVersion.DEFAULT_TLS; + this.protocolVersion = ProtocolVersion.NONE; } @Override public synchronized void close() throws IOException { if (!isClosed) { - if (alertMemos != null && !alertMemos.isEmpty()) { + if (fragmenter != null && fragmenter.hasAlert()) { isCloseWaiting = true; } else { super.close(); @@ -66,19 +65,11 @@ @Override void encodeAlert(byte level, byte description) throws IOException { - RecordMemo memo = new RecordMemo(); + if (fragmenter == null) { + fragmenter = new HandshakeFragment(); + } - memo.contentType = Record.ct_alert; - memo.majorVersion = protocolVersion.major; - memo.minorVersion = protocolVersion.minor; - memo.encodeCipher = writeCipher; - memo.encodeAuthenticator = writeAuthenticator; - - memo.fragment = new byte[2]; - memo.fragment[0] = level; - memo.fragment[1] = description; - - alertMemos.add(memo); + fragmenter.queueUpAlert(level, description); } @Override @@ -93,7 +84,7 @@ firstMessage = false; if ((helloVersion == ProtocolVersion.SSL20Hello) && - (source[offset] == HandshakeMessage.ht_client_hello) && + (source[offset] == SSLHandshake.CLIENT_HELLO.id) && // 5: recode header size (source[offset + 4 + 2 + 32] == 0)) { // V3 session ID is empty @@ -106,7 +97,7 @@ source, (offset + 4), (length - 4)); v2ClientHello.position(2); // exclude the header - handshakeHash.update(v2ClientHello); + handshakeHash.deliver(v2ClientHello); v2ClientHello.position(0); return; @@ -114,8 +105,8 @@ } byte handshakeType = source[offset]; - if (handshakeType != HandshakeMessage.ht_hello_request) { - handshakeHash.update(source, offset, length); + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.deliver(source, offset, length); } fragmenter.queueUpFragment(source, offset, length); @@ -135,22 +126,43 @@ } @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"); } - 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. + Ciphertext ct = acquireCiphertext(destination); + if (ct != null) { + return ct; + } + + if (sources == null || sources.length == 0) { + return null; + } + + int srcsRemains = 0; + for (int i = offset; i < offset + length; i++) { + srcsRemains += sources[i].remaining(); + } + + if (srcsRemains == 0) { + return null; } int dstLim = destination.limit(); @@ -170,7 +182,7 @@ if (packetLeftSize > 0) { fragLen = writeCipher.calculateFragmentSize( - packetLeftSize, macLen, headerSize); + packetLeftSize, headerSize); fragLen = Math.min(fragLen, Record.maxDataSize); } else { @@ -208,26 +220,24 @@ 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. - recordSN = encrypt(writeAuthenticator, writeCipher, - Record.ct_application_data, destination, + recordSN = encrypt(writeCipher, + ContentType.APPLICATION_DATA.id, destination, dstPos, dstLim, headerSize, - protocolVersion, false); + 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); } packetLeftSize -= destination.position() - dstPos; @@ -238,103 +248,62 @@ if (isFirstAppOutputRecord) { isFirstAppOutputRecord = false; } + + // atKeyLimit() inactive when limits not checked, tc set when limits + // are active. + if (writeCipher.atKeyLimit()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyUpdate: triggered, write side."); + } + + PostHandshakeContext p = new PostHandshakeContext(tc); + KeyUpdate.handshakeProducer.produce(p, + new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); + } } - 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 (isTalkingToV2) { // SSLv2Hello // We don't support SSLv2. Send an SSLv2 error message // so that the connection can be closed gracefully. // // Please don't change the limit of the destination buffer. destination.put(SSLRecord.v2NoCipher); - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + SSLRecord.v2NoCipher.length, - SSLRecord.v2NoCipher); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); } isTalkingToV2 = false; - return new Ciphertext(RecordType.RECORD_ALERT, -1L); + return new Ciphertext(ContentType.ALERT.id, + SSLHandshake.NOT_APPLICABLE.id, -1L); } if (v2ClientHello != null) { // deliver the SSLv2 format ClientHello message // // Please don't change the limit of the destination buffer. - if (debug != null) { - if (Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + + if (SSLLogger.isOn) { + if (SSLLogger.isOn("record")) { + SSLLogger.fine(Thread.currentThread().getName() + ", WRITE: SSLv2 ClientHello message" + ", length = " + v2ClientHello.remaining()); } - if (Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + v2ClientHello.remaining(), - v2ClientHello); + if (SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", v2ClientHello); } } destination.put(v2ClientHello); v2ClientHello = null; - return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L); - } - - 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 + - writeCipher.getExplicitNonceSize(); - destination.position(dstContent); - - destination.put(memo.fragment); - - 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) + - ", 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, - ProtocolVersion.valueOf(memo.majorVersion, - memo.minorVersion), false); - - if ((debug != null) && Debug.isOn("packet")) { - ByteBuffer temporary = destination.duplicate(); - temporary.limit(temporary.position()); - temporary.position(dstPos); - Debug.printHex( - "[Raw write]: length = " + temporary.remaining(), - temporary); - } - - // remain the limit unchanged - destination.limit(dstLim); - - if (isCloseWaiting && (memo.contentType == Record.ct_alert)) { - isCloseWaiting = true; - close(); - } - return new Ciphertext(RecordType.RECORD_ALERT, recordSN); + return new Ciphertext(ContentType.HANDSHAKE.id, + SSLHandshake.CLIENT_HELLO.id, -1L); } if (fragmenter != null) { @@ -347,8 +316,7 @@ @Override boolean isEmpty() { return (!isTalkingToV2) && (v2ClientHello == null) && - ((fragmenter == null) || fragmenter.isEmpty()) && - ((alertMemos == null) || alertMemos.isEmpty()); + ((fragmenter == null) || fragmenter.isEmpty()); } // buffered record fragment @@ -356,8 +324,7 @@ byte contentType; byte majorVersion; byte minorVersion; - CipherBox encodeCipher; - Authenticator encodeAuthenticator; + SSLWriteCipher encodeCipher; byte[] fragment; } @@ -372,14 +339,12 @@ void queueUpFragment(byte[] source, int offset, int length) throws IOException { - HandshakeMemo memo = new HandshakeMemo(); - memo.contentType = Record.ct_handshake; + memo.contentType = ContentType.HANDSHAKE.id; memo.majorVersion = protocolVersion.major; // kick start version? memo.minorVersion = protocolVersion.minor; memo.encodeCipher = writeCipher; - memo.encodeAuthenticator = writeAuthenticator; memo.handshakeType = source[offset]; memo.acquireOffset = 0; @@ -394,11 +359,10 @@ void queueUpChangeCipherSpec() { 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.encodeCipher = writeCipher; - memo.encodeAuthenticator = writeAuthenticator; memo.fragment = new byte[1]; memo.fragment[0] = 1; @@ -406,6 +370,21 @@ handshakeMemos.add(memo); } + void queueUpAlert(byte level, byte description) { + RecordMemo memo = new RecordMemo(); + + memo.contentType = ContentType.ALERT.id; + memo.majorVersion = protocolVersion.major; + memo.minorVersion = protocolVersion.minor; + memo.encodeCipher = writeCipher; + + memo.fragment = new byte[2]; + memo.fragment[0] = level; + memo.fragment[1] = description; + + handshakeMemos.add(memo); + } + Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { if (isEmpty()) { return null; @@ -413,22 +392,17 @@ RecordMemo memo = handshakeMemos.getFirst(); 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, headerSize); + fragLen, headerSize); } else { fragLen = Record.maxDataSize; } @@ -474,11 +448,12 @@ !handshakeMemos.isEmpty()) { // look for the next buffered record fragment - RecordMemo reMemo = handshakeMemos.getFirst(); - if (reMemo.contentType == Record.ct_handshake) { - hsMemo = (HandshakeMemo)reMemo; + RecordMemo rm = handshakeMemos.getFirst(); + if (rm.contentType == ContentType.HANDSHAKE.id && + rm.encodeCipher == hsMemo.encodeCipher) { + hsMemo = (HandshakeMemo)rm; } else { - // not handshake message, break the loop + // not of the flight, break the loop break; } } @@ -486,8 +461,6 @@ remainingFragLen -= chipLen; } - - fragLen -= remainingFragLen; } else { fragLen = Math.min(fragLen, memo.fragment.length); dstBuf.put(memo.fragment, 0, fragLen); @@ -498,27 +471,26 @@ 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), false); + 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); } // remain the limit unchanged @@ -526,17 +498,32 @@ // Reset the fragmentation offset. if (hsMemo != null) { - return new Ciphertext(RecordType.valueOf( - hsMemo.contentType, hsMemo.handshakeType), recordSN); + return new Ciphertext(hsMemo.contentType, + hsMemo.handshakeType, recordSN); } else { - return new Ciphertext( - RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN); + if (isCloseWaiting && + memo.contentType == ContentType.ALERT.id) { + close(); + } + + return new Ciphertext(memo.contentType, + SSLHandshake.NOT_APPLICABLE.id, recordSN); } } boolean isEmpty() { return handshakeMemos.isEmpty(); } + + boolean hasAlert() { + for (RecordMemo memo : handshakeMemos) { + if (memo.contentType == ContentType.ALERT.id) { + return true; + } + } + + return false; + } } /* diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,685 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Locale; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.HexDumpEncoder; + +enum SSLExtension implements SSLStringizer { + // Extensions defined in RFC 6066 + CH_SERVER_NAME (0x0000, "server_name", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + ServerNameExtension.chNetworkProducer, + ServerNameExtension.chOnLoadConsumer, + null, + null, + null, + ServerNameExtension.chStringizer), + SH_SERVER_NAME (0x0000, "server_name", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + ServerNameExtension.shNetworkProducer, + ServerNameExtension.shOnLoadConsumer, + null, + null, + null, + ServerNameExtension.shStringizer), + EE_SERVER_NAME (0x0000, "server_name", + SSLHandshake.ENCRYPTED_EXTENSIONS, + ProtocolVersion.PROTOCOLS_OF_13, + ServerNameExtension.eeNetworkProducer, + ServerNameExtension.eeOnLoadConsumer, + null, + null, + null, + ServerNameExtension.shStringizer), + CH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + MaxFragExtension.chNetworkProducer, + MaxFragExtension.chOnLoadConsumer, + null, + null, + null, + MaxFragExtension.maxFragLenStringizer), + SH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + MaxFragExtension.shNetworkProducer, + MaxFragExtension.shOnLoadConsumer, + null, + MaxFragExtension.shOnTradeConsumer, + null, + MaxFragExtension.maxFragLenStringizer), + EE_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length", + SSLHandshake.ENCRYPTED_EXTENSIONS, + ProtocolVersion.PROTOCOLS_OF_13, + MaxFragExtension.eeNetworkProducer, + MaxFragExtension.eeOnLoadConsumer, + null, + MaxFragExtension.eeOnTradeConsumer, + null, + MaxFragExtension.maxFragLenStringizer), + CLIENT_CERTIFICATE_URL (0x0002, "client_certificate_url"), + TRUSTED_CA_KEYS (0x0003, "trusted_ca_keys"), + TRUNCATED_HMAC (0x0004, "truncated_hmac"), + + CH_STATUS_REQUEST (0x0005, "status_request", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + CertStatusExtension.chNetworkProducer, + CertStatusExtension.chOnLoadConsumer, + null, + null, + null, + CertStatusExtension.certStatusReqStringizer), + SH_STATUS_REQUEST (0x0005, "status_request", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + CertStatusExtension.shNetworkProducer, + CertStatusExtension.shOnLoadConsumer, + null, + null, + null, + CertStatusExtension.certStatusReqStringizer), + + CR_STATUS_REQUEST (0x0005, "status_request"), + CT_STATUS_REQUEST (0x0005, "status_request", + SSLHandshake.CERTIFICATE, + ProtocolVersion.PROTOCOLS_OF_13, + CertStatusExtension.ctNetworkProducer, + CertStatusExtension.ctOnLoadConsumer, + null, + null, + null, + CertStatusExtension.certStatusRespStringizer), + // extensions defined in RFC 4681 + USER_MAPPING (0x0006, "user_mapping"), + + // extensions defined in RFC 5878 + CLIENT_AUTHZ (0x0007, "client_authz"), + SERVER_AUTHZ (0x0008, "server_authz"), + + // extensions defined in RFC 5081 + CERT_TYPE (0x0009, "cert_type"), + + // extensions defined in RFC 4492 (ECC) + CH_SUPPORTED_GROUPS (0x000A, "supported_groups", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + SupportedGroupsExtension.chNetworkProducer, + SupportedGroupsExtension.chOnLoadConsumer, + null, + null, + null, + SupportedGroupsExtension.sgsStringizer), + EE_SUPPORTED_GROUPS (0x000A, "supported_groups", + SSLHandshake.ENCRYPTED_EXTENSIONS, + ProtocolVersion.PROTOCOLS_OF_13, + SupportedGroupsExtension.eeNetworkProducer, + SupportedGroupsExtension.eeOnLoadConsumer, + null, + null, + null, + SupportedGroupsExtension.sgsStringizer), + + CH_EC_POINT_FORMATS (0x000B, "ec_point_formats", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + ECPointFormatsExtension.chNetworkProducer, + ECPointFormatsExtension.chOnLoadConsumer, + null, + null, + null, + ECPointFormatsExtension.epfStringizer), + SH_EC_POINT_FORMATS (0x000B, "ec_point_formats", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + null, // not use of the producer + ECPointFormatsExtension.shOnLoadConsumer, + null, + null, + null, + ECPointFormatsExtension.epfStringizer), + + // extensions defined in RFC 5054 + SRP (0x000C, "srp"), + + // extensions defined in RFC 5246 + CH_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_12_13, + SignatureAlgorithmsExtension.chNetworkProducer, + SignatureAlgorithmsExtension.chOnLoadConsumer, + SignatureAlgorithmsExtension.chOnLoadAbsence, + SignatureAlgorithmsExtension.chOnTradeConsumer, + SignatureAlgorithmsExtension.chOnTradeAbsence, + SignatureAlgorithmsExtension.ssStringizer), + CR_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms", + SSLHandshake.CERTIFICATE_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + SignatureAlgorithmsExtension.crNetworkProducer, + SignatureAlgorithmsExtension.crOnLoadConsumer, + SignatureAlgorithmsExtension.crOnLoadAbsence, + SignatureAlgorithmsExtension.crOnTradeConsumer, + null, + SignatureAlgorithmsExtension.ssStringizer), + + CH_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_12_13, + CertSignAlgsExtension.chNetworkProducer, + CertSignAlgsExtension.chOnLoadConsumer, + null, + CertSignAlgsExtension.chOnTradeConsumer, + null, + CertSignAlgsExtension.ssStringizer), + CR_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert", + SSLHandshake.CERTIFICATE_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + CertSignAlgsExtension.crNetworkProducer, + CertSignAlgsExtension.crOnLoadConsumer, + null, + CertSignAlgsExtension.crOnTradeConsumer, + null, + CertSignAlgsExtension.ssStringizer), + + // extensions defined in RFC 5764 + USE_SRTP (0x000E, "use_srtp"), + + // extensions defined in RFC 6520 + HEARTBEAT (0x000E, "heartbeat"), + + // extension defined in RFC 7301 (ALPN) + CH_ALPN (0x0010, "application_layer_protocol_negotiation", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + AlpnExtension.chNetworkProducer, + AlpnExtension.chOnLoadConsumer, + AlpnExtension.chOnLoadAbsence, + null, + null, + AlpnExtension.alpnStringizer), + SH_ALPN (0x0010, "application_layer_protocol_negotiation", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + AlpnExtension.shNetworkProducer, + AlpnExtension.shOnLoadConsumer, + AlpnExtension.shOnLoadAbsence, + null, + null, + AlpnExtension.alpnStringizer), + EE_ALPN (0x0010, "application_layer_protocol_negotiation", + SSLHandshake.ENCRYPTED_EXTENSIONS, + ProtocolVersion.PROTOCOLS_OF_13, + AlpnExtension.shNetworkProducer, + AlpnExtension.shOnLoadConsumer, + AlpnExtension.shOnLoadAbsence, + null, + null, + AlpnExtension.alpnStringizer), + + // extensions defined in RFC 6961 + CH_STATUS_REQUEST_V2 (0x0011, "status_request_v2", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + CertStatusExtension.chV2NetworkProducer, + CertStatusExtension.chV2OnLoadConsumer, + null, + null, + null, + CertStatusExtension.certStatusReqV2Stringizer), + SH_STATUS_REQUEST_V2 (0x0011, "status_request_v2", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + CertStatusExtension.shV2NetworkProducer, + CertStatusExtension.shV2OnLoadConsumer, + null, + null, + null, + CertStatusExtension.certStatusReqV2Stringizer), + + // extensions defined in RFC 6962 + SIGNED_CERT_TIMESTAMP (0x0012, "signed_certificate_timestamp"), + + // extensions defined in RFC 7250 + CLIENT_CERT_TYPE (0x0013, "padding"), + SERVER_CERT_TYPE (0x0014, "server_certificate_type"), + + // extensions defined in RFC 7685 + PADDING (0x0015, "client_certificate_type"), + + // extensions defined in RFC 7366 + ENCRYPT_THEN_MAC (0x0016, "encrypt_then_mac"), + + // extensions defined in RFC 7627 + CH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + ExtendedMasterSecretExtension.chNetworkProducer, + ExtendedMasterSecretExtension.chOnLoadConsumer, + ExtendedMasterSecretExtension.chOnLoadAbsence, + null, + null, + ExtendedMasterSecretExtension.emsStringizer), + SH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + ExtendedMasterSecretExtension.shNetworkProducer, + ExtendedMasterSecretExtension.shOnLoadConsumer, + ExtendedMasterSecretExtension.shOnLoadAbsence, + null, + null, + ExtendedMasterSecretExtension.emsStringizer), + + // extensions defined in RFC draft-ietf-tokbind-negotiation + TOKEN_BINDING (0x0018, "token_binding "), + + // extensions defined in RFC 7924 + CACHED_INFO (0x0019, "cached_info"), + + // extensions defined in RFC 4507/5077 + SESSION_TICKET (0x0023, "session_ticket"), + + // extensions defined in TLS 1.3 + CH_EARLY_DATA (0x002A, "early_data"), + EE_EARLY_DATA (0x002A, "early_data"), + NST_EARLY_DATA (0x002A, "early_data"), + + CH_SUPPORTED_VERSIONS (0x002B, "supported_versions", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_13, + SupportedVersionsExtension.chNetworkProducer, + SupportedVersionsExtension.chOnLoadConsumer, + null, + null, + null, + SupportedVersionsExtension.chStringizer), + SH_SUPPORTED_VERSIONS (0x002B, "supported_versions", + SSLHandshake.SERVER_HELLO, + // and HelloRetryRequest + ProtocolVersion.PROTOCOLS_OF_13, + SupportedVersionsExtension.shNetworkProducer, + SupportedVersionsExtension.shOnLoadConsumer, + null, + null, + null, + SupportedVersionsExtension.shStringizer), + HRR_SUPPORTED_VERSIONS (0x002B, "supported_versions", + SSLHandshake.HELLO_RETRY_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + SupportedVersionsExtension.hrrNetworkProducer, + SupportedVersionsExtension.hrrOnLoadConsumer, + null, + null, + null, + SupportedVersionsExtension.hrrStringizer), + MH_SUPPORTED_VERSIONS (0x002B, "supported_versions", + SSLHandshake.MESSAGE_HASH, + ProtocolVersion.PROTOCOLS_OF_13, + SupportedVersionsExtension.hrrReproducer, + null, null, null, + null, + SupportedVersionsExtension.hrrStringizer), + + CH_COOKIE (0x002C, "cookie", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + CookieExtension.chNetworkProducer, + CookieExtension.chOnLoadConsumer, + null, + CookieExtension.chOnTradeConsumer, + null, + CookieExtension.cookieStringizer), + HRR_COOKIE (0x002C, "cookie", + SSLHandshake.HELLO_RETRY_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + CookieExtension.hrrNetworkProducer, + CookieExtension.hrrOnLoadConsumer, + null, null, + null, + CookieExtension.cookieStringizer), + MH_COOKIE (0x002C, "cookie", + SSLHandshake.MESSAGE_HASH, + ProtocolVersion.PROTOCOLS_OF_13, + CookieExtension.hrrNetworkReproducer, + null, null, null, + null, + CookieExtension.cookieStringizer), + + PSK_KEY_EXCHANGE_MODES (0x002D, "psk_key_exchange_modes", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + PskKeyExchangeModesExtension.chNetworkProducer, + PskKeyExchangeModesExtension.chOnLoadConsumer, + PskKeyExchangeModesExtension.chOnLoadAbsence, + null, + PskKeyExchangeModesExtension.chOnTradeAbsence, + PskKeyExchangeModesExtension.pkemStringizer), + CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities"), + OID_FILTERS (0x0030, "oid_filters"), + POST_HANDSHAKE_AUTH (0x0030, "post_handshake_auth"), + + CH_KEY_SHARE (0x0033, "key_share", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + KeyShareExtension.chNetworkProducer, + KeyShareExtension.chOnLoadConsumer, + null, null, null, + KeyShareExtension.chStringizer), + SH_KEY_SHARE (0x0033, "key_share", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + KeyShareExtension.shNetworkProducer, + KeyShareExtension.shOnLoadConsumer, + KeyShareExtension.shOnLoadAbsence, + null, + null, + KeyShareExtension.shStringizer), + HRR_KEY_SHARE (0x0033, "key_share", + SSLHandshake.HELLO_RETRY_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + KeyShareExtension.hrrNetworkProducer, + KeyShareExtension.hrrOnLoadConsumer, + null, null, null, + KeyShareExtension.hrrStringizer), + MH_KEY_SHARE (0x0033, "key_share", + SSLHandshake.MESSAGE_HASH, + ProtocolVersion.PROTOCOLS_OF_13, + KeyShareExtension.hrrNetworkReproducer, + null, null, null, null, + KeyShareExtension.hrrStringizer), + + // Extensions defined in RFC 5746 + CH_RENEGOTIATION_INFO (0xff01, "renegotiation_info", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + RenegoInfoExtension.chNetworkProducer, + RenegoInfoExtension.chOnLoadConsumer, + RenegoInfoExtension.chOnLoadAbsence, + null, + null, + RenegoInfoExtension.rniStringizer), + SH_RENEGOTIATION_INFO (0xff01, "renegotiation_info", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_TO_12, + RenegoInfoExtension.shNetworkProducer, + RenegoInfoExtension.shOnLoadConsumer, + RenegoInfoExtension.shOnLoadAbsence, + null, + null, + RenegoInfoExtension.rniStringizer), + + // TLS 1.3 PSK extension must be last + CH_PRE_SHARED_KEY (0x0029, "pre_shared_key", + SSLHandshake.CLIENT_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + PreSharedKeyExtension.chNetworkProducer, + PreSharedKeyExtension.chOnLoadConsumer, + PreSharedKeyExtension.chOnLoadAbsence, + PreSharedKeyExtension.chOnTradeConsumer, + null, + PreSharedKeyExtension.chStringizer), + SH_PRE_SHARED_KEY (0x0029, "pre_shared_key", + SSLHandshake.SERVER_HELLO, + ProtocolVersion.PROTOCOLS_OF_13, + PreSharedKeyExtension.shNetworkProducer, + PreSharedKeyExtension.shOnLoadConsumer, + PreSharedKeyExtension.shOnLoadAbsence, + null, null, + PreSharedKeyExtension.shStringizer); + + final int id; + final SSLHandshake handshakeType; + final String name; + final ProtocolVersion[] supportedProtocols; + final HandshakeProducer networkProducer; + final ExtensionConsumer onLoadConsumer; + final HandshakeAbsence onLoadAbsence; + final HandshakeConsumer onTradeConsumer; + final HandshakeAbsence onTradeAbsence; + final SSLStringizer stringizer; + + // known but unsupported extension + private SSLExtension(int id, String name) { + this.id = id; + this.handshakeType = SSLHandshake.NOT_APPLICABLE; + this.name = name; + this.supportedProtocols = new ProtocolVersion[0]; + this.networkProducer = null; + this.onLoadConsumer = null; + this.onLoadAbsence = null; + this.onTradeConsumer = null; + this.onTradeAbsence = null; + this.stringizer = null; + } + + // supported extension + private SSLExtension(int id, String name, SSLHandshake handshakeType, + ProtocolVersion[] supportedProtocols, + HandshakeProducer producer, + ExtensionConsumer onLoadConsumer, HandshakeAbsence onLoadAbsence, + HandshakeConsumer onTradeConsumer, HandshakeAbsence onTradeAbsence, + SSLStringizer stringize) { + this.id = id; + this.handshakeType = handshakeType; + this.name = name; + this.supportedProtocols = supportedProtocols; + this.networkProducer = producer; + this.onLoadConsumer = onLoadConsumer; + this.onLoadAbsence = onLoadAbsence; + this.onTradeConsumer = onTradeConsumer; + this.onTradeAbsence = onTradeAbsence; + this.stringizer = stringize; + } + + static SSLExtension valueOf(SSLHandshake handshakeType, int extensionType) { + for (SSLExtension ext : SSLExtension.values()) { + if (ext.id == extensionType && + ext.handshakeType == handshakeType) { + return ext; + } + } + + return null; + } + + static boolean isConsumable(int extensionType) { + for (SSLExtension ext : SSLExtension.values()) { + if (ext.id == extensionType && + ext.onLoadConsumer != null) { + return true; + } + } + + return false; + } + + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + if (networkProducer != null) { + return networkProducer.produce(context, message); + } else { + throw new UnsupportedOperationException( + "Not yet supported extension producing."); + } + } + + public void consumeOnLoad(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + if (onLoadConsumer != null) { + onLoadConsumer.consume(context, message, buffer); + } else { + throw new UnsupportedOperationException( + "Not yet supported extension loading."); + } + } + + public void consumeOnTrade(ConnectionContext context, + HandshakeMessage message) throws IOException { + if (onTradeConsumer != null) { + onTradeConsumer.consume(context, message); + } else { + throw new UnsupportedOperationException( + "Not yet supported extension processing."); + } + } + + void absentOnLoad(ConnectionContext context, + HandshakeMessage message) throws IOException { + if (onLoadAbsence != null) { + onLoadAbsence.absent(context, message); + } else { + throw new UnsupportedOperationException( + "Not yet supported extension absence processing."); + } + } + + void absentOnTrade(ConnectionContext context, + HandshakeMessage message) throws IOException { + if (onTradeAbsence != null) { + onTradeAbsence.absent(context, message); + } else { + throw new UnsupportedOperationException( + "Not yet supported extension absence processing."); + } + } + + public boolean isAvailable(ProtocolVersion protocolVersion) { + for (int i = 0; i < supportedProtocols.length; i++) { + if (supportedProtocols[i] == protocolVersion) { + return true; + } + } + + return false; + } + + @Override + public String toString() { + return name; + } + + @Override + public String toString(ByteBuffer byteBuffer) { + MessageFormat messageFormat = new MessageFormat( + "\"{0} ({1})\": '{'\n" + + "{2}\n" + + "'}'", + Locale.ENGLISH); + + String extData; + if (stringizer == null) { + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + String encoded = hexEncoder.encode(byteBuffer.duplicate()); + extData = encoded; + } else { + extData = stringizer.toString(byteBuffer); + } + + Object[] messageFields = { + this.name, + this.id, + Utilities.indent(extData) + }; + + return messageFormat.format(messageFields); + } + + ////////////////////////////////////////////////////// + // Nested extension, consumer and producer interfaces. + + static interface ExtensionConsumer { + void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException; + } + + /** + * A (transparent) specification of extension data. + * + * This interface contains no methods or constants. Its only purpose is to + * group all extension data. All extension data should implement this + * interface if the data is expected to handle in the following handshake + * processes. + */ + static interface SSLExtensionSpec { + // blank + } + + // Default enabled client extensions. + static final class ClientExtensions { + static final Collection<SSLExtension> defaults; + + static { + Collection<SSLExtension> extensions = new LinkedList<>(); + for (SSLExtension extension : SSLExtension.values()) { + if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) { + extensions.add(extension); + } + } + + // Switch off SNI extention? + boolean enableExtension = + Utilities.getBooleanProperty("jsse.enableSNIExtension", true); + if (!enableExtension) { + extensions.remove(CH_SERVER_NAME); + } + + // To switch off the max_fragment_length extension. + enableExtension = + Utilities.getBooleanProperty("jsse.enableMFLExtension", false); + if (!enableExtension) { + extensions.remove(CH_MAX_FRAGMENT_LENGTH); + } + + defaults = Collections.unmodifiableCollection(extensions); + } + } + + // Default enabled server extensions. + static final class ServerExtensions { + static final Collection<SSLExtension> defaults; + + static { + Collection<SSLExtension> extensions = new LinkedList<>(); + for (SSLExtension extension : SSLExtension.values()) { + if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) { + extensions.add(extension); + } + } + + defaults = Collections.unmodifiableCollection(extensions); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLExtensions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtensions.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,362 @@ +/* + * 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 sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.HexDumpEncoder; + +/** + * SSL/(D)TLS extensions in a handshake message. + */ +final class SSLExtensions { + private final HandshakeMessage handshakeMessage; + private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>(); + private int encodedLength; + + // Extension map for debug logging + private final Map<Integer, byte[]> logMap = + SSLLogger.isOn ? null : new LinkedHashMap<>(); + + SSLExtensions(HandshakeMessage handshakeMessage) { + this.handshakeMessage = handshakeMessage; + this.encodedLength = 2; // 2: the length of the extensions. + } + + SSLExtensions(HandshakeMessage hm, + ByteBuffer m, SSLExtension[] extensions) throws IOException { + this.handshakeMessage = hm; + + int len = Record.getInt16(m); + encodedLength = len + 2; // 2: the length of the extensions. + while (len > 0) { + int extId = Record.getInt16(m); + int extLen = Record.getInt16(m); + if (extLen > m.remaining()) { + hm.handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Error parsing extension (" + extId + + "): no sufficient data"); + } + + SSLHandshake handshakeType = hm.handshakeType(); + if (SSLExtension.isConsumable(extId) && + SSLExtension.valueOf(handshakeType, extId) == null) { + hm.handshakeContext.conContext.fatal( + Alert.UNSUPPORTED_EXTENSION, + "extension (" + extId + + ") should not be presented in " + handshakeType.name); + } + + boolean isSupported = false; + for (SSLExtension extension : extensions) { + if ((extension.id != extId) || + (extension.onLoadConsumer == null)) { + continue; + } + + if (extension.handshakeType != handshakeType) { + hm.handshakeContext.conContext.fatal( + Alert.UNSUPPORTED_EXTENSION, + "extension (" + extId + ") should not be " + + "presented in " + handshakeType.name); + } + + byte[] extData = new byte[extLen]; + m.get(extData); + extMap.put(extension, extData); + if (logMap != null) { + logMap.put(extId, extData); + } + + isSupported = true; + break; + } + + if (!isSupported) { + if (logMap != null) { + // cache the extension for debug logging + byte[] extData = new byte[extLen]; + m.get(extData); + logMap.put(extId, extData); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unknown or unsupported extension", + toString(extId, extData)); + } + } else { + // ignore the extension + int pos = m.position() + extLen; + m.position(pos); + } + } + + len -= extLen + 4; + } + } + + byte[] get(SSLExtension ext) { + return extMap.get(ext); + } + + /** + * Consume the specified extensions. + */ + void consumeOnLoad(HandshakeContext context, + SSLExtension[] extensions) throws IOException { + for (SSLExtension extension : extensions) { + if (context.negotiatedProtocol != null && + !extension.isAvailable(context.negotiatedProtocol)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unsupported extension: " + extension.name); + } + continue; + } + + if (!extMap.containsKey(extension)) { + if (extension.onLoadAbsence != null) { + extension.absentOnLoad(context, handshakeMessage); + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + extension.name); + } + continue; + } + + + if (extension.onLoadConsumer == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore unsupported extension: " + extension.name); + } + continue; + } + + ByteBuffer m = ByteBuffer.wrap(extMap.get(extension)); + extension.consumeOnLoad(context, handshakeMessage, m); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Consumed extension: " + extension.name); + } + } + } + + /** + * Consider impact of the specified extensions. + */ + void consumeOnTrade(HandshakeContext context, + SSLExtension[] extensions) throws IOException { + for (SSLExtension extension : extensions) { + if (!extMap.containsKey(extension)) { + if (extension.onTradeAbsence != null) { + extension.absentOnTrade(context, handshakeMessage); + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + extension.name); + } + continue; + } + + if (extension.onTradeConsumer == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore impact of unsupported extension: " + + extension.name); + } + continue; + } + + extension.consumeOnTrade(context, handshakeMessage); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Populated with extension: " + extension.name); + } + } + } + + /** + * Produce extension values for the specified extensions. + */ + void produce(HandshakeContext context, + SSLExtension[] extensions) throws IOException { + for (SSLExtension extension : extensions) { + if (extMap.containsKey(extension)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore, duplicated extension: " + + extension.name); + } + continue; + } + + if (extension.networkProducer == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore, no extension producer defined: " + + extension.name); + } + continue; + } + + byte[] encoded = extension.produce(context, handshakeMessage); + if (encoded != null) { + extMap.put(extension, encoded); + encodedLength += encoded.length + 4; // extension_type (2) + // extension_data length(2) + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + // The extension is not available in the context. + SSLLogger.fine( + "Ignore, context unavailable extension: " + + extension.name); + } + } + } + + /** + * Produce extension values for the specified extensions, replacing if + * there is an existing extension value for a specified extension. + */ + void reproduce(HandshakeContext context, + SSLExtension[] extensions) throws IOException { + for (SSLExtension extension : extensions) { + if (extension.networkProducer == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore, no extension producer defined: " + + extension.name); + } + continue; + } + + byte[] encoded = extension.produce(context, handshakeMessage); + if (encoded != null) { + if (extMap.containsKey(extension)) { + byte[] old = extMap.replace(extension, encoded); + if (old != null) { + encodedLength -= old.length + 4; + } + encodedLength += encoded.length + 4; + } else { + extMap.put(extension, encoded); + encodedLength += encoded.length + 4; + // extension_type (2) + // extension_data length(2) + } + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + // The extension is not available in the context. + SSLLogger.fine( + "Ignore, context unavailable extension: " + + extension.name); + } + } + } + + // Note that TLS 1.3 may use empty extensions. Please consider it while + // using this method. + int length() { + if (extMap.isEmpty()) { + return 0; + } else { + return encodedLength; + } + } + + // Note that TLS 1.3 may use empty extensions. Please consider it while + // using this method. + void send(HandshakeOutStream hos) throws IOException { + int extsLen = length(); + if (extsLen == 0) { + return; + } + hos.putInt16(extsLen - 2); + // extensions must be sent in the order they appear in the enum + for (SSLExtension ext : SSLExtension.values()) { + byte[] extData = extMap.get(ext); + if (extData != null) { + hos.putInt16(ext.id); + hos.putBytes16(extData); + } + } + } + + @Override + public String toString() { + if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) { + return "<no extension>"; + } else { + StringBuilder builder = new StringBuilder(512); + if (logMap != null) { + for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) { + SSLExtension ext = SSLExtension.valueOf( + handshakeMessage.handshakeType(), en.getKey()); + if (builder.length() != 0) { + builder.append(",\n"); + } + if (ext != null) { + builder.append( + ext.toString(ByteBuffer.wrap(en.getValue()))); + } else { + builder.append(toString(en.getKey(), en.getValue())); + } + } + + return builder.toString(); + } else { + for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) { + if (builder.length() != 0) { + builder.append(",\n"); + } + builder.append( + en.getKey().toString(ByteBuffer.wrap(en.getValue()))); + } + + return builder.toString(); + } + } + } + + private static String toString(int extId, byte[] extData) { + MessageFormat messageFormat = new MessageFormat( + "\"unknown extension ({0})\": '{'\n" + + "{1}\n" + + "'}'", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + String encoded = hexEncoder.encodeBuffer(extData); + + Object[] messageFields = { + extId, + Utilities.indent(encoded) + }; + + return messageFormat.format(messageFields); + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLHandshake.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Map; +import javax.net.ssl.SSLException; + +enum SSLHandshake implements SSLConsumer, HandshakeProducer { + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_REQUEST ((byte)0x00, "hello_request", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + HelloRequest.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + HelloRequest.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CLIENT_HELLO ((byte)0x01, "client_hello", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ClientHello.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ClientHello.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_HELLO ((byte)0x02, "server_hello", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ServerHello.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ServerHello.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ServerHello.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ServerHello.handshakeConsumer, // Use ServerHello consumer + ProtocolVersion.PROTOCOLS_TO_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ServerHello.hrrHandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + HELLO_VERIFY_REQUEST ((byte)0x03, "hello_verify_request", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + HelloVerifyRequest.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + HelloVerifyRequest.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + NewSessionTicket.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + NewSessionTicket.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + ENCRYPTED_EXTENSIONS ((byte)0x08, "encrypted_extensions", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + EncryptedExtensions.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + EncryptedExtensions.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE ((byte)0x0B, "certificate", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateMessage.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateMessage.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateMessage.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateMessage.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_KEY_EXCHANGE ((byte)0x0C, "server_key_exchange", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ServerKeyExchange.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ServerKeyExchange.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_REQUEST ((byte)0x0D, "certificate_request", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateRequest.t10HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_11 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateRequest.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateRequest.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateRequest.t10HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_11 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateRequest.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateRequest.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + SERVER_HELLO_DONE ((byte)0x0E, "server_hello_done", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ServerHelloDone.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ServerHelloDone.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_VERIFY ((byte)0x0F, "certificate_verify", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateVerify.s30HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_30 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateVerify.t10HandshakeConsumer, + ProtocolVersion.PROTOCOLS_10_11 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateVerify.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateVerify.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateVerify.s30HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_30 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateVerify.t10HandshakeProducer, + ProtocolVersion.PROTOCOLS_10_11 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateVerify.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_12 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateVerify.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CLIENT_KEY_EXCHANGE ((byte)0x10, "client_key_exchange", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + ClientKeyExchange.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + ClientKeyExchange.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + FINISHED ((byte)0x14, "finished", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + Finished.t12HandshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + Finished.t13HandshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + Finished.t12HandshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ), + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + Finished.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + + CERTIFICATE_URL ((byte)0x15, "certificate_url"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + CERTIFICATE_STATUS ((byte)0x16, "certificate_status", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + CertificateStatus.handshakeConsumer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + CertificateStatus.handshakeProducer, + ProtocolVersion.PROTOCOLS_TO_12 + ) + }), + (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeAbsence, ProtocolVersion[]>( + CertificateStatus.handshakeAbsence, + ProtocolVersion.PROTOCOLS_TO_12 + ) + })), + + SUPPLEMENTAL_DATA ((byte)0x17, "supplemental_data"), + + @SuppressWarnings({"unchecked", "rawtypes"}) + KEY_UPDATE ((byte)0x18, "key_update", + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>( + KeyUpdate.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] { + new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>( + KeyUpdate.handshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + MESSAGE_HASH ((byte)0xFE, "message_hash"), + NOT_APPLICABLE ((byte)0xFF, "not_applicable"); + + final byte id; + final String name; + final Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers; + final Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers; + final Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsences; + + @SuppressWarnings({"unchecked", "rawtypes"}) + SSLHandshake(byte id, String name) { + this(id, name, + (Map.Entry<SSLConsumer, ProtocolVersion[]>[])( + new Map.Entry[0]), + (Map.Entry<HandshakeProducer, ProtocolVersion[]>[])( + new Map.Entry[0]), + (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])( + new Map.Entry[0])); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + SSLHandshake(byte id, String name, + Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers, + Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers) { + this(id, name, handshakeConsumers, handshakeProducers, + (Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])( + new Map.Entry[0])); + } + + SSLHandshake(byte id, String name, + Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers, + Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers, + Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsence) { + this.id = id; + this.name = name; + this.handshakeConsumers = handshakeConsumers; + this.handshakeProducers = handshakeProducers; + this.handshakeAbsences = handshakeAbsence; + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + SSLConsumer hc = getHandshakeConsumer(context); + if (hc != null) { + hc.consume(context, message); + } else { + throw new UnsupportedOperationException( + "Unsupported handshake consumer: " + this.name); + } + } + + private SSLConsumer getHandshakeConsumer(ConnectionContext context) { + if (handshakeConsumers.length == 0) { + return null; + } + + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + ProtocolVersion protocolVersion; + if ((hc.negotiatedProtocol == null) || + (hc.negotiatedProtocol == ProtocolVersion.NONE)) { + protocolVersion = hc.maximumActiveProtocol; + } else { + protocolVersion = hc.negotiatedProtocol; + } + + for (Map.Entry<SSLConsumer, + ProtocolVersion[]> phe : handshakeConsumers) { + for (ProtocolVersion pv : phe.getValue()) { + if (protocolVersion == pv) { + return phe.getKey(); + } + } + } + + return null; + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + HandshakeProducer hp = getHandshakeProducer(context); + if (hp != null) { + return hp.produce(context, message); + } else { + throw new UnsupportedOperationException( + "Unsupported handshake producer: " + this.name); + } + } + + private HandshakeProducer getHandshakeProducer( + ConnectionContext context) { + if (handshakeConsumers.length == 0) { + return null; + } + + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + ProtocolVersion protocolVersion; + if ((hc.negotiatedProtocol == null) || + (hc.negotiatedProtocol == ProtocolVersion.NONE)) { + protocolVersion = hc.maximumActiveProtocol; + } else { + protocolVersion = hc.negotiatedProtocol; + } + + for (Map.Entry<HandshakeProducer, + ProtocolVersion[]> phe : handshakeProducers) { + for (ProtocolVersion pv : phe.getValue()) { + if (protocolVersion == pv) { + return phe.getKey(); + } + } + } + + return null; + } + + @Override + public String toString() { + return name; + } + + static String nameOf(byte id) { + // If two handshake message share the same handshake type, returns + // the first handshake message name. + // + // It is not a big issue at present as only ServerHello and + // HellRetryRequest share a handshake type. + for (SSLHandshake hs : SSLHandshake.values()) { + if (hs.id == id) { + return hs.name; + } + } + + return "UNKNOWN-HANDSHAKE-MESSAGE(" + id + ")"; + } + + static final void kickstart(HandshakeContext context) throws IOException { + if (context instanceof ClientHandshakeContext) { + // For initial handshaking, including session resumption, + // ClientHello message is used as the kickstart message. + // + // (D)TLS 1.2 and older protocols support renegotiation on existing + // connections. A ClientHello messages is used to kickstart the + // renegotiation. + // + // (D)TLS 1.3 forbids renegotiation. The post-handshake KeyUpdate + // message is used to update the sending cryptographic keys. + if (context.conContext.isNegotiated && + context.conContext.protocolVersion.useTLS13PlusSpec()) { + // Use KeyUpdate message for renegotiation. + KeyUpdate.kickstartProducer.produce(context); + } else { + // Using ClientHello message for the initial handshaking + // (including session resumption) or renegotiation. + // SSLHandshake.CLIENT_HELLO.produce(context); + ClientHello.kickstartProducer.produce(context); + } + } else { + // The server side can delivering kickstart message after the + // connection has established. + // + // (D)TLS 1.2 and older protocols use HelloRequest to begin a + // negotiation process anew. + // + // While (D)TLS 1.3 uses the post-handshake KeyUpdate message + // to update the sending cryptographic keys. + if (context.conContext.protocolVersion.useTLS13PlusSpec()) { + // Use KeyUpdate message for renegotiation. + KeyUpdate.kickstartProducer.produce(context); + } else { + // SSLHandshake.HELLO_REQUEST.produce(context); + HelloRequest.kickstartProducer.produce(context); + } + } + } + + /** + * A (transparent) specification of handshake message. + */ + static abstract class HandshakeMessage { + final HandshakeContext handshakeContext; + + HandshakeMessage(HandshakeContext handshakeContext) { + this.handshakeContext = handshakeContext; + } + + abstract SSLHandshake handshakeType(); + abstract int messageLength(); + abstract void send(HandshakeOutStream hos) throws IOException; + + void write(HandshakeOutStream hos) throws IOException { + int len = messageLength(); + if (len >= Record.OVERFLOW_OF_INT24) { + throw new SSLException("Handshake message is overflow" + + ", type = " + handshakeType() + ", len = " + len); + } + hos.write(handshakeType().id); + hos.putInt24(len); + send(hos); + hos.complete(); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLHandshakeBinding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshakeBinding.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.Map; + +interface SSLHandshakeBinding { + default SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + return new SSLHandshake[0]; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + default Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers( + HandshakeContext handshakeContext) { + return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + default Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers( + HandshakeContext handshakeContext) { + return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]); + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLKeyAgreement.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyAgreement.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +interface SSLKeyAgreement + extends SSLPossessionGenerator, SSLKeyAgreementGenerator, + SSLHandshakeBinding { + // blank +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLKeyAgreementGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyAgreementGenerator.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +interface SSLKeyAgreementGenerator { + SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivation.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.SecretKey; + +interface SSLKeyDerivation { + SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLKeyDerivationGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyDerivationGenerator.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import javax.crypto.SecretKey; + +interface SSLKeyDerivationGenerator { + SSLKeyDerivation createKeyDerivation(HandshakeContext context, + SecretKey secretKey) throws IOException; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,596 @@ +/* + * 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.util.AbstractMap.SimpleImmutableEntry; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import sun.security.ssl.DHKeyExchange.DHEPossession; +import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; +import sun.security.ssl.SupportedGroupsExtension.NamedGroup; +import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; +import sun.security.ssl.X509Authentication.X509Possession; + +final class SSLKeyExchange implements SSLKeyAgreementGenerator, + SSLHandshakeBinding { + private final SSLAuthentication authentication; + private final SSLKeyAgreement keyAgreement; + + SSLKeyExchange(X509Authentication authentication, + SSLKeyAgreement keyAgreement) { + this.authentication = authentication; + this.keyAgreement = keyAgreement; + } + + SSLPossession[] createPossessions(HandshakeContext context) { + // authentication + SSLPossession authPossession = null; + if (authentication != null) { + authPossession = authentication.createPossession(context); + if (authPossession == null) { + return new SSLPossession[0]; + } else if (context instanceof ServerHandshakeContext) { + // The authentication information may be used further for + // key agreement parameters negotiation. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + shc.interimAuthn = authPossession; + } + } + + // key agreement + SSLPossession kaPossession; + if (keyAgreement == T12KeyAgreement.RSA_EXPORT) { + // a special case + X509Possession x509Possession = (X509Possession)authPossession; + if (JsseJce.getRSAKeyLength( + x509Possession.popCerts[0].getPublicKey()) > 512) { + kaPossession = keyAgreement.createPossession(context); + + if (kaPossession == null) { + return new SSLPossession[0]; + } else { + return authentication != null ? + new SSLPossession[] {authPossession, kaPossession} : + new SSLPossession[] {kaPossession}; + } + } else { + return authentication != null ? + new SSLPossession[] {authPossession} : + new SSLPossession[0]; + } + } else { + kaPossession = keyAgreement.createPossession(context); + if (kaPossession == null) { + // special cases + if (keyAgreement == T12KeyAgreement.RSA || + keyAgreement == T12KeyAgreement.ECDH) { + return authentication != null ? + new SSLPossession[] {authPossession} : + new SSLPossession[0]; + } else { + return new SSLPossession[0]; + } + } else { + return authentication != null ? + new SSLPossession[] {authPossession, kaPossession} : + new SSLPossession[] {kaPossession}; + } + } + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext handshakeContext) throws IOException { + return keyAgreement.createKeyDerivation(handshakeContext); + } + + @Override + public SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + SSLHandshake[] auHandshakes; + if (authentication != null) { + auHandshakes = + authentication.getRelatedHandshakers(handshakeContext); + } else { + auHandshakes = null; + } + + SSLHandshake[] kaHandshakes = + keyAgreement.getRelatedHandshakers(handshakeContext); + + if (auHandshakes == null || auHandshakes.length == 0) { + return kaHandshakes; + } else if (kaHandshakes == null || kaHandshakes.length == 0) { + return auHandshakes; + } else { + SSLHandshake[] producers = Arrays.copyOf( + auHandshakes, auHandshakes.length + kaHandshakes.length); + System.arraycopy(kaHandshakes, 0, + producers, auHandshakes.length, kaHandshakes.length); + return producers; + } + } + + @Override + public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers( + HandshakeContext handshakeContext) { + Map.Entry<Byte, HandshakeProducer>[] auProducers; + if (authentication != null) { + auProducers = + authentication.getHandshakeProducers(handshakeContext); + } else { + auProducers = null; + } + + Map.Entry<Byte, HandshakeProducer>[] kaProducers = + keyAgreement.getHandshakeProducers(handshakeContext); + + if (auProducers == null || auProducers.length == 0) { + return kaProducers; + } else if (kaProducers == null || kaProducers.length == 0) { + return auProducers; + } else { + Map.Entry<Byte, HandshakeProducer>[] producers = Arrays.copyOf( + auProducers, auProducers.length + kaProducers.length); + System.arraycopy(kaProducers, 0, + producers, auProducers.length, kaProducers.length); + return producers; + } + } + + @Override + public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers( + HandshakeContext handshakeContext) { + Map.Entry<Byte, SSLConsumer>[] auConsumers; + if (authentication != null) { + auConsumers = + authentication.getHandshakeConsumers(handshakeContext); + } else { + auConsumers = null; + } + + Map.Entry<Byte, SSLConsumer>[] kaConsumers = + keyAgreement.getHandshakeConsumers(handshakeContext); + + if (auConsumers == null || auConsumers.length == 0) { + return kaConsumers; + } else if (kaConsumers == null || kaConsumers.length == 0) { + return auConsumers; + } else { + Map.Entry<Byte, SSLConsumer>[] producers = Arrays.copyOf( + auConsumers, auConsumers.length + kaConsumers.length); + System.arraycopy(kaConsumers, 0, + producers, auConsumers.length, kaConsumers.length); + return producers; + } + } + + // SSL 3.0 - (D)TLS 1.2 + static SSLKeyExchange valueOf( + CipherSuite.KeyExchange keyExchange, + ProtocolVersion protocolVersion) { + if (keyExchange == null || protocolVersion == null) { + return null; + } + + switch (keyExchange) { + case K_RSA: + return SSLKeyExRSA.KE; + case K_RSA_EXPORT: + return SSLKeyExRSAExport.KE; + case K_DHE_DSS: + return SSLKeyExDHEDSS.KE; + case K_DHE_DSS_EXPORT: + return SSLKeyExDHEDSSExport.KE; + case K_DHE_RSA: + if (protocolVersion.useTLS12PlusSpec()) { // (D)TLS 1.2 + return SSLKeyExDHERSAOrPSS.KE; + } else { // SSL 3.0, TLS 1.0/1.1 + return SSLKeyExDHERSA.KE; + } + case K_DHE_RSA_EXPORT: + return SSLKeyExDHERSAExport.KE; + case K_DH_ANON: + return SSLKeyExDHANON.KE; + case K_DH_ANON_EXPORT: + return SSLKeyExDHANONExport.KE; + case K_ECDH_ECDSA: + return SSLKeyExECDHECDSA.KE; + case K_ECDH_RSA: + return SSLKeyExECDHRSA.KE; + case K_ECDHE_ECDSA: + return SSLKeyExECDHEECDSA.KE; + case K_ECDHE_RSA: + if (protocolVersion.useTLS12PlusSpec()) { // (D)TLS 1.2 + return SSLKeyExECDHERSAOrPSS.KE; + } else { // SSL 3.0, TLS 1.0/1.1 + return SSLKeyExECDHERSA.KE; + } + case K_ECDH_ANON: + return SSLKeyExECDHANON.KE; + } + + return null; + } + + // TLS 1.3 + static SSLKeyExchange valueOf(NamedGroup namedGroup) { + SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup); + if (ka != null) { + return new SSLKeyExchange( + null, T13KeyAgreement.valueOf(namedGroup)); + } + + return null; + } + + private static class SSLKeyExRSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA, T12KeyAgreement.RSA); + } + + private static class SSLKeyExRSAExport { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT); + } + + private static class SSLKeyExDHEDSS { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.DSA, T12KeyAgreement.DHE); + } + + private static class SSLKeyExDHEDSSExport { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT); + } + + private static class SSLKeyExDHERSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA, T12KeyAgreement.DHE); + } + + private static class SSLKeyExDHERSAOrPSS { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE); + } + + private static class SSLKeyExDHERSAExport { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT); + } + + private static class SSLKeyExDHANON { + private static SSLKeyExchange KE = new SSLKeyExchange( + null, T12KeyAgreement.DHE); + } + + private static class SSLKeyExDHANONExport { + private static SSLKeyExchange KE = new SSLKeyExchange( + null, T12KeyAgreement.DHE_EXPORT); + } + + private static class SSLKeyExECDHECDSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.EC, T12KeyAgreement.ECDH); + } + + private static class SSLKeyExECDHRSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.EC, T12KeyAgreement.ECDH); + } + + private static class SSLKeyExECDHEECDSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.EC, T12KeyAgreement.ECDHE); + } + + private static class SSLKeyExECDHERSA { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA, T12KeyAgreement.ECDHE); + } + + private static class SSLKeyExECDHERSAOrPSS { + private static SSLKeyExchange KE = new SSLKeyExchange( + X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE); + } + + private static class SSLKeyExECDHANON { + private static SSLKeyExchange KE = new SSLKeyExchange( + null, T12KeyAgreement.ECDHE); + } + + private enum T12KeyAgreement implements SSLKeyAgreement { + RSA ("rsa", null, + RSAKeyExchange.kaGenerator), + RSA_EXPORT ("rsa_export", RSAKeyExchange.poGenerator, + RSAKeyExchange.kaGenerator), + DHE ("dhe", DHKeyExchange.poGenerator, + DHKeyExchange.kaGenerator), + DHE_EXPORT ("dhe_export", DHKeyExchange.poExportableGenerator, + DHKeyExchange.kaGenerator), + ECDH ("ecdh", null, + ECDHKeyExchange.ecdhKAGenerator), + ECDHE ("ecdhe", ECDHKeyExchange.poGenerator, + ECDHKeyExchange.ecdheKAGenerator); + + final String name; + final SSLPossessionGenerator possessionGenerator; + final SSLKeyAgreementGenerator keyAgreementGenerator; + + T12KeyAgreement(String name, + SSLPossessionGenerator possessionGenerator, + SSLKeyAgreementGenerator keyAgreementGenerator) { + this.name = name; + this.possessionGenerator = possessionGenerator; + this.keyAgreementGenerator = keyAgreementGenerator; + } + + @Override + public SSLPossession createPossession(HandshakeContext context) { + if (possessionGenerator != null) { + return possessionGenerator.createPossession(context); + } + + return null; + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + return keyAgreementGenerator.createKeyDerivation(context); + } + + @Override + public SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + if (this.possessionGenerator != null) { + return new SSLHandshake[] { + SSLHandshake.SERVER_KEY_EXCHANGE + }; + } + } + + return new SSLHandshake[0]; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]); + } + + if (handshakeContext.sslConfig.isClientMode) { + switch (this) { + case RSA: + case RSA_EXPORT: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + RSAClientKeyExchange.rsaHandshakeProducer + ) + }); + + case DHE: + case DHE_EXPORT: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<Byte, HandshakeProducer>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + DHClientKeyExchange.dhHandshakeProducer + ) + }); + + case ECDH: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + ECDHClientKeyExchange.ecdhHandshakeProducer + ) + }); + + case ECDHE: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + ECDHClientKeyExchange.ecdheHandshakeProducer + ) + }); + } + } else { + switch (this) { + case RSA_EXPORT: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + RSAServerKeyExchange.rsaHandshakeProducer + ) + }); + + case DHE: + case DHE_EXPORT: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + DHServerKeyExchange.dhHandshakeProducer + ) + }); + + case ECDHE: + return (Map.Entry<Byte, + HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + ECDHServerKeyExchange.ecdheHandshakeProducer + ) + }); + } + } + + return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers( + HandshakeContext handshakeContext) { + if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]); + } + + if (handshakeContext.sslConfig.isClientMode) { + switch (this) { + case RSA_EXPORT: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + RSAServerKeyExchange.rsaHandshakeConsumer + ) + }); + + case DHE: + case DHE_EXPORT: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + DHServerKeyExchange.dhHandshakeConsumer + ) + }); + + case ECDHE: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.SERVER_KEY_EXCHANGE.id, + ECDHServerKeyExchange.ecdheHandshakeConsumer + ) + }); + } + } else { + switch (this) { + case RSA: + case RSA_EXPORT: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + RSAClientKeyExchange.rsaHandshakeConsumer + ) + }); + + case DHE: + case DHE_EXPORT: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + DHClientKeyExchange.dhHandshakeConsumer + ) + }); + + case ECDH: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + ECDHClientKeyExchange.ecdhHandshakeConsumer + ) + }); + + case ECDHE: + return (Map.Entry<Byte, + SSLConsumer>[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + SSLHandshake.CLIENT_KEY_EXCHANGE.id, + ECDHClientKeyExchange.ecdheHandshakeConsumer + ) + }); + } + } + + return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]); + } + } + + private static final class T13KeyAgreement implements SSLKeyAgreement { + private final NamedGroup namedGroup; + static final Map<NamedGroup, T13KeyAgreement> + supportedKeyShares = new HashMap<>(); + + static { + for (NamedGroup namedGroup : + SupportedGroups.supportedNamedGroups) { + supportedKeyShares.put( + namedGroup, new T13KeyAgreement(namedGroup)); + } + } + + private T13KeyAgreement(NamedGroup namedGroup) { + this.namedGroup = namedGroup; + } + + static T13KeyAgreement valueOf(NamedGroup namedGroup) { + return supportedKeyShares.get(namedGroup); + } + + @Override + public SSLPossession createPossession(HandshakeContext hc) { + if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { + return new ECDHEPossession( + namedGroup, hc.sslContext.getSecureRandom()); + } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { + return new DHEPossession( + namedGroup, hc.sslContext.getSecureRandom()); + } + + return null; + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext hc) throws IOException { + if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { + return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc); + } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { + return DHKeyExchange.kaGenerator.createKeyDerivation(hc); + } + + return null; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLLogger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.security.cert.Certificate; +import java.security.cert.Extension; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import sun.security.action.GetPropertyAction; +import sun.security.util.HexDumpEncoder; +import sun.security.x509.*; + +/** + * Implementation of SSL logger. + * + * If the system property "javax.net.debug" is not defined, the debug logging + * is turned off. If the system property "javax.net.debug" is defined as + * empty, the debug logger is specified by System.getLogger("javax.net.ssl"), + * and applications can customize and configure the logger or use external + * logging mechanisms. If the system property "javax.net.debug" is defined + * and non-empty, a private debug logger implemented in this class is used. + */ +public final class SSLLogger { + private static final System.Logger logger; + private static final String property; + public static final boolean isOn; + + static { + String p = GetPropertyAction.privilegedGetProperty("javax.net.debug"); + if (p != null) { + if (p.isEmpty()) { + property = ""; + logger = System.getLogger("javax.net.ssl"); + } else { + property = p.toLowerCase(Locale.ENGLISH); + if (property.equals("help")) { + help(); + } + + logger = new SSLConsoleLogger("javax.net.ssl", p); + } + isOn = true; + } else { + property = null; + logger = null; + isOn = false; + } + } + + private static void help() { + System.err.println(); + System.err.println("help print the help messages"); + System.err.println("expand expand debugging information"); + System.err.println(); + System.err.println("all turn on all debugging"); + System.err.println("ssl turn on ssl debugging"); + System.err.println(); + System.err.println("The following can be used with ssl:"); + System.err.println("\trecord enable per-record tracing"); + System.err.println("\thandshake print each handshake message"); + System.err.println("\tkeygen print key generation data"); + System.err.println("\tsession print session activity"); + System.err.println("\tdefaultctx print default SSL initialization"); + System.err.println("\tsslctx print SSLContext tracing"); + System.err.println("\tsessioncache print session cache tracing"); + System.err.println("\tkeymanager print key manager tracing"); + System.err.println("\ttrustmanager print trust manager tracing"); + System.err.println("\tpluggability print pluggability tracing"); + System.err.println(); + System.err.println("\thandshake debugging can be widened with:"); + System.err.println("\tdata hex dump of each handshake message"); + System.err.println("\tverbose verbose handshake message printing"); + System.err.println(); + System.err.println("\trecord debugging can be widened with:"); + System.err.println("\tplaintext hex dump of record plaintext"); + System.err.println("\tpacket print raw SSL/TLS packets"); + System.err.println(); + System.exit(0); + } + + /** + * Return true if the "javax.net.debug" property contains the + * debug check points, or System.Logger is used. + */ + public static boolean isOn(String checkPoints) { + if (property == null) { // debugging is turned off + return false; + } else if (property.isEmpty()) { // use System.Logger + return true; + } // use provider logger + + String[] options = checkPoints.split(","); + for (String option : options) { + option = option.trim(); + if (!SSLLogger.hasOption(option)) { + return false; + } + } + + return true; + } + + private static boolean hasOption(String option) { + option = option.toLowerCase(Locale.ENGLISH); + if (property.contains("all")) { + return true; + } else { + int offset = property.indexOf("ssl"); + if (offset != -1 && property.indexOf("sslctx", offset) != -1) { + // don't enable data and plaintext options by default + if (!(option.equals("data") + || option.equals("packet") + || option.equals("plaintext"))) { + return true; + } + } + } + + return property.contains(option); + } + + public static void severe(String msg, Object... params) { + SSLLogger.log(Level.ERROR, msg, params); + } + + public static void warning(String msg, Object... params) { + SSLLogger.log(Level.WARNING, msg, params); + } + + public static void info(String msg, Object... params) { + SSLLogger.log(Level.INFO, msg, params); + } + + public static void fine(String msg, Object... params) { + SSLLogger.log(Level.DEBUG, msg, params); + } + + public static void finer(String msg, Object... params) { + SSLLogger.log(Level.TRACE, msg, params); + } + + public static void finest(String msg, Object... params) { + SSLLogger.log(Level.ALL, msg, params); + } + + private static void log(Level level, String msg, Object... params) { + if (logger.isLoggable(level)) { + if (params == null || params.length == 0) { + logger.log(level, msg); + } else { + try { + String formatted = + SSLSimpleFormatter.formatParameters(params); + logger.log(level, msg, formatted); + } catch (Exception exp) { + // ignore it, just for debugging. + } + } + } + } + + static String toString(Object... params) { + try { + return SSLSimpleFormatter.formatParameters(params); + } catch (Exception exp) { + return "unexpected exception thrown: " + exp.getMessage(); + } + } + + private static class SSLConsoleLogger implements Logger { + private final String loggerName; + private final boolean useCompactFormat; + + SSLConsoleLogger(String loggerName, String options) { + this.loggerName = loggerName; + options = options.toLowerCase(Locale.ENGLISH); + this.useCompactFormat = !options.contains("expand"); + } + + @Override + public String getName() { + return loggerName; + } + + @Override + public boolean isLoggable(Level level) { + return (level != Level.OFF); + } + + @Override + public void log(Level level, + ResourceBundle rb, String message, Throwable thrwbl) { + if (isLoggable(level)) { + try { + String formatted = + SSLSimpleFormatter.format(this, level, message, thrwbl); + System.err.write(formatted.getBytes("UTF-8")); + } catch (Exception exp) { + // ignore it, just for debugging. + } + } + } + + @Override + public void log(Level level, + ResourceBundle rb, String message, Object... params) { + if (isLoggable(level)) { + try { + String formatted = + SSLSimpleFormatter.format(this, level, message, params); + System.err.write(formatted.getBytes("UTF-8")); + } catch (Exception exp) { + // ignore it, just for debugging. + } + } + } + } + + private static class SSLSimpleFormatter { + private static final ThreadLocal<SimpleDateFormat> dateFormat = + new ThreadLocal<SimpleDateFormat>() { + @Override protected SimpleDateFormat initialValue() { + return new SimpleDateFormat( + "yyyy-MM-dd kk:mm:ss.SSS z", Locale.ENGLISH); + } + }; + + private static final MessageFormat basicCertFormat = new MessageFormat( + "\"version\" : \"v{0}\",\n" + + "\"serial number\" : \"{1}\",\n" + + "\"signature algorithm\": \"{2}\",\n" + + "\"issuer\" : \"{3}\",\n" + + "\"not before\" : \"{4}\",\n" + + "\"not after\" : \"{5}\",\n" + + "\"subject\" : \"{6}\",\n" + + "\"subject public key\" : \"{7}\"\n", + Locale.ENGLISH); + + private static final MessageFormat extendedCertFormart = + new MessageFormat( + "\"version\" : \"v{0}\",\n" + + "\"serial number\" : \"{1}\",\n" + + "\"signature algorithm\": \"{2}\",\n" + + "\"issuer\" : \"{3}\",\n" + + "\"not before\" : \"{4}\",\n" + + "\"not after\" : \"{5}\",\n" + + "\"subject\" : \"{6}\",\n" + + "\"subject public key\" : \"{7}\",\n" + + "\"extensions\" : [\n" + + "{8}\n" + + "]\n", + Locale.ENGLISH); + + // + // private static MessageFormat certExtFormat = new MessageFormat( + // "{0} [{1}] '{'\n" + + // " critical: {2}\n" + + // " value: {3}\n" + + // "'}'", + // Locale.ENGLISH); + // + + private static final MessageFormat messageFormatNoParas = + new MessageFormat( + "'{'\n" + + " \"logger\" : \"{0}\",\n" + + " \"level\" : \"{1}\",\n" + + " \"thread id\" : \"{2}\",\n" + + " \"thread name\" : \"{3}\",\n" + + " \"time\" : \"{4}\",\n" + + " \"caller\" : \"{5}\",\n" + + " \"message\" : \"{6}\"\n" + + "'}'\n", + Locale.ENGLISH); + + private static final MessageFormat messageCompactFormatNoParas = + new MessageFormat( + "{0}|{1}|{2}|{3}|{4}|{5}|{6}\n", + Locale.ENGLISH); + + private static final MessageFormat messageFormatWithParas = + new MessageFormat( + "'{'\n" + + " \"logger\" : \"{0}\",\n" + + " \"level\" : \"{1}\",\n" + + " \"thread id\" : \"{2}\",\n" + + " \"thread name\" : \"{3}\",\n" + + " \"time\" : \"{4}\",\n" + + " \"caller\" : \"{5}\",\n" + + " \"message\" : \"{6}\",\n" + + " \"specifics\" : [\n" + + "{7}\n" + + " ]\n" + + "'}'\n", + Locale.ENGLISH); + + private static final MessageFormat messageCompactFormatWithParas = + new MessageFormat( + "{0}|{1}|{2}|{3}|{4}|{5}|{6} (\n" + + "{7}\n" + + ")\n", + Locale.ENGLISH); + + private static final MessageFormat keyObjectFormat = new MessageFormat( + "\"{0}\" : '{'\n" + + "{1}" + + "'}'\n", + Locale.ENGLISH); + + // INFO: [TH: 123450] 2011-08-20 23:12:32.3225 PDT + // log message + // log message + // ... + private static String format(SSLConsoleLogger logger, Level level, + String message, Object ... parameters) { + + if (parameters == null || parameters.length == 0) { + Object[] messageFields = { + logger.loggerName, + level.getName(), + Utilities.toHexString(Thread.currentThread().getId()), + Thread.currentThread().getName(), + dateFormat.get().format(new Date(System.currentTimeMillis())), + formatCaller(), + message + }; + + if (logger.useCompactFormat) { + return messageCompactFormatNoParas.format(messageFields); + } else { + return messageFormatNoParas.format(messageFields); + } + } + + Object[] messageFields = { + logger.loggerName, + level.getName(), + Utilities.toHexString(Thread.currentThread().getId()), + Thread.currentThread().getName(), + dateFormat.get().format(new Date(System.currentTimeMillis())), + formatCaller(), + message, + (logger.useCompactFormat ? + formatParameters(parameters) : + Utilities.indent(formatParameters(parameters))) + }; + + if (logger.useCompactFormat) { + return messageCompactFormatWithParas.format(messageFields); + } else { + return messageFormatWithParas.format(messageFields); + } + } + + private static String formatCaller() { + return StackWalker.getInstance().walk(s -> + s.dropWhile(f -> + f.getClassName().startsWith("sun.security.ssl.SSLLogger") || + f.getClassName().startsWith("java.lang.System")) + .map(f -> f.getFileName() + ":" + f.getLineNumber()) + .findFirst().orElse("unknown caller")); + } + + private static String formatParameters(Object ... parameters) { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (Object parameter : parameters) { + if (isFirst) { + isFirst = false; + } else { + builder.append(",\n"); + } + + if (parameter instanceof Throwable) { + builder.append(formatThrowable((Throwable)parameter)); + } else if (parameter instanceof Certificate) { + builder.append(formatCertificate((Certificate)parameter)); + } else if (parameter instanceof ByteArrayInputStream) { + builder.append(formatByteArrayInputStream( + (ByteArrayInputStream)parameter)); + } else if (parameter instanceof ByteBuffer) { + builder.append(formatByteBuffer((ByteBuffer)parameter)); + } else if (parameter instanceof byte[]) { + builder.append(formatByteArrayInputStream( + new ByteArrayInputStream((byte[])parameter))); + } else if (parameter instanceof Map.Entry) { + @SuppressWarnings("unchecked") + Map.Entry<String, ?> mapParameter = + (Map.Entry<String, ?>)parameter; + builder.append(formatMapEntry(mapParameter)); + } else { + builder.append(formatObject(parameter)); + } + } + + return builder.toString(); + } + + // "throwable": { + // ... + // } + private static String formatThrowable(Throwable throwable) { + StringBuilder builder = new StringBuilder(512); + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (PrintStream out = new PrintStream(bytesOut)) { + throwable.printStackTrace(out); + builder.append(Utilities.indent(bytesOut.toString())); + } + Object[] fields = { + "throwable", + builder.toString() + }; + + return keyObjectFormat.format(fields); + } + + // "certificate": { + // ... + // } + private static String formatCertificate(Certificate certificate) { + + if (!(certificate instanceof X509Certificate)) { + return Utilities.indent(certificate.toString()); + } + + StringBuilder builder = new StringBuilder(512); + try { + X509CertImpl x509 = + X509CertImpl.toImpl((X509Certificate)certificate); + X509CertInfo certInfo = + (X509CertInfo)x509.get(X509CertImpl.NAME + "." + + X509CertImpl.INFO); + CertificateExtensions certExts = (CertificateExtensions) + certInfo.get(X509CertInfo.EXTENSIONS); + if (certExts == null) { + Object[] certFields = { + x509.getVersion(), + Utilities.toHexString( + x509.getSerialNumber().toByteArray()), + x509.getSigAlgName(), + x509.getIssuerX500Principal().toString(), + dateFormat.get().format(x509.getNotBefore()), + dateFormat.get().format(x509.getNotAfter()), + x509.getSubjectX500Principal().toString(), + x509.getPublicKey().getAlgorithm() + }; + builder.append(Utilities.indent( + basicCertFormat.format(certFields))); + } else { + StringBuilder extBuilder = new StringBuilder(512); + boolean isFirst = true; + for (Extension certExt : certExts.getAllExtensions()) { + if (isFirst) { + isFirst = false; + } else { + extBuilder.append(",\n"); + } + extBuilder.append("{\n" + + Utilities.indent(certExt.toString()) + "\n}"); + } + Object[] certFields = { + x509.getVersion(), + Utilities.toHexString( + x509.getSerialNumber().toByteArray()), + x509.getSigAlgName(), + x509.getIssuerX500Principal().toString(), + dateFormat.get().format(x509.getNotBefore()), + dateFormat.get().format(x509.getNotAfter()), + x509.getSubjectX500Principal().toString(), + x509.getPublicKey().getAlgorithm(), + Utilities.indent(extBuilder.toString()) + }; + builder.append(Utilities.indent( + extendedCertFormart.format(certFields))); + } + } catch (Exception ce) { + // ignore the exception + } + + Object[] fields = { + "certificate", + builder.toString() + }; + + return Utilities.indent(keyObjectFormat.format(fields)); + } + + private static String formatByteArrayInputStream( + ByteArrayInputStream bytes) { + StringBuilder builder = new StringBuilder(512); + + try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) { + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + hexEncoder.encodeBuffer(bytes, bytesOut); + + builder.append(Utilities.indent(bytesOut.toString())); + } catch (IOException ioe) { + // ignore it, just for debugging. + } + + return builder.toString(); + } + + private static String formatByteBuffer(ByteBuffer byteBuffer) { + StringBuilder builder = new StringBuilder(512); + try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) { + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + hexEncoder.encodeBuffer(byteBuffer.duplicate(), bytesOut); + builder.append(Utilities.indent(bytesOut.toString())); + } catch (IOException ioe) { + // ignore it, just for debugging. + } + + return builder.toString(); + } + + private static String formatMapEntry(Map.Entry<String, ?> entry) { + String key = entry.getKey(); + Object value = entry.getValue(); + + String formatted; + if (value instanceof String) { + // "key": "value" + formatted = "\"" + key + "\": \"" + (String)value + "\""; + } else if (value instanceof String[]) { + // "key": [ "string a", + // "string b", + // "string c" + // ] + StringBuilder builder = new StringBuilder(512); + String[] strings = (String[])value; + builder.append("\"" + key + "\": [\n"); + for (String string : strings) { + builder.append(" \"" + string + "\""); + if (string != strings[strings.length - 1]) { + builder.append(","); + } + builder.append("\n"); + } + builder.append(" ]"); + + formatted = builder.toString(); + } else if (value instanceof byte[]) { + formatted = "\"" + key + "\": \"" + + Utilities.toHexString((byte[])value) + "\""; + } else if (value instanceof Byte) { + formatted = "\"" + key + "\": \"" + + Utilities.toHexString((byte)value) + "\""; + } else { + formatted = "\"" + key + "\": " + + "\"" + value.toString() + "\""; + } + + return Utilities.indent(formatted); + } + + private static String formatObject(Object obj) { + return obj.toString(); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLMasterKeyDerivation.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import sun.security.internal.spec.TlsMasterSecretParameterSpec; +import sun.security.ssl.CipherSuite.HashAlg; +import static sun.security.ssl.CipherSuite.HashAlg.H_NONE; + +enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator { + SSL30 ("kdf_ssl30"), + TLS10 ("kdf_tls10"), + TLS12 ("kdf_tls12"); + + final String name; + + private SSLMasterKeyDerivation(String name) { + this.name = name; + } + + static SSLMasterKeyDerivation valueOf(ProtocolVersion protocolVersion) { + switch (protocolVersion) { + case SSL30: + return SSLMasterKeyDerivation.SSL30; + case TLS10: + case TLS11: + case DTLS10: + return SSLMasterKeyDerivation.TLS10; + case TLS12: + case DTLS12: + return SSLMasterKeyDerivation.TLS12; + default: + return null; + } + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext context, + SecretKey secretKey) throws IOException { + return new LegacyMasterKeyDerivation(context, secretKey); + } + + // Note, we may use different key derivation implementation in the future. + private static final + class LegacyMasterKeyDerivation implements SSLKeyDerivation { + + final HandshakeContext context; + final SecretKey preMasterSecret; + + LegacyMasterKeyDerivation( + HandshakeContext context, SecretKey preMasterSecret) { + this.context = context; + this.preMasterSecret = preMasterSecret; + } + + @Override + @SuppressWarnings("deprecation") + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + + CipherSuite cipherSuite = context.negotiatedCipherSuite; + ProtocolVersion protocolVersion = context.negotiatedProtocol; + + // What algs/params do we need to use? + String masterAlg; + HashAlg hashAlg; + + byte majorVersion = protocolVersion.major; + byte minorVersion = protocolVersion.minor; + if (protocolVersion.isDTLS) { + // Use TLS version number for DTLS key calculation + if (protocolVersion.id == ProtocolVersion.DTLS10.id) { + majorVersion = ProtocolVersion.TLS11.major; + minorVersion = ProtocolVersion.TLS11.minor; + + masterAlg = "SunTlsMasterSecret"; + hashAlg = H_NONE; + } else { // DTLS 1.2 + majorVersion = ProtocolVersion.TLS12.major; + minorVersion = ProtocolVersion.TLS12.minor; + + masterAlg = "SunTls12MasterSecret"; + hashAlg = cipherSuite.hashAlg; + } + } else { + if (protocolVersion.id >= ProtocolVersion.TLS12.id) { + masterAlg = "SunTls12MasterSecret"; + hashAlg = cipherSuite.hashAlg; + } else { + masterAlg = "SunTlsMasterSecret"; + hashAlg = H_NONE; + } + } + + TlsMasterSecretParameterSpec spec; + if (context.handshakeSession.useExtendedMasterSecret) { + // reset to use the extended master secret algorithm + masterAlg = "SunTlsExtendedMasterSecret"; + + // For the session hash, use the handshake messages up to and + // including the ClientKeyExchange message. + context.handshakeHash.utilize(); + byte[] sessionHash = context.handshakeHash.digest(); + spec = new TlsMasterSecretParameterSpec( + preMasterSecret, + (majorVersion & 0xFF), (minorVersion & 0xFF), + sessionHash, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + } else { + spec = new TlsMasterSecretParameterSpec( + preMasterSecret, + (majorVersion & 0xFF), (minorVersion & 0xFF), + context.clientHelloRandom.randomBytes, + context.serverHelloRandom.randomBytes, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + } + + try { + KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); + kg.init(spec); + return kg.generateKey(); + } catch (InvalidAlgorithmParameterException | + NoSuchAlgorithmException iae) { + // unlikely to happen, otherwise, must be a provider exception + // + // For RSA premaster secrets, do not signal a protocol error + // due to the Bleichenbacher attack. See comments further down. + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.fine("RSA master secret generation error.", iae); + } + throw new ProviderException(iae); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLPossession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLPossession.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +interface SSLPossession { + default byte[] encode() { + return new byte[0]; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLPossessionGenerator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLPossessionGenerator.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +interface SSLPossessionGenerator { + SSLPossession createPossession(HandshakeContext handshakeContext); +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLProducer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLProducer.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +interface SSLProducer { + // return the encoded producing if it has not been dumped to the context + byte[] produce(ConnectionContext context) throws IOException; +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -32,7 +32,8 @@ */ interface SSLRecord extends Record { - static final int headerSize = 5; // SSLv3 record header + static final int headerSize = 5; // SSLv3 record header + static final int handshakeHeaderSize = 4; // SSLv3 handshake header /* * The size of the header plus the max IV length @@ -67,19 +68,6 @@ + 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 - ); - - /* * The maximum large record size. * * Some SSL/TLS implementations support large fragment upto 2^15 bytes, @@ -92,18 +80,6 @@ maxRecordSize // Max size with a conforming implementation + maxDataSize; // extra 2^14 bytes for large data packets. - - /* - * 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 - /* * We may need to send this SSL v2 "No Cipher" message back, if we * are faced with an SSLv2 "hello" that's not saying "I talk v3". diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSecretDerivation.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.SecretKey; +import javax.net.ssl.SSLHandshakeException; +import sun.security.ssl.CipherSuite.HashAlg; + +final class SSLSecretDerivation implements SSLKeyDerivation { + private static final byte[] sha256EmptyDigest = new byte[] { + (byte)0xE3, (byte)0xB0, (byte)0xC4, (byte)0x42, + (byte)0x98, (byte)0xFC, (byte)0x1C, (byte)0x14, + (byte)0x9A, (byte)0xFB, (byte)0xF4, (byte)0xC8, + (byte)0x99, (byte)0x6F, (byte)0xB9, (byte)0x24, + (byte)0x27, (byte)0xAE, (byte)0x41, (byte)0xE4, + (byte)0x64, (byte)0x9B, (byte)0x93, (byte)0x4C, + (byte)0xA4, (byte)0x95, (byte)0x99, (byte)0x1B, + (byte)0x78, (byte)0x52, (byte)0xB8, (byte)0x55 + }; + + private static final byte[] sha384EmptyDigest = new byte[] { + (byte)0x38, (byte)0xB0, (byte)0x60, (byte)0xA7, + (byte)0x51, (byte)0xAC, (byte)0x96, (byte)0x38, + (byte)0x4C, (byte)0xD9, (byte)0x32, (byte)0x7E, + (byte)0xB1, (byte)0xB1, (byte)0xE3, (byte)0x6A, + (byte)0x21, (byte)0xFD, (byte)0xB7, (byte)0x11, + (byte)0x14, (byte)0xBE, (byte)0x07, (byte)0x43, + (byte)0x4C, (byte)0x0C, (byte)0xC7, (byte)0xBF, + (byte)0x63, (byte)0xF6, (byte)0xE1, (byte)0xDA, + (byte)0x27, (byte)0x4E, (byte)0xDE, (byte)0xBF, + (byte)0xE7, (byte)0x6F, (byte)0x65, (byte)0xFB, + (byte)0xD5, (byte)0x1A, (byte)0xD2, (byte)0xF1, + (byte)0x48, (byte)0x98, (byte)0xB9, (byte)0x5B + }; + + private final HandshakeContext context; + private final String hkdfAlg; + private final HashAlg hashAlg; + private final SecretKey secret; + private final byte[] transcriptHash; // handshake messages transcript hash + + SSLSecretDerivation( + HandshakeContext context, SecretKey secret) { + this.context = context; + this.secret = secret; + this.hashAlg = context.negotiatedCipherSuite.hashAlg; + this.hkdfAlg = + "HKDF-Expand/Hmac" + hashAlg.name.replace("-", ""); + context.handshakeHash.update(); + this.transcriptHash = context.handshakeHash.digest(); + } + + SSLSecretDerivation forContext(HandshakeContext context) { + return new SSLSecretDerivation(context, secret); + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + SecretSchedule ks = SecretSchedule.valueOf(algorithm); + try { + byte[] expandContext; + if (ks == SecretSchedule.TlsSaltSecret) { + if (hashAlg == HashAlg.H_SHA256) { + expandContext = sha256EmptyDigest; + } else if (hashAlg == HashAlg.H_SHA384) { + expandContext = sha384EmptyDigest; + } else { + // unlikely, but please update if more hash algorithm + // get supported in the future. + throw new SSLHandshakeException( + "Unexpected unsupported hash algorithm: " + + algorithm); + } + } else { + expandContext = transcriptHash; + } + byte[] hkdfInfo = createHkdfInfo(ks.label, + expandContext, hashAlg.hashLength); + + HKDF hkdf = new HKDF(hashAlg.name); + return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } + + public static byte[] createHkdfInfo( + byte[] label, byte[] context, int length) { + byte[] info = new byte[4 + label.length + context.length]; + ByteBuffer m = ByteBuffer.wrap(info); + try { + Record.putInt16(m, length); + Record.putBytes8(m, label); + Record.putBytes8(m, context); + } catch (IOException ioe) { + // unlikely + throw new RuntimeException("Unexpected exception", ioe); + } + + return info; + } + + private enum SecretSchedule { + // Note that we use enum name as the key/secret name. + TlsSaltSecret ("derived"), + TlsExtBinderKey ("ext binder"), + TlsResBinderKey ("res binder"), + TlsClientEarlyTrafficSecret ("c e traffic"), + TlsEarlyExporterMasterSecret ("e exp master"), + TlsClientHandshakeTrafficSecret ("c hs traffic"), + TlsServerHandshakeTrafficSecret ("s hs traffic"), + TlsClientAppTrafficSecret ("c ap traffic"), + TlsServerAppTrafficSecret ("s ap traffic"), + TlsExporterMasterSecret ("exp master"), + TlsResumptionMasterSecret ("res master"); + + private final byte[] label; + + private SecretSchedule(String label) { + this.label = ("tls13 " + label).getBytes(); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLServerSocketFactoryImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; - import javax.net.ssl.SSLServerSocketFactory; /** @@ -36,11 +35,9 @@ * * @author David Brownell */ -final -public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory -{ +public final class SSLServerSocketFactoryImpl extends SSLServerSocketFactory { private static final int DEFAULT_BACKLOG = 50; - private SSLContextImpl context; + private final SSLContextImpl context; /** @@ -55,8 +52,7 @@ /** * Called from SSLContextImpl's getSSLServerSocketFactory(). */ - SSLServerSocketFactoryImpl (SSLContextImpl context) - { + SSLServerSocketFactoryImpl(SSLContextImpl context) { this.context = context; } @@ -73,26 +69,22 @@ } @Override - public ServerSocket createServerSocket (int port) - throws IOException - { - return new SSLServerSocketImpl (port, DEFAULT_BACKLOG, context); + public ServerSocket createServerSocket(int port) throws IOException { + return new SSLServerSocketImpl(context, port, DEFAULT_BACKLOG); } @Override - public ServerSocket createServerSocket (int port, int backlog) - throws IOException - { - return new SSLServerSocketImpl (port, backlog, context); + public ServerSocket createServerSocket ( + int port, int backlog) throws IOException { + return new SSLServerSocketImpl(context, port, backlog); } @Override public ServerSocket - createServerSocket (int port, int backlog, InetAddress ifAddress) - throws IOException - { - return new SSLServerSocketImpl (port, backlog, ifAddress, context); + createServerSocket (int port, + int backlog, InetAddress ifAddress) throws IOException { + return new SSLServerSocketImpl(context, port, backlog, ifAddress); } /** @@ -104,7 +96,7 @@ */ @Override public String[] getDefaultCipherSuites() { - return context.getDefaultCipherSuiteList(true).toStringArray(); + return CipherSuite.namesOf(context.getDefaultCipherSuites(true)); } /** @@ -119,7 +111,6 @@ */ @Override public String[] getSupportedCipherSuites() { - return context.getSupportedCipherSuiteList().toStringArray(); + return CipherSuite.namesOf(context.getSupportedCipherSuites()); } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLServerSocketImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,22 +23,13 @@ * questions. */ - package sun.security.ssl; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; - -import java.security.AlgorithmConstraints; - -import java.util.*; - -import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SNIMatcher; - /** * This class provides a simple way for servers to support conventional @@ -62,301 +53,157 @@ * * @author David Brownell */ -final -class SSLServerSocketImpl extends SSLServerSocket -{ - private SSLContextImpl sslContext; - - /* Do newly accepted connections require clients to authenticate? */ - private ClientAuthType clientAuthType = ClientAuthType.CLIENT_AUTH_NONE; - - /* Do new connections created here use the "server" mode of SSL? */ - private boolean useServerMode = true; +final class SSLServerSocketImpl extends SSLServerSocket { + private final SSLContextImpl sslContext; + private final SSLConfiguration sslConfig; - /* Can new connections created establish new sessions? */ - private boolean enableSessionCreation = true; - - /* what cipher suites to use by default */ - private CipherSuiteList enabledCipherSuites = null; - - /* which protocol to use by default */ - private ProtocolList enabledProtocols = null; - - // the endpoint identification protocol to use by default - private String identificationProtocol = null; - - // The cryptographic algorithm constraints - private AlgorithmConstraints algorithmConstraints = null; + SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException { - // The server name indication - Collection<SNIMatcher> sniMatchers = - Collections.<SNIMatcher>emptyList(); - - // Configured application protocol values - String[] applicationProtocols = new String[0]; - - /* - * Whether local cipher suites preference in server side should be - * honored during handshaking? - */ - private boolean preferLocalCipherSuites = false; + super(); + this.sslContext = sslContext; + this.sslConfig = new SSLConfiguration(sslContext, false); + this.sslConfig.isClientMode = false; + } - /** - * Create an SSL server socket on a port, using a non-default - * authentication context and a specified connection backlog. - * - * @param port the port on which to listen - * @param backlog how many connections may be pending before - * the system should start rejecting new requests - * @param context authentication context for this server - */ - SSLServerSocketImpl(int port, int backlog, SSLContextImpl context) - throws IOException, SSLException - { + SSLServerSocketImpl(SSLContextImpl sslContext, + int port, int backlog) throws IOException { + super(port, backlog); - initServer(context); + this.sslContext = sslContext; + this.sslConfig = new SSLConfiguration(sslContext, false); + this.sslConfig.isClientMode = false; } + SSLServerSocketImpl(SSLContextImpl sslContext, + int port, int backlog, InetAddress address) throws IOException { - /** - * Create an SSL server socket on a port, using a specified - * authentication context and a specified backlog of connections - * as well as a particular specified network interface. This - * constructor is used on multihomed hosts, such as those used - * for firewalls or as routers, to control through which interface - * a network service is provided. - * - * @param port the port on which to listen - * @param backlog how many connections may be pending before - * the system should start rejecting new requests - * @param address the address of the network interface through - * which connections will be accepted - * @param context authentication context for this server - */ - SSLServerSocketImpl( - int port, - int backlog, - InetAddress address, - SSLContextImpl context) - throws IOException - { super(port, backlog, address); - initServer(context); - } - - - /** - * Creates an unbound server socket. - */ - SSLServerSocketImpl(SSLContextImpl context) throws IOException { - super(); - initServer(context); + this.sslContext = sslContext; + this.sslConfig = new SSLConfiguration(sslContext, false); + this.sslConfig.isClientMode = false; } - - /** - * Initializes the server socket. - */ - private void initServer(SSLContextImpl context) throws SSLException { - if (context == null) { - throw new SSLException("No Authentication context given"); - } - sslContext = context; - enabledCipherSuites = sslContext.getDefaultCipherSuiteList(true); - enabledProtocols = sslContext.getDefaultProtocolList(true); + @Override + public synchronized String[] getEnabledCipherSuites() { + return CipherSuite.namesOf(sslConfig.enabledCipherSuites); } - /** - * Returns the names of the cipher suites which could be enabled for use - * on an SSL connection. Normally, only a subset of these will actually - * be enabled by default, since this list may include cipher suites which - * do not support the mutual authentication of servers and clients, or - * which do not protect data confidentiality. Servers may also need - * certain kinds of certificates to use certain cipher suites. - * - * @return an array of cipher suite names - */ @Override - public String[] getSupportedCipherSuites() { - return sslContext.getSupportedCipherSuiteList().toStringArray(); + public synchronized void setEnabledCipherSuites(String[] suites) { + sslConfig.enabledCipherSuites = + CipherSuite.validValuesOf(suites); } - /** - * Returns the list of cipher suites which are currently enabled - * for use by newly accepted connections. A null return indicates - * that the system defaults are in effect. - */ @Override - public synchronized String[] getEnabledCipherSuites() { - return enabledCipherSuites.toStringArray(); - } - - /** - * Controls which particular SSL cipher suites are enabled for use - * by accepted connections. - * - * @param suites Names of all the cipher suites to enable; null - * means to accept system defaults. - */ - @Override - public synchronized void setEnabledCipherSuites(String[] suites) { - enabledCipherSuites = new CipherSuiteList(suites); + public String[] getSupportedCipherSuites() { + return CipherSuite.namesOf(sslContext.getSupportedCipherSuites()); } @Override public String[] getSupportedProtocols() { - return sslContext.getSuportedProtocolList().toStringArray(); - } - - /** - * Controls which protocols are enabled for use. - * The protocols must have been listed by - * getSupportedProtocols() as being supported. - * - * @param protocols protocols to enable. - * @exception IllegalArgumentException when one of the protocols - * named by the parameter is not supported. - */ - @Override - public synchronized void setEnabledProtocols(String[] protocols) { - enabledProtocols = new ProtocolList(protocols); + return ProtocolVersion.toStringArray( + sslContext.getSupportedProtocolVersions()); } @Override public synchronized String[] getEnabledProtocols() { - return enabledProtocols.toStringArray(); + return ProtocolVersion.toStringArray(sslConfig.enabledProtocols); } - /** - * Controls whether the connections which are accepted must include - * client authentication. - */ @Override - public void setNeedClientAuth(boolean flag) { - clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED : - ClientAuthType.CLIENT_AUTH_NONE); + public synchronized void setEnabledProtocols(String[] protocols) { + if (protocols == null) { + throw new IllegalArgumentException("Protocols cannot be null"); + } + + sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols); } @Override - public boolean getNeedClientAuth() { - return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED); - } - - /** - * Controls whether the connections which are accepted should request - * client authentication. - */ - @Override - public void setWantClientAuth(boolean flag) { - clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED : - ClientAuthType.CLIENT_AUTH_NONE); + public synchronized void setNeedClientAuth(boolean need) { + sslConfig.clientAuthType = + (need ? ClientAuthType.CLIENT_AUTH_REQUIRED : + ClientAuthType.CLIENT_AUTH_NONE); } @Override - public boolean getWantClientAuth() { - return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED); + public synchronized boolean getNeedClientAuth() { + return (sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUIRED); } - /** - * Makes the returned sockets act in SSL "client" mode, not the usual - * server mode. The canonical example of why this is needed is for - * FTP clients, which accept connections from servers and should be - * rejoining the already-negotiated SSL connection. - */ @Override - public void setUseClientMode(boolean flag) { - /* - * If we need to change the socket mode and the enabled - * protocols haven't specifically been set by the user, - * change them to the corresponding default ones. - */ - if (useServerMode != (!flag) && - sslContext.isDefaultProtocolList(enabledProtocols)) { - enabledProtocols = sslContext.getDefaultProtocolList(!flag); - } + public synchronized void setWantClientAuth(boolean want) { + sslConfig.clientAuthType = + (want ? ClientAuthType.CLIENT_AUTH_REQUESTED : + ClientAuthType.CLIENT_AUTH_NONE); + } - useServerMode = !flag; + @Override + public synchronized boolean getWantClientAuth() { + return (sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUESTED); } @Override - public boolean getUseClientMode() { - return !useServerMode; + public synchronized void setUseClientMode(boolean useClientMode) { + /* + * If we need to change the client mode and the enabled + * protocols and cipher suites haven't specifically been + * set by the user, change them to the corresponding + * default ones. + */ + if (sslConfig.isClientMode != useClientMode) { + if (sslContext.isDefaultProtocolVesions( + sslConfig.enabledProtocols)) { + sslConfig.enabledProtocols = + sslContext.getDefaultProtocolVersions(!useClientMode); + } + + if (sslContext.isDefaultCipherSuiteList( + sslConfig.enabledCipherSuites)) { + sslConfig.enabledCipherSuites = + sslContext.getDefaultCipherSuites(!useClientMode); + } + + sslConfig.isClientMode = useClientMode; + } } - - /** - * Controls whether new connections may cause creation of new SSL - * sessions. - */ @Override - public void setEnableSessionCreation(boolean flag) { - enableSessionCreation = flag; + public synchronized boolean getUseClientMode() { + return sslConfig.isClientMode; } - /** - * Returns true if new connections may cause creation of new SSL - * sessions. - */ @Override - public boolean getEnableSessionCreation() { - return enableSessionCreation; + public synchronized void setEnableSessionCreation(boolean flag) { + sslConfig.enableSessionCreation = flag; } - /** - * Returns the SSLParameters in effect for newly accepted connections. - */ + @Override + public synchronized boolean getEnableSessionCreation() { + return sslConfig.enableSessionCreation; + } + @Override public synchronized SSLParameters getSSLParameters() { - SSLParameters params = super.getSSLParameters(); - - // the super implementation does not handle the following parameters - params.setEndpointIdentificationAlgorithm(identificationProtocol); - params.setAlgorithmConstraints(algorithmConstraints); - params.setSNIMatchers(sniMatchers); - params.setUseCipherSuitesOrder(preferLocalCipherSuites); - params.setApplicationProtocols(applicationProtocols); - - return params; + return sslConfig.getSSLParameters(); } - /** - * Applies SSLParameters to newly accepted connections. - */ @Override public synchronized void setSSLParameters(SSLParameters params) { - super.setSSLParameters(params); - - // the super implementation does not handle the following parameters - identificationProtocol = params.getEndpointIdentificationAlgorithm(); - algorithmConstraints = params.getAlgorithmConstraints(); - preferLocalCipherSuites = params.getUseCipherSuitesOrder(); - Collection<SNIMatcher> matchers = params.getSNIMatchers(); - if (matchers != null) { - sniMatchers = params.getSNIMatchers(); - } - applicationProtocols = params.getApplicationProtocols(); + sslConfig.setSSLParameters(params); } - /** - * Accept a new SSL connection. This server identifies itself with - * information provided in the authentication context which was - * presented during construction. - */ @Override public Socket accept() throws IOException { - SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode, - enabledCipherSuites, clientAuthType, enableSessionCreation, - enabledProtocols, identificationProtocol, algorithmConstraints, - sniMatchers, preferLocalCipherSuites, applicationProtocols); + SSLSocketImpl s = new SSLSocketImpl(sslContext, sslConfig); implAccept(s); s.doneConnect(); return s; } - /** - * Provides a brief description of this SSL socket. - */ @Override public String toString() { return "[SSL: "+ super.toString() + "]"; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -23,23 +23,21 @@ * questions. */ - package sun.security.ssl; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; -import java.util.Vector; import java.util.Locale; - import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; - import sun.security.util.Cache; final class SSLSessionContextImpl implements SSLSessionContext { - private Cache<SessionId, SSLSessionImpl> sessionCache; + private final Cache<SessionId, SSLSessionImpl> sessionCache; // session cache, session id as key - private Cache<String, SSLSessionImpl> sessionHostPortCache; + private final Cache<String, SSLSessionImpl> sessionHostPortCache; // session cache, "host:port" as key private int cacheLimit; // the max cache size private int timeout; // timeout in seconds @@ -137,7 +135,6 @@ return cacheLimit; } - // package-private method, used ONLY by ServerHandshaker SSLSessionImpl get(byte[] id) { return (SSLSessionImpl)getSession(id); @@ -161,7 +158,7 @@ return null; } - private String getKey(String hostname, int port) { + private static String getKey(String hostname, int port) { return (hostname + ":" + String.valueOf(port)).toLowerCase(Locale.ENGLISH); } @@ -192,29 +189,30 @@ if (s != null) { sessionCache.remove(key); sessionHostPortCache.remove( - getKey(s.getPeerHost(), s.getPeerPort())); + getKey(s.getPeerHost(), s.getPeerPort())); } } - private int getDefaultCacheLimit() { - int cacheLimit = 0; + private static int getDefaultCacheLimit() { + int defaultCacheLimit = 0; try { - String s = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<String>() { - @Override - public String run() { - return System.getProperty( - "javax.net.ssl.sessionCacheSize"); - } - }); - cacheLimit = (s != null) ? Integer.valueOf(s).intValue() : 0; + String s = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<String>() { + @Override + public String run() { + return System.getProperty( + "javax.net.ssl.sessionCacheSize"); + } + }); + defaultCacheLimit = (s != null) ? Integer.parseInt(s) : 0; } catch (Exception e) { + // swallow the exception } - return (cacheLimit > 0) ? cacheLimit : 0; + return (defaultCacheLimit > 0) ? defaultCacheLimit : 0; } - boolean isTimedout(SSLSession sess) { + private boolean isTimedout(SSLSession sess) { if (timeout == 0) { return false; } @@ -228,27 +226,26 @@ return false; } - final class SessionCacheVisitor + private final class SessionCacheVisitor implements Cache.CacheVisitor<SessionId, SSLSessionImpl> { - Vector<byte[]> ids = null; + ArrayList<byte[]> ids = null; // public void visit(java.util.Map<K,V> map) {} @Override public void visit(java.util.Map<SessionId, SSLSessionImpl> map) { - ids = new Vector<>(map.size()); + ids = new ArrayList<>(map.size()); for (SessionId key : map.keySet()) { SSLSessionImpl value = map.get(key); if (!isTimedout(value)) { - ids.addElement(key.getId()); + ids.add(key.getId()); } } } - public Enumeration<byte[]> getSessionIds() { - return ids != null ? ids.elements() : - new Vector<byte[]>().elements(); + Enumeration<byte[]> getSessionIds() { + return ids != null ? Collections.enumeration(ids) : + Collections.emptyEnumeration(); } } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, 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 @@ -22,36 +22,31 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - - package sun.security.ssl; -import java.net.*; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; - +import java.math.BigInteger; +import java.net.InetAddress; import java.security.Principal; import java.security.PrivateKey; -import java.security.SecureRandom; +import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; -import java.security.cert.CertificateEncodingException; - +import java.util.ArrayList; +import java.util.Queue; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import javax.crypto.SecretKey; - -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.SSLSessionBindingListener; -import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLPermission; -import javax.net.ssl.ExtendedSSLSession; -import javax.net.ssl.SNIServerName; - -import static sun.security.ssl.CipherSuite.KeyExchange.*; +import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.SSLSessionBindingListener; +import javax.net.ssl.SSLSessionContext; /** * Implements the SSL session interface, and exposes the session context @@ -78,9 +73,6 @@ */ static final SSLSessionImpl nullSession = new SSLSessionImpl(); - // compression methods - private static final byte compression_null = 0; - /* * The state of a single session, as described in section 7.1 * of the SSLv3 spec. @@ -88,35 +80,37 @@ private final ProtocolVersion protocolVersion; private final SessionId sessionId; private X509Certificate[] peerCerts; - private byte compressionMethod; private CipherSuite cipherSuite; private SecretKey masterSecret; - private final boolean useExtendedMasterSecret; + final boolean useExtendedMasterSecret; /* * Information not part of the SSLv3 protocol spec, but used * to support session management policies. */ - private final long creationTime = System.currentTimeMillis(); + private final long creationTime; private long lastUsedTime = 0; private final String host; private final int port; private SSLSessionContextImpl context; - private int sessionCount; private boolean invalidated; private X509Certificate[] localCerts; private PrivateKey localPrivateKey; - private String[] localSupportedSignAlgs; - private String[] peerSupportedSignAlgs; - private List<SNIServerName> requestedServerNames; + private final String[] localSupportedSignAlgs; + private String[] peerSupportedSignAlgs; // for certificate + private boolean useDefaultPeerSignAlgs = false; private List<byte[]> statusResponses; + private SecretKey resumptionMasterSecret; + private SecretKey preSharedKey; + private byte[] pskIdentity; + private final long ticketCreationTime = System.currentTimeMillis(); + private int ticketAgeAdd; - private int negotiatedMaxFragLen; + private int negotiatedMaxFragLen = -1; private int maximumPacketSize; - // Principals for non-certificate based cipher suites - private Principal peerPrincipal; - private Principal localPrincipal; + private final Queue<SSLSessionImpl> childSessions = + new ConcurrentLinkedQueue<>(); /* * Is the session currently re-established with a session-resumption @@ -127,19 +121,16 @@ private boolean isSessionResumption = false; /* - * We count session creations, eventually for statistical data but - * also since counters make shorter debugging IDs than the big ones - * we use in the protocol for uniqueness-over-time. - */ - private static volatile int counter; - - /* * Use of session caches is globally enabled/disabled. */ private static boolean defaultRejoinable = true; - /* Class and subclass dynamic debugging support */ - private static final Debug debug = Debug.getInstance("ssl"); + // server name indication + final SNIServerName serverNameIndication; + private final List<SNIServerName> requestedServerNames; + + // Counter used to create unique nonces in NewSessionTicket + private BigInteger ticketNonceCounter = BigInteger.ONE; /* * Create a new non-rejoinable session, using the default (null) @@ -148,8 +139,16 @@ * first opened and before handshaking begins. */ private SSLSessionImpl() { - this(ProtocolVersion.NONE, CipherSuite.C_NULL, null, - new SessionId(false, null), null, -1, false); + this.protocolVersion = ProtocolVersion.NONE; + this.cipherSuite = CipherSuite.C_NULL; + this.sessionId = new SessionId(false, null); + this.host = null; + this.port = -1; + this.localSupportedSignAlgs = new String[0]; + this.serverNameIndication = null; + this.requestedServerNames = Collections.<SNIServerName>emptyList(); + this.useExtendedMasterSecret = false; + this.creationTime = System.currentTimeMillis(); } /* @@ -157,48 +156,81 @@ * be rejoinable if session caching is enabled; the constructor * is intended mostly for use by serves. */ - SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, - Collection<SignatureAndHashAlgorithm> algorithms, - SecureRandom generator, String host, int port, - boolean useExtendedMasterSecret) { - this(protocolVersion, cipherSuite, algorithms, - new SessionId(defaultRejoinable, generator), host, port, - useExtendedMasterSecret); + SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite) { + this(hc, cipherSuite, + new SessionId(defaultRejoinable, hc.sslContext.getSecureRandom())); } /* * Record a new session, using a given cipher spec and session ID. */ - SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, - Collection<SignatureAndHashAlgorithm> algorithms, - SessionId id, String host, int port, - boolean useExtendedMasterSecret) { - this.protocolVersion = protocolVersion; - sessionId = id; - peerCerts = null; - compressionMethod = compression_null; + SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite, SessionId id) { + this(hc, cipherSuite, id, System.currentTimeMillis()); + } + + /* + * Record a new session, using a given cipher spec, session ID, + * and creation time + */ + SSLSessionImpl(HandshakeContext hc, + CipherSuite cipherSuite, SessionId id, long creationTime) { + this.protocolVersion = hc.negotiatedProtocol; this.cipherSuite = cipherSuite; - masterSecret = null; - this.host = host; - this.port = port; - sessionCount = ++counter; - localSupportedSignAlgs = - SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); - negotiatedMaxFragLen = -1; - statusResponses = null; - this.useExtendedMasterSecret = useExtendedMasterSecret; + this.sessionId = id; + this.host = hc.conContext.transport.getPeerHost(); + this.port = hc.conContext.transport.getPeerPort(); + this.localSupportedSignAlgs = + SignatureScheme.getAlgorithmNames(hc.localSupportedSignAlgs); + this.serverNameIndication = hc.negotiatedServerName; + this.requestedServerNames = Collections.<SNIServerName>unmodifiableList( + hc.getRequestedServerNames()); + if (hc.sslConfig.isClientMode) { + this.useExtendedMasterSecret = + (hc.handshakeExtensions.get( + SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) && + (hc.handshakeExtensions.get( + SSLExtension.SH_EXTENDED_MASTER_SECRET) != null); + } else { + this.useExtendedMasterSecret = + (hc.handshakeExtensions.get( + SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) && + (!hc.negotiatedProtocol.useTLS13PlusSpec()); + } + this.creationTime = creationTime; - if (debug != null && Debug.isOn("session")) { - System.out.println("%% Initialized: " + this); + if (SSLLogger.isOn && SSLLogger.isOn("session")) { + SSLLogger.finest("Session initialized: " + this); } } void setMasterSecret(SecretKey secret) { - if (masterSecret == null) { - masterSecret = secret; - } else { - throw new RuntimeException("setMasterSecret() error"); - } + masterSecret = secret; + } + + void setResumptionMasterSecret(SecretKey secret) { + resumptionMasterSecret = secret; + } + + void setPreSharedKey(SecretKey key) { + preSharedKey = key; + } + + void addChild(SSLSessionImpl session) { + childSessions.add(session); + } + + void setTicketAgeAdd(int ticketAgeAdd) { + this.ticketAgeAdd = ticketAgeAdd; + } + + void setPskIdentity(byte[] pskIdentity) { + this.pskIdentity = pskIdentity; + } + + BigInteger incrTicketNonceCounter() { + BigInteger result = ticketNonceCounter; + ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1)); + return result; } /** @@ -208,8 +240,39 @@ return masterSecret; } - boolean getUseExtendedMasterSecret() { - return useExtendedMasterSecret; + Optional<SecretKey> getResumptionMasterSecret() { + return Optional.ofNullable(resumptionMasterSecret); + } + + synchronized Optional<SecretKey> getPreSharedKey() { + return Optional.ofNullable(preSharedKey); + } + + synchronized Optional<SecretKey> consumePreSharedKey() { + Optional<SecretKey> result = Optional.ofNullable(preSharedKey); + preSharedKey = null; + return result; + } + + int getTicketAgeAdd() { + return ticketAgeAdd; + } + + /* + * Get the PSK identity. Take care not to use it in multiple connections. + */ + synchronized Optional<byte[]> getPskIdentity() { + return Optional.ofNullable(pskIdentity); + } + + /* PSK identities created from new_session_ticket messages should only + * be used once. This method will return the identity and then clear it + * so it cannot be used again. + */ + synchronized Optional<byte[]> consumePskIdentity() { + Optional<byte[]> result = Optional.ofNullable(pskIdentity); + pskIdentity = null; + return result; } void setPeerCertificates(X509Certificate[] peer) { @@ -227,13 +290,35 @@ } void setPeerSupportedSignatureAlgorithms( - Collection<SignatureAndHashAlgorithm> algorithms) { + Collection<SignatureScheme> signatureSchemes) { peerSupportedSignAlgs = - SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); + SignatureScheme.getAlgorithmNames(signatureSchemes); } - void setRequestedServerNames(List<SNIServerName> requestedServerNames) { - this.requestedServerNames = new ArrayList<>(requestedServerNames); + // TLS 1.2 only + // + // Per RFC 5246, If the client supports only the default hash + // and signature algorithms, it MAY omit the + // signature_algorithms extension. If the client does not + // support the default algorithms, or supports other hash + // and signature algorithms (and it is willing to use them + // for verifying messages sent by the server, i.e., server + // certificates and server key exchange), it MUST send the + // signature_algorithms extension, listing the algorithms it + // is willing to accept. + void setUseDefaultPeerSignAlgs() { + useDefaultPeerSignAlgs = true; + peerSupportedSignAlgs = new String[] { + "SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"}; + } + + // Returns the connection session. + SSLSessionImpl finish() { + if (useDefaultPeerSignAlgs) { + this.peerSupportedSignAlgs = new String[0]; + } + + return this; } /** @@ -250,22 +335,6 @@ } /** - * Set the peer principal. - */ - void setPeerPrincipal(Principal principal) { - if (peerPrincipal == null) { - peerPrincipal = principal; - } - } - - /** - * Set the local principal. - */ - void setLocalPrincipal(Principal principal) { - localPrincipal = principal; - } - - /** * Returns true iff this session may be resumed ... sessions are * usually resumable. Security policies may suggest otherwise, * for example sessions that haven't been used for a while (say, @@ -286,7 +355,7 @@ * Check if the authentication used when establishing this session * is still valid. Returns true if no authentication was used */ - boolean isLocalAuthenticationValid() { + private boolean isLocalAuthenticationValid() { if (localPrivateKey != null) { try { // if the private key is no longer valid, getAlgorithm() @@ -298,6 +367,7 @@ return false; } } + return true; } @@ -354,8 +424,8 @@ void setSuite(CipherSuite suite) { cipherSuite = suite; - if (debug != null && Debug.isOn("session")) { - System.out.println("%% Negotiating: " + this); + if (SSLLogger.isOn && SSLLogger.isOn("session")) { + SSLLogger.finest("Negotiating session: " + this); } } @@ -396,13 +466,6 @@ } /** - * Returns the compression technique used in this session - */ - byte getCompression() { - return compressionMethod; - } - - /** * Returns the hashcode for this session */ @Override @@ -410,7 +473,6 @@ return sessionId.hashCode(); } - /** * Returns true if sessions have same ids, false otherwise. */ @@ -435,8 +497,8 @@ * Return the cert chain presented by the peer in the * java.security.cert format. * Note: This method can be used only when using certificate-based - * cipher suites; using it with non-certificate-based cipher suites, - * such as Kerberos, will throw an SSLPeerUnverifiedException. + * cipher suites; using it with non-certificate-based cipher suites + * will throw an SSLPeerUnverifiedException. * * @return array of peer X.509 certs, with the peer's own cert * first in the chain, and with the "root" CA last. @@ -449,10 +511,6 @@ // change record of peer identity even by accident, much // less do it intentionally. // - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { - throw new SSLPeerUnverifiedException("no certificates expected" - + " for " + cipherSuite.keyExchange + " cipher suites"); - } if (peerCerts == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); } @@ -485,8 +543,8 @@ * Return the cert chain presented by the peer in the * javax.security.cert format. * Note: This method can be used only when using certificate-based - * cipher suites; using it with non-certificate-based cipher suites, - * such as Kerberos, will throw an SSLPeerUnverifiedException. + * cipher suites; using it with non-certificate-based cipher suites + * will throw an SSLPeerUnverifiedException. * * @return array of peer X.509 certs, with the peer's own cert * first in the chain, and with the "root" CA last. @@ -504,10 +562,6 @@ // change record of peer identity even by accident, much // less do it intentionally. // - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { - throw new SSLPeerUnverifiedException("no certificates expected" - + " for " + cipherSuite.keyExchange + " cipher suites"); - } if (peerCerts == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); } @@ -531,8 +585,8 @@ /** * Return the cert chain presented by the peer. * Note: This method can be used only when using certificate-based - * cipher suites; using it with non-certificate-based cipher suites, - * such as Kerberos, will throw an SSLPeerUnverifiedException. + * cipher suites; using it with non-certificate-based cipher suites + * will throw an SSLPeerUnverifiedException. * * @return array of peer X.509 certs, with the peer's own cert * first in the chain, and with the "root" CA last. @@ -544,10 +598,6 @@ * change record of peer identity even by accident, much * less do it intentionally. */ - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { - throw new SSLPeerUnverifiedException("no certificates expected" - + " for " + cipherSuite.keyExchange + " cipher suites"); - } if (peerCerts != null) { return peerCerts.clone(); } else { @@ -584,8 +634,7 @@ * defining the session. * * @return the peer's principal. Returns an X500Principal of the - * end-entity certificate for X509-based cipher suites, and - * Principal for Kerberos cipher suites, etc. + * end-entity certificate for X509-based cipher suites. * * @throws SSLPeerUnverifiedException if the peer's identity has not * been verified @@ -594,13 +643,6 @@ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { - if (peerPrincipal == null) { - throw new SSLPeerUnverifiedException("peer not authenticated"); - } else { - return peerPrincipal; - } - } if (peerCerts == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); } @@ -611,18 +653,20 @@ * Returns the principal that was sent to the peer during handshaking. * * @return the principal sent to the peer. Returns an X500Principal - * of the end-entity certificate for X509-based cipher suites, and - * Principal for Kerberos cipher suites, etc. If no principal was - * sent, then null is returned. + * of the end-entity certificate for X509-based cipher suites. + * If no principal was sent, then null is returned. */ @Override public Principal getLocalPrincipal() { + return ((localCerts == null && localCerts.length != 0) ? null : + localCerts[0].getSubjectX500Principal()); + } - if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { - return (localPrincipal == null ? null : localPrincipal); - } - return (localCerts == null ? null : - localCerts[0].getSubjectX500Principal()); + /* + * Return the time the ticket for this session was created. + */ + public long getTicketCreationTime() { + return ticketCreationTime; } /** @@ -695,14 +739,21 @@ if (this == nullSession) { return; } - invalidated = true; - if (debug != null && Debug.isOn("session")) { - System.out.println("%% Invalidated: " + this); - } + if (context != null) { context.remove(sessionId); context = null; } + if (invalidated) { + return; + } + invalidated = true; + if (SSLLogger.isOn && SSLLogger.isOn("session")) { + SSLLogger.finest("Invalidated session: " + this); + } + for (SSLSessionImpl child : childSessions) { + child.invalidate(); + } } /* @@ -710,7 +761,8 @@ * key and the calling security context. This is important since * sessions can be shared across different protection domains. */ - private Hashtable<SecureKey, Object> table = new Hashtable<>(); + private final ConcurrentHashMap<SecureKey, Object> boundValues = + new ConcurrentHashMap<>(); /** * Assigns a session value. Session change events are given if @@ -723,7 +775,7 @@ } SecureKey secureKey = new SecureKey(key); - Object oldValue = table.put(secureKey, value); + Object oldValue = boundValues.put(secureKey, value); if (oldValue instanceof SSLSessionBindingListener) { SSLSessionBindingEvent e; @@ -739,7 +791,6 @@ } } - /** * Returns the specified session value. */ @@ -750,7 +801,7 @@ } SecureKey secureKey = new SecureKey(key); - return table.get(secureKey); + return boundValues.get(secureKey); } @@ -765,7 +816,7 @@ } SecureKey secureKey = new SecureKey(key); - Object value = table.remove(secureKey); + Object value = boundValues.remove(secureKey); if (value instanceof SSLSessionBindingListener) { SSLSessionBindingEvent e; @@ -781,22 +832,17 @@ */ @Override public String[] getValueNames() { - Enumeration<SecureKey> e; - Vector<Object> v = new Vector<>(); - SecureKey key; + ArrayList<Object> v = new ArrayList<>(); Object securityCtx = SecureKey.getCurrentSecurityContext(); - - for (e = table.keys(); e.hasMoreElements(); ) { - key = e.nextElement(); - + for (Enumeration<SecureKey> e = boundValues.keys(); + e.hasMoreElements(); ) { + SecureKey key = e.nextElement(); if (securityCtx.equals(key.getSecurityContext())) { - v.addElement(key.getAppKey()); + v.add(key.getAppKey()); } } - String[] names = new String[v.size()]; - v.copyInto(names); - return names; + return v.toArray(new String[0]); } /** @@ -813,7 +859,8 @@ * to "true". */ private boolean acceptLargeFragments = - Debug.getBooleanProperty("jsse.SSLEngine.acceptLargeFragments", false); + Utilities.getBooleanProperty( + "jsse.SSLEngine.acceptLargeFragments", false); /** * Expand the buffer size of both SSL/TLS network packet and @@ -835,7 +882,7 @@ if (negotiatedMaxFragLen > 0) { packetSize = cipherSuite.calculatePacketSize( negotiatedMaxFragLen, protocolVersion, - protocolVersion.isDTLSProtocol()); + protocolVersion.isDTLS); } if (maximumPacketSize > 0) { @@ -847,7 +894,7 @@ return packetSize; } - if (protocolVersion.isDTLSProtocol()) { + if (protocolVersion.isDTLS) { return DTLSRecord.maxRecordSize; } else { return acceptLargeFragments ? @@ -867,7 +914,7 @@ if (maximumPacketSize > 0) { fragmentSize = cipherSuite.calculateFragSize( maximumPacketSize, protocolVersion, - protocolVersion.isDTLSProtocol()); + protocolVersion.isDTLS); } if (negotiatedMaxFragLen > 0) { @@ -879,7 +926,7 @@ return fragmentSize; } - if (protocolVersion.isDTLSProtocol()) { + if (protocolVersion.isDTLS) { return Record.maxDataSize; } else { int maxPacketSize = acceptLargeFragments ? @@ -953,33 +1000,24 @@ */ @Override public List<SNIServerName> getRequestedServerNames() { - if (requestedServerNames != null && !requestedServerNames.isEmpty()) { - return Collections.<SNIServerName>unmodifiableList( - requestedServerNames); - } - - return Collections.<SNIServerName>emptyList(); + return requestedServerNames; } /** Returns a string representation of this SSL session */ @Override public String toString() { - return "[Session-" + sessionCount - + ", " + getCipherSuite() - + "]"; + return "Session(" + creationTime + "|" + getCipherSuite() + ")"; } - } - /** * This "struct" class serves as a Hash Key that combines an * application-specific key and a security context. */ class SecureKey { - private static Object nullObject = new Object(); - private Object appKey; - private Object securityCtx; + private static final Object nullObject = new Object(); + private final Object appKey; + private final Object securityCtx; static Object getCurrentSecurityContext() { SecurityManager sm = System.getSecurityManager(); diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketFactoryImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ */ public final class SSLSocketFactoryImpl extends SSLSocketFactory { - private SSLContextImpl context; + private final SSLContextImpl context; /** * Constructor used to instantiate the default factory. This method is @@ -180,7 +180,7 @@ */ @Override public String[] getDefaultCipherSuites() { - return context.getDefaultCipherSuiteList(false).toStringArray(); + return CipherSuite.namesOf(context.getDefaultCipherSuites(false)); } /** @@ -193,6 +193,6 @@ */ @Override public String[] getSupportedCipherSuites() { - return context.getSupportedCipherSuiteList().toStringArray(); + return CipherSuite.namesOf(context.getSupportedCipherSuites()); } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,35 +23,41 @@ * questions. */ - package sun.security.ssl; -import java.io.*; -import java.nio.*; -import java.net.*; -import java.security.GeneralSecurityException; -import java.security.AccessController; -import java.security.AccessControlContext; -import java.security.PrivilegedAction; -import java.security.AlgorithmConstraints; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.List; import java.util.function.BiFunction; - -import javax.crypto.BadPaddingException; -import javax.net.ssl.*; - +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; import jdk.internal.misc.JavaNetInetAddressAccess; import jdk.internal.misc.SharedSecrets; /** - * Implementation of an SSL socket. This is a normal connection type - * socket, implementing SSL over some lower level socket, such as TCP. - * Because it is layered over some lower level socket, it MUST override - * all default socket methods. - * - * <P> This API offers a non-traditional option for establishing SSL + * Implementation of an SSL socket. + * <P> + * This is a normal connection type socket, implementing SSL over some lower + * level socket, such as TCP. Because it is layered over some lower level + * socket, it MUST override all default socket methods. + * <P> + * This API offers a non-traditional option for establishing SSL * connections. You may first establish the connection directly, then pass * that connection to the SSL socket constructor with a flag saying which * role should be taken in the handshake protocol. (The two ends of the @@ -64,328 +70,19 @@ * * @author David Brownell */ -public final class SSLSocketImpl extends BaseSSLSocketImpl { - - /* - * ERROR HANDLING GUIDELINES - * (which exceptions to throw and catch and which not to throw and catch) - * - * . if there is an IOException (SocketException) when accessing the - * underlying Socket, pass it through - * - * . do not throw IOExceptions, throw SSLExceptions (or a subclass) - * - * . for internal errors (things that indicate a bug in JSSE or a - * grossly misconfigured J2RE), throw either an SSLException or - * a RuntimeException at your convenience. - * - * . handshaking code (Handshaker or HandshakeMessage) should generally - * pass through exceptions, but can handle them if they know what to - * do. - * - * . exception chaining should be used for all new code. If you happen - * to touch old code that does not use chaining, you should change it. - * - * . there is a top level exception handler that sits at all entry - * points from application code to SSLSocket read/write code. It - * makes sure that all errors are handled (see handleException()). - * - * . JSSE internal code should generally not call close(), call - * closeInternal(). - */ +public final class SSLSocketImpl + extends BaseSSLSocketImpl implements SSLTransport { - /* - * There's a state machine associated with each connection, which - * among other roles serves to negotiate session changes. - * - * - START with constructor, until the TCP connection's around. - * - HANDSHAKE picks session parameters before allowing traffic. - * There are many substates due to sequencing requirements - * for handshake messages. - * - DATA may be transmitted. - * - RENEGOTIATE state allows concurrent data and handshaking - * traffic ("same" substates as HANDSHAKE), and terminates - * in selection of new session (and connection) parameters - * - ERROR state immediately precedes abortive disconnect. - * - SENT_CLOSE sent a close_notify to the peer. For layered, - * non-autoclose socket, must now read close_notify - * from peer before closing the connection. For nonlayered or - * non-autoclose socket, close connection and go onto - * cs_CLOSED state. - * - CLOSED after sending close_notify alert, & socket is closed. - * SSL connection objects are not reused. - * - APP_CLOSED once the application calls close(). Then it behaves like - * a closed socket, e.g.. getInputStream() throws an Exception. - * - * State affects what SSL record types may legally be sent: - * - * - Handshake ... only in HANDSHAKE and RENEGOTIATE states - * - App Data ... only in DATA and RENEGOTIATE states - * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE - * - * Re what may be received: same as what may be sent, except that - * HandshakeRequest handshaking messages can come from servers even - * in the application data state, to request entry to RENEGOTIATE. - * - * The state machine within HANDSHAKE and RENEGOTIATE states controls - * the pending session, not the connection state, until the change - * cipher spec and "Finished" handshake messages are processed and - * make the "new" session become the current one. - * - * NOTE: details of the SMs always need to be nailed down better. - * The text above illustrates the core ideas. - * - * +---->-------+------>--------->-------+ - * | | | - * <-----< ^ ^ <-----< v - *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE SENT_CLOSE - * v v v | | - * | | | | v - * +------------+---------------+ v ERROR - * | | | - * v | | - * ERROR>------>----->CLOSED<--------<----+-- + - * | - * v - * APP_CLOSED - * - * ALSO, note that the purpose of handshaking (renegotiation is - * included) is to assign a different, and perhaps new, session to - * the connection. The SSLv3 spec is a bit confusing on that new - * protocol feature. - */ - private static final int cs_START = 0; - private static final int cs_HANDSHAKE = 1; - private static final int cs_DATA = 2; - private static final int cs_RENEGOTIATE = 3; - private static final int cs_ERROR = 4; - private static final int cs_SENT_CLOSE = 5; - private static final int cs_CLOSED = 6; - private static final int cs_APP_CLOSED = 7; - - /* - * Drives the protocol state machine. - */ - private volatile int connectionState; - - /* - * Flag indicating if the next record we receive MUST be a Finished - * message. Temporarily set during the handshake to ensure that - * a change cipher spec message is followed by a finished message. - */ - private boolean expectingFinished; - - /* - * For improved diagnostics, we detail connection closure - * If the socket is closed (connectionState >= cs_ERROR), - * closeReason != null indicates if the socket was closed - * because of an error or because or normal shutdown. - */ - private SSLException closeReason; - - /* - * Per-connection private state that doesn't change when the - * session is changed. - */ - private ClientAuthType doClientAuth = - ClientAuthType.CLIENT_AUTH_NONE; - private boolean roleIsServer; - private boolean enableSessionCreation = true; - private String host; - private boolean autoClose = true; - private AccessControlContext acc; - - // The cipher suites enabled for use on this connection. - private CipherSuiteList enabledCipherSuites; - - // The endpoint identification protocol - private String identificationProtocol = null; - - // The cryptographic algorithm constraints - private AlgorithmConstraints algorithmConstraints = null; - - // The server name indication and matchers - List<SNIServerName> serverNames = - Collections.<SNIServerName>emptyList(); - Collection<SNIMatcher> sniMatchers = - Collections.<SNIMatcher>emptyList(); - - // Is the serverNames set to empty with SSLParameters.setServerNames()? - private boolean noSniExtension = false; - - // Is the sniMatchers set to empty with SSLParameters.setSNIMatchers()? - private boolean noSniMatcher = false; - - // Configured application protocol values - String[] applicationProtocols = new String[0]; - - // Negotiated application protocol value. - // - // The value under negotiation will be obtained from handshaker. - String applicationProtocol = null; + final SSLContextImpl sslContext; + final TransportContext conContext; - // Callback function that selects the application protocol value during - // the SSL/TLS handshake. - BiFunction<SSLSocket, List<String>, String> applicationProtocolSelector; - - /* - * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * - * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES. - * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * - * - * There are several locks here. - * - * The primary lock is the per-instance lock used by - * synchronized(this) and the synchronized methods. It controls all - * access to things such as the connection state and variables which - * affect handshaking. If we are inside a synchronized method, we - * can access the state directly, otherwise, we must use the - * synchronized equivalents. - * - * The handshakeLock is used to ensure that only one thread performs - * the *complete initial* handshake. If someone is handshaking, any - * stray application or startHandshake() requests who find the - * connection state is cs_HANDSHAKE will stall on handshakeLock - * until handshaking is done. Once the handshake is done, we either - * succeeded or failed, but we can never go back to the cs_HANDSHAKE - * or cs_START state again. - * - * Note that the read/write() calls here in SSLSocketImpl are not - * obviously synchronized. In fact, it's very nonintuitive, and - * requires careful examination of code paths. Grab some coffee, - * and be careful with any code changes. - * - * There can be only three threads active at a time in the I/O - * subsection of this class. - * 1. startHandshake - * 2. AppInputStream - * 3. AppOutputStream - * One thread could call startHandshake(). - * AppInputStream/AppOutputStream read() and write() calls are each - * synchronized on 'this' in their respective classes, so only one - * app. thread will be doing a SSLSocketImpl.read() or .write()'s at - * a time. - * - * If handshaking is required (state cs_HANDSHAKE), and - * getConnectionState() for some/all threads returns cs_HANDSHAKE, - * only one can grab the handshakeLock, and the rest will stall - * either on getConnectionState(), or on the handshakeLock if they - * happen to successfully race through the getConnectionState(). - * - * If a writer is doing the initial handshaking, it must create a - * temporary reader to read the responses from the other side. As a - * side-effect, the writer's reader will have priority over any - * other reader. However, the writer's reader is not allowed to - * consume any application data. When handshakeLock is finally - * released, we either have a cs_DATA connection, or a - * cs_CLOSED/cs_ERROR socket. - * - * The writeLock is held while writing on a socket connection and - * also to protect the MAC and cipher for their direction. The - * writeLock is package private for Handshaker which holds it while - * writing the ChangeCipherSpec message. - * - * To avoid the problem of a thread trying to change operational - * modes on a socket while handshaking is going on, we synchronize - * on 'this'. If handshaking has not started yet, we tell the - * handshaker to change its mode. If handshaking has started, - * we simply store that request until the next pending session - * is created, at which time the new handshaker's state is set. - * - * The readLock is held during readRecord(), which is responsible - * for reading an SSLInputRecord, decrypting it, and processing it. - * The readLock ensures that these three steps are done atomically - * and that once started, no other thread can block on SSLInputRecord.read. - * This is necessary so that processing of close_notify alerts - * from the peer are handled properly. - */ - private final Object handshakeLock = new Object(); - final ReentrantLock writeLock = new ReentrantLock(); - private final Object readLock = new Object(); + private final AppInputStream appInput = new AppInputStream(); + private final AppOutputStream appOutput = new AppOutputStream(); - InputRecord inputRecord; - OutputRecord outputRecord; - - /* - * security parameters for secure renegotiation. - */ - private boolean secureRenegotiation; - private byte[] clientVerifyData; - private byte[] serverVerifyData; - - /* - * The authentication context holds all information used to establish - * who this end of the connection is (certificate chains, private keys, - * etc) and who is trusted (e.g. as CAs or websites). - */ - private SSLContextImpl sslContext; - - - /* - * This connection is one of (potentially) many associated with - * any given session. The output of the handshake protocol is a - * new session ... although all the protocol description talks - * about changing the cipher spec (and it does change), in fact - * that's incidental since it's done by changing everything that - * is associated with a session at the same time. (TLS/IETF may - * change that to add client authentication w/o new key exchg.) - */ - private Handshaker handshaker; - private SSLSessionImpl sess; - private volatile SSLSessionImpl handshakeSession; - - - /* - * If anyone wants to get notified about handshake completions, - * they'll show up on this list. - */ - private HashMap<HandshakeCompletedListener, AccessControlContext> - handshakeListeners; - - /* - * Reuse the same internal input/output streams. - */ - private InputStream sockInput; - private OutputStream sockOutput; - - - /* - * These input and output streams block their data in SSL records, - * and usually arrange integrity and privacy protection for those - * records. The guts of the SSL protocol are wrapped up in these - * streams, and in the handshaking that establishes the details of - * that integrity and privacy protection. - */ - private AppInputStream input; - private AppOutputStream output; - - /* - * The protocol versions enabled for use on this connection. - * - * Note: we support a pseudo protocol called SSLv2Hello which when - * set will result in an SSL v2 Hello being sent with SSL (version 3.0) - * or TLS (version 3.1, 3.2, etc.) version info. - */ - private ProtocolList enabledProtocols; - - /* - * The SSL version associated with this connection. - */ - private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT_TLS; - - /* Class and subclass dynamic debugging support */ - private static final Debug debug = Debug.getInstance("ssl"); - - /* - * Whether local cipher suites preference in server side should be - * honored during handshaking? - */ - private boolean preferLocalCipherSuites = false; - - /* - * The maximum expected network packet size for SSL/TLS/DTLS records. - */ - private int maximumPacketSize = 0; + private String peerHost; + private boolean autoClose; + private boolean isConnected = false; + private boolean tlsIsClosed = false; /* * Is the local name service trustworthy? @@ -393,177 +90,127 @@ * If the local name service is not trustworthy, reverse host name * resolution should not be performed for endpoint identification. */ - static final boolean trustNameService = - Debug.getBooleanProperty("jdk.tls.trustNameService", false); + private static final boolean trustNameService = + Utilities.getBooleanProperty("jdk.tls.trustNameService", false); - // - // CONSTRUCTORS AND INITIALIZATION CODE - // + /** + * Package-private constructor used to instantiate an unconnected + * socket. + * + * This instance is meant to set handshake state to use "client mode". + */ + SSLSocketImpl(SSLContextImpl sslContext) { + super(); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); + } /** - * Constructs an SSL connection to a named host at a specified port, - * using the authentication context provided. This endpoint acts as - * the client, and may rejoin an existing SSL session if appropriate. + * Package-private constructor used to instantiate a server socket. * - * @param context authentication context to use - * @param host name of the host with which to connect - * @param port number of the server's port + * This instance is meant to set handshake state to use "server mode". */ - SSLSocketImpl(SSLContextImpl context, String host, int port) - throws IOException, UnknownHostException { + SSLSocketImpl(SSLContextImpl sslContext, SSLConfiguration sslConfig) { super(); - this.host = host; - this.serverNames = - Utilities.addToSNIServerNameList(this.serverNames, this.host); - init(context, false); - SocketAddress socketAddress = - host != null ? new InetSocketAddress(host, port) : - new InetSocketAddress(InetAddress.getByName(null), port); - connect(socketAddress, 0); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, sslConfig, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash)); } - /** - * Constructs an SSL connection to a server at a specified address. - * and TCP port, using the authentication context provided. This - * endpoint acts as the client, and may rejoin an existing SSL session - * if appropriate. + * Constructs an SSL connection to a named host at a specified + * port, using the authentication context provided. * - * @param context authentication context to use - * @param address the server's host - * @param port its port + * This endpoint acts as the client, and may rejoin an existing SSL session + * if appropriate. */ - SSLSocketImpl(SSLContextImpl context, InetAddress host, int port) - throws IOException { + SSLSocketImpl(SSLContextImpl sslContext, String peerHost, + int peerPort) throws IOException, UnknownHostException { super(); - init(context, false); - SocketAddress socketAddress = new InetSocketAddress(host, port); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); + this.peerHost = peerHost; + SocketAddress socketAddress = + peerHost != null ? new InetSocketAddress(peerHost, peerPort) : + new InetSocketAddress(InetAddress.getByName(null), peerPort); connect(socketAddress, 0); } /** - * Constructs an SSL connection to a named host at a specified port, - * using the authentication context provided. This endpoint acts as - * the client, and may rejoin an existing SSL session if appropriate. + * Constructs an SSL connection to a server at a specified + * address, and TCP port, using the authentication context + * provided. * - * @param context authentication context to use - * @param host name of the host with which to connect - * @param port number of the server's port - * @param localAddr the local address the socket is bound to - * @param localPort the local port the socket is bound to + * This endpoint acts as the client, and may rejoin an existing SSL + * session if appropriate. */ - SSLSocketImpl(SSLContextImpl context, String host, int port, - InetAddress localAddr, int localPort) - throws IOException, UnknownHostException { + SSLSocketImpl(SSLContextImpl sslContext, + InetAddress address, int peerPort) throws IOException { super(); - this.host = host; - this.serverNames = - Utilities.addToSNIServerNameList(this.serverNames, this.host); - init(context, false); - bind(new InetSocketAddress(localAddr, localPort)); - SocketAddress socketAddress = - host != null ? new InetSocketAddress(host, port) : - new InetSocketAddress(InetAddress.getByName(null), port); - connect(socketAddress, 0); - } - + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); - /** - * Constructs an SSL connection to a server at a specified address. - * and TCP port, using the authentication context provided. This - * endpoint acts as the client, and may rejoin an existing SSL session - * if appropriate. - * - * @param context authentication context to use - * @param address the server's host - * @param port its port - * @param localAddr the local address the socket is bound to - * @param localPort the local port the socket is bound to - */ - SSLSocketImpl(SSLContextImpl context, InetAddress host, int port, - InetAddress localAddr, int localPort) - throws IOException { - super(); - init(context, false); - bind(new InetSocketAddress(localAddr, localPort)); - SocketAddress socketAddress = new InetSocketAddress(host, port); + SocketAddress socketAddress = new InetSocketAddress(address, peerPort); connect(socketAddress, 0); } - /* - * Package-private constructor used ONLY by SSLServerSocket. The - * java.net package accepts the TCP connection after this call is - * made. This just initializes handshake state to use "server mode", - * giving control over the use of SSL client authentication. + /** + * Constructs an SSL connection to a named host at a specified + * port, using the authentication context provided. + * + * This endpoint acts as the client, and may rejoin an existing SSL + * session if appropriate. */ - SSLSocketImpl(SSLContextImpl context, boolean serverMode, - CipherSuiteList suites, ClientAuthType clientAuth, - boolean sessionCreation, ProtocolList protocols, - String identificationProtocol, - AlgorithmConstraints algorithmConstraints, - Collection<SNIMatcher> sniMatchers, - boolean preferLocalCipherSuites, - String[] applicationProtocols) throws IOException { - + SSLSocketImpl(SSLContextImpl sslContext, + String peerHost, int peerPort, InetAddress localAddr, + int localPort) throws IOException, UnknownHostException { super(); - doClientAuth = clientAuth; - enableSessionCreation = sessionCreation; - this.identificationProtocol = identificationProtocol; - this.algorithmConstraints = algorithmConstraints; - this.sniMatchers = sniMatchers; - this.preferLocalCipherSuites = preferLocalCipherSuites; - this.applicationProtocols = applicationProtocols; - init(context, serverMode); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); + this.peerHost = peerHost; - /* - * Override what was picked out for us. - */ - enabledCipherSuites = suites; - enabledProtocols = protocols; + bind(new InetSocketAddress(localAddr, localPort)); + SocketAddress socketAddress = + peerHost != null ? new InetSocketAddress(peerHost, peerPort) : + new InetSocketAddress(InetAddress.getByName(null), peerPort); + connect(socketAddress, 0); } - /** - * Package-private constructor used to instantiate an unconnected - * socket. The java.net package will connect it, either when the - * connect() call is made by the application. This instance is - * meant to set handshake state to use "client mode". - */ - SSLSocketImpl(SSLContextImpl context) { - super(); - init(context, false); - } - - - /** - * Layer SSL traffic over an existing connection, rather than creating - * a new connection. The existing connection may be used only for SSL - * traffic (using this SSLSocket) until the SSLSocket.close() call - * returns. However, if a protocol error is detected, that existing - * connection is automatically closed. + * Constructs an SSL connection to a server at a specified + * address, and TCP port, using the authentication context + * provided. * - * <P> This particular constructor always uses the socket in the - * role of an SSL client. It may be useful in cases which start - * using SSL after some initial data transfers, for example in some - * SSL tunneling applications or as part of some kinds of application - * protocols which negotiate use of a SSL based security. - * - * @param sock the existing connection - * @param context the authentication context to use + * This endpoint acts as the client, and may rejoin an existing SSL + * session if appropriate. */ - SSLSocketImpl(SSLContextImpl context, Socket sock, String host, - int port, boolean autoClose) throws IOException { - super(sock); - // We always layer over a connected socket - if (!sock.isConnected()) { - throw new SocketException("Underlying socket is not connected"); - } - this.host = host; - this.serverNames = - Utilities.addToSNIServerNameList(this.serverNames, this.host); - init(context, false); - this.autoClose = autoClose; - doneConnect(); + SSLSocketImpl(SSLContextImpl sslContext, + InetAddress peerAddr, int peerPort, + InetAddress localAddr, int localPort) throws IOException { + super(); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); + + bind(new InetSocketAddress(localAddr, localPort)); + SocketAddress socketAddress = new InetSocketAddress(peerAddr, peerPort); + connect(socketAddress, 0); } /** @@ -572,7 +219,7 @@ * already been consumed/removed from the {@link Socket}'s * underlying {@link InputStream}. */ - SSLSocketImpl(SSLContextImpl context, Socket sock, + SSLSocketImpl(SSLContextImpl sslContext, Socket sock, InputStream consumed, boolean autoClose) throws IOException { super(sock, consumed); // We always layer over a connected socket @@ -580,70 +227,51 @@ throw new SocketException("Underlying socket is not connected"); } - // In server mode, it is not necessary to set host and serverNames. - // Otherwise, would require a reverse DNS lookup to get the hostname. - - init(context, true); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), false); this.autoClose = autoClose; doneConnect(); } /** - * Initializes the client socket. + * Layer SSL traffic over an existing connection, rather than + * creating a new connection. + * + * The existing connection may be used only for SSL traffic (using this + * SSLSocket) until the SSLSocket.close() call returns. However, if a + * protocol error is detected, that existing connection is automatically + * closed. + * <p> + * This particular constructor always uses the socket in the + * role of an SSL client. It may be useful in cases which start + * using SSL after some initial data transfers, for example in some + * SSL tunneling applications or as part of some kinds of application + * protocols which negotiate use of a SSL based security. */ - private void init(SSLContextImpl context, boolean isServer) { - sslContext = context; - sess = SSLSessionImpl.nullSession; - handshakeSession = null; - - /* - * role is as specified, state is START until after - * the low level connection's established. - */ - roleIsServer = isServer; - connectionState = cs_START; + SSLSocketImpl(SSLContextImpl sslContext, Socket sock, + String peerHost, int port, boolean autoClose) throws IOException { + super(sock); + // We always layer over a connected socket + if (!sock.isConnected()) { + throw new SocketException("Underlying socket is not connected"); + } - // initial security parameters for secure renegotiation - secureRenegotiation = false; - clientVerifyData = new byte[0]; - serverVerifyData = new byte[0]; - - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(roleIsServer); - enabledProtocols = - sslContext.getDefaultProtocolList(roleIsServer); - - inputRecord = new SSLSocketInputRecord();; - outputRecord = new SSLSocketOutputRecord(); - - maximumPacketSize = outputRecord.getMaxPacketSize(); - - // save the acc - acc = AccessController.getContext(); - - input = new AppInputStream(this); - output = new AppOutputStream(this); + this.sslContext = sslContext; + HandshakeHash handshakeHash = new HandshakeHash(); + this.conContext = new TransportContext(sslContext, this, + new SSLSocketInputRecord(handshakeHash), + new SSLSocketOutputRecord(handshakeHash), true); + this.peerHost = peerHost; + this.autoClose = autoClose; + doneConnect(); } - /** - * Connects this socket to the server with a specified timeout - * value. - * - * This method is either called on an unconnected SSLSocketImpl by the - * application, or it is called in the constructor of a regular - * SSLSocketImpl. If we are layering on top on another socket, then - * this method should not be called, because we assume that the - * underlying socket is already connected by the time it is passed to - * us. - * - * @param endpoint the <code>SocketAddress</code> - * @param timeout the timeout value to be used, 0 is no timeout - * @throws IOException if an error occurs during the connection - * @throws SocketTimeoutException if timeout expires before connecting - */ @Override - public void connect(SocketAddress endpoint, int timeout) - throws IOException { + public void connect(SocketAddress endpoint, + int timeout) throws IOException { if (isLayered()) { throw new SocketException("Already connected"); @@ -651,116 +279,504 @@ if (!(endpoint instanceof InetSocketAddress)) { throw new SocketException( - "Cannot handle non-Inet socket addresses."); + "Cannot handle non-Inet socket addresses."); } super.connect(endpoint, timeout); + doneConnect(); + } - if (host == null || host.length() == 0) { - useImplicitHost(false); + @Override + public String[] getSupportedCipherSuites() { + return CipherSuite.namesOf(sslContext.getSupportedCipherSuites()); + } + + @Override + public synchronized String[] getEnabledCipherSuites() { + return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites); + } + + @Override + public synchronized void setEnabledCipherSuites(String[] suites) { + conContext.sslConfig.enabledCipherSuites = + CipherSuite.validValuesOf(suites); + } + + @Override + public String[] getSupportedProtocols() { + return ProtocolVersion.toStringArray( + sslContext.getSupportedProtocolVersions()); + } + + @Override + public synchronized String[] getEnabledProtocols() { + return ProtocolVersion.toStringArray( + conContext.sslConfig.enabledProtocols); + } + + @Override + public synchronized void setEnabledProtocols(String[] protocols) { + if (protocols == null) { + throw new IllegalArgumentException("Protocols cannot be null"); + } + + conContext.sslConfig.enabledProtocols = + ProtocolVersion.namesOf(protocols); + } + + @Override + public synchronized SSLSession getSession() { + try { + // start handshaking, if failed, the connection will be closed. + ensureNegotiated(); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { + SSLLogger.severe("handshake failed", ioe); + } + + return SSLSessionImpl.nullSession; + } + + return conContext.conSession; + } + + @Override + public synchronized SSLSession getHandshakeSession() { + if (conContext.handshakeContext != null) { + return conContext.handshakeContext.handshakeSession; + } + + return null; + } + + @Override + public synchronized void addHandshakeCompletedListener( + HandshakeCompletedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener is null"); + } + + conContext.sslConfig.addHandshakeCompletedListener(listener); + } + + @Override + public synchronized void removeHandshakeCompletedListener( + HandshakeCompletedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener is null"); } - doneConnect(); + conContext.sslConfig.removeHandshakeCompletedListener(listener); + } + + @Override + public synchronized void startHandshake() throws IOException { + checkWrite(); + try { + conContext.kickstart(); + + // All initial handshaking goes through this operation until we + // have a valid SSL connection. + // + // Handle handshake messages only, need no application data. + if (!conContext.isNegotiated) { + readRecord(); + } + } catch (IOException ioe) { + conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Couldn't kickstart handshaking", ioe); + } catch (Exception oe) { // including RuntimeException + handleException(oe); + } + } + + @Override + public synchronized void setUseClientMode(boolean mode) { + conContext.setUseClientMode(mode); + } + + @Override + public synchronized boolean getUseClientMode() { + return conContext.sslConfig.isClientMode; + } + + @Override + public synchronized void setNeedClientAuth(boolean need) { + conContext.sslConfig.clientAuthType = + (need ? ClientAuthType.CLIENT_AUTH_REQUIRED : + ClientAuthType.CLIENT_AUTH_NONE); + } + + @Override + public synchronized boolean getNeedClientAuth() { + return (conContext.sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUIRED); + } + + @Override + public synchronized void setWantClientAuth(boolean want) { + conContext.sslConfig.clientAuthType = + (want ? ClientAuthType.CLIENT_AUTH_REQUESTED : + ClientAuthType.CLIENT_AUTH_NONE); + } + + @Override + public synchronized boolean getWantClientAuth() { + return (conContext.sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUESTED); + } + + @Override + public synchronized void setEnableSessionCreation(boolean flag) { + conContext.sslConfig.enableSessionCreation = flag; + } + + @Override + public synchronized boolean getEnableSessionCreation() { + return conContext.sslConfig.enableSessionCreation; + } + + @Override + public synchronized boolean isClosed() { + return tlsIsClosed && conContext.isClosed(); + } + + @Override + public synchronized void close() throws IOException { + try { + conContext.close(); + } catch (IOException ioe) { + // ignore the exception + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("connection context closure failed", ioe); + } + } finally { + tlsIsClosed = true; + } + } + + @Override + public synchronized InputStream getInputStream() throws IOException { + if (isClosed() || conContext.isInboundDone()) { + throw new SocketException("Socket or inbound is closed"); + } + + if (!isConnected) { + throw new SocketException("Socket is not connected"); + } + + return appInput; + } + + private synchronized void ensureNegotiated() throws IOException { + if (conContext.isNegotiated || + conContext.isClosed() || conContext.isBroken) { + return; + } + + startHandshake(); } /** - * Initialize the handshaker and socket streams. - * - * Called by connect, the layered constructor, and SSLServerSocket. + * InputStream for application data as returned by + * SSLSocket.getInputStream(). */ - void doneConnect() throws IOException { - /* - * Save the input and output streams. May be done only after - * java.net actually connects using the socket "self", else - * we get some pretty bizarre failure modes. + private class AppInputStream extends InputStream { + // One element array used to implement the single byte read() method + private final byte[] oneByte = new byte[1]; + + // the temporary buffer used to read network + private ByteBuffer buffer; + + // Is application data available in the stream? + private boolean appDataIsAvailable; + + AppInputStream() { + this.appDataIsAvailable = false; + this.buffer = ByteBuffer.allocate(4096); + } + + /** + * Return the minimum number of bytes that can be read + * without blocking. + */ + @Override + public int available() throws IOException { + // Currently not synchronized. + if ((!appDataIsAvailable) || checkEOF()) { + return 0; + } + + return buffer.remaining(); + } + + /** + * Read a single byte, returning -1 on non-fault EOF status. */ - sockInput = super.getInputStream(); - sockOutput = super.getOutputStream(); + @Override + public synchronized int read() throws IOException { + int n = read(oneByte, 0, 1); + if (n <= 0) { // EOF + return -1; + } + + return oneByte[0] & 0xFF; + } - inputRecord.setDeliverStream(sockOutput); - outputRecord.setDeliverStream(sockOutput); + /** + * 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("the target buffer is null"); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException( + "buffer length: " + b.length + ", offset; " + off + + ", bytes to read:" + len); + } else if (len == 0) { + return 0; + } + + if (checkEOF()) { + return -1; + } + + // start handshaking if the connection has not been negotiated. + if (!conContext.isNegotiated && + !conContext.isClosed() && !conContext.isBroken) { + ensureNegotiated(); + } + + // 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; + } - /* - * Move to handshaking state, with pending session initialized - * to defaults and the appropriate kind of handshaker set up. - */ - initHandshaker(); - } + 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 + int inLen = conContext.inputRecord.bytesInCompletePacket(); + if (inLen < 0) { // EOF + handleEOF(null); + + // if no exception thrown + return -1; + } + + // Is this packet bigger than SSL/TLS normally allows? + if (inLen > SSLRecord.maxLargeRecordSize) { + throw new SSLProtocolException( + "Illegal packet size: " + inLen); + } + + if (inLen > buffer.remaining()) { + buffer = ByteBuffer.allocate(inLen); + } + + volume = readRecord(buffer); + buffer.flip(); + if (volume < 0) { // EOF + // treat like receiving a close_notify warning message. + conContext.isInputCloseNotified = true; + conContext.closeInbound(); + return -1; + } else if (volume > 0) { + appDataIsAvailable = true; + break; + } + } - private synchronized int getConnectionState() { - return connectionState; - } + // file the destination buffer + int howmany = Math.min(len, volume); + buffer.get(b, off, howmany); + return howmany; + } catch (Exception e) { // including RuntimeException + // shutdown and rethrow (wrapped) exception as appropriate + handleException(e); + + // dummy for compiler + return -1; + } + } - private synchronized void setConnectionState(int state) { - connectionState = state; - } + /** + * 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 { + // dummy array used to implement skip() + byte[] skipArray = new byte[256]; - AccessControlContext getAcc() { - return acc; + long skipped = 0; + while (n > 0) { + int len = (int)Math.min(n, skipArray.length); + int r = read(skipArray, 0, len); + if (r <= 0) { + break; + } + n -= r; + skipped += r; + } + + return skipped; + } + + @Override + public void close() throws IOException { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("Closing input stream"); + } + + conContext.closeInbound(); + } } - // - // READING AND WRITING RECORDS - // + @Override + public synchronized OutputStream getOutputStream() throws IOException { + if (isClosed() || conContext.isOutboundDone()) { + throw new SocketException("Socket or outbound is closed"); + } - /* - * Application data record output. - * - * Application data can't be sent until the first handshake establishes - * a session. + if (!isConnected) { + throw new SocketException("Socket is not connected"); + } + + return appOutput; + } + + + /** + * OutputStream for application data as returned by + * SSLSocket.getOutputStream(). */ - void writeRecord(byte[] source, int offset, int length) throws IOException { - /* - * The loop is in case of HANDSHAKE --> ERROR transitions, etc - */ - // Don't bother to check the emptiness of source applicatoin data - // before the security connection established. - for (boolean readyForApp = false; !readyForApp;) { - /* - * Not all states support passing application data. We - * synchronize access to the connection state, so that - * synchronous handshakes can complete cleanly. - */ - switch (getConnectionState()) { + private class AppOutputStream extends OutputStream { + // One element array used to implement the write(byte) method + private final byte[] oneByte = new byte[1]; + + @Override + public void write(int i) throws IOException { + oneByte[0] = (byte)i; + write(oneByte, 0, 1); + } + + @Override + public synchronized void write(byte[] b, + int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException("the source buffer is null"); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException( + "buffer length: " + b.length + ", offset; " + off + + ", bytes to read:" + len); + } else if (len == 0) { + return; + } + + // start handshaking if the connection has not been negotiated. + if (!conContext.isNegotiated && + !conContext.isClosed() && !conContext.isBroken) { + ensureNegotiated(); + } + + // check if the Socket is invalid (error or closed) + checkWrite(); - /* - * We've deferred the initial handshaking till just now, - * when presumably a thread's decided it's OK to block for - * longish periods of time for I/O purposes (as well as - * configured the cipher suites it wants to use). - */ - case cs_HANDSHAKE: - performInitialHandshake(); - break; + // Delegate the writing to the underlying socket. + try { + writeRecord(b, off, len); + checkWrite(); + } catch (IOException ioe) { + // shutdown and rethrow (wrapped) exception as appropriate + handleException(ioe); + } + } - case cs_DATA: - case cs_RENEGOTIATE: - readyForApp = true; - break; + @Override + public void close() throws IOException { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("Closing output stream"); + } + + conContext.closeOutbound(); + } + } + + @Override + public synchronized SSLParameters getSSLParameters() { + return conContext.sslConfig.getSSLParameters(); + } + + @Override + public synchronized void setSSLParameters(SSLParameters params) { + conContext.sslConfig.setSSLParameters(params); - case cs_ERROR: - fatal(Alerts.alert_close_notify, - "error while writing to socket"); - break; // dummy + if (conContext.sslConfig.maximumPacketSize != 0) { + conContext.outputRecord.changePacketSize( + conContext.sslConfig.maximumPacketSize); + } + } + + @Override + public synchronized String getApplicationProtocol() { + return conContext.applicationProtocol; + } + + @Override + public synchronized String getHandshakeApplicationProtocol() { + if (conContext.handshakeContext != null) { + return conContext.handshakeContext.applicationProtocol; + } - case cs_SENT_CLOSE: - case cs_CLOSED: - case cs_APP_CLOSED: - // we should never get here (check in AppOutputStream) - // this is just a fallback - if (closeReason != null) { - throw closeReason; - } else { - throw new SocketException("Socket closed"); - } + return null; + } + + @Override + public synchronized void setHandshakeApplicationProtocolSelector( + BiFunction<SSLSocket, List<String>, String> selector) { + conContext.sslConfig.socketAPSelector = selector; + } - /* - * Else something's goofy in this state machine's use. - */ - default: - throw new SSLProtocolException( - "State error, send app data"); - } + @Override + public synchronized BiFunction<SSLSocket, List<String>, String> + getHandshakeApplicationProtocolSelector() { + return conContext.sslConfig.socketAPSelector; + } + + private synchronized void writeRecord(byte[] source, + int offset, int length) throws IOException { + if (conContext.isOutboundDone()) { + throw new SocketException("Socket or outbound closed"); } // @@ -772,1374 +788,208 @@ // records, so this also increases robustness. // if (length > 0) { - IOException ioe = null; - byte description = 0; // 0: never used, make the compiler happy - writeLock.lock(); try { - outputRecord.deliver(source, offset, length); + conContext.outputRecord.deliver(source, offset, length); } catch (SSLHandshakeException she) { // may be record sequence number overflow - description = Alerts.alert_handshake_failure; - ioe = she; + conContext.fatal(Alert.HANDSHAKE_FAILURE, she); } catch (IOException e) { - description = Alerts.alert_unexpected_message; - ioe = e; - } finally { - writeLock.unlock(); - } - - // Be care of deadlock. Please don't place the call to fatal() - // into the writeLock locked block. - if (ioe != null) { - fatal(description, ioe); + conContext.fatal(Alert.UNEXPECTED_MESSAGE, e); } } - /* - * Check the sequence number state - * - * Note that in order to maintain the connection I/O - * properly, we check the sequence number after the last - * record writing process. As we request renegotiation - * or close the connection for wrapped sequence number - * when there is enough sequence number space left to - * handle a few more records, so the sequence number - * of the last record cannot be wrapped. - * - * Don't bother to kickstart the renegotiation when the - * local is asking for it. - */ - if ((connectionState == cs_DATA) && outputRecord.seqNumIsHuge()) { - /* - * Ask for renegotiation when need to renew sequence number. - * - * Don't bother to kickstart the renegotiation when the local is - * asking for it. - */ - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", request renegotiation " + - "to avoid sequence number overflow"); - } - - startHandshake(); - } - } - - /* - * Alert record output. - */ - void writeAlert(byte level, byte description) throws IOException { - - // If the record is a close notify alert, we need to honor - // socket option SO_LINGER. Note that we will try to send - // the close notify even if the SO_LINGER set to zero. - if ((description == Alerts.alert_close_notify) && getSoLinger() >= 0) { - - // keep and clear the current thread interruption status. - boolean interrupted = Thread.interrupted(); - try { - if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) { - try { - outputRecord.encodeAlert(level, description); - } finally { - writeLock.unlock(); - } - } else { - SSLException ssle = new SSLException( - "SO_LINGER timeout," + - " close_notify message cannot be sent."); - - - // For layered, non-autoclose sockets, we are not - // able to bring them into a usable state, so we - // treat it as fatal error. - if (isLayered() && !autoClose) { - // Note that the alert description is - // specified as -1, so no message will be send - // to peer anymore. - fatal((byte)(-1), ssle); - } else if ((debug != null) && Debug.isOn("ssl")) { - System.out.println( - Thread.currentThread().getName() + - ", received Exception: " + ssle); - } - - // RFC2246 requires that the session becomes - // unresumable if any connection is terminated - // without proper close_notify messages with - // level equal to warning. - // - // RFC4346 no longer requires that a session not be - // resumed if failure to properly close a connection. - // - // We choose to make the session unresumable if - // failed to send the close_notify message. - // - sess.invalidate(); - } - } catch (InterruptedException ie) { - // keep interrupted status - interrupted = true; - } - - // restore the interrupted status - if (interrupted) { - Thread.currentThread().interrupt(); - } - } else { - writeLock.lock(); - try { - outputRecord.encodeAlert(level, description); - } finally { - writeLock.unlock(); - } - } - - // Don't bother to check sequence number overlap here. If sequence - // number is huge, there should be enough sequence number space to - // request renegotiation in next application data read and write. - } - - - int bytesInCompletePacket() throws IOException { - if (getConnectionState() == cs_HANDSHAKE) { - performInitialHandshake(); - } - - synchronized (readLock) { - int state = getConnectionState(); - if ((state == cs_CLOSED) || - (state == cs_ERROR) || (state == cs_APP_CLOSED)) { - return -1; - } - - try { - return inputRecord.bytesInCompletePacket(sockInput); - } catch (EOFException eofe) { - boolean handshaking = (connectionState <= cs_HANDSHAKE); - boolean rethrow = requireCloseNotify || handshaking; - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", received EOFException: " - + (rethrow ? "error" : "ignored")); - } - - if (!rethrow) { - // treat as if we had received a close_notify - closeInternal(false); - } else { - SSLException e; - if (handshaking) { - e = new SSLHandshakeException( - "Remote host terminated the handshake"); - } else { - e = new SSLProtocolException( - "Remote host terminated the handshake"); - } - e.initCause(eofe); - throw e; - } - } - - return -1; + // Is the sequence number is nearly overflow? + if (conContext.outputRecord.seqNumIsHuge()) { + tryKeyUpdate(); } } - // the caller have synchronized readLock - void expectingFinishFlight() { - inputRecord.expectingFinishFlight(); - } - - /* - * Read an application data record. - * - * Alerts and handshake messages are internally handled directly. - */ - int readRecord(ByteBuffer buffer) throws IOException { - if (getConnectionState() == cs_HANDSHAKE) { - performInitialHandshake(); + private synchronized int readRecord() throws IOException { + while (!conContext.isInboundDone()) { + try { + Plaintext plainText = decode(null); + if ((plainText.contentType == ContentType.HANDSHAKE.id) && + conContext.isNegotiated) { + return 0; + } + } catch (SSLException ssle) { + throw ssle; + } catch (IOException ioe) { + if (!(ioe instanceof SSLException)) { + throw new SSLException("readRecord", ioe); + } else { + throw ioe; + } + } } - return readRecord(buffer, true); - } - - /* - * Read a record, no application data input required. - * - * Alerts and handshake messages are internally handled directly. - */ - int readRecord(boolean needAppData) throws IOException { - return readRecord(null, needAppData); - } - - /* - * Clear the pipeline of records from the peer, optionally returning - * application data. Caller is responsible for knowing that it's - * possible to do this kind of clearing, if they don't want app - * data -- e.g. since it's the initial SSL handshake. - * - * Don't synchronize (this) during a blocking read() since it - * protects data which is accessed on the write side as well. - */ - private int readRecord(ByteBuffer buffer, boolean needAppData) - throws IOException { - int state; - - // readLock protects reading and processing of an SSLInputRecord. - // It keeps the reading from sockInput and processing of the record - // atomic so that no two threads can be blocked on the - // read from the same input stream at the same time. - // This is required for example when a reader thread is - // blocked on the read and another thread is trying to - // close the socket. For a non-autoclose, layered socket, - // the thread performing the close needs to read the close_notify. - // - // Use readLock instead of 'this' for locking because - // 'this' also protects data accessed during writing. - synchronized (readLock) { - /* - * Read and handle records ... return application data - * ONLY if it's needed. - */ - Plaintext plainText = null; - while (((state = getConnectionState()) != cs_CLOSED) && - (state != cs_ERROR) && (state != cs_APP_CLOSED)) { - - /* - * clean the buffer and check if it is too small, e.g. because - * the AppInputStream did not have the chance to see the - * current packet length but rather something like that of the - * handshake before. In that case we return 0 at this point to - * give the caller the chance to adjust the buffer. - */ - if (buffer != null) { - buffer.clear(); - - if (buffer.remaining() < - inputRecord.bytesInCompletePacket(sockInput)) { - return 0; - } - } - - /* - * Read a record ... maybe emitting an alert if we get a - * comprehensible but unsupported "hello" message during - * format checking (e.g. V2). - */ - try { - plainText = inputRecord.decode(sockInput, buffer); - } catch (BadPaddingException bpe) { - byte alertType = (state != cs_DATA) ? - Alerts.alert_handshake_failure : - Alerts.alert_bad_record_mac; - fatal(alertType, bpe.getMessage(), bpe); - } catch (SSLProtocolException spe) { - try { - fatal(Alerts.alert_unexpected_message, spe); - } catch (IOException x) { - // discard this exception, throw the original exception - } - throw spe; - } catch (SSLHandshakeException she) { - // may be record sequence number overflow - fatal(Alerts.alert_handshake_failure, she); - } catch (EOFException eof) { - boolean handshaking = (connectionState <= cs_HANDSHAKE); - boolean rethrow = requireCloseNotify || handshaking; - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", received EOFException: " - + (rethrow ? "error" : "ignored")); - } - if (rethrow) { - SSLException e; - if (handshaking) { - e = new SSLHandshakeException( - "Remote host terminated the handshake"); - } else { - e = new SSLProtocolException( - "Remote host terminated the connection"); - } - e.initCause(eof); - throw e; - } else { - // treat as if we had received a close_notify - closeInternal(false); - continue; - } - } - - // PlainText should never be null. Process input record. - int volume = processInputRecord(plainText, needAppData); - - if (plainText.contentType == Record.ct_application_data) { - return volume; - } - - if (plainText.contentType == Record.ct_handshake) { - if (!needAppData && connectionState == cs_DATA) { - return volume; - } // otherwise, need to read more for app data. - } - - // continue to read more net data - } // while - - // - // couldn't read, due to some kind of error - // - return -1; - } // readLock synchronization + return -1; } - /* - * Process the plainText input record. - */ - private synchronized int processInputRecord( - Plaintext plainText, boolean needAppData) throws IOException { - - /* - * Process the record. - */ - int volume = 0; // no application data - switch (plainText.contentType) { - case Record.ct_handshake: - /* - * Handshake messages always go to a pending session - * handshaker ... if there isn't one, create one. This - * must work asynchronously, for renegotiation. - * - * NOTE that handshaking will either resume a session - * which was in the cache (and which might have other - * connections in it already), or else will start a new - * session (new keys exchanged) with just this connection - * in it. - */ - initHandshaker(); - if (!handshaker.activated()) { - // prior to handshaking, activate the handshake - if (connectionState == cs_RENEGOTIATE) { - // don't use SSLv2Hello when renegotiating - handshaker.activate(protocolVersion); - } else { - handshaker.activate(null); - } - } + private synchronized int readRecord(ByteBuffer buffer) throws IOException { + while (!conContext.isInboundDone()) { + /* + * clean the buffer and check if it is too small, e.g. because + * the AppInputStream did not have the chance to see the + * current packet length but rather something like that of the + * handshake before. In that case we return 0 at this point to + * give the caller the chance to adjust the buffer. + */ + buffer.clear(); + int inLen = conContext.inputRecord.bytesInCompletePacket(); + if (inLen < 0) { // EOF + handleEOF(null); - /* - * process the handshake record ... may contain just - * a partial handshake message or multiple messages. - * - * The handshaker state machine will ensure that it's - * a finished message. - */ - handshaker.processRecord(plainText.fragment, expectingFinished); - expectingFinished = false; - - if (handshaker.invalidated) { - handshaker = null; - inputRecord.setHandshakeHash(null); - outputRecord.setHandshakeHash(null); - - // if state is cs_RENEGOTIATE, revert it to cs_DATA - if (connectionState == cs_RENEGOTIATE) { - connectionState = cs_DATA; - } - } else if (handshaker.isDone()) { - // reset the parameters for secure renegotiation. - secureRenegotiation = - handshaker.isSecureRenegotiation(); - clientVerifyData = handshaker.getClientVerifyData(); - serverVerifyData = handshaker.getServerVerifyData(); - // set connection ALPN value - applicationProtocol = - handshaker.getHandshakeApplicationProtocol(); - - sess = handshaker.getSession(); - handshakeSession = null; - handshaker = null; - inputRecord.setHandshakeHash(null); - outputRecord.setHandshakeHash(null); - connectionState = cs_DATA; - - // - // Tell folk about handshake completion, but do - // it in a separate thread. - // - if (handshakeListeners != null) { - HandshakeCompletedEvent event = - new HandshakeCompletedEvent(this, sess); + // if no exception thrown + return -1; + } - Thread thread = new Thread( - null, - new NotifyHandshake( - handshakeListeners.entrySet(), event), - "HandshakeCompletedNotify-Thread", - 0, - false); - thread.start(); - } - } - - break; - - case Record.ct_application_data: - if (connectionState != cs_DATA - && connectionState != cs_RENEGOTIATE - && connectionState != cs_SENT_CLOSE) { - throw new SSLProtocolException( - "Data received in non-data state: " + - connectionState); - } - if (expectingFinished) { - throw new SSLProtocolException - ("Expecting finished message, received data"); - } - if (!needAppData) { - throw new SSLException("Discarding app data"); - } - - volume = plainText.fragment.remaining(); - break; - - case Record.ct_alert: - recvAlert(plainText.fragment); - break; + if (buffer.remaining() < inLen) { + return 0; + } - case Record.ct_change_cipher_spec: - if ((connectionState != cs_HANDSHAKE - && connectionState != cs_RENEGOTIATE)) { - // For the CCS message arriving in the wrong state - fatal(Alerts.alert_unexpected_message, - "illegal change cipher spec msg, conn state = " - + connectionState); - } else if (plainText.fragment.remaining() != 1 - || plainText.fragment.get() != 1) { - // For structural/content issues with the CCS - fatal(Alerts.alert_unexpected_message, - "Malformed change cipher spec msg"); + try { + Plaintext plainText = decode(buffer); + if (plainText.contentType == ContentType.APPLICATION_DATA.id) { + return buffer.position(); } - - // - // The first message after a change_cipher_spec - // record MUST be a "Finished" handshake record, - // else it's a protocol violation. We force this - // to be checked by a minor tweak to the state - // machine. - // - handshaker.receiveChangeCipherSpec(); - - CipherBox readCipher; - Authenticator readAuthenticator; - try { - readCipher = handshaker.newReadCipher(); - readAuthenticator = handshaker.newReadAuthenticator(); - } catch (GeneralSecurityException e) { - // can't happen - throw new SSLException("Algorithm missing: ", e); + } catch (SSLException ssle) { + throw ssle; + } catch (IOException ioe) { + if (!(ioe instanceof SSLException)) { + throw new SSLException("readRecord", ioe); + } else { + throw ioe; } - inputRecord.changeReadCiphers(readAuthenticator, readCipher); - - // next message MUST be a finished message - expectingFinished = true; - - break; - - default: - // - // TLS requires that unrecognized records be ignored. - // - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", Received record type: " + plainText.contentType); - } - break; + } } - /* - * Check the sequence number state - * - * Note that in order to maintain the connection I/O - * properly, we check the sequence number after the last - * record reading process. As we request renegotiation - * or close the connection for wrapped sequence number - * when there is enough sequence number space left to - * handle a few more records, so the sequence number - * of the last record cannot be wrapped. - * - * Don't bother to kickstart the renegotiation when the - * local is asking for it. - */ - if ((connectionState == cs_DATA) && inputRecord.seqNumIsHuge()) { - /* - * Ask for renegotiation when need to renew sequence number. - * - * Don't bother to kickstart the renegotiation when the local is - * asking for it. - */ - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", request renegotiation " + - "to avoid sequence number overflow"); + // + // couldn't read, due to some kind of error + // + return -1; + } + + private Plaintext decode(ByteBuffer destination) throws IOException { + Plaintext plainText; + try { + if (destination == null) { + plainText = SSLTransport.decode(conContext, + null, 0, 0, null, 0, 0); + } else { + plainText = SSLTransport.decode(conContext, + null, 0, 0, new ByteBuffer[]{destination}, 0, 1); } - - startHandshake(); + } catch (EOFException eofe) { + // EOFException is special as it is related to close_notify. + plainText = handleEOF(eofe); } - return volume; - } - + // Is the sequence number is nearly overflow? + if (plainText != Plaintext.PLAINTEXT_NULL && + conContext.inputRecord.seqNumIsHuge()) { + tryKeyUpdate(); + } - // - // HANDSHAKE RELATED CODE - // - - /** - * Return the AppInputStream. For use by Handshaker only. - */ - AppInputStream getAppInputStream() { - return input; + return plainText; } /** - * Return the AppOutputStream. For use by Handshaker only. + * Try renegotiation or key update for sequence number wrap. + * + * Note that in order to maintain the handshake status properly, we check + * the sequence number after the last record reading/writing process. As + * we request renegotiation or close the connection for wrapped sequence + * number when there is enough sequence number space left to handle a few + * more records, so the sequence number of the last record cannot be + * wrapped. */ - AppOutputStream getAppOutputStream() { - return output; + private void tryKeyUpdate() throws IOException { + // Don't bother to kickstart the renegotiation or key update when the + // local is asking for it. + if ((conContext.handshakeContext == null) && + !conContext.isClosed() && !conContext.isBroken) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("key update to wrap sequence number"); + } + conContext.keyUpdate(); + } } - /** - * Initialize the handshaker object. This means: - * - * . if a handshake is already in progress (state is cs_HANDSHAKE - * or cs_RENEGOTIATE), do nothing and return - * - * . if the socket is already closed, throw an Exception (internal error) - * - * . otherwise (cs_START or cs_DATA), create the appropriate handshaker - * object, and advance the connection state (to cs_HANDSHAKE or - * cs_RENEGOTIATE, respectively). - * - * This method is called right after a new socket is created, when - * starting renegotiation, or when changing client/ server mode of the - * socket. - */ - private void initHandshaker() { - switch (connectionState) { - - // - // Starting a new handshake. - // - case cs_START: - case cs_DATA: - break; - - // - // We're already in the middle of a handshake. - // - case cs_HANDSHAKE: - case cs_RENEGOTIATE: - return; - - // - // Anyone allowed to call this routine is required to - // do so ONLY if the connection state is reasonable... - // - default: - throw new IllegalStateException("Internal error"); + private void closeSocket(boolean selfInitiated) throws IOException { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("close the ssl connection " + + (selfInitiated ? "(initiative)" : "(passive)")); } - // state is either cs_START or cs_DATA - if (connectionState == cs_START) { - connectionState = cs_HANDSHAKE; - } else { // cs_DATA - connectionState = cs_RENEGOTIATE; + if (autoClose || !isLayered()) { + super.close(); + } else if (selfInitiated) { + // wait for close_notify alert to clear input stream. + waitForClose(); + } + } + + /** + * Wait for close_notify alert for a graceful closure. + * + * [RFC 5246] If the application protocol using TLS provides that any + * data may be carried over the underlying transport after the TLS + * connection is closed, the TLS implementation must receive the responding + * close_notify alert before indicating to the application layer that + * the TLS connection has ended. If the application protocol will not + * transfer any additional data, but will only close the underlying + * transport connection, then the implementation MAY choose to close the + * transport without waiting for the responding close_notify. + */ + private void waitForClose() throws IOException { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("wait for close_notify or alert"); } - if (roleIsServer) { - handshaker = new ServerHandshaker(this, sslContext, - enabledProtocols, doClientAuth, - protocolVersion, connectionState == cs_HANDSHAKE, - secureRenegotiation, clientVerifyData, serverVerifyData); - handshaker.setSNIMatchers(sniMatchers); - handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); - } else { - handshaker = new ClientHandshaker(this, sslContext, - enabledProtocols, - protocolVersion, connectionState == cs_HANDSHAKE, - secureRenegotiation, clientVerifyData, serverVerifyData); - handshaker.setSNIServerNames(serverNames); - } - handshaker.setMaximumPacketSize(maximumPacketSize); - handshaker.setEnabledCipherSuites(enabledCipherSuites); - handshaker.setEnableSessionCreation(enableSessionCreation); - handshaker.setApplicationProtocols(applicationProtocols); - handshaker.setApplicationProtocolSelectorSSLSocket( - applicationProtocolSelector); - } - - /** - * Synchronously perform the initial handshake. - * - * If the handshake is already in progress, this method blocks until it - * is completed. If the initial handshake has already been completed, - * it returns immediately. - */ - private void performInitialHandshake() throws IOException { - // use handshakeLock and the state check to make sure only - // one thread performs the handshake - synchronized (handshakeLock) { - if (getConnectionState() == cs_HANDSHAKE) { - kickstartHandshake(); - - /* - * All initial handshaking goes through this operation - * until we have a valid SSL connection. - * - * Handle handshake messages only, need no application data. - */ - readRecord(false); + while (!conContext.isInboundDone()) { + try { + Plaintext plainText = decode(null); + // discard and continue + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + "discard plaintext while waiting for close", plainText); + } + } catch (Exception e) { // including RuntimeException + handleException(e); } } } /** - * Starts an SSL handshake on this connection. - */ - @Override - public void startHandshake() throws IOException { - // start an ssl handshake that could be resumed from timeout exception - startHandshake(true); - } - - /** - * Starts an ssl handshake on this connection. - * - * @param resumable indicates the handshake process is resumable from a - * certain exception. If <code>resumable</code>, the socket will - * be reserved for exceptions like timeout; otherwise, the socket - * will be closed, no further communications could be done. - */ - private void startHandshake(boolean resumable) throws IOException { - checkWrite(); - try { - if (getConnectionState() == cs_HANDSHAKE) { - // do initial handshake - performInitialHandshake(); - } else { - // start renegotiation - kickstartHandshake(); - } - } catch (Exception e) { - // shutdown and rethrow (wrapped) exception as appropriate - handleException(e, resumable); - } - } - - /** - * Kickstart the handshake if it is not already in progress. - * This means: - * - * . if handshaking is already underway, do nothing and return - * - * . if the socket is not connected or already closed, throw an - * Exception. + * Initialize the handshaker and socket streams. * - * . otherwise, call initHandshake() to initialize the handshaker - * object and progress the state. Then, send the initial - * handshaking message if appropriate (always on clients and - * on servers when renegotiating). - */ - private synchronized void kickstartHandshake() throws IOException { - - switch (connectionState) { - - case cs_HANDSHAKE: - // handshaker already setup, proceed - break; - - case cs_DATA: - if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { - throw new SSLHandshakeException( - "Insecure renegotiation is not allowed"); - } - - if (!secureRenegotiation) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning: Using insecure renegotiation"); - } - } - - // initialize the handshaker, move to cs_RENEGOTIATE - initHandshaker(); - break; - - case cs_RENEGOTIATE: - // handshaking already in progress, return - return; - - /* - * The only way to get a socket in the state is when - * you have an unconnected socket. - */ - case cs_START: - throw new SocketException( - "handshaking attempted on unconnected socket"); - - default: - throw new SocketException("connection is closed"); - } - - // - // Kickstart handshake state machine if we need to ... - // - // Note that handshaker.kickstart() writes the message - // to its HandshakeOutStream, which calls back into - // SSLSocketImpl.writeRecord() to send it. - // - if (!handshaker.activated()) { - // prior to handshaking, activate the handshake - if (connectionState == cs_RENEGOTIATE) { - // don't use SSLv2Hello when renegotiating - handshaker.activate(protocolVersion); - } else { - handshaker.activate(null); - } - - if (handshaker instanceof ClientHandshaker) { - // send client hello - handshaker.kickstart(); - } else { - if (connectionState == cs_HANDSHAKE) { - // initial handshake, no kickstart message to send - } else { - // we want to renegotiate, send hello request - handshaker.kickstart(); - } - } - } - } - - // - // CLOSURE RELATED CALLS - // - - /** - * Return whether the socket has been explicitly closed by the application. + * Called by connect, the layered constructor, and SSLServerSocket. */ - @Override - public boolean isClosed() { - return connectionState == cs_APP_CLOSED; - } - - /** - * Return whether we have reached end-of-file. - * - * If the socket is not connected, has been shutdown because of an error - * or has been closed, throw an Exception. - */ - boolean checkEOF() throws IOException { - switch (getConnectionState()) { - case cs_START: - throw new SocketException("Socket is not connected"); - - case cs_HANDSHAKE: - case cs_DATA: - case cs_RENEGOTIATE: - case cs_SENT_CLOSE: - return false; - - case cs_APP_CLOSED: - throw new SocketException("Socket is closed"); - - case cs_ERROR: - case cs_CLOSED: - default: - // either closed because of error, or normal EOF - if (closeReason == null) { - return true; - } - IOException e = new SSLException - ("Connection has been shutdown: " + closeReason); - e.initCause(closeReason); - throw e; - - } - } - - /** - * Check if we can write data to this socket. If not, throw an IOException. - */ - void checkWrite() throws IOException { - if (checkEOF() || (getConnectionState() == cs_SENT_CLOSE)) { - // we are at EOF, write must throw Exception - throw new SocketException("Connection closed by remote host"); - } - } - - private void closeSocket() throws IOException { - - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called closeSocket()"); - } - - super.close(); - } - - private void closeSocket(boolean selfInitiated) throws IOException { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called closeSocket(" + selfInitiated + ")"); - } - if (!isLayered() || autoClose) { - super.close(); - } else if (selfInitiated) { - // layered && non-autoclose - // read close_notify alert to clear input stream - waitForClose(false); - } - } - - /* - * Closing the connection is tricky ... we can't officially close the - * connection until we know the other end is ready to go away too, - * and if ever the connection gets aborted we must forget session - * state (it becomes invalid). - */ - - /** - * Closes the SSL connection. SSL includes an application level - * shutdown handshake; you should close SSL sockets explicitly - * rather than leaving it for finalization, so that your remote - * peer does not experience a protocol error. - */ - @Override - public void close() throws IOException { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called close()"); - } - closeInternal(true); // caller is initiating close - - // Clearup the resources. - try { - synchronized (readLock) { - inputRecord.close(); - } - - writeLock.lock(); - try { - outputRecord.close(); - } finally { - writeLock.unlock(); - } - } catch (IOException ioe) { - // ignore - } - - setConnectionState(cs_APP_CLOSED); - } - - /** - * Don't synchronize the whole method because waitForClose() - * (which calls readRecord()) might be called. - * - * @param selfInitiated Indicates which party initiated the close. - * If selfInitiated, this side is initiating a close; for layered and - * non-autoclose socket, wait for close_notify response. - * If !selfInitiated, peer sent close_notify; we reciprocate but - * no need to wait for response. - */ - private void closeInternal(boolean selfInitiated) throws IOException { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", called closeInternal(" + selfInitiated + ")"); + synchronized void doneConnect() throws IOException { + // In server mode, it is not necessary to set host and serverNames. + // Otherwise, would require a reverse DNS lookup to get the hostname. + if ((peerHost == null) || (peerHost.length() == 0)) { + boolean useNameService = + trustNameService && conContext.sslConfig.isClientMode; + useImplicitHost(useNameService); + } else { + conContext.sslConfig.serverNames = + Utilities.addToSNIServerNameList( + conContext.sslConfig.serverNames, peerHost); } - int state = getConnectionState(); - boolean closeSocketCalled = false; - Throwable cachedThrowable = null; - try { - switch (state) { - case cs_START: - // unconnected socket or handshaking has not been initialized - closeSocket(selfInitiated); - break; - - /* - * If we're closing down due to error, we already sent (or else - * received) the fatal alert ... no niceties, blow the connection - * away as quickly as possible (even if we didn't allocate the - * socket ourselves; it's unusable, regardless). - */ - case cs_ERROR: - closeSocket(); - break; - - /* - * Sometimes close() gets called more than once. - */ - case cs_CLOSED: - case cs_APP_CLOSED: - break; + InputStream sockInput = super.getInputStream(); + conContext.inputRecord.setReceiverStream(sockInput); - /* - * Otherwise we indicate clean termination. - */ - // case cs_HANDSHAKE: - // case cs_DATA: - // case cs_RENEGOTIATE: - // case cs_SENT_CLOSE: - default: - synchronized (this) { - if (((state = getConnectionState()) == cs_CLOSED) || - (state == cs_ERROR) || (state == cs_APP_CLOSED)) { - return; // connection was closed while we waited - } - if (state != cs_SENT_CLOSE) { - try { - warning(Alerts.alert_close_notify); - connectionState = cs_SENT_CLOSE; - } catch (Throwable th) { - // we need to ensure socket is closed out - // if we encounter any errors. - connectionState = cs_ERROR; - // cache this for later use - cachedThrowable = th; - closeSocketCalled = true; - closeSocket(selfInitiated); - } - } - } - // If state was cs_SENT_CLOSE before, we don't do the actual - // closing since it is already in progress. - if (state == cs_SENT_CLOSE) { - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", close invoked again; state = " + - getConnectionState()); - } - if (selfInitiated == false) { - // We were called because a close_notify message was - // received. This may be due to another thread calling - // read() or due to our call to waitForClose() below. - // In either case, just return. - return; - } - // Another thread explicitly called close(). We need to - // wait for the closing to complete before returning. - synchronized (this) { - while (connectionState < cs_CLOSED) { - try { - this.wait(); - } catch (InterruptedException e) { - // ignore - } - } - } - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", after primary close; state = " + - getConnectionState()); - } - return; - } - - if (!closeSocketCalled) { - closeSocketCalled = true; - closeSocket(selfInitiated); - } + OutputStream sockOutput = super.getOutputStream(); + conContext.inputRecord.setDeliverStream(sockOutput); + conContext.outputRecord.setDeliverStream(sockOutput); - break; - } - } finally { - synchronized (this) { - // Upon exit from this method, the state is always >= cs_CLOSED - connectionState = (connectionState == cs_APP_CLOSED) - ? cs_APP_CLOSED : cs_CLOSED; - // notify any threads waiting for the closing to finish - this.notifyAll(); - } - - if (cachedThrowable != null) { - /* - * Rethrow the error to the calling method - * The Throwable caught can only be an Error or RuntimeException - */ - if (cachedThrowable instanceof Error) { - throw (Error)cachedThrowable; - } else if (cachedThrowable instanceof RuntimeException) { - throw (RuntimeException)cachedThrowable; - } // Otherwise, unlikely - } - } - } - - /** - * Reads a close_notify or a fatal alert from the input stream. - * Keep reading records until we get a close_notify or until - * the connection is otherwise closed. The close_notify or alert - * might be read by another reader, - * which will then process the close and set the connection state. - */ - void waitForClose(boolean rethrow) throws IOException { - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", waiting for close_notify or alert: state " - + getConnectionState()); - } - - try { - int state; - - while (((state = getConnectionState()) != cs_CLOSED) && - (state != cs_ERROR) && (state != cs_APP_CLOSED)) { - - // Ask for app data and then throw it away - try { - readRecord(true); - } catch (SocketTimeoutException e) { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println( - Thread.currentThread().getName() + - ", received Exception: " + e); - } - fatal((byte)(-1), "Did not receive close_notify from peer", e); - } - } - } catch (IOException e) { - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", Exception while waiting for close " +e); - } - if (rethrow) { - throw e; // pass exception up - } - } - } - - // - // EXCEPTION AND ALERT HANDLING - // - - /** - * Handle an exception. This method is called by top level exception - * handlers (in read(), write()) to make sure we always shutdown the - * connection correctly and do not pass runtime exception to the - * application. - */ - void handleException(Exception e) throws IOException { - handleException(e, true); + this.isConnected = true; } - /** - * Handle an exception. This method is called by top level exception - * handlers (in read(), write(), startHandshake()) to make sure we - * always shutdown the connection correctly and do not pass runtime - * exception to the application. - * - * This method never returns normally, it always throws an IOException. - * - * We first check if the socket has already been shutdown because of an - * error. If so, we just rethrow the exception. If the socket has not - * been shutdown, we sent a fatal alert and remember the exception. - * - * @param e the Exception - * @param resumable indicates the caller process is resumable from the - * exception. If <code>resumable</code>, the socket will be - * reserved for exceptions like timeout; otherwise, the socket - * will be closed, no further communications could be done. - */ - private synchronized void handleException(Exception e, boolean resumable) - throws IOException { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", handling exception: " + e.toString()); - } - - // don't close the Socket in case of timeouts or interrupts if - // the process is resumable. - if (e instanceof InterruptedIOException && resumable) { - throw (IOException)e; - } - - // if we've already shutdown because of an error, - // there is nothing to do except rethrow the exception - if (closeReason != null) { - if (e instanceof IOException) { // includes SSLException - throw (IOException)e; - } else { - // this is odd, not an IOException. - // normally, this should not happen - // if closeReason has been already been set - throw Alerts.getSSLException(Alerts.alert_internal_error, e, - "Unexpected exception"); - } - } - - // need to perform error shutdown - boolean isSSLException = (e instanceof SSLException); - if ((!isSSLException) && (e instanceof IOException)) { - // IOException from the socket - // this means the TCP connection is already dead - // we call fatal just to set the error status - try { - fatal(Alerts.alert_unexpected_message, e); - } catch (IOException ee) { - // ignore (IOException wrapped in SSLException) - } - // rethrow original IOException - throw (IOException)e; - } - - // must be SSLException or RuntimeException - byte alertType; - if (isSSLException) { - if (e instanceof SSLHandshakeException) { - alertType = Alerts.alert_handshake_failure; - } else { - alertType = Alerts.alert_unexpected_message; - } - } else { - alertType = Alerts.alert_internal_error; - } - fatal(alertType, e); - } - - /* - * Send a warning alert. - */ - void warning(byte description) { - sendAlert(Alerts.alert_warning, description); - } - - synchronized void fatal(byte description, String diagnostic) - throws IOException { - fatal(description, diagnostic, null); - } - - synchronized void fatal(byte description, Throwable cause) - throws IOException { - fatal(description, null, cause); - } - - /* - * Send a fatal alert, and throw an exception so that callers will - * need to stand on their heads to accidentally continue processing. - */ - synchronized void fatal(byte description, String diagnostic, - Throwable cause) throws IOException { - - // Be care of deadlock. Please don't synchronize readLock. - try { - inputRecord.close(); - } catch (IOException ioe) { - // ignore - } - - sess.invalidate(); - if (handshakeSession != null) { - handshakeSession.invalidate(); - } - - int oldState = connectionState; - if (connectionState < cs_ERROR) { - connectionState = cs_ERROR; - } - - /* - * Has there been an error received yet? If not, remember it. - * By RFC 2246, we don't bother waiting for a response. - * Fatal errors require immediate shutdown. - */ - if (closeReason == null) { - /* - * Try to clear the kernel buffer to avoid TCP connection resets. - */ - if (oldState == cs_HANDSHAKE) { - sockInput.skip(sockInput.available()); - } - - // If the description equals -1, the alert won't be sent to peer. - if (description != -1) { - sendAlert(Alerts.alert_fatal, description); - } - if (cause instanceof SSLException) { // only true if != null - closeReason = (SSLException)cause; - } else { - closeReason = - Alerts.getSSLException(description, cause, diagnostic); - } - } - - /* - * Clean up our side. - */ - closeSocket(); - - // Be care of deadlock. Please don't synchronize writeLock. - try { - outputRecord.close(); - } catch (IOException ioe) { - // ignore - } - - throw closeReason; - } - - - /* - * Process an incoming alert ... caller must already have synchronized - * access to "this". - */ - private void recvAlert(ByteBuffer fragment) throws IOException { - byte level = fragment.get(); - byte description = fragment.get(); - - if (description == -1) { // check for short message - fatal(Alerts.alert_illegal_parameter, "Short alert message"); - } - - if (debug != null && (Debug.isOn("record") || - Debug.isOn("handshake"))) { - synchronized (System.out) { - System.out.print(Thread.currentThread().getName()); - System.out.print(", RECV " + protocolVersion + " ALERT: "); - if (level == Alerts.alert_fatal) { - System.out.print("fatal, "); - } else if (level == Alerts.alert_warning) { - System.out.print("warning, "); - } else { - System.out.print("<level " + (0x0ff & level) + ">, "); - } - System.out.println(Alerts.alertDescription(description)); - } - } - - if (level == Alerts.alert_warning) { - if (description == Alerts.alert_close_notify) { - if (connectionState == cs_HANDSHAKE) { - fatal(Alerts.alert_unexpected_message, - "Received close_notify during handshake"); - } else { - closeInternal(false); // reply to close - } - } else { - - // - // The other legal warnings relate to certificates, - // e.g. no_certificate, bad_certificate, etc; these - // are important to the handshaking code, which can - // also handle illegal protocol alerts if needed. - // - if (handshaker != null) { - handshaker.handshakeAlert(description); - } - } - } else { // fatal or unknown level - String reason = "Received fatal alert: " - + Alerts.alertDescription(description); - if (closeReason == null) { - closeReason = Alerts.getSSLException(description, reason); - } - fatal(Alerts.alert_unexpected_message, reason); - } - } - - - /* - * Emit alerts. Caller must have synchronized with "this". - */ - private void sendAlert(byte level, byte description) { - // the connectionState cannot be cs_START - if (connectionState >= cs_SENT_CLOSE) { - return; - } - - // For initial handshaking, don't send alert message to peer if - // handshaker has not started. - // - // Shall we send an fatal alter to terminate the connection gracefully? - if (connectionState <= cs_HANDSHAKE && - (handshaker == null || !handshaker.started() || - !handshaker.activated())) { - return; - } - - boolean useDebug = debug != null && Debug.isOn("ssl"); - if (useDebug) { - synchronized (System.out) { - System.out.print(Thread.currentThread().getName()); - System.out.print(", SEND " + protocolVersion + " ALERT: "); - if (level == Alerts.alert_fatal) { - System.out.print("fatal, "); - } else if (level == Alerts.alert_warning) { - System.out.print("warning, "); - } else { - System.out.print("<level = " + (0x0ff & level) + ">, "); - } - System.out.println("description = " - + Alerts.alertDescription(description)); - } - } - - try { - writeAlert(level, description); - } catch (IOException e) { - if (useDebug) { - System.out.println(Thread.currentThread().getName() + - ", Exception sending alert: " + e); - } - } - } - - // - // VARIOUS OTHER METHODS - // - - // used by Handshaker - void changeWriteCiphers() throws IOException { - Authenticator writeAuthenticator; - CipherBox writeCipher; - try { - writeCipher = handshaker.newWriteCipher(); - writeAuthenticator = handshaker.newWriteAuthenticator(); - } catch (GeneralSecurityException e) { - // "can't happen" - throw new SSLException("Algorithm missing: ", e); - } - outputRecord.changeWriteCiphers(writeAuthenticator, writeCipher); - } - - /* - * Updates the SSL version associated with this connection. - * Called from Handshaker once it has determined the negotiated version. - */ - synchronized void setVersion(ProtocolVersion protocolVersion) { - this.protocolVersion = protocolVersion; - outputRecord.setVersion(protocolVersion); - } - - // - // ONLY used by ClientHandshaker for the server hostname during handshaking - // - synchronized String getHost() { - // Note that the host may be null or empty for localhost. - if (host == null || host.length() == 0) { - useImplicitHost(true); - } - - return host; - } - - /* - * Try to set and use the implicit specified hostname - */ - private synchronized void useImplicitHost(boolean noSniUpdate) { - + private void useImplicitHost(boolean useNameService) { // Note: If the local name service is not trustworthy, reverse // host name resolution should not be performed for endpoint // identification. Use the application original specified @@ -2157,27 +1007,24 @@ if ((originalHostname != null) && (originalHostname.length() != 0)) { - host = originalHostname; - if (!noSniUpdate && serverNames.isEmpty() && !noSniExtension) { - serverNames = - Utilities.addToSNIServerNameList(serverNames, host); - - if (!roleIsServer && - (handshaker != null) && !handshaker.activated()) { - handshaker.setSNIServerNames(serverNames); - } + this.peerHost = originalHostname; + if (conContext.sslConfig.serverNames.isEmpty() && + !conContext.sslConfig.noSniExtension) { + conContext.sslConfig.serverNames = + Utilities.addToSNIServerNameList( + conContext.sslConfig.serverNames, peerHost); } return; } // No explicitly specified hostname, no server name indication. - if (!trustNameService) { + if (!useNameService) { // The local name service is not trustworthy, use IP address. - host = inetAddress.getHostAddress(); + this.peerHost = inetAddress.getHostAddress(); } else { // Use the underlying reverse host name resolution service. - host = getInetAddress().getHostName(); + this.peerHost = getInetAddress().getHostName(); } } @@ -2187,557 +1034,144 @@ // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter // may override SNIHostName in the customized server name indication. public synchronized void setHost(String host) { - this.host = host; - this.serverNames = - Utilities.addToSNIServerNameList(this.serverNames, this.host); - - if (!roleIsServer && (handshaker != null) && !handshaker.activated()) { - handshaker.setSNIServerNames(serverNames); - } - } - - /** - * Gets an input stream to read from the peer on the other side. - * Data read from this stream was always integrity protected in - * transit, and will usually have been confidentiality protected. - */ - @Override - public synchronized InputStream getInputStream() throws IOException { - if (isClosed()) { - throw new SocketException("Socket is closed"); - } - - /* - * Can't call isConnected() here, because the Handshakers - * do some initialization before we actually connect. - */ - if (connectionState == cs_START) { - throw new SocketException("Socket is not connected"); - } - - return input; - } - - /** - * Gets an output stream to write to the peer on the other side. - * Data written on this stream is always integrity protected, and - * will usually be confidentiality protected. - */ - @Override - public synchronized OutputStream getOutputStream() throws IOException { - if (isClosed()) { - throw new SocketException("Socket is closed"); - } - - /* - * Can't call isConnected() here, because the Handshakers - * do some initialization before we actually connect. - */ - if (connectionState == cs_START) { - throw new SocketException("Socket is not connected"); - } - - return output; + this.peerHost = host; + this.conContext.sslConfig.serverNames = + Utilities.addToSNIServerNameList( + conContext.sslConfig.serverNames, host); } /** - * Returns the SSL Session in use by this connection. These can - * be long lived, and frequently correspond to an entire login session - * for some user. + * Return whether we have reached end-of-file. + * + * If the socket is not connected, has been shutdown because of an error + * or has been closed, throw an Exception. */ - @Override - public SSLSession getSession() { - /* - * Force a synchronous handshake, if appropriate. - */ - if (getConnectionState() == cs_HANDSHAKE) { - try { - // start handshaking, if failed, the connection will be closed. - startHandshake(false); - } catch (IOException e) { - // handshake failed. log and return a nullSession - if (debug != null && Debug.isOn("handshake")) { - System.out.println(Thread.currentThread().getName() + - ", IOException in getSession(): " + e); - } + synchronized boolean checkEOF() throws IOException { + if (conContext.isClosed()) { + return true; + } else if (conContext.isInputCloseNotified || conContext.isBroken) { + if (conContext.closeReason == null) { + return true; + } else { + throw new SSLException( + "Connection has been shutdown: " + conContext.closeReason, + conContext.closeReason); } } - synchronized (this) { - return sess; - } - } - @Override - public synchronized SSLSession getHandshakeSession() { - return handshakeSession; - } - - synchronized void setHandshakeSession(SSLSessionImpl session) { - // update the fragment size, which may be negotiated during handshaking - inputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize()); - outputRecord.changeFragmentSize(session.getNegotiatedMaxFragSize()); - - handshakeSession = session; + return false; } /** - * Controls whether new connections may cause creation of new SSL - * sessions. - * - * As long as handshaking has not started, we can change - * whether we enable session creations. Otherwise, - * we will need to wait for the next handshake. + * Check if we can write data to this socket. */ - @Override - public synchronized void setEnableSessionCreation(boolean flag) { - enableSessionCreation = flag; - - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnableSessionCreation(enableSessionCreation); + synchronized void checkWrite() throws IOException { + if (checkEOF() || conContext.isOutboundClosed()) { + // we are at EOF, write must throw Exception + throw new SocketException("Connection closed"); + } + if (!isConnected) { + throw new SocketException("Socket is not connected"); } } /** - * Returns true if new connections may cause creation of new SSL - * sessions. - */ - @Override - public synchronized boolean getEnableSessionCreation() { - return enableSessionCreation; - } - - - /** - * Sets the flag controlling whether a server mode socket - * *REQUIRES* SSL client authentication. + * Handle an exception. + * + * This method is called by top level exception handlers (in read(), + * write()) to make sure we always shutdown the connection correctly + * and do not pass runtime exception to the application. * - * As long as handshaking has not started, we can change - * whether client authentication is needed. Otherwise, - * we will need to wait for the next handshake. + * This method never returns normally, it always throws an IOException. */ - @Override - public synchronized void setNeedClientAuth(boolean flag) { - doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED : - ClientAuthType.CLIENT_AUTH_NONE); + private void handleException(Exception cause) throws IOException { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("handling exception", cause); + } + + // Don't close the Socket in case of timeouts or interrupts. + if (cause instanceof InterruptedIOException) { + throw (IOException)cause; + } - if ((handshaker != null) && - (handshaker instanceof ServerHandshaker) && - !handshaker.activated()) { - ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + // need to perform error shutdown + boolean isSSLException = (cause instanceof SSLException); + Alert alert; + if (isSSLException) { + if (cause instanceof SSLHandshakeException) { + alert = Alert.HANDSHAKE_FAILURE; + } else { + alert = Alert.UNEXPECTED_MESSAGE; + } + } else { + if (cause instanceof IOException) { + alert = Alert.UNEXPECTED_MESSAGE; + } else { + // RuntimeException + alert = Alert.INTERNAL_ERROR; + } } + conContext.fatal(alert, cause); } - @Override - public synchronized boolean getNeedClientAuth() { - return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED); - } + private Plaintext handleEOF(EOFException eofe) throws IOException { + if (requireCloseNotify || conContext.handshakeContext != null) { + SSLException ssle; + if (conContext.handshakeContext != null) { + ssle = new SSLHandshakeException( + "Remote host terminated the handshake"); + } else { + ssle = new SSLProtocolException( + "Remote host terminated the connection"); + } - /** - * Sets the flag controlling whether a server mode socket - * *REQUESTS* SSL client authentication. - * - * As long as handshaking has not started, we can change - * whether client authentication is requested. Otherwise, - * we will need to wait for the next handshake. - */ - @Override - public synchronized void setWantClientAuth(boolean flag) { - doClientAuth = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED : - ClientAuthType.CLIENT_AUTH_NONE); + if (eofe != null) { + ssle.initCause(eofe); + } + throw ssle; + } else { + // treat as if we had received a close_notify + conContext.isInputCloseNotified = true; + conContext.transport.shutdown(); - if ((handshaker != null) && - (handshaker instanceof ServerHandshaker) && - !handshaker.activated()) { - ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + return Plaintext.PLAINTEXT_NULL; } } - @Override - public synchronized boolean getWantClientAuth() { - return (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED); - } - - /** - * Sets the flag controlling whether the socket is in SSL - * client or server mode. Must be called before any SSL - * traffic has started. - */ @Override - @SuppressWarnings("fallthrough") - public synchronized void setUseClientMode(boolean flag) { - switch (connectionState) { - - case cs_START: - /* - * If we need to change the socket mode and the enabled - * protocols and cipher suites haven't specifically been - * set by the user, change them to the corresponding - * default ones. - */ - if (roleIsServer != (!flag)) { - if (sslContext.isDefaultProtocolList(enabledProtocols)) { - enabledProtocols = - sslContext.getDefaultProtocolList(!flag); - } - - if (sslContext.isDefaultCipherSuiteList(enabledCipherSuites)) { - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(!flag); - } - } - - roleIsServer = !flag; - break; - - case cs_HANDSHAKE: - /* - * If we have a handshaker, but haven't started - * SSL traffic, we can throw away our current - * handshaker, and start from scratch. Don't - * need to call doneConnect() again, we already - * have the streams. - */ - assert(handshaker != null); - if (!handshaker.activated()) { - /* - * If we need to change the socket mode and the enabled - * protocols and cipher suites haven't specifically been - * set by the user, change them to the corresponding - * default ones. - */ - if (roleIsServer != (!flag)) { - if (sslContext.isDefaultProtocolList(enabledProtocols)) { - enabledProtocols = - sslContext.getDefaultProtocolList(!flag); - } - - if (sslContext.isDefaultCipherSuiteList( - enabledCipherSuites)) { - enabledCipherSuites = - sslContext.getDefaultCipherSuiteList(!flag); - } - } - - roleIsServer = !flag; - connectionState = cs_START; - initHandshaker(); - break; - } - - // If handshake has started, that's an error. Fall through... - - default: - if (debug != null && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", setUseClientMode() invoked in state = " + - connectionState); - } - throw new IllegalArgumentException( - "Cannot change mode after SSL traffic has started"); - } - } - - @Override - public synchronized boolean getUseClientMode() { - return !roleIsServer; - } - - - /** - * Returns the names of the cipher suites which could be enabled for use - * on an SSL connection. Normally, only a subset of these will actually - * be enabled by default, since this list may include cipher suites which - * do not support the mutual authentication of servers and clients, or - * which do not protect data confidentiality. Servers may also need - * certain kinds of certificates to use certain cipher suites. - * - * @return an array of cipher suite names - */ - @Override - public String[] getSupportedCipherSuites() { - return sslContext.getSupportedCipherSuiteList().toStringArray(); - } - - /** - * Controls which particular cipher suites are enabled for use on - * this connection. The cipher suites must have been listed by - * getCipherSuites() as being supported. Even if a suite has been - * enabled, it might never be used if no peer supports it or the - * requisite certificates (and private keys) are not available. - * - * @param suites Names of all the cipher suites to enable. - */ - @Override - public synchronized void setEnabledCipherSuites(String[] suites) { - enabledCipherSuites = new CipherSuiteList(suites); - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnabledCipherSuites(enabledCipherSuites); - } - } - - /** - * Returns the names of the SSL cipher suites which are currently enabled - * for use on this connection. When an SSL socket is first created, - * all enabled cipher suites <em>(a)</em> protect data confidentiality, - * by traffic encryption, and <em>(b)</em> can mutually authenticate - * both clients and servers. Thus, in some environments, this value - * might be empty. - * - * @return an array of cipher suite names - */ - @Override - public synchronized String[] getEnabledCipherSuites() { - return enabledCipherSuites.toStringArray(); - } - - - /** - * Returns the protocols that are supported by this implementation. - * A subset of the supported protocols may be enabled for this connection - * @return an array of protocol names. - */ - @Override - public String[] getSupportedProtocols() { - return sslContext.getSuportedProtocolList().toStringArray(); - } - - /** - * Controls which protocols are enabled for use on - * this connection. The protocols must have been listed by - * getSupportedProtocols() as being supported. - * - * @param protocols protocols to enable. - * @exception IllegalArgumentException when one of the protocols - * named by the parameter is not supported. - */ - @Override - public synchronized void setEnabledProtocols(String[] protocols) { - enabledProtocols = new ProtocolList(protocols); - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setEnabledProtocols(enabledProtocols); - } + public String getPeerHost() { + return peerHost; } @Override - public synchronized String[] getEnabledProtocols() { - return enabledProtocols.toStringArray(); - } - - /** - * Assigns the socket timeout. - * @see java.net.Socket#setSoTimeout - */ - @Override - public void setSoTimeout(int timeout) throws SocketException { - if ((debug != null) && Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", setSoTimeout(" + timeout + ") called"); - } - - super.setSoTimeout(timeout); + public int getPeerPort() { + return getPort(); } - /** - * Registers an event listener to receive notifications that an - * SSL handshake has completed on this connection. - */ @Override - public synchronized void addHandshakeCompletedListener( - HandshakeCompletedListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener is null"); - } - if (handshakeListeners == null) { - handshakeListeners = new - HashMap<HandshakeCompletedListener, AccessControlContext>(4); - } - handshakeListeners.put(listener, AccessController.getContext()); - } - - - /** - * Removes a previously registered handshake completion listener. - */ - @Override - public synchronized void removeHandshakeCompletedListener( - HandshakeCompletedListener listener) { - if (handshakeListeners == null) { - throw new IllegalArgumentException("no listeners"); - } - if (handshakeListeners.remove(listener) == null) { - throw new IllegalArgumentException("listener not registered"); - } - if (handshakeListeners.isEmpty()) { - handshakeListeners = null; - } + public boolean useDelegatedTask() { + return false; } - /** - * Returns the SSLParameters in effect for this SSLSocket. - */ @Override - public synchronized SSLParameters getSSLParameters() { - SSLParameters params = super.getSSLParameters(); - - // the super implementation does not handle the following parameters - params.setEndpointIdentificationAlgorithm(identificationProtocol); - params.setAlgorithmConstraints(algorithmConstraints); - - if (sniMatchers.isEmpty() && !noSniMatcher) { - // 'null' indicates none has been set - params.setSNIMatchers(null); - } else { - params.setSNIMatchers(sniMatchers); - } - - if (serverNames.isEmpty() && !noSniExtension) { - // 'null' indicates none has been set - params.setServerNames(null); - } else { - params.setServerNames(serverNames); - } - - params.setUseCipherSuitesOrder(preferLocalCipherSuites); - params.setMaximumPacketSize(maximumPacketSize); - params.setApplicationProtocols(applicationProtocols); - - // DTLS handshake retransmissions parameter does not apply here. - - return params; - } + public void shutdown() throws IOException { + if (!isClosed()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("close the underlying socket"); + } - /** - * Applies SSLParameters to this socket. - */ - @Override - public synchronized void setSSLParameters(SSLParameters params) { - super.setSSLParameters(params); - - // the super implementation does not handle the following parameters - identificationProtocol = params.getEndpointIdentificationAlgorithm(); - algorithmConstraints = params.getAlgorithmConstraints(); - preferLocalCipherSuites = params.getUseCipherSuitesOrder(); - maximumPacketSize = params.getMaximumPacketSize(); - - // DTLS handshake retransmissions parameter does not apply here. - - if (maximumPacketSize != 0) { - outputRecord.changePacketSize(maximumPacketSize); - } else { - // use the implicit maximum packet size. - maximumPacketSize = outputRecord.getMaxPacketSize(); - } - - List<SNIServerName> sniNames = params.getServerNames(); - if (sniNames != null) { - noSniExtension = sniNames.isEmpty(); - serverNames = sniNames; - } - - Collection<SNIMatcher> matchers = params.getSNIMatchers(); - if (matchers != null) { - noSniMatcher = matchers.isEmpty(); - sniMatchers = matchers; - } - - applicationProtocols = params.getApplicationProtocols(); - - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setIdentificationProtocol(identificationProtocol); - handshaker.setAlgorithmConstraints(algorithmConstraints); - handshaker.setMaximumPacketSize(maximumPacketSize); - handshaker.setApplicationProtocols(applicationProtocols); - if (roleIsServer) { - handshaker.setSNIMatchers(sniMatchers); - handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); - } else { - handshaker.setSNIServerNames(serverNames); + try { + if (conContext.isInputCloseNotified) { + // Close the connection, no wait for more peer response. + closeSocket(false); + } else { + // Close the connection, may wait for peer close_notify. + closeSocket(true); + } + } finally { + tlsIsClosed = true; } } } - - @Override - public synchronized String getApplicationProtocol() { - return applicationProtocol; - } - - @Override - public synchronized String getHandshakeApplicationProtocol() { - if ((handshaker != null) && handshaker.started()) { - return handshaker.getHandshakeApplicationProtocol(); - } - return null; - } - - @Override - public synchronized void setHandshakeApplicationProtocolSelector( - BiFunction<SSLSocket, List<String>, String> selector) { - applicationProtocolSelector = selector; - if ((handshaker != null) && !handshaker.activated()) { - handshaker.setApplicationProtocolSelectorSSLSocket(selector); - } - } - - @Override - public synchronized BiFunction<SSLSocket, List<String>, String> - getHandshakeApplicationProtocolSelector() { - return this.applicationProtocolSelector; - } - - // - // We allocate a separate thread to deliver handshake completion - // events. This ensures that the notifications don't block the - // protocol state machine. - // - private static class NotifyHandshake implements Runnable { - - private Set<Map.Entry<HandshakeCompletedListener,AccessControlContext>> - targets; // who gets notified - private HandshakeCompletedEvent event; // the notification - - NotifyHandshake( - Set<Map.Entry<HandshakeCompletedListener,AccessControlContext>> - entrySet, HandshakeCompletedEvent e) { - - targets = new HashSet<>(entrySet); // clone the entry set - event = e; - } - - @Override - public void run() { - // Don't need to synchronize, as it only runs in one thread. - for (Map.Entry<HandshakeCompletedListener,AccessControlContext> - entry : targets) { - - final HandshakeCompletedListener l = entry.getKey(); - AccessControlContext acc = entry.getValue(); - AccessController.doPrivileged(new PrivilegedAction<Void>() { - @Override - public Void run() { - l.handshakeCompleted(event); - return null; - } - }, acc); - } - } - } - - /** - * Returns a printable representation of this end of the connection. - */ - @Override - public String toString() { - StringBuilder retval = new StringBuilder(80); - - retval.append(Integer.toHexString(hashCode())); - retval.append("["); - retval.append(sess.getCipherSuite()); - retval.append(": "); - - retval.append(super.toString()); - retval.append("]"); - - return retval.toString(); - } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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,15 +25,21 @@ package sun.security.ssl; -import java.io.*; -import java.nio.*; - +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.ArrayList; import javax.crypto.BadPaddingException; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLProtocolException; -import javax.net.ssl.*; - -import sun.security.util.HexDumpEncoder; - +import sun.security.ssl.SSLCipher.SSLReadCipher; +import sun.security.ssl.KeyUpdate.KeyUpdateMessage; +import sun.security.ssl.KeyUpdate.KeyUpdateRequest; /** * {@code InputRecord} implementation for {@code SSLSocket}. @@ -41,30 +47,34 @@ * @author David Brownell */ final class SSLSocketInputRecord extends InputRecord implements SSLRecord { - private OutputStream deliverStream = null; - private byte[] temporary = new byte[1024]; - - // used by handshake hash computation for handshake fragment - private byte prevType = -1; - private int hsMsgOff = 0; - private int hsMsgLen = 0; + private InputStream is = null; + private OutputStream os = null; + private final byte[] temporary = new byte[1024]; private boolean formatVerified = false; // SSLv2 ruled out? + // Cache for incomplete handshake messages. + private ByteBuffer handshakeBuffer = null; + private boolean hasHeader = false; // Had read the record header - SSLSocketInputRecord() { - this.readAuthenticator = MAC.TLS_NULL; + SSLSocketInputRecord(HandshakeHash handshakeHash) { + super(handshakeHash, SSLReadCipher.nullTlsReadCipher()); } @Override - int bytesInCompletePacket(InputStream is) throws IOException { - + int bytesInCompletePacket() throws IOException { if (!hasHeader) { // read exactly one record - int really = read(is, temporary, 0, headerSize); - if (really < 0) { - throw new EOFException("SSL peer shut down incorrectly"); + try { + int really = read(is, temporary, 0, headerSize); + if (really < 0) { + // EOF: peer shut down incorrectly + return -1; + } + } catch (EOFException eofe) { + // The caller will handle EOF. + return -1; } hasHeader = true; } @@ -75,19 +85,21 @@ /* * If we have already verified previous packets, we can * ignore the verifications steps, and jump right to the - * determination. Otherwise, try one last hueristic to + * determination. Otherwise, try one last heuristic to * see if it's SSL/TLS. */ if (formatVerified || - (byteZero == ct_handshake) || (byteZero == ct_alert)) { + (byteZero == ContentType.HANDSHAKE.id) || + (byteZero == ContentType.ALERT.id)) { /* * Last sanity check that it's not a wild record */ - ProtocolVersion recordVersion = - ProtocolVersion.valueOf(temporary[1], temporary[2]); - - // check the record version - checkRecordVersion(recordVersion, false); + if (!ProtocolVersion.isNegotiable( + temporary[1], temporary[2], false, false)) { + throw new SSLException("Unrecognized record version " + + ProtocolVersion.nameOf(temporary[1], temporary[2]) + + " , plaintext connection?"); + } /* * Reasonably sure this is a V3, disable further checks. @@ -112,11 +124,12 @@ boolean isShort = ((byteZero & 0x80) != 0); if (isShort && ((temporary[2] == 1) || (temporary[2] == 4))) { - ProtocolVersion recordVersion = - ProtocolVersion.valueOf(temporary[3], temporary[4]); - - // check the record version - checkRecordVersion(recordVersion, true); + if (!ProtocolVersion.isNegotiable( + temporary[3], temporary[4], false, false)) { + throw new SSLException("Unrecognized record version " + + ProtocolVersion.nameOf(temporary[3], temporary[4]) + + " , plaintext connection?"); + } /* * Client or Server Hello @@ -140,10 +153,10 @@ return len; } - // destination.position() is zero. + // Note that the input arguments are not used actually. @Override - Plaintext decode(InputStream is, ByteBuffer destination) - throws IOException, BadPaddingException { + Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, + int srcsLength) throws IOException, BadPaddingException { if (isClosed) { return null; @@ -167,36 +180,44 @@ * alert message. If it's not, it is either invalid or an * SSLv2 message. */ - if ((temporary[0] != ct_handshake) && - (temporary[0] != ct_alert)) { - - plaintext = handleUnknownRecord(is, temporary, destination); + if ((temporary[0] != ContentType.HANDSHAKE.id) && + (temporary[0] != ContentType.ALERT.id)) { + hasHeader = false; + return handleUnknownRecord(temporary); } } - if (plaintext == null) { - plaintext = decodeInputRecord(is, temporary, destination); - } + // The record header should has consumed. + hasHeader = false; + return decodeInputRecord(temporary); + } - // The record header should has comsumed. - hasHeader = false; - - return plaintext; + @Override + void setReceiverStream(InputStream inputStream) { + this.is = inputStream; } @Override void setDeliverStream(OutputStream outputStream) { - this.deliverStream = outputStream; + this.os = outputStream; } // Note that destination may be null - private Plaintext decodeInputRecord(InputStream is, byte[] header, - ByteBuffer destination) throws IOException, BadPaddingException { + private Plaintext[] decodeInputRecord( + byte[] header) throws IOException, BadPaddingException { + byte contentType = header[0]; // pos: 0 + byte majorVersion = header[1]; // pos: 1 + byte minorVersion = header[2]; // pos: 2 + int contentLen = ((header[3] & 0xFF) << 8) + + (header[4] & 0xFF); // pos: 3, 4 - byte contentType = header[0]; - byte majorVersion = header[1]; - byte minorVersion = header[2]; - int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF); + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "READ: " + + ProtocolVersion.nameOf(majorVersion, minorVersion) + + " " + ContentType.nameOf(contentType) + ", length = " + + contentLen); + } // // Check for upper bound. @@ -210,10 +231,7 @@ // // Read a complete record. // - if (destination == null) { - destination = ByteBuffer.allocate(headerSize + contentLen); - } // Otherwise, the destination buffer should have enough room. - + ByteBuffer destination = ByteBuffer.allocate(headerSize + contentLen); int dstPos = destination.position(); destination.put(temporary, 0, headerSize); while (contentLen > 0) { @@ -229,100 +247,129 @@ destination.flip(); destination.position(dstPos + headerSize); - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", 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 = " + destination.remaining()); } // // Decrypt the fragment // - ByteBuffer plaintext = - decrypt(readAuthenticator, readCipher, contentType, destination); + ByteBuffer fragment; + try { + Plaintext plaintext = + readCipher.decrypt(contentType, destination, null); + fragment = plaintext.fragment; + contentType = plaintext.contentType; + } catch (BadPaddingException bpe) { + throw bpe; + } catch (GeneralSecurityException gse) { + throw (SSLProtocolException)(new SSLProtocolException( + "Unexpected exception")).initCause(gse); + } - if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) { + if (contentType != ContentType.HANDSHAKE.id && + handshakeBuffer != null && handshakeBuffer.hasRemaining()) { throw new SSLProtocolException( - "Expected to get a handshake fragment"); + "Expecting a handshake fragment, but received " + + ContentType.nameOf(contentType)); } // - // handshake hashing + // parse handshake messages // - if (contentType == ct_handshake) { - int pltPos = plaintext.position(); - int pltLim = plaintext.limit(); - int frgPos = pltPos; - for (int remains = plaintext.remaining(); remains > 0;) { - int howmuch; - byte handshakeType; - if (hsMsgOff < hsMsgLen) { - // a fragment of the handshake message - howmuch = Math.min((hsMsgLen - hsMsgOff), remains); - handshakeType = prevType; + if (contentType == ContentType.HANDSHAKE.id) { + ByteBuffer handshakeFrag = fragment; + if ((handshakeBuffer != null) && + (handshakeBuffer.remaining() != 0)) { + ByteBuffer bb = ByteBuffer.wrap(new byte[ + handshakeBuffer.remaining() + fragment.remaining()]); + bb.put(handshakeBuffer); + bb.put(fragment); + handshakeFrag = bb.rewind(); + handshakeBuffer = null; + } - hsMsgOff += howmuch; - if (hsMsgOff == hsMsgLen) { - // Now is a complete handshake message. - hsMsgOff = 0; - hsMsgLen = 0; - } - } else { // hsMsgOff == hsMsgLen, a new handshake message - handshakeType = plaintext.get(); - int handshakeLen = ((plaintext.get() & 0xFF) << 16) | - ((plaintext.get() & 0xFF) << 8) | - (plaintext.get() & 0xFF); - plaintext.position(frgPos); - if (remains < (handshakeLen + 1)) { // 1: handshake type - // This handshake message is fragmented. - prevType = handshakeType; - hsMsgOff = remains - 4; // 4: handshake header - hsMsgLen = handshakeLen; - } - - howmuch = Math.min(handshakeLen + 4, remains); + ArrayList<Plaintext> plaintexts = new ArrayList<>(5); + while (handshakeFrag.hasRemaining()) { + int remaining = handshakeFrag.remaining(); + if (remaining < handshakeHeaderSize) { + handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); + handshakeBuffer.put(handshakeFrag); + handshakeBuffer.rewind(); + break; } - plaintext.limit(frgPos + howmuch); + handshakeFrag.mark(); + // skip the first byte: handshake type + byte handshakeType = handshakeFrag.get(); + int handshakeBodyLen = Record.getInt24(handshakeFrag); + handshakeFrag.reset(); + int handshakeMessageLen = + handshakeHeaderSize + handshakeBodyLen; + if (remaining < handshakeMessageLen) { + handshakeBuffer = ByteBuffer.wrap(new byte[remaining]); + handshakeBuffer.put(handshakeFrag); + handshakeBuffer.rewind(); + break; + } if (remaining == handshakeMessageLen) { + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.receive(handshakeFrag); + } - if (handshakeType == HandshakeMessage.ht_hello_request) { - // omitted from handshake hash computation - } else if ((handshakeType != HandshakeMessage.ht_finished) && - (handshakeType != HandshakeMessage.ht_certificate_verify)) { - - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); - } - handshakeHash.update(plaintext); + plaintexts.add( + new Plaintext(contentType, + majorVersion, minorVersion, -1, -1L, handshakeFrag) + ); + break; } else { - // Reserve until this handshake message has been processed. - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); + int fragPos = handshakeFrag.position(); + int fragLim = handshakeFrag.limit(); + int nextPos = fragPos + handshakeMessageLen; + handshakeFrag.limit(nextPos); + + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.receive(handshakeFrag); } - handshakeHash.reserve(plaintext); - } - plaintext.position(frgPos + howmuch); - plaintext.limit(pltLim); + plaintexts.add( + new Plaintext(contentType, majorVersion, minorVersion, + -1, -1L, handshakeFrag.slice()) + ); - frgPos += howmuch; - remains -= howmuch; + handshakeFrag.position(nextPos); + handshakeFrag.limit(fragLim); + } } - plaintext.position(pltPos); + + return plaintexts.toArray(new Plaintext[0]); } - return new Plaintext(contentType, - majorVersion, minorVersion, -1, -1L, plaintext); - // recordEpoch, recordSeq, plaintext); + // KeyLimit check during application data. + // atKeyLimit() inactive when limits not checked, tc set when limits + // are active. + + if (readCipher.atKeyLimit()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyUpdate: triggered, read side."); + } + + PostHandshakeContext p = new PostHandshakeContext(tc); + KeyUpdate.handshakeProducer.produce(p, + new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); + } + + return new Plaintext[] { + new Plaintext(contentType, + majorVersion, minorVersion, -1, -1L, fragment) + }; } - private Plaintext handleUnknownRecord(InputStream is, byte[] header, - ByteBuffer destination) throws IOException, BadPaddingException { - + private Plaintext[] handleUnknownRecord( + byte[] header) throws IOException, BadPaddingException { byte firstByte = header[0]; byte thirdByte = header[2]; @@ -347,19 +394,16 @@ * error message, one that's treated as fatal by * clients (Otherwise we'll hang.) */ - deliverStream.write(SSLRecord.v2NoCipher); // SSLv2Hello + os.write(SSLRecord.v2NoCipher); // SSLv2Hello - if (debug != null) { - if (Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + + if (SSLLogger.isOn) { + if (SSLLogger.isOn("record")) { + SSLLogger.fine( "Requested to negotiate unsupported SSLv2!"); } - if (Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + - SSLRecord.v2NoCipher.length, - SSLRecord.v2NoCipher); + if (SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", SSLRecord.v2NoCipher); } } @@ -368,9 +412,7 @@ int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF); - if (destination == null) { - destination = ByteBuffer.allocate(headerSize + msgLen); - } + ByteBuffer destination = ByteBuffer.allocate(headerSize + msgLen); destination.put(temporary, 0, headerSize); msgLen -= 3; // had read 3 bytes of content as header while (msgLen > 0) { @@ -391,23 +433,20 @@ * V3 ClientHello message, and pass it up. */ destination.position(2); // exclude the header - - if (handshakeHash == null) { - // used for cache only - handshakeHash = new HandshakeHash(false); - } - handshakeHash.update(destination); + handshakeHash.receive(destination); destination.position(0); ByteBuffer converted = convertToClientHello(destination); - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine( "[Converted] ClientHello", converted); } - return new Plaintext(ct_handshake, - majorVersion, minorVersion, -1, -1L, converted); + return new Plaintext[] { + new Plaintext(ContentType.HANDSHAKE.id, + majorVersion, minorVersion, -1, -1L, converted) + }; } else { if (((firstByte & 0x80) != 0) && (thirdByte == 4)) { throw new SSLException("SSL V2.0 servers are not supported."); @@ -424,13 +463,15 @@ while (n < len) { int readLen = is.read(buffer, offset + n, len - n); if (readLen < 0) { + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw read: EOF"); + } return -1; } - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw read]: length = " + readLen, - buffer, offset + n, readLen); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + ByteBuffer bb = ByteBuffer.wrap(buffer, offset + n, readLen); + SSLLogger.fine("Raw read", bb); } n += readLen; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketOutputRecord.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -25,14 +25,14 @@ package sun.security.ssl; -import java.io.*; -import java.nio.*; -import java.util.Arrays; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import sun.security.util.HexDumpEncoder; - +import sun.security.ssl.KeyUpdate.KeyUpdateMessage; +import sun.security.ssl.KeyUpdate.KeyUpdateRequest; /** * {@code OutputRecord} implementation for {@code SSLSocket}. @@ -40,11 +40,16 @@ final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord { private OutputStream deliverStream = null; - SSLSocketOutputRecord() { - this.writeAuthenticator = MAC.TLS_NULL; + SSLSocketOutputRecord(HandshakeHash handshakeHash) { + this(handshakeHash, null); + } + SSLSocketOutputRecord(HandshakeHash handshakeHash, + TransportContext tc) { + super(handshakeHash, SSLCipher.SSLWriteCipher.nullTlsWriteCipher()); + this.tc = tc; this.packetSize = SSLRecord.maxRecordSize; - this.protocolVersion = ProtocolVersion.DEFAULT_TLS; + this.protocolVersion = ProtocolVersion.NONE; } @Override @@ -55,25 +60,22 @@ write(level); write(description); - - 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 = " + (count - headerSize)); } // Encrypt the fragment and wrap up a record. - encrypt(writeAuthenticator, writeCipher, - Record.ct_alert, headerSize); + encrypt(writeCipher, ContentType.ALERT.id, headerSize); // deliver this message deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, buf, 0, count); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(buf, 0, count))); } // reset the internal buffer @@ -83,12 +85,11 @@ @Override void encodeHandshake(byte[] source, int offset, int length) throws IOException { - if (firstMessage) { firstMessage = false; if ((helloVersion == ProtocolVersion.SSL20Hello) && - (source[offset] == HandshakeMessage.ht_client_hello) && + (source[offset] == SSLHandshake.CLIENT_HELLO.id) && // 5: recode header size (source[offset + 4 + 2 + 32] == 0)) { // V3 session ID is empty @@ -101,12 +102,12 @@ byte[] record = v2ClientHello.array(); // array offset is zero int limit = v2ClientHello.limit(); - handshakeHash.update(record, 2, (limit - 2)); + handshakeHash.deliver(record, 2, (limit - 2)); - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", WRITE: SSLv2 ClientHello message" + - ", length = " + limit); + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "WRITE: SSLv2 ClientHello message" + + ", length = " + limit); } // deliver this message @@ -117,9 +118,9 @@ deliverStream.write(record, 0, limit); deliverStream.flush(); - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, record, 0, limit); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(record, 0, limit))); } return; @@ -127,8 +128,8 @@ } byte handshakeType = source[0]; - if (handshakeType != HandshakeMessage.ht_hello_request) { - handshakeHash.update(source, offset, length); + if (handshakeHash.isHashable(handshakeType)) { + handshakeHash.deliver(source, offset, length); } int fragLimit = getFragLimit(); @@ -153,24 +154,23 @@ return; } - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", WRITE: " + protocolVersion + - " " + Record.contentName(Record.ct_handshake) + + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "WRITE: " + protocolVersion + + " " + ContentType.HANDSHAKE.name + ", length = " + (count - headerSize)); } // Encrypt the fragment and wrap up a record. - encrypt(writeAuthenticator, writeCipher, - Record.ct_handshake, headerSize); + encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); // deliver this message deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, buf, 0, count); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(buf, 0, count))); } // reset the offset @@ -190,24 +190,16 @@ write((byte)1); // byte 1: change_cipher_spec( - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", WRITE: " + protocolVersion + - " " + Record.contentName(Record.ct_change_cipher_spec) + - ", length = " + (count - headerSize)); - } - // Encrypt the fragment and wrap up a record. - encrypt(writeAuthenticator, writeCipher, - Record.ct_change_cipher_spec, headerSize); + encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize); // deliver this message deliverStream.write(buf, 0, count); // may throw IOException // deliverStream.flush(); // flush in Finished - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, buf, 0, count); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(buf, 0, count))); } // reset the internal buffer @@ -221,24 +213,23 @@ return; } - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", WRITE: " + protocolVersion + - " " + Record.contentName(Record.ct_handshake) + + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "WRITE: " + protocolVersion + + " " + ContentType.HANDSHAKE.name + ", length = " + (count - headerSize)); } // Encrypt the fragment and wrap up a record. - encrypt(writeAuthenticator, writeCipher, - Record.ct_handshake, headerSize); + encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); // deliver this message deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, buf, 0, count); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(buf, 0, count))); } // reset the internal buffer @@ -247,11 +238,10 @@ @Override void deliver(byte[] source, int offset, int length) 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."); } @@ -260,16 +250,11 @@ boolean isFirstRecordOfThePayload = true; for (int limit = (offset + length); offset < limit;) { - int macLen = 0; - if (writeAuthenticator instanceof MAC) { - macLen = ((MAC)writeAuthenticator).MAClen(); - } - int fragLen; if (packetSize > 0) { fragLen = Math.min(maxRecordSize, packetSize); - fragLen = writeCipher.calculateFragmentSize( - fragLen, macLen, headerSize); + fragLen = + writeCipher.calculateFragmentSize(fragLen, headerSize); fragLen = Math.min(fragLen, Record.maxDataSize); } else { @@ -292,24 +277,23 @@ count = position; write(source, offset, fragLen); - if (debug != null && Debug.isOn("record")) { - System.out.println(Thread.currentThread().getName() + - ", WRITE: " + protocolVersion + - " " + Record.contentName(Record.ct_application_data) + - ", length = " + (count - headerSize)); + if (SSLLogger.isOn && SSLLogger.isOn("record")) { + SSLLogger.fine( + "WRITE: " + protocolVersion + + " " + ContentType.APPLICATION_DATA.name + + ", length = " + (count - position)); } // Encrypt the fragment and wrap up a record. - encrypt(writeAuthenticator, writeCipher, - Record.ct_application_data, headerSize); + encrypt(writeCipher, ContentType.APPLICATION_DATA.id, headerSize); // deliver this message deliverStream.write(buf, 0, count); // may throw IOException deliverStream.flush(); // may throw IOException - if (debug != null && Debug.isOn("packet")) { - Debug.printHex( - "[Raw write]: length = " + count, buf, 0, count); + if (SSLLogger.isOn && SSLLogger.isOn("packet")) { + SSLLogger.fine("Raw write", + (new ByteArrayInputStream(buf, 0, count))); } // reset the internal buffer @@ -320,6 +304,18 @@ } offset += fragLen; + + // atKeyLimit() inactive when limits not checked, tc set when limits + // are active. + if (writeCipher.atKeyLimit()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine("KeyUpdate: triggered, write side."); + } + + PostHandshakeContext p = new PostHandshakeContext(tc); + KeyUpdate.handshakeProducer.produce(p, + new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); + } } } @@ -358,16 +354,11 @@ } private int getFragLimit() { - int macLen = 0; - if (writeAuthenticator instanceof MAC) { - macLen = ((MAC)writeAuthenticator).MAClen(); - } - int fragLimit; if (packetSize > 0) { fragLimit = Math.min(maxRecordSize, packetSize); - fragLimit = writeCipher.calculateFragmentSize( - fragLimit, macLen, headerSize); + fragLimit = + writeCipher.calculateFragmentSize(fragLimit, headerSize); fragLimit = Math.min(fragLimit, Record.maxDataSize); } else { diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLStringizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLStringizer.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,41 @@ +/* + * 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.nio.ByteBuffer; + +/** + * Interface to decode a {@code ByteBuffer} into legible {@code String}. + */ +interface SSLStringizer { + /** + * Returns a legible string representation of a {@code ByteBuffer}. + * + * Note that the implementation MUST not change the internal status of + * the {@code buffer}. + */ + String toString(ByteBuffer buffer); +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLTrafficKeyDerivation.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.ProviderException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.SSLHandshakeException; +import sun.security.internal.spec.TlsKeyMaterialParameterSpec; +import sun.security.internal.spec.TlsKeyMaterialSpec; +import sun.security.ssl.CipherSuite.HashAlg; +import static sun.security.ssl.CipherSuite.HashAlg.H_NONE; + +enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator { + SSL30 ("kdf_ssl30", new S30TrafficKeyDerivationGenerator()), + TLS10 ("kdf_tls10", new T10TrafficKeyDerivationGenerator()), + TLS12 ("kdf_tls12", new T12TrafficKeyDerivationGenerator()), + TLS13 ("kdf_tls13", new T13TrafficKeyDerivationGenerator()); + + final String name; + final SSLKeyDerivationGenerator keyDerivationGenerator; + + SSLTrafficKeyDerivation(String name, + SSLKeyDerivationGenerator keyDerivationGenerator) { + this.name = name; + this.keyDerivationGenerator = keyDerivationGenerator; + } + + static SSLTrafficKeyDerivation valueOf(ProtocolVersion protocolVersion) { + switch (protocolVersion) { + case SSL30: + return SSLTrafficKeyDerivation.SSL30; + case TLS10: + case TLS11: + case DTLS10: + return SSLTrafficKeyDerivation.TLS10; + case TLS12: + case DTLS12: + return SSLTrafficKeyDerivation.TLS12; + case TLS13: + return SSLTrafficKeyDerivation.TLS13; + } + + return null; + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext context, + SecretKey secretKey) throws IOException { + return keyDerivationGenerator.createKeyDerivation(context, secretKey); + } + + private static final class S30TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private S30TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T10TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T10TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T12TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T12TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, SecretKey secretKey) throws IOException { + return new LegacyTrafficKeyDerivation(context, secretKey); + } + } + + private static final class T13TrafficKeyDerivationGenerator + implements SSLKeyDerivationGenerator { + private T13TrafficKeyDerivationGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context, + SecretKey secretKey) throws IOException { + return new T13TrafficKeyDerivation(context, secretKey); + } + } + + static final class T13TrafficKeyDerivation implements SSLKeyDerivation { + private final CipherSuite cs; + private final SecretKey secret; + + T13TrafficKeyDerivation( + HandshakeContext context, SecretKey secret) { + this.secret = secret; + this.cs = context.negotiatedCipherSuite; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + KeySchedule ks = KeySchedule.valueOf(algorithm); + try { + HKDF hkdf = new HKDF(cs.hashAlg.name); + byte[] hkdfInfo = + createHkdfInfo(ks.label, ks.getKeyLength(cs)); + return hkdf.expand(secret, hkdfInfo, + ks.getKeyLength(cs), + ks.getAlgorithm(cs, algorithm)); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException)(new SSLHandshakeException( + "Could not generate secret").initCause(gse)); + } + } + + private static byte[] createHkdfInfo( + byte[] label, int length) throws IOException { + byte[] info = new byte[4 + label.length]; + ByteBuffer m = ByteBuffer.wrap(info); + try { + Record.putInt16(m, length); + Record.putBytes8(m, label); + Record.putInt8(m, 0x00); // zero-length context + } catch (IOException ioe) { + // unlikely + throw new RuntimeException("Unexpected exception", ioe); + } + + return info; + } + } + + private enum KeySchedule { + // Note that we use enum name as the key/ name. + TlsKey ("key", false), + TlsIv ("iv", true), + TlsUpdateNplus1 ("traffic upd", false); + + private final byte[] label; + private final boolean isIv; + + private KeySchedule(String label, boolean isIv) { + this.label = ("tls13 " + label).getBytes(); + this.isIv = isIv; + } + + int getKeyLength(CipherSuite cs) { + if (this == KeySchedule.TlsUpdateNplus1) + return cs.hashAlg.hashLength; + return isIv ? cs.bulkCipher.ivSize : cs.bulkCipher.keySize; + } + + String getAlgorithm(CipherSuite cs, String algorithm) { + return isIv ? algorithm : cs.bulkCipher.algorithm; + } + } + + @SuppressWarnings("deprecation") + static final class LegacyTrafficKeyDerivation implements SSLKeyDerivation { + private final HandshakeContext context; + private final SecretKey masterSecret; + private final TlsKeyMaterialSpec keyMaterialSpec; + + LegacyTrafficKeyDerivation( + HandshakeContext context, SecretKey masterSecret) { + this.context = context; + this.masterSecret = masterSecret; + + CipherSuite cipherSuite = context.negotiatedCipherSuite; + ProtocolVersion protocolVersion = context.negotiatedProtocol; + + /* + * For both the read and write sides of the protocol, we use the + * master to generate MAC secrets and cipher keying material. Block + * ciphers need initialization vectors, which we also generate. + * + * First we figure out how much keying material is needed. + */ + int hashSize = cipherSuite.macAlg.size; + boolean is_exportable = cipherSuite.exportable; + SSLCipher cipher = cipherSuite.bulkCipher; + int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; + + // Which algs/params do we need to use? + String keyMaterialAlg; + HashAlg hashAlg; + + byte majorVersion = protocolVersion.major; + byte minorVersion = protocolVersion.minor; + if (protocolVersion.isDTLS) { + // Use TLS version number for DTLS key calculation + if (protocolVersion.id == ProtocolVersion.DTLS10.id) { + majorVersion = ProtocolVersion.TLS11.major; + minorVersion = ProtocolVersion.TLS11.minor; + + keyMaterialAlg = "SunTlsKeyMaterial"; + hashAlg = H_NONE; + } else { // DTLS 1.2+ + majorVersion = ProtocolVersion.TLS12.major; + minorVersion = ProtocolVersion.TLS12.minor; + + keyMaterialAlg = "SunTls12KeyMaterial"; + hashAlg = cipherSuite.hashAlg; + } + } else { + if (protocolVersion.id >= ProtocolVersion.TLS12.id) { + keyMaterialAlg = "SunTls12KeyMaterial"; + hashAlg = cipherSuite.hashAlg; + } else { + keyMaterialAlg = "SunTlsKeyMaterial"; + hashAlg = H_NONE; + } + } + + // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to + // protect against the CBC attacks. AEAD/GCM cipher suites in + // TLS v1.2 or later use a fixed IV as the implicit part of the + // partially implicit nonce technique described in RFC 5116. + int ivSize = cipher.ivSize; + if (cipher.cipherType == CipherType.AEAD_CIPHER) { + ivSize = cipher.fixedIvSize; + } else if ( + cipher.cipherType == CipherType.BLOCK_CIPHER && + protocolVersion.useTLS11PlusSpec()) { + ivSize = 0; + } + + TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( + masterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF), + context.clientHelloRandom.randomBytes, + context.serverHelloRandom.randomBytes, + cipher.algorithm, cipher.keySize, expandedKeySize, + ivSize, hashSize, + hashAlg.name, hashAlg.hashLength, hashAlg.blockSize); + + try { + KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); + kg.init(spec); + + this.keyMaterialSpec = (TlsKeyMaterialSpec)kg.generateKey(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + SecretKey getTrafficKey(String algorithm) { + switch (algorithm) { + case "clientMacKey": + return keyMaterialSpec.getClientMacKey(); + case "serverMacKey": + return keyMaterialSpec.getServerMacKey(); + case "clientWriteKey": + return keyMaterialSpec.getClientCipherKey(); + case "serverWriteKey": + return keyMaterialSpec.getServerCipherKey(); + case "clientWriteIv": + IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv(); + return (cliIvSpec == null) ? null : + new SecretKeySpec(cliIvSpec.getIV(), "TlsIv"); + case "serverWriteIv": + IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv(); + return (srvIvSpec == null) ? null : + new SecretKeySpec(srvIvSpec.getIV(), "TlsIv"); + } + + return null; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + return getTrafficKey(algorithm); + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SSLTransport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SSLTransport.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,204 @@ +/* + * 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.EOFException; +import java.io.IOException; +import java.nio.ByteBuffer; +import javax.crypto.BadPaddingException; +import javax.net.ssl.SSLHandshakeException; + +/** + * Interface for SSL/(D)TLS transportation. + */ +interface SSLTransport { + + /** + * Returns the host name of the peer. + * + * @return the host name of the peer, or null if nothing is + * available. + */ + String getPeerHost(); + + /** + * Returns the port number of the peer. + * + * @return the port number of the peer, or -1 if nothing is + * available. + */ + int getPeerPort(); + + /** + * Shutdown the transport. + */ + default void shutdown() throws IOException { + // blank + } + + /** + * Return true if delegated tasks used for handshaking operations. + * + * @return true if delegated tasks used for handshaking operations. + */ + boolean useDelegatedTask(); + + /** + * Decodes an array of SSL/(D)TLS network source data into the + * destination application data buffers. + * + * For SSL/TLS connections, if no source data, the network data may be + * received from the underlying underlying SSL/TLS input stream. + * + * @param context the transportation context + * @param srcs an array of {@code ByteBuffers} containing the + * inbound network data + * @param srcsOffset The offset within the {@code srcs} buffer array + * of the first buffer from which bytes are to be + * retrieved; it must be non-negative and no larger + * than {@code srcs.length}. + * @param srcsLength The maximum number of {@code srcs} buffers to be + * accessed; it must be non-negative and no larger than + * {@code srcs.length}&nbsp;-&nbsp;{@code srcsOffset}. + * @param dsts an array of {@code ByteBuffers} to hold inbound + * application data + * @param dstsOffset The offset within the {@code dsts} buffer array + * of the first buffer from which bytes are to be + * placed; it must be non-negative and no larger + * than {@code dsts.length}. + * @param dstsLength The maximum number of {@code dsts} buffers to be + * accessed; it must be non-negative and no larger than + * {@code dsts.length}&nbsp;-&nbsp;{@code dstsOffset}. + * + * @return a {@code Plaintext} describing the result of + * the operation + * @throws IOException if a problem was encountered while receiving or + * decoding networking data + */ + static Plaintext decode(TransportContext context, + ByteBuffer[] srcs, int srcsOffset, int srcsLength, + ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException { + + Plaintext[] plaintexts = null; + try { + plaintexts = + context.inputRecord.decode(srcs, srcsOffset, srcsLength); + } catch (UnsupportedOperationException unsoe) { // SSLv2Hello + // Hack code to deliver SSLv2 error message for SSL/TLS connections. + if (!context.sslContext.isDTLS()) { + context.outputRecord.encodeV2NoCipher(); + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("may be talking to SSLv2"); + } + } + + context.fatal(Alert.UNEXPECTED_MESSAGE, unsoe); + } catch (BadPaddingException bpe) { + /* + * The basic SSLv3 record protection involves (optional) + * encryption for privacy, and an integrity check ensuring + * data origin authentication. We do them both here, and + * throw a fatal alert if the integrity check fails. + */ + Alert alert = (context.handshakeContext != null) ? + Alert.HANDSHAKE_FAILURE : + Alert.BAD_RECORD_MAC; + context.fatal(alert, bpe); + } catch (SSLHandshakeException she) { + // may be record sequence number overflow + context.fatal(Alert.HANDSHAKE_FAILURE, she); + } catch (EOFException eofe) { + // rethrow EOFException, the call will handle it if neede. + throw eofe; + } catch (IOException ioe) { + context.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + } + + if (plaintexts == null || plaintexts.length == 0) { + // Connection closed or record should be discarded. + return Plaintext.PLAINTEXT_NULL; + } + + Plaintext finalPlaintext = Plaintext.PLAINTEXT_NULL; + for (Plaintext plainText : plaintexts) { + // plainText should never be null for TLS protocols + if (plainText == Plaintext.PLAINTEXT_NULL) { + // Only happens for DTLS protocols. + // + // Received a retransmitted flight, and need to retransmit the + // previous delivered handshake flight messages. + if (context.handshakeContext != null && + context.handshakeContext.sslConfig.enableRetransmissions && + context.sslContext.isDTLS()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) { + SSLLogger.finest("retransmited handshake flight"); + } + + context.outputRecord.launchRetransmission(); + } // Otherwise, discard the retransmitted flight. + } else if (plainText != null && + plainText.contentType != ContentType.APPLICATION_DATA.id) { + context.dispatch(plainText); + } + + if (plainText == null) { + plainText = Plaintext.PLAINTEXT_NULL; + } else { + // File the destination buffers. + if (dsts != null && dstsLength > 0 && + plainText.contentType == ContentType.APPLICATION_DATA.id) { + + ByteBuffer fragment = plainText.fragment; + int remains = fragment.remaining(); + + // Should have enough room in the destination buffers. + int limit = dstsOffset + dstsLength; + for (int i = dstsOffset; + ((i < limit) && (remains > 0)); i++) { + + int amount = Math.min(dsts[i].remaining(), remains); + fragment.limit(fragment.position() + amount); + dsts[i].put(fragment); + remains -= amount; + + if (!dsts[i].hasRemaining()) { + dstsOffset++; + } + } + + if (remains > 0) { + context.fatal(Alert.INTERNAL_ERROR, + "no sufficient room in the destination buffers"); + } + } + } + + finalPlaintext = plainText; + } + + return finalPlaintext; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,82 @@ +/* + * 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.AccessController; +import sun.security.util.LegacyAlgorithmConstraints; +import sun.security.action.GetLongAction; + +class ServerHandshakeContext extends HandshakeContext { + // To prevent the TLS renegotiation issues, by setting system property + // "jdk.tls.rejectClientInitiatedRenegotiation" to true, applications in + // server side can disable all client initiated SSL renegotiation + // regardless of the support of TLS protocols. + // + // By default, allow client initiated renegotiation. + static final boolean rejectClientInitiatedRenego = + Utilities.getBooleanProperty( + "jdk.tls.rejectClientInitiatedRenegotiation", false); + + // legacy algorithm constraints + static final AlgorithmConstraints legacyAlgorithmConstraints = + new LegacyAlgorithmConstraints( + LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS, + new SSLAlgorithmDecomposer()); + + // temporary authentication information + SSLPossession interimAuthn; + + StatusResponseManager.StaplingParameters stapleParams; + CertificateMessage.CertificateEntry currentCertEntry; + private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; + final long statusRespTimeout; + + + ServerHandshakeContext(SSLContextImpl sslContext, + TransportContext conContext) throws IOException { + super(sslContext, conContext); + long respTimeOut = AccessController.doPrivileged( + new GetLongAction("jdk.tls.stapling.responseTimeout", + DEFAULT_STATUS_RESP_DELAY)); + statusRespTimeout = respTimeOut >= 0 ? respTimeOut : + DEFAULT_STATUS_RESP_DELAY; + handshakeConsumers.put( + SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO); + } + + @Override + void kickstart() throws IOException { + if (!conContext.isNegotiated || kickstartMessageDelivered) { + return; + } + + SSLHandshake.kickstart(this); + kickstartMessageDelivered = true; + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java --- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2344 +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.io.*; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.security.*; -import java.security.cert.*; -import java.security.interfaces.*; -import java.security.spec.ECParameterSpec; -import java.math.BigInteger; -import java.util.function.BiFunction; - -import javax.crypto.SecretKey; -import javax.net.ssl.*; - -import sun.security.action.GetLongAction; -import sun.security.util.KeyUtil; -import sun.security.util.LegacyAlgorithmConstraints; -import sun.security.action.GetPropertyAction; -import sun.security.ssl.HandshakeMessage.*; -import sun.security.ssl.CipherSuite.*; -import sun.security.ssl.SignatureAndHashAlgorithm.*; -import static sun.security.ssl.CipherSuite.KeyExchange.*; - -/** - * ServerHandshaker does the protocol handshaking from the point - * of view of a server. It is driven asychronously by handshake messages - * as delivered by the parent Handshaker class, and also uses - * common functionality (e.g. key generation) that is provided there. - * - * @author David Brownell - */ -final class ServerHandshaker extends Handshaker { - - // The default number of milliseconds the handshaker will wait for - // revocation status responses. - private static final long DEFAULT_STATUS_RESP_DELAY = 5000; - - // is the server going to require the client to authenticate? - private ClientAuthType doClientAuth; - - // our authentication info - private X509Certificate[] certs; - private PrivateKey privateKey; - - private Object serviceCreds; - - // flag to check for clientCertificateVerify message - private boolean needClientVerify = false; - - /* - * For exportable ciphersuites using non-exportable key sizes, we use - * ephemeral RSA keys. We could also do anonymous RSA in the same way - * but there are no such ciphersuites currently defined. - */ - private PrivateKey tempPrivateKey; - private PublicKey tempPublicKey; - - /* - * For anonymous and ephemeral Diffie-Hellman key exchange, we use - * ephemeral Diffie-Hellman keys. - */ - private DHCrypt dh; - - // Helper for ECDH based key exchanges - private ECDHCrypt ecdh; - - // version request by the client in its ClientHello - // we remember it for the RSA premaster secret version check - private ProtocolVersion clientRequestedVersion; - - // client supported elliptic curves - private SupportedGroupsExtension requestedGroups; - - // the preferable signature algorithm used by ServerKeyExchange message - SignatureAndHashAlgorithm preferableSignatureAlgorithm; - - // 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; - - // legacy algorithm constraints - private static final AlgorithmConstraints legacyAlgorithmConstraints = - new LegacyAlgorithmConstraints( - LegacyAlgorithmConstraints.PROPERTY_TLS_LEGACY_ALGS, - new SSLAlgorithmDecomposer()); - - private long statusRespTimeout; - - 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 can only range from 1024 to 8192 (inclusive)"); - } - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException( - "Invalid system property jdk.tls.ephemeralDHKeySize"); - } - } - } - - /* - * Constructor ... use the keys found in the auth context. - */ - ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context, - ProtocolList enabledProtocols, ClientAuthType clientAuth, - ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, - boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { - - super(socket, context, enabledProtocols, - (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); - doClientAuth = clientAuth; - statusRespTimeout = AccessController.doPrivileged( - new GetLongAction("jdk.tls.stapling.responseTimeout", - DEFAULT_STATUS_RESP_DELAY)); - statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout : - DEFAULT_STATUS_RESP_DELAY; - } - - /* - * Constructor ... use the keys found in the auth context. - */ - ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context, - ProtocolList enabledProtocols, ClientAuthType clientAuth, - ProtocolVersion activeProtocolVersion, - boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData, - boolean isDTLS) { - - super(engine, context, enabledProtocols, - (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false, - activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData, isDTLS); - doClientAuth = clientAuth; - statusRespTimeout = AccessController.doPrivileged( - new GetLongAction("jdk.tls.stapling.responseTimeout", - DEFAULT_STATUS_RESP_DELAY)); - statusRespTimeout = statusRespTimeout >= 0 ? statusRespTimeout : - DEFAULT_STATUS_RESP_DELAY; - } - - /* - * As long as handshaking has not started, we can change - * whether client authentication is required. Otherwise, - * we will need to wait for the next handshake. - */ - void setClientAuth(ClientAuthType clientAuth) { - doClientAuth = clientAuth; - } - - /* - * This routine handles all the server side handshake messages, one at - * a time. Given the message type (and in some cases the pending cipher - * spec) it parses the type-specific message. Then it calls a function - * that handles that specific message. - * - * It updates the state machine as each message is processed, and writes - * responses as needed using the connection in the constructor. - */ - @Override - void processMessage(byte type, int message_len) - throws IOException { - - // check the handshake state - handshakeState.check(type); - - switch (type) { - case HandshakeMessage.ht_client_hello: - ClientHello ch = new ClientHello(input, message_len, isDTLS); - handshakeState.update(ch, resumingSession); - - /* - * send it off for processing. - */ - this.clientHello(ch); - break; - - case HandshakeMessage.ht_certificate: - if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) { - fatalSE(Alerts.alert_unexpected_message, - "client sent unsolicited cert chain"); - // NOTREACHED - } - CertificateMsg certificateMsg = new CertificateMsg(input); - handshakeState.update(certificateMsg, resumingSession); - this.clientCertificate(certificateMsg); - break; - - case HandshakeMessage.ht_client_key_exchange: - SecretKey preMasterSecret; - switch (keyExchange) { - case K_RSA: - case K_RSA_EXPORT: - /* - * The client's pre-master secret is decrypted using - * either the server's normal private RSA key, or the - * temporary one used for non-export or signing-only - * certificates/keys. - */ - RSAClientKeyExchange pms = new RSAClientKeyExchange( - protocolVersion, clientRequestedVersion, - sslContext.getSecureRandom(), input, - message_len, privateKey); - handshakeState.update(pms, resumingSession); - preMasterSecret = this.clientKeyExchange(pms); - break; - case K_DHE_RSA: - case K_DHE_DSS: - case K_DH_ANON: - /* - * The pre-master secret is derived using the normal - * Diffie-Hellman calculation. Note that the main - * protocol difference in these five flavors is in how - * the ServerKeyExchange message was constructed! - */ - DHClientKeyExchange dhcke = new DHClientKeyExchange(input); - handshakeState.update(dhcke, resumingSession); - preMasterSecret = this.clientKeyExchange(dhcke); - break; - case K_ECDH_RSA: - case K_ECDH_ECDSA: - case K_ECDHE_RSA: - case K_ECDHE_ECDSA: - case K_ECDH_ANON: - ECDHClientKeyExchange ecdhcke = - new ECDHClientKeyExchange(input); - handshakeState.update(ecdhcke, resumingSession); - preMasterSecret = this.clientKeyExchange(ecdhcke); - break; - default: - ClientKeyExchangeService p = - ClientKeyExchangeService.find(keyExchange.name); - if (p == null) { - throw new SSLProtocolException - ("Unrecognized key exchange: " + keyExchange); - } - byte[] encodedTicket = input.getBytes16(); - input.getBytes16(); - byte[] secret = input.getBytes16(); - ClientKeyExchange cke = p.createServerExchange(protocolVersion, - clientRequestedVersion, - sslContext.getSecureRandom(), - encodedTicket, - secret, - this.getAccSE(), serviceCreds); - handshakeState.update(cke, resumingSession); - preMasterSecret = this.clientKeyExchange(cke); - break; - } - - // - // All keys are calculated from the premaster secret - // and the exchanged nonces in the same way. - // - calculateKeys(preMasterSecret, clientRequestedVersion); - break; - - case HandshakeMessage.ht_certificate_verify: - CertificateVerify cvm = - new CertificateVerify(input, - getLocalSupportedSignAlgs(), protocolVersion); - handshakeState.update(cvm, resumingSession); - this.clientCertificateVerify(cvm); - - break; - - case HandshakeMessage.ht_finished: - Finished cfm = - new Finished(protocolVersion, input, cipherSuite); - handshakeState.update(cfm, resumingSession); - this.clientFinished(cfm); - - break; - - default: - throw new SSLProtocolException( - "Illegal server handshake msg, " + type); - } - - } - - - /* - * ClientHello presents the server with a bunch of options, to which the - * server replies with a ServerHello listing the ones which this session - * will use. If needed, it also writes its Certificate plus in some cases - * a ServerKeyExchange message. It may also write a CertificateRequest, - * to elicit a client certificate. - * - * All these messages are terminated by a ServerHelloDone message. In - * most cases, all this can be sent in a single Record. - */ - private void clientHello(ClientHello mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // Reject client initiated renegotiation? - // - // If server side should reject client-initiated renegotiation, - // send an alert_handshake_failure fatal alert, not a no_renegotiation - // warning alert (no_renegotiation must be a warning: RFC 2246). - // no_renegotiation might seem more natural at first, but warnings - // are not appropriate because the sending party does not know how - // the receiving party will behave. This state must be treated as - // a fatal server condition. - // - // This will not have any impact on server initiated renegotiation. - if (rejectClientInitiatedRenego && !isInitialHandshake && - !serverHelloRequested) { - fatalSE(Alerts.alert_handshake_failure, - "Client initiated renegotiation is not allowed"); - } - - // check the server name indication if required - ServerNameExtension clientHelloSNIExt = (ServerNameExtension) - mesg.extensions.get(ExtensionType.EXT_SERVER_NAME); - if (!sniMatchers.isEmpty()) { - // we do not reject client without SNI extension - if (clientHelloSNIExt != null && - !clientHelloSNIExt.isMatched(sniMatchers)) { - fatalSE(Alerts.alert_unrecognized_name, - "Unrecognized server name indication"); - } - } - - // Does the message include security renegotiation indication? - boolean renegotiationIndicated = false; - - // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV - CipherSuiteList cipherSuites = mesg.getCipherSuites(); - if (cipherSuites.contains(CipherSuite.C_SCSV)) { - renegotiationIndicated = true; - if (isInitialHandshake) { - secureRenegotiation = true; - } else { - // abort the handshake with a fatal handshake_failure alert - if (secureRenegotiation) { - fatalSE(Alerts.alert_handshake_failure, - "The SCSV is present in a secure renegotiation"); - } else { - fatalSE(Alerts.alert_handshake_failure, - "The SCSV is present in a insecure renegotiation"); - } - } - } - - // check the "renegotiation_info" extension - RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension) - mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); - if (clientHelloRI != null) { - renegotiationIndicated = true; - if (isInitialHandshake) { - // verify the length of the "renegotiated_connection" field - if (!clientHelloRI.isEmpty()) { - // abort the handshake with a fatal handshake_failure alert - fatalSE(Alerts.alert_handshake_failure, - "The renegotiation_info field is not empty"); - } - - secureRenegotiation = true; - } else { - if (!secureRenegotiation) { - // unexpected RI extension for insecure renegotiation, - // abort the handshake with a fatal handshake_failure alert - fatalSE(Alerts.alert_handshake_failure, - "The renegotiation_info is present in a insecure " + - "renegotiation"); - } - - // verify the client_verify_data value - if (!MessageDigest.isEqual(clientVerifyData, - clientHelloRI.getRenegotiatedConnection())) { - fatalSE(Alerts.alert_handshake_failure, - "Incorrect verify data in ClientHello " + - "renegotiation_info message"); - } - } - } else if (!isInitialHandshake && secureRenegotiation) { - // if the connection's "secure_renegotiation" flag is set to TRUE - // and the "renegotiation_info" extension is not present, abort - // the handshake. - fatalSE(Alerts.alert_handshake_failure, - "Inconsistent secure renegotiation indication"); - } - - // if there is no security renegotiation indication or the previous - // handshake is insecure. - if (!renegotiationIndicated || !secureRenegotiation) { - if (isInitialHandshake) { - if (!allowLegacyHelloMessages) { - // abort the handshake with a fatal handshake_failure alert - fatalSE(Alerts.alert_handshake_failure, - "Failed to negotiate the use of secure renegotiation"); - } - - // continue with legacy ClientHello - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning: No renegotiation " + - "indication in ClientHello, allow legacy ClientHello"); - } - } else if (!allowUnsafeRenegotiation) { - // abort the handshake - if (activeProtocolVersion.useTLS10PlusSpec()) { - // respond with a no_renegotiation warning - warningSE(Alerts.alert_no_renegotiation); - - // invalidate the handshake so that the caller can - // dispose this object. - invalidated = true; - - // If there is still unread block in the handshake - // input stream, it would be truncated with the disposal - // and the next handshake message will become incomplete. - // - // However, according to SSL/TLS specifications, no more - // handshake message could immediately follow ClientHello - // or HelloRequest. But in case of any improper messages, - // we'd better check to ensure there is no remaining bytes - // in the handshake input stream. - if (input.available() > 0) { - fatalSE(Alerts.alert_unexpected_message, - "ClientHello followed by an unexpected " + - "handshake message"); - } - - return; - } else { - // For SSLv3, send the handshake_failure fatal error. - // Note that SSLv3 does not define a no_renegotiation - // alert like TLSv1. However we cannot ignore the message - // simply, otherwise the other side was waiting for a - // response that would never come. - fatalSE(Alerts.alert_handshake_failure, - "Renegotiation is not allowed"); - } - } else { // !isInitialHandshake && allowUnsafeRenegotiation - // continue with unsafe renegotiation. - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Warning: continue with insecure renegotiation"); - } - } - } - - // check the "max_fragment_length" extension - MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension) - mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH); - if ((maxFragLenExt != null) && (maximumPacketSize != 0)) { - // Not yet consider the impact of IV/MAC/padding. - int estimatedMaxFragSize = maximumPacketSize; - if (isDTLS) { - estimatedMaxFragSize -= DTLSRecord.headerSize; - } else { - estimatedMaxFragSize -= SSLRecord.headerSize; - } - - if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) { - // For better interoperability, abort the maximum fragment - // length negotiation, rather than terminate the connection - // with a fatal alert. - maxFragLenExt = null; - - // fatalSE(Alerts.alert_illegal_parameter, - // "Not an allowed max_fragment_length value"); - } - } - - // check out the "extended_master_secret" extension - if (useExtendedMasterSecret) { - ExtendedMasterSecretExtension extendedMasterSecretExtension = - (ExtendedMasterSecretExtension)mesg.extensions.get( - ExtensionType.EXT_EXTENDED_MASTER_SECRET); - if (extendedMasterSecretExtension != null) { - requestedToUseEMS = true; - } else if (mesg.protocolVersion.useTLS10PlusSpec()) { - if (!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. - fatalSE(Alerts.alert_handshake_failure, - "Extended Master Secret extension is required"); - } - } - } - - // check the ALPN extension - ALPNExtension clientHelloALPN = (ALPNExtension) - mesg.extensions.get(ExtensionType.EXT_ALPN); - - // Use the application protocol callback when provided. - // Otherwise use the local list of application protocols. - boolean hasAPCallback = - ((engine != null && appProtocolSelectorSSLEngine != null) || - (conn != null && appProtocolSelectorSSLSocket != null)); - - if (!hasAPCallback) { - if ((clientHelloALPN != null) && (localApl.length > 0)) { - - // Intersect the requested and the locally supported, - // and save for later. - String negotiatedValue = null; - List<String> protocols = clientHelloALPN.getPeerAPs(); - - // Use server preference order - for (String ap : localApl) { - if (protocols.contains(ap)) { - negotiatedValue = ap; - break; - } - } - - if (negotiatedValue == null) { - fatalSE(Alerts.alert_no_application_protocol, - new SSLHandshakeException( - "No matching ALPN values")); - } - applicationProtocol = negotiatedValue; - - } else { - applicationProtocol = ""; - } - } // Otherwise, applicationProtocol will be set by the callback. - - session = null; // forget about the current session - // - // Here we go down either of two paths: (a) the fast one, where - // the client's asked to rejoin an existing session, and the server - // permits this; (b) the other one, where a new session is created. - // - if (mesg.sessionId.length() != 0) { - // client is trying to resume a session, let's see... - - SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext - .engineGetServerSessionContext()) - .get(mesg.sessionId.getId()); - // - // Check if we can use the fast path, resuming a session. We - // can do so iff we have a valid record for that session, and - // the cipher suite for that session was on the list which the - // client requested, and if we're not forgetting any needed - // authentication on the part of the client. - // - if (previous != null) { - resumingSession = previous.isRejoinable(); - - if (resumingSession) { - ProtocolVersion oldVersion = previous.getProtocolVersion(); - // cannot resume session with different version - if (oldVersion != mesg.protocolVersion) { - resumingSession = false; - } - } - - if (resumingSession && useExtendedMasterSecret) { - if (requestedToUseEMS && - !previous.getUseExtendedMasterSecret()) { - // 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. - resumingSession = false; - } else if (!requestedToUseEMS && - previous.getUseExtendedMasterSecret()) { - // 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. - fatalSE(Alerts.alert_handshake_failure, - "Missing Extended Master Secret extension " + - "on session resumption"); - } else if (!requestedToUseEMS && - !previous.getUseExtendedMasterSecret()) { - // For abbreviated handshake request, if neither the - // original session nor the new ClientHello uses the - // extension, the server SHOULD abort the handshake. - if (!allowLegacyResumption) { - fatalSE(Alerts.alert_handshake_failure, - "Missing Extended Master Secret extension " + - "on session resumption"); - } else { // Otherwise, continue with a full handshake. - resumingSession = false; - } - } - } - - // cannot resume session with different server name indication - if (resumingSession) { - List<SNIServerName> oldServerNames = - previous.getRequestedServerNames(); - if (clientHelloSNIExt != null) { - if (!clientHelloSNIExt.isIdentical(oldServerNames)) { - resumingSession = false; - } - } else if (!oldServerNames.isEmpty()) { - resumingSession = false; - } - - if (!resumingSession && - debug != null && Debug.isOn("handshake")) { - System.out.println( - "The requested server name indication " + - "is not identical to the previous one"); - } - } - - if (resumingSession && - (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) { - try { - previous.getPeerPrincipal(); - } catch (SSLPeerUnverifiedException e) { - resumingSession = false; - } - } - - // validate subject identity - if (resumingSession) { - CipherSuite suite = previous.getSuite(); - ClientKeyExchangeService p = - ClientKeyExchangeService.find(suite.keyExchange.name); - if (p != null) { - Principal localPrincipal = previous.getLocalPrincipal(); - - if (p.isRelated( - false, getAccSE(), localPrincipal)) { - if (debug != null && Debug.isOn("session")) - System.out.println("Subject can" + - " provide creds for princ"); - } else { - resumingSession = false; - if (debug != null && Debug.isOn("session")) - System.out.println("Subject cannot" + - " provide creds for princ"); - } - } - } - - if (resumingSession) { - CipherSuite suite = previous.getSuite(); - // verify that the ciphersuite from the cached session - // is in the list of client requested ciphersuites and - // we have it enabled - if ((isNegotiable(suite) == false) || - (mesg.getCipherSuites().contains(suite) == false)) { - resumingSession = false; - } else { - // everything looks ok, set the ciphersuite - // this should be done last when we are sure we - // will resume - setCipherSuite(suite); - } - } - - if (resumingSession) { - session = previous; - if (debug != null && - (Debug.isOn("handshake") || Debug.isOn("session"))) { - System.out.println("%% Resuming " + session); - } - } - } - } // else client did not try to resume - - // cookie exchange - if (isDTLS && !resumingSession) { - HelloCookieManager hcMgr = sslContext.getHelloCookieManager(); - if ((mesg.cookie == null) || (mesg.cookie.length == 0) || - (!hcMgr.isValid(mesg))) { - - // - // Perform cookie exchange for DTLS handshaking if no cookie - // or the cookie is invalid in the ClientHello message. - // - HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg); - - if (debug != null && Debug.isOn("handshake")) { - m0.print(System.out); - } - - m0.write(output); - handshakeState.update(m0, resumingSession); - output.flush(); - - return; - } - } - - /* - * FIRST, construct the ServerHello using the options and priorities - * from the ClientHello. Update the (pending) cipher spec as we do - * so, and save the client's version to protect against rollback - * attacks. - * - * There are a bunch of minor tasks here, and one major one: deciding - * if the short or the full handshake sequence will be used. - */ - ServerHello m1 = new ServerHello(); - - clientRequestedVersion = mesg.protocolVersion; - - // select a proper protocol version. - ProtocolVersion selectedVersion = - selectProtocolVersion(clientRequestedVersion); - if (selectedVersion == null || - selectedVersion.v == ProtocolVersion.SSL20Hello.v) { - fatalSE(Alerts.alert_handshake_failure, - "Client requested protocol " + clientRequestedVersion + - " not enabled or not supported"); - } - - handshakeHash.protocolDetermined(selectedVersion); - setVersion(selectedVersion); - - m1.protocolVersion = protocolVersion; - - // - // random ... save client and server values for later use - // in computing the master secret (from pre-master secret) - // and thence the other crypto keys. - // - // NOTE: this use of three inputs to generating _each_ set - // of ciphers slows things down, but it does increase the - // security since each connection in the session can hold - // its own authenticated (and strong) keys. One could make - // creation of a session a rare thing... - // - clnt_random = mesg.clnt_random; - svr_random = new RandomCookie(sslContext.getSecureRandom()); - m1.svr_random = svr_random; - - // - // If client hasn't specified a session we can resume, start a - // new one and choose its cipher suite and compression options. - // Unless new session creation is disabled for this connection! - // - if (session == null) { - if (!enableNewSession) { - throw new SSLException("Client did not resume a session"); - } - - requestedGroups = (SupportedGroupsExtension) - mesg.extensions.get(ExtensionType.EXT_SUPPORTED_GROUPS); - - // We only need to handle the "signature_algorithm" extension - // for full handshakes and TLS 1.2 or later. - if (protocolVersion.useTLS12PlusSpec()) { - SignatureAlgorithmsExtension signAlgs = - (SignatureAlgorithmsExtension)mesg.extensions.get( - ExtensionType.EXT_SIGNATURE_ALGORITHMS); - if (signAlgs != null) { - Collection<SignatureAndHashAlgorithm> peerSignAlgs = - signAlgs.getSignAlgorithms(); - if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No peer supported signature algorithms"); - } - - Collection<SignatureAndHashAlgorithm> - supportedPeerSignAlgs = - SignatureAndHashAlgorithm.getSupportedAlgorithms( - algorithmConstraints, peerSignAlgs); - if (supportedPeerSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No signature and hash algorithm in common"); - } - - setPeerSupportedSignAlgs(supportedPeerSignAlgs); - } // else, need to use peer implicit supported signature algs - } - - session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL, - getLocalSupportedSignAlgs(), - sslContext.getSecureRandom(), - getHostAddressSE(), getPortSE(), - (requestedToUseEMS && - protocolVersion.useTLS10PlusSpec())); - - if (protocolVersion.useTLS12PlusSpec()) { - if (peerSupportedSignAlgs != null) { - session.setPeerSupportedSignatureAlgorithms( - peerSupportedSignAlgs); - } // else, we will set the implicit peer supported signature - // algorithms in chooseCipherSuite() - } - - // set the server name indication in the session - List<SNIServerName> clientHelloSNI = - Collections.<SNIServerName>emptyList(); - if (clientHelloSNIExt != null) { - clientHelloSNI = clientHelloSNIExt.getServerNames(); - } - session.setRequestedServerNames(clientHelloSNI); - - // set the handshake session - setHandshakeSessionSE(session); - - // choose cipher suite and corresponding private key - chooseCipherSuite(mesg); - - session.setSuite(cipherSuite); - session.setLocalPrivateKey(privateKey); - - // chooseCompression(mesg); - - // set the negotiated maximum fragment in the session - // - // The protocol version and cipher suite have been negotiated - // in previous processes. - if (maxFragLenExt != null) { - int maxFragLen = maxFragLenExt.getMaxFragLen(); - - // More check of the requested "max_fragment_length" extension. - if (maximumPacketSize != 0) { - int estimatedMaxFragSize = cipherSuite.calculatePacketSize( - maxFragLen, protocolVersion, isDTLS); - if (estimatedMaxFragSize > maximumPacketSize) { - // For better interoperability, abort the maximum - // fragment length negotiation, rather than terminate - // the connection with a fatal alert. - maxFragLenExt = null; - - // fatalSE(Alerts.alert_illegal_parameter, - // "Not an allowed max_fragment_length value"); - } - } - - if (maxFragLenExt != null) { - session.setNegotiatedMaxFragSize(maxFragLen); - } - } - - session.setMaximumPacketSize(maximumPacketSize); - } else { - // set the handshake session - setHandshakeSessionSE(session); - } - - if (protocolVersion.useTLS12PlusSpec()) { - handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); - } - - m1.cipherSuite = cipherSuite; - m1.sessionId = session.getSessionId(); - m1.compression_method = session.getCompression(); - - if (secureRenegotiation) { - // For ServerHellos that are initial handshakes, then the - // "renegotiated_connection" field in "renegotiation_info" - // extension is of zero length. - // - // For ServerHellos that are renegotiating, this field contains - // the concatenation of client_verify_data and server_verify_data. - // - // Note that for initial handshakes, both the clientVerifyData - // variable and serverVerifyData variable are of zero length. - HelloExtension serverHelloRI = new RenegotiationInfoExtension( - clientVerifyData, serverVerifyData); - m1.extensions.add(serverHelloRI); - } - - if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) { - // When resuming a session, the server MUST NOT include a - // server_name extension in the server hello. - if (!resumingSession) { - ServerNameExtension serverHelloSNI = new ServerNameExtension(); - m1.extensions.add(serverHelloSNI); - } - } - - if ((maxFragLenExt != null) && !resumingSession) { - // When resuming a session, the server MUST NOT include a - // max_fragment_length extension in the server hello. - // - // Otherwise, use the same value as the requested extension. - m1.extensions.add(maxFragLenExt); - } - - if (session.getUseExtendedMasterSecret()) { - m1.extensions.add(new ExtendedMasterSecretExtension()); - } - - StaplingParameters staplingParams = processStapling(mesg); - if (staplingParams != null) { - // We now can safely assert status_request[_v2] in our - // ServerHello, and know for certain that we can provide - // responses back to this client for this connection. - if (staplingParams.statusRespExt == - ExtensionType.EXT_STATUS_REQUEST) { - m1.extensions.add(new CertStatusReqExtension()); - } else if (staplingParams.statusRespExt == - ExtensionType.EXT_STATUS_REQUEST_V2) { - m1.extensions.add(new CertStatusReqListV2Extension()); - } - } - - // Prepare the ALPN response - if (clientHelloALPN != null) { - List<String> peerAPs = clientHelloALPN.getPeerAPs(); - - // check for a callback function - if (hasAPCallback) { - if (conn != null) { - applicationProtocol = - appProtocolSelectorSSLSocket.apply(conn, peerAPs); - } else { - applicationProtocol = - appProtocolSelectorSSLEngine.apply(engine, peerAPs); - } - } - - // check for no-match and that the selected name was also proposed - // by the TLS peer - if (applicationProtocol == null || - (!applicationProtocol.isEmpty() && - !peerAPs.contains(applicationProtocol))) { - - fatalSE(Alerts.alert_no_application_protocol, - new SSLHandshakeException( - "No matching ALPN values")); - - } else if (!applicationProtocol.isEmpty()) { - m1.extensions.add(new ALPNExtension(applicationProtocol)); - } - } else { - // Nothing was negotiated, returned at end of the handshake - applicationProtocol = ""; - } - - if (debug != null && Debug.isOn("handshake")) { - m1.print(System.out); - System.out.println("Cipher suite: " + session.getSuite()); - } - m1.write(output); - handshakeState.update(m1, resumingSession); - - // - // If we are resuming a session, we finish writing handshake - // messages right now and then finish. - // - if (resumingSession) { - calculateConnectionKeys(session.getMasterSecret()); - sendChangeCipherAndFinish(false); - - // expecting the final ChangeCipherSpec and Finished messages - expectingFinishFlightSE(); - - return; - } - - - /* - * SECOND, write the server Certificate(s) if we need to. - * - * NOTE: while an "anonymous RSA" mode is explicitly allowed by - * the protocol, we can't support it since all of the SSL flavors - * defined in the protocol spec are explicitly stated to require - * using RSA certificates. - */ - if (ClientKeyExchangeService.find( - cipherSuite.keyExchange.name) != null) { - // No external key exchange provider needs a cert now. - } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) { - if (certs == null) { - throw new RuntimeException("no certificates"); - } - - CertificateMsg m2 = new CertificateMsg(certs); - - /* - * Set local certs in the SSLSession, output - * debug info, and then actually write to the client. - */ - session.setLocalCertificates(certs); - if (debug != null && Debug.isOn("handshake")) { - m2.print(System.out); - } - m2.write(output); - handshakeState.update(m2, resumingSession); - - // XXX has some side effects with OS TCP buffering, - // leave it out for now - - // let client verify chain in the meantime... - // output.flush(); - } else { - if (certs != null) { - throw new RuntimeException("anonymous keyexchange with certs"); - } - } - - /** - * The CertificateStatus message ... only if it is needed. - * This would only be needed if we've established that this handshake - * supports status stapling and there is at least one response to - * return to the client. - */ - if (staplingParams != null) { - CertificateStatus csMsg = new CertificateStatus( - staplingParams.statReqType, certs, - staplingParams.responseMap); - if (debug != null && Debug.isOn("handshake")) { - csMsg.print(System.out); - } - csMsg.write(output); - handshakeState.update(csMsg, resumingSession); - } - - /* - * THIRD, the ServerKeyExchange message ... iff it's needed. - * - * It's usually needed unless there's an encryption-capable - * RSA cert, or a D-H cert. The notable exception is that - * exportable ciphers used with big RSA keys need to downgrade - * to use short RSA keys, even when the key/cert encrypts OK. - */ - - ServerKeyExchange m3; - switch (keyExchange) { - case K_RSA: - // no server key exchange for RSA ciphersuites - m3 = null; - break; - case K_RSA_EXPORT: - if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { - try { - m3 = new RSA_ServerKeyExchange( - tempPublicKey, privateKey, - clnt_random, svr_random, - sslContext.getSecureRandom()); - privateKey = tempPrivateKey; - } catch (GeneralSecurityException e) { - m3 = null; // make compiler happy - throw new SSLException( - "Error generating RSA server key exchange", e); - } - } else { - // RSA_EXPORT with short key, don't need ServerKeyExchange - m3 = null; - } - break; - case K_DHE_RSA: - case K_DHE_DSS: - try { - m3 = new DH_ServerKeyExchange(dh, - privateKey, - clnt_random.random_bytes, - svr_random.random_bytes, - sslContext.getSecureRandom(), - preferableSignatureAlgorithm, - protocolVersion); - } catch (GeneralSecurityException e) { - m3 = null; // make compiler happy - throw new SSLException( - "Error generating DH server key exchange", e); - } - break; - case K_DH_ANON: - m3 = new DH_ServerKeyExchange(dh, protocolVersion); - break; - case K_ECDHE_RSA: - case K_ECDHE_ECDSA: - case K_ECDH_ANON: - try { - m3 = new ECDH_ServerKeyExchange(ecdh, - privateKey, - clnt_random.random_bytes, - svr_random.random_bytes, - sslContext.getSecureRandom(), - preferableSignatureAlgorithm, - protocolVersion); - } catch (GeneralSecurityException e) { - m3 = null; // make compiler happy - throw new SSLException( - "Error generating ECDH server key exchange", e); - } - break; - case K_ECDH_RSA: - case K_ECDH_ECDSA: - // ServerKeyExchange not used for fixed ECDH - m3 = null; - break; - default: - ClientKeyExchangeService p = - ClientKeyExchangeService.find(keyExchange.name); - if (p != null) { - // No external key exchange provider needs a cert now. - m3 = null; - break; - } - throw new RuntimeException("internal error: " + keyExchange); - } - if (m3 != null) { - if (debug != null && Debug.isOn("handshake")) { - m3.print(System.out); - } - m3.write(output); - handshakeState.update(m3, resumingSession); - } - - // - // FOURTH, the CertificateRequest message. The details of - // the message can be affected by the key exchange algorithm - // in use. For example, certs with fixed Diffie-Hellman keys - // are only useful with the DH_DSS and DH_RSA key exchange - // algorithms. - // - // Needed only if server requires client to authenticate self. - // Illegal for anonymous flavors, so we need to check that. - // - // No external key exchange provider needs a cert now. - if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE && - keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON && - ClientKeyExchangeService.find(keyExchange.name) == null) { - - CertificateRequest m4; - X509Certificate[] caCerts; - - Collection<SignatureAndHashAlgorithm> localSignAlgs = null; - if (protocolVersion.useTLS12PlusSpec()) { - // We currently use all local upported signature and hash - // algorithms. However, to minimize the computation cost - // of requested hash algorithms, we may use a restricted - // set of signature algorithms in the future. - localSignAlgs = getLocalSupportedSignAlgs(); - if (localSignAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No supported signature algorithm"); - } - - Set<String> localHashAlgs = - SignatureAndHashAlgorithm.getHashAlgorithmNames( - localSignAlgs); - if (localHashAlgs.isEmpty()) { - throw new SSLHandshakeException( - "No supported signature algorithm"); - } - } - - caCerts = sslContext.getX509TrustManager().getAcceptedIssuers(); - m4 = new CertificateRequest(caCerts, keyExchange, - localSignAlgs, protocolVersion); - - if (debug != null && Debug.isOn("handshake")) { - m4.print(System.out); - } - m4.write(output); - handshakeState.update(m4, resumingSession); - } - - /* - * FIFTH, say ServerHelloDone. - */ - ServerHelloDone m5 = new ServerHelloDone(); - - if (debug != null && Debug.isOn("handshake")) { - m5.print(System.out); - } - m5.write(output); - handshakeState.update(m5, resumingSession); - - /* - * Flush any buffered messages so the client will see them. - * Ideally, all the messages above go in a single network level - * message to the client. Without big Certificate chains, it's - * going to be the common case. - */ - output.flush(); - } - - /* - * Choose cipher suite from among those supported by client. Sets - * the cipherSuite and keyExchange variables. - */ - private void chooseCipherSuite(ClientHello mesg) throws IOException { - CipherSuiteList prefered; - CipherSuiteList proposed; - if (preferLocalCipherSuites) { - prefered = getActiveCipherSuites(); - proposed = mesg.getCipherSuites(); - } else { - prefered = mesg.getCipherSuites(); - proposed = getActiveCipherSuites(); - } - - List<CipherSuite> legacySuites = new ArrayList<>(); - for (CipherSuite suite : prefered.collection()) { - if (isNegotiable(proposed, suite) == false) { - continue; - } - - if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) { - if ((suite.keyExchange == K_DH_ANON) || - (suite.keyExchange == K_ECDH_ANON)) { - continue; - } - } - - if (!legacyAlgorithmConstraints.permits(null, suite.name, null)) { - legacySuites.add(suite); - continue; - } - - if (trySetCipherSuite(suite) == false) { - continue; - } - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Standard ciphersuite chosen: " + suite); - } - return; - } - - for (CipherSuite suite : legacySuites) { - if (trySetCipherSuite(suite)) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Legacy ciphersuite chosen: " + suite); - } - return; - } - } - - fatalSE(Alerts.alert_handshake_failure, "no cipher suites in common"); - } - - /** - * Set the given CipherSuite, if possible. Return the result. - * The call succeeds if the CipherSuite is available and we have - * the necessary certificates to complete the handshake. We don't - * check if the CipherSuite is actually enabled. - * - * If successful, this method also generates ephemeral keys if - * required for this ciphersuite. This may take some time, so this - * method should only be called if you really want to use the - * CipherSuite. - * - * This method is called from chooseCipherSuite() in this class. - */ - boolean trySetCipherSuite(CipherSuite suite) { - /* - * If we're resuming a session we know we can - * support this key exchange algorithm and in fact - * have already cached the result of it in - * the session state. - */ - if (resumingSession) { - return true; - } - - if (suite.isNegotiable() == false) { - return false; - } - - // must not negotiate the obsoleted weak cipher suites. - if (protocolVersion.obsoletes(suite)) { - return false; - } - - // must not negotiate unsupported cipher suites. - if (!protocolVersion.supports(suite)) { - return false; - } - - KeyExchange keyExchange = suite.keyExchange; - - // null out any existing references - privateKey = null; - certs = null; - dh = null; - tempPrivateKey = null; - tempPublicKey = null; - - Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null; - if (protocolVersion.useTLS12PlusSpec()) { - if (peerSupportedSignAlgs != null) { - supportedSignAlgs = peerSupportedSignAlgs; - } else { - SignatureAndHashAlgorithm algorithm = null; - - // we may optimize the performance - switch (keyExchange) { - // If the negotiated key exchange algorithm is one of - // (RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), - // behave as if client had sent the value {sha1,rsa}. - case K_RSA: - case K_DHE_RSA: - case K_DH_RSA: - // case K_RSA_PSK: - case K_ECDH_RSA: - case K_ECDHE_RSA: - algorithm = SignatureAndHashAlgorithm.valueOf( - HashAlgorithm.SHA1.value, - SignatureAlgorithm.RSA.value, 0); - break; - // If the negotiated key exchange algorithm is one of - // (DHE_DSS, DH_DSS), behave as if the client had - // sent the value {sha1,dsa}. - case K_DHE_DSS: - case K_DH_DSS: - algorithm = SignatureAndHashAlgorithm.valueOf( - HashAlgorithm.SHA1.value, - SignatureAlgorithm.DSA.value, 0); - break; - // If the negotiated key exchange algorithm is one of - // (ECDH_ECDSA, ECDHE_ECDSA), behave as if the client - // had sent value {sha1,ecdsa}. - case K_ECDH_ECDSA: - case K_ECDHE_ECDSA: - algorithm = SignatureAndHashAlgorithm.valueOf( - HashAlgorithm.SHA1.value, - SignatureAlgorithm.ECDSA.value, 0); - break; - default: - // no peer supported signature algorithms - } - - if (algorithm == null) { - supportedSignAlgs = - Collections.<SignatureAndHashAlgorithm>emptySet(); - } else { - supportedSignAlgs = - new ArrayList<SignatureAndHashAlgorithm>(1); - supportedSignAlgs.add(algorithm); - - supportedSignAlgs = - SignatureAndHashAlgorithm.getSupportedAlgorithms( - algorithmConstraints, supportedSignAlgs); - - // May be no default activated signature algorithm, but - // let the following process make the final decision. - } - - // Sets the peer supported signature algorithm to use in KM - // temporarily. - session.setPeerSupportedSignatureAlgorithms(supportedSignAlgs); - } - } - - // The named group used for ECDHE and FFDHE. - NamedGroup namedGroup = null; - switch (keyExchange) { - case K_RSA: - // need RSA certs for authentication - if (setupPrivateKeyAndChain("RSA") == false) { - return false; - } - break; - case K_RSA_EXPORT: - // need RSA certs for authentication - if (setupPrivateKeyAndChain("RSA") == false) { - return false; - } - - try { - if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { - if (!setupEphemeralRSAKeys(suite.exportable)) { - return false; - } - } - } catch (RuntimeException e) { - // could not determine keylength, ignore key - return false; - } - break; - case K_DHE_RSA: - // Is ephemeral DH cipher suite usable for the connection? - // - // [RFC 7919] If a compatible TLS server receives a Supported - // Groups extension from a client that includes any FFDHE group - // (i.e., any codepoint between 256 and 511, inclusive, even if - // unknown to the server), and if none of the client-proposed - // FFDHE groups are known and acceptable to the server, then - // the server MUST NOT select an FFDHE cipher suite. In this - // case, the server SHOULD select an acceptable non-FFDHE cipher - // suite from the client's offered list. If the extension is - // present with FFDHE groups, none of the client's offered - // groups are acceptable by the server, and none of the client's - // proposed non-FFDHE cipher suites are acceptable to the server, - // the server MUST end the connection with a fatal TLS alert - // of type insufficient_security(71). - // - // Note: For compatibility, if an application is customized to - // use legacy sizes (512 bits for exportable cipher suites and - // 768 bits for others), or the cipher suite is exportable, the - // FFDHE extension will not be used. - if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) && - (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) { - - namedGroup = requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE); - if (namedGroup == null) { - // no match found, cannot use this cipher suite. - return false; - } - } - - // need RSA certs for authentication - if (setupPrivateKeyAndChain("RSA") == false) { - return false; - } - - // get preferable peer signature algorithm for server key exchange - if (protocolVersion.useTLS12PlusSpec()) { - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.getPreferableAlgorithm( - supportedSignAlgs, "RSA", privateKey); - if (preferableSignatureAlgorithm == null) { - if ((debug != null) && Debug.isOn("handshake")) { - System.out.println( - "No signature and hash algorithm for cipher " + - suite); - } - return false; - } - } - - setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey); - break; - case K_ECDHE_RSA: - // Is ECDHE cipher suite usable for the connection? - namedGroup = (requestedGroups != null) ? - requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) : - SupportedGroupsExtension.getPreferredECGroup( - algorithmConstraints); - if (namedGroup == null) { - // no match found, cannot use this ciphersuite - return false; - } - - // need RSA certs for authentication - if (setupPrivateKeyAndChain("RSA") == false) { - return false; - } - - // get preferable peer signature algorithm for server key exchange - if (protocolVersion.useTLS12PlusSpec()) { - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.getPreferableAlgorithm( - supportedSignAlgs, "RSA", privateKey); - if (preferableSignatureAlgorithm == null) { - if ((debug != null) && Debug.isOn("handshake")) { - System.out.println( - "No signature and hash algorithm for cipher " + - suite); - } - return false; - } - } - - setupEphemeralECDHKeys(namedGroup); - break; - case K_DHE_DSS: - // Is ephemeral DH cipher suite usable for the connection? - // - // See comment in K_DHE_RSA case. - if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) && - (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) { - - namedGroup = requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE); - if (namedGroup == null) { - // no match found, cannot use this cipher suite. - return false; - } - } - - // get preferable peer signature algorithm for server key exchange - if (protocolVersion.useTLS12PlusSpec()) { - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.getPreferableAlgorithm( - supportedSignAlgs, "DSA"); - if (preferableSignatureAlgorithm == null) { - if ((debug != null) && Debug.isOn("handshake")) { - System.out.println( - "No signature and hash algorithm for cipher " + - suite); - } - return false; - } - } - - // need DSS certs for authentication - if (setupPrivateKeyAndChain("DSA") == false) { - return false; - } - - setupEphemeralDHKeys(namedGroup, suite.exportable, privateKey); - break; - case K_ECDHE_ECDSA: - // Is ECDHE cipher suite usable for the connection? - namedGroup = (requestedGroups != null) ? - requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) : - SupportedGroupsExtension.getPreferredECGroup( - algorithmConstraints); - if (namedGroup == null) { - // no match found, cannot use this ciphersuite - return false; - } - - // get preferable peer signature algorithm for server key exchange - if (protocolVersion.useTLS12PlusSpec()) { - preferableSignatureAlgorithm = - SignatureAndHashAlgorithm.getPreferableAlgorithm( - supportedSignAlgs, "ECDSA"); - if (preferableSignatureAlgorithm == null) { - if ((debug != null) && Debug.isOn("handshake")) { - System.out.println( - "No signature and hash algorithm for cipher " + - suite); - } - return false; - } - } - - // need EC cert - if (setupPrivateKeyAndChain("EC") == false) { - return false; - } - - setupEphemeralECDHKeys(namedGroup); - break; - case K_ECDH_RSA: - // need EC cert - if (setupPrivateKeyAndChain("EC") == false) { - return false; - } - setupStaticECDHKeys(); - break; - case K_ECDH_ECDSA: - // need EC cert - if (setupPrivateKeyAndChain("EC") == false) { - return false; - } - setupStaticECDHKeys(); - break; - case K_DH_ANON: - // Is ephemeral DH cipher suite usable for the connection? - // - // See comment in K_DHE_RSA case. - if ((!useLegacyEphemeralDHKeys) && (!suite.exportable) && - (requestedGroups != null) && requestedGroups.hasFFDHEGroup()) { - namedGroup = requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_FFDHE); - if (namedGroup == null) { - // no match found, cannot use this cipher suite. - return false; - } - } - - // no certs needed for anonymous - setupEphemeralDHKeys(namedGroup, suite.exportable, null); - break; - case K_ECDH_ANON: - // Is ECDHE cipher suite usable for the connection? - namedGroup = (requestedGroups != null) ? - requestedGroups.getPreferredGroup( - algorithmConstraints, NamedGroupType.NAMED_GROUP_ECDHE) : - SupportedGroupsExtension.getPreferredECGroup( - algorithmConstraints); - if (namedGroup == null) { - // no match found, cannot use this ciphersuite - return false; - } - - // no certs needed for anonymous - setupEphemeralECDHKeys(namedGroup); - break; - default: - ClientKeyExchangeService p = - ClientKeyExchangeService.find(keyExchange.name); - if (p == null) { - // internal error, unknown key exchange - throw new RuntimeException( - "Unrecognized cipherSuite: " + suite); - } - // need service creds - if (serviceCreds == null) { - AccessControlContext acc = getAccSE(); - serviceCreds = p.getServiceCreds(acc); - if (serviceCreds != null) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Using serviceCreds"); - } - } - if (serviceCreds == null) { - return false; - } - } - break; - } - setCipherSuite(suite); - - // set the peer implicit supported signature algorithms - if (protocolVersion.useTLS12PlusSpec()) { - if (peerSupportedSignAlgs == null) { - setPeerSupportedSignAlgs(supportedSignAlgs); - // we had alreay update the session - } - } - return true; - } - - /* - * Get some "ephemeral" RSA keys for this context. This means - * generating them if it's not already been done. - * - * Note that we currently do not implement any ciphersuites that use - * strong ephemeral RSA. (We do not support the EXPORT1024 ciphersuites - * and standard RSA ciphersuites prohibit ephemeral mode for some reason) - * This means that export is always true and 512 bit keys are generated. - */ - private boolean setupEphemeralRSAKeys(boolean export) { - KeyPair kp = sslContext.getEphemeralKeyManager(). - getRSAKeyPair(export, sslContext.getSecureRandom()); - if (kp == null) { - return false; - } else { - tempPublicKey = kp.getPublic(); - tempPrivateKey = kp.getPrivate(); - return true; - } - } - - /* - * Acquire some "ephemeral" Diffie-Hellman keys for this handshake. - * We don't reuse these, for improved forward secrecy. - */ - private void setupEphemeralDHKeys( - NamedGroup namedGroup, boolean export, Key key) { - // Are the client and server willing to negotiate FFDHE groups? - if ((!useLegacyEphemeralDHKeys) && (!export) && (namedGroup != null)) { - dh = new DHCrypt(namedGroup, sslContext.getSecureRandom()); - - return; - } // Otherwise, the client is not compatible with FFDHE extension. - - /* - * 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 = export ? 512 : 1024; // default mode - if (!export) { - if (useLegacyEphemeralDHKeys) { // legacy mode - keySize = 768; - } else if (useSmartEphemeralDHKeys) { // matched mode - 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 (see DHCrypt class). - // - // 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 : (ks >= 2048 ? 2048 : ks); - keySize = ks <= 1024 ? 1024 : 2048; - } // Otherwise, anonymous cipher suites, 1024-bit is used. - } else if (customizedDHKeySize > 0) { // customized mode - keySize = customizedDHKeySize; - } - } - - dh = new DHCrypt(keySize, sslContext.getSecureRandom()); - } - - /** - * Setup the ephemeral ECDH parameters. - */ - private void setupEphemeralECDHKeys(NamedGroup namedGroup) { - ecdh = new ECDHCrypt(namedGroup, sslContext.getSecureRandom()); - } - - private void setupStaticECDHKeys() { - // don't need to check whether the curve is supported, already done - // in setupPrivateKeyAndChain(). - ecdh = new ECDHCrypt(privateKey, certs[0].getPublicKey()); - } - - /** - * Retrieve the server key and certificate for the specified algorithm - * from the KeyManager and set the instance variables. - * - * @return true if successful, false if not available or invalid - */ - private boolean setupPrivateKeyAndChain(String algorithm) { - X509ExtendedKeyManager km = sslContext.getX509KeyManager(); - String alias; - if (conn != null) { - alias = km.chooseServerAlias(algorithm, null, conn); - } else { - alias = km.chooseEngineServerAlias(algorithm, null, engine); - } - if (alias == null) { - return false; - } - PrivateKey tempPrivateKey = km.getPrivateKey(alias); - if (tempPrivateKey == null) { - return false; - } - X509Certificate[] tempCerts = km.getCertificateChain(alias); - if ((tempCerts == null) || (tempCerts.length == 0)) { - return false; - } - String keyAlgorithm = algorithm.split("_")[0]; - PublicKey publicKey = tempCerts[0].getPublicKey(); - if ((tempPrivateKey.getAlgorithm().equals(keyAlgorithm) == false) - || (publicKey.getAlgorithm().equals(keyAlgorithm) == false)) { - return false; - } - // For ECC certs, check whether we support the EC domain parameters. - // If the client sent a SupportedEllipticCurves ClientHello extension, - // check against that too. - if (keyAlgorithm.equals("EC")) { - if (publicKey instanceof ECPublicKey == false) { - return false; - } - ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); - NamedGroup namedGroup = NamedGroup.valueOf(params); - if ((namedGroup == null) || - (!SupportedGroupsExtension.supports(namedGroup)) || - ((requestedGroups != null) && - !requestedGroups.contains(namedGroup.id))) { - return false; - } - } - this.privateKey = tempPrivateKey; - this.certs = tempCerts; - return true; - } - - /* - * Returns premaster secret for external key exchange services. - */ - private SecretKey clientKeyExchange(ClientKeyExchange mesg) - throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - // Record the principals involved in exchange - session.setPeerPrincipal(mesg.getPeerPrincipal()); - session.setLocalPrincipal(mesg.getLocalPrincipal()); - - return mesg.clientKeyExchange(); - } - - /* - * Diffie Hellman key exchange is used when the server presented - * D-H parameters in its certificate (signed using RSA or DSS/DSA), - * or else the server presented no certificate but sent D-H params - * in a ServerKeyExchange message. Use of D-H is specified by the - * cipher suite chosen. - * - * The message optionally contains the client's D-H public key (if - * it wasn't not sent in a client certificate). As always with D-H, - * if a client and a server have each other's D-H public keys and - * they use common algorithm parameters, they have a shared key - * that's derived via the D-H calculation. That key becomes the - * pre-master secret. - */ - private SecretKey clientKeyExchange(DHClientKeyExchange mesg) - throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - BigInteger publicKeyValue = mesg.getClientPublicKey(); - - // check algorithm constraints - dh.checkConstraints(algorithmConstraints, publicKeyValue); - - return dh.getAgreedSecret(publicKeyValue, false); - } - - private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg) - throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - byte[] publicPoint = mesg.getEncodedPoint(); - - // check algorithm constraints - ecdh.checkConstraints(algorithmConstraints, publicPoint); - - return ecdh.getAgreedSecret(publicPoint); - } - - /* - * Client wrote a message to verify the certificate it sent earlier. - * - * Note that this certificate isn't involved in key exchange. Client - * authentication messages are included in the checksums used to - * validate the handshake (e.g. Finished messages). Other than that, - * the _exact_ identity of the client is less fundamental to protocol - * security than its role in selecting keys via the pre-master secret. - */ - private void clientCertificateVerify(CertificateVerify mesg) - throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - if (protocolVersion.useTLS12PlusSpec()) { - SignatureAndHashAlgorithm signAlg = - mesg.getPreferableSignatureAlgorithm(); - if (signAlg == null) { - throw new SSLHandshakeException( - "Illegal CertificateVerify message"); - } - - String hashAlg = - SignatureAndHashAlgorithm.getHashAlgorithmName(signAlg); - if (hashAlg == null || hashAlg.length() == 0) { - throw new SSLHandshakeException( - "No supported hash algorithm"); - } - } - - try { - PublicKey publicKey = - session.getPeerCertificates()[0].getPublicKey(); - - boolean valid = mesg.verify(protocolVersion, handshakeHash, - publicKey, session.getMasterSecret()); - if (valid == false) { - fatalSE(Alerts.alert_bad_certificate, - "certificate verify message signature error"); - } - } catch (GeneralSecurityException e) { - fatalSE(Alerts.alert_bad_certificate, - "certificate verify format error", e); - } - - // reset the flag for clientCertificateVerify message - needClientVerify = false; - } - - - /* - * Client writes "finished" at the end of its handshake, after cipher - * spec is changed. We verify it and then send ours. - * - * When we're resuming a session, we'll have already sent our own - * Finished message so just the verification is needed. - */ - private void clientFinished(Finished mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - /* - * Verify if client did send the certificate when client - * authentication was required, otherwise server should not proceed - */ - if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) { - // get X500Principal of the end-entity certificate for X509-based - // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc - session.getPeerPrincipal(); - } - - /* - * Verify if client did send clientCertificateVerify message following - * the client Certificate, otherwise server should not proceed - */ - if (needClientVerify) { - fatalSE(Alerts.alert_handshake_failure, - "client did not send certificate verify message"); - } - - /* - * Verify the client's message with the "before" digest of messages, - * and forget about continuing to use that digest. - */ - boolean verified = mesg.verify(handshakeHash, Finished.CLIENT, - session.getMasterSecret()); - - if (!verified) { - fatalSE(Alerts.alert_handshake_failure, - "client 'finished' message doesn't verify"); - // NOTREACHED - } - - /* - * save client verify data for secure renegotiation - */ - if (secureRenegotiation) { - clientVerifyData = mesg.getVerifyData(); - } - - /* - * OK, it verified. If we're doing the full handshake, add that - * "Finished" message to the hash of handshake messages, then send - * the change_cipher_spec and Finished message. - */ - if (!resumingSession) { - sendChangeCipherAndFinish(true); - } else { - handshakeFinished = true; - } - - /* - * Update the session cache only after the handshake completed, else - * we're open to an attack against a partially completed handshake. - */ - session.setLastAccessedTime(System.currentTimeMillis()); - if (!resumingSession && session.isRejoinable()) { - ((SSLSessionContextImpl)sslContext.engineGetServerSessionContext()) - .put(session); - if (debug != null && Debug.isOn("session")) { - System.out.println( - "%% Cached server session: " + session); - } - } else if (!resumingSession && - debug != null && Debug.isOn("session")) { - System.out.println( - "%% Didn't cache non-resumable server session: " - + session); - } - } - - /* - * Compute finished message with the "server" digest (and then forget - * about that digest, it can't be used again). - */ - private void sendChangeCipherAndFinish(boolean finishedTag) - throws IOException { - - // Reload if this message has been reserved. - handshakeHash.reload(); - - Finished mesg = new Finished(protocolVersion, handshakeHash, - Finished.SERVER, session.getMasterSecret(), cipherSuite); - - /* - * Send the change_cipher_spec record; then our Finished handshake - * message will be the last handshake message. Flush, and now we - * are ready for application data!! - */ - sendChangeCipherSpec(mesg, finishedTag); - - /* - * save server verify data for secure renegotiation - */ - if (secureRenegotiation) { - serverVerifyData = mesg.getVerifyData(); - } - } - - - /* - * Returns a HelloRequest message to kickstart renegotiations - */ - @Override - HandshakeMessage getKickstartMessage() { - return new HelloRequest(); - } - - - /* - * Fault detected during handshake. - */ - @Override - void handshakeAlert(byte description) throws SSLProtocolException { - - String message = Alerts.alertDescription(description); - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("SSL -- handshake alert: " - + message); - } - - /* - * It's ok to get a no_certificate alert from a client of which - * we *requested* authentication information. - * However, if we *required* it, then this is not acceptable. - * - * Anyone calling getPeerCertificates() on the - * session will get an SSLPeerUnverifiedException. - */ - if ((description == Alerts.alert_no_certificate) && - (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) { - return; - } - - throw new SSLProtocolException("handshake alert: " + message); - } - - /* - * RSA key exchange is normally used. The client encrypts a "pre-master - * secret" with the server's public key, from the Certificate (or else - * ServerKeyExchange) message that was sent to it by the server. That's - * decrypted using the private key before we get here. - */ - private SecretKey clientKeyExchange(RSAClientKeyExchange mesg) - throws IOException { - - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - return mesg.preMaster; - } - - /* - * Verify the certificate sent by the client. We'll only get one if we - * sent a CertificateRequest to request client authentication. If we - * are in TLS mode, the client may send a message with no certificates - * to indicate it does not have an appropriate chain. (In SSLv3 mode, - * it would send a no certificate alert). - */ - private void clientCertificate(CertificateMsg mesg) throws IOException { - if (debug != null && Debug.isOn("handshake")) { - mesg.print(System.out); - } - - X509Certificate[] peerCerts = mesg.getCertificateChain(); - - if (peerCerts.length == 0) { - /* - * If the client authentication is only *REQUESTED* (e.g. - * not *REQUIRED*, this is an acceptable condition.) - */ - if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) { - return; - } else { - fatalSE(Alerts.alert_bad_certificate, - "null cert chain"); - } - } - - // ask the trust manager to verify the chain - X509TrustManager tm = sslContext.getX509TrustManager(); - - try { - // find out the types of client authentication used - PublicKey key = peerCerts[0].getPublicKey(); - String keyAlgorithm = key.getAlgorithm(); - String authType; - if (keyAlgorithm.equals("RSA")) { - authType = "RSA"; - } else if (keyAlgorithm.equals("DSA")) { - authType = "DSA"; - } else if (keyAlgorithm.equals("EC")) { - authType = "EC"; - } else { - // unknown public key type - authType = "UNKNOWN"; - } - - if (tm instanceof X509ExtendedTrustManager) { - if (conn != null) { - ((X509ExtendedTrustManager)tm).checkClientTrusted( - peerCerts.clone(), - authType, - conn); - } else { - ((X509ExtendedTrustManager)tm).checkClientTrusted( - peerCerts.clone(), - authType, - engine); - } - } else { - // Unlikely to happen, because we have wrapped the old - // X509TrustManager with the new X509ExtendedTrustManager. - throw new CertificateException( - "Improper X509TrustManager implementation"); - } - } catch (CertificateException e) { - // This will throw an exception, so include the original error. - fatalSE(Alerts.alert_certificate_unknown, e); - } - // set the flag for clientCertificateVerify message - needClientVerify = true; - - session.setPeerCertificates(peerCerts); - } - - private StaplingParameters processStapling(ClientHello mesg) { - StaplingParameters params = null; - ExtensionType ext = null; - StatusRequestType type = null; - StatusRequest req = null; - Map<X509Certificate, byte[]> responses; - - // If this feature has not been enabled, then no more processing - // is necessary. Also we will only staple if we're doing a full - // handshake. - if (!sslContext.isStaplingEnabled(false) || resumingSession) { - return null; - } - - // Check if the client has asserted the status_request[_v2] extension(s) - CertStatusReqExtension statReqExt = (CertStatusReqExtension) - mesg.extensions.get(ExtensionType.EXT_STATUS_REQUEST); - CertStatusReqListV2Extension statReqExtV2 = - (CertStatusReqListV2Extension)mesg.extensions.get( - ExtensionType.EXT_STATUS_REQUEST_V2); - - // Determine which type of stapling we are doing and assert the - // proper extension in the server hello. - // Favor status_request_v2 over status_request and ocsp_multi - // over ocsp. - // If multiple ocsp or ocsp_multi types exist, select the first - // instance of a given type. Also since we don't support ResponderId - // selection yet, only accept a request if the ResponderId field - // is empty. - if (statReqExtV2 != null) { // RFC 6961 stapling - ext = ExtensionType.EXT_STATUS_REQUEST_V2; - List<CertStatusReqItemV2> reqItems = - statReqExtV2.getRequestItems(); - int ocspIdx = -1; - int ocspMultiIdx = -1; - for (int pos = 0; (pos < reqItems.size() && - (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) { - CertStatusReqItemV2 item = reqItems.get(pos); - StatusRequestType curType = item.getType(); - if (ocspIdx < 0 && curType == StatusRequestType.OCSP) { - OCSPStatusRequest ocspReq = - (OCSPStatusRequest)item.getRequest(); - if (ocspReq.getResponderIds().isEmpty()) { - ocspIdx = pos; - } - } else if (ocspMultiIdx < 0 && - curType == StatusRequestType.OCSP_MULTI) { - // If the type is OCSP, then the request - // is guaranteed to be OCSPStatusRequest - OCSPStatusRequest ocspReq = - (OCSPStatusRequest)item.getRequest(); - if (ocspReq.getResponderIds().isEmpty()) { - ocspMultiIdx = pos; - } - } - } - if (ocspMultiIdx >= 0) { - type = reqItems.get(ocspMultiIdx).getType(); - req = reqItems.get(ocspMultiIdx).getRequest(); - } else if (ocspIdx >= 0) { - type = reqItems.get(ocspIdx).getType(); - req = reqItems.get(ocspIdx).getRequest(); - } else { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning: No suitable request " + - "found in the status_request_v2 extension."); - } - } - } - - // Only attempt to process a status_request extension if: - // * The status_request extension is set AND - // * either the status_request_v2 extension is not present OR - // * none of the underlying OCSPStatusRequest structures is suitable - // for stapling. - // If either of the latter two bullet items is true the ext, type and - // req variables should all be null. If any are null we will try - // processing an asserted status_request. - if ((statReqExt != null) && - (ext == null || type == null || req == null)) { - ext = ExtensionType.EXT_STATUS_REQUEST; - type = statReqExt.getType(); - if (type == StatusRequestType.OCSP) { - // If the type is OCSP, then the request is guaranteed - // to be OCSPStatusRequest - OCSPStatusRequest ocspReq = - (OCSPStatusRequest)statReqExt.getRequest(); - if (ocspReq.getResponderIds().isEmpty()) { - req = ocspReq; - } else { - if (debug != null && Debug.isOn("handshake")) { - req = null; - System.out.println("Warning: No suitable request " + - "found in the status_request extension."); - } - } - } - } - - // If, after walking through the extensions we were unable to - // find a suitable StatusRequest, then stapling is disabled. - // The ext, type and req variables must have been set to continue. - if (type == null || req == null || ext == null) { - return null; - } - - // Get the OCSP responses from the StatusResponseManager - StatusResponseManager statRespMgr = - sslContext.getStatusResponseManager(); - if (statRespMgr != null) { - responses = statRespMgr.get(type, req, certs, statusRespTimeout, - TimeUnit.MILLISECONDS); - if (!responses.isEmpty()) { - // If this RFC 6066-style stapling (SSL cert only) then the - // response cannot be zero length - if (type == StatusRequestType.OCSP) { - byte[] respDER = responses.get(certs[0]); - if (respDER == null || respDER.length <= 0) { - return null; - } - } - params = new StaplingParameters(ext, type, req, responses); - } - } else { - // This should not happen, but if lazy initialization of the - // StatusResponseManager doesn't occur we should turn off stapling. - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning: lazy initialization " + - "of the StatusResponseManager failed. " + - "Stapling has been disabled."); - } - } - - return params; - } - - /** - * Inner class used to hold stapling parameters needed by the handshaker - * when stapling is active. - */ - private class StaplingParameters { - private final ExtensionType statusRespExt; - private final StatusRequestType statReqType; - private final StatusRequest statReqData; - private final Map<X509Certificate, byte[]> responseMap; - - StaplingParameters(ExtensionType ext, StatusRequestType type, - StatusRequest req, Map<X509Certificate, byte[]> responses) { - statusRespExt = ext; - statReqType = type; - statReqData = req; - responseMap = responses; - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerHello.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,1458 @@ +/* + * 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.AlgorithmConstraints; +import java.security.GeneralSecurityException; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import sun.security.ssl.CipherSuite.KeyExchange; +import sun.security.ssl.ClientHello.ClientHelloMessage; +import sun.security.ssl.SSLCipher.SSLReadCipher; +import sun.security.ssl.SSLCipher.SSLWriteCipher; +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec; + +/** + * Pack of the ServerHello/HelloRetryRequest handshake message. + */ +final class ServerHello { + static final SSLConsumer handshakeConsumer = + new ServerHelloConsumer(); + static final HandshakeProducer t12HandshakeProducer = + new T12ServerHelloProducer(); + static final HandshakeProducer t13HandshakeProducer = + new T13ServerHelloProducer(); + static final HandshakeProducer hrrHandshakeProducer = + new T13HelloRetryRequestProducer(); + + static final HandshakeProducer hrrReproducer = + new T13HelloRetryRequestReproducer(); + + private static final HandshakeConsumer t12HandshakeConsumer = + new T12ServerHelloConsumer(); + private static final HandshakeConsumer t13HandshakeConsumer = + new T13ServerHelloConsumer(); + + private static final HandshakeConsumer d12HandshakeConsumer = + new T12ServerHelloConsumer(); + private static final HandshakeConsumer d13HandshakeConsumer = + new T13ServerHelloConsumer(); + + private static final HandshakeConsumer t13HrrHandshakeConsumer = + new T13HelloRetryRequestConsumer(); + private static final HandshakeConsumer d13HrrHandshakeConsumer = + new T13HelloRetryRequestConsumer(); + + /** + * The ServerHello handshake message. + */ + static final class ServerHelloMessage extends HandshakeMessage { + final ProtocolVersion serverVersion; // TLS 1.3 legacy + final RandomCookie serverRandom; + final SessionId sessionId; // TLS 1.3 legacy + final CipherSuite cipherSuite; + final byte compressionMethod; // TLS 1.3 legacy + final SSLExtensions extensions; + + // The HelloRetryRequest producer needs to use the ClientHello message + // for cookie generation. Please don't use this field for other + // purpose unless it is really necessary. + final ClientHelloMessage clientHello; + + // Reserved for HelloRetryRequest consumer. Please don't use this + // field for other purpose unless it is really necessary. + final ByteBuffer handshakeRecord; + + ServerHelloMessage(HandshakeContext context, + ProtocolVersion serverVersion, SessionId sessionId, + CipherSuite cipherSuite, RandomCookie serverRandom, + ClientHelloMessage clientHello) { + super(context); + + this.serverVersion = serverVersion; + this.serverRandom = serverRandom; + this.sessionId = sessionId; + this.cipherSuite = cipherSuite; + this.compressionMethod = 0x00; // Don't support compression. + this.extensions = new SSLExtensions(this); + + // Reserve the ClientHello message for cookie generation. + this.clientHello = clientHello; + + // The handshakeRecord field is used for HelloRetryRequest consumer + // only. It's fine to set it to null for generating side of the + // ServerHello/HelloRetryRequest message. + this.handshakeRecord = null; + } + + ServerHelloMessage(HandshakeContext context, + ByteBuffer m) throws IOException { + super(context); + + // Reserve for HelloRetryRequest consumer if needed. + this.handshakeRecord = m.duplicate(); + + byte major = m.get(); + byte minor = m.get(); + this.serverVersion = ProtocolVersion.valueOf(major, minor); + if (this.serverVersion == null) { + // The client should only request for known protocol versions. + context.conContext.fatal(Alert.PROTOCOL_VERSION, + "Unsupported protocol version: " + + ProtocolVersion.nameOf(major, minor)); + } + + this.serverRandom = new RandomCookie(m); + this.sessionId = new SessionId(Record.getBytes8(m)); + sessionId.checkLength(serverVersion.id); + + + int cipherSuiteId = Record.getInt16(m); + this.cipherSuite = CipherSuite.valueOf(cipherSuiteId); + if (cipherSuite == null || !context.isNegotiable(cipherSuite)) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Server selected improper ciphersuite " + + CipherSuite.nameOf(cipherSuiteId)); + } + + this.compressionMethod = m.get(); + if (compressionMethod != 0) { + context.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "compression type not supported, " + compressionMethod); + } + + SSLExtension[] supportedExtensions; + if (serverRandom.isHelloRetryRequest()) { + supportedExtensions = context.sslConfig.getEnabledExtensions( + SSLHandshake.HELLO_RETRY_REQUEST); + } else { + supportedExtensions = context.sslConfig.getEnabledExtensions( + SSLHandshake.SERVER_HELLO); + } + + if (m.hasRemaining()) { + this.extensions = + new SSLExtensions(this, m, supportedExtensions); + } else { + this.extensions = new SSLExtensions(this); + } + + // The clientHello field is used for HelloRetryRequest producer + // only. It's fine to set it to null for receiving side of + // ServerHello/HelloRetryRequest message. + this.clientHello = null; // not used, let it be null; + } + + @Override + public SSLHandshake handshakeType() { + return serverRandom.isHelloRetryRequest() ? + SSLHandshake.HELLO_RETRY_REQUEST : SSLHandshake.SERVER_HELLO; + } + + @Override + public int messageLength() { + // almost fixed header size, except session ID and extensions: + // major + minor = 2 + // random = 32 + // session ID len field = 1 + // cipher suite = 2 + // compression = 1 + // extensions: if present, 2 + length of extensions + // In TLS 1.3, use of certain extensions is mandatory. + return 38 + sessionId.length() + extensions.length(); + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt8(serverVersion.major); + hos.putInt8(serverVersion.minor); + hos.write(serverRandom.randomBytes); + hos.putBytes8(sessionId.getId()); + hos.putInt8((cipherSuite.id >> 8) & 0xFF); + hos.putInt8(cipherSuite.id & 0xff); + hos.putInt8(compressionMethod); + + extensions.send(hos); // In TLS 1.3, use of certain + // extensions is mandatory. + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"{0}\": '{'\n" + + " \"server version\" : \"{1}\",\n" + + " \"random\" : \"{2}\",\n" + + " \"session id\" : \"{3}\",\n" + + " \"cipher suite\" : \"{4}\",\n" + + " \"compression methods\" : \"{5}\",\n" + + " \"extensions\" : [\n" + + "{6}\n" + + " ]\n" + + "'}'", + Locale.ENGLISH); + Object[] messageFields = { + serverRandom.isHelloRetryRequest() ? + "HelloRetryRequest" : "ServerHello", + serverVersion.name, + Utilities.toHexString(serverRandom.randomBytes), + sessionId.toString(), + cipherSuite.name + "(" + + Utilities.byte16HexString(cipherSuite.id) + ")", + Utilities.toHexString(compressionMethod), + Utilities.indent(extensions.toString(), " ") + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "ServerHello" handshake message producer. + */ + private static final class T12ServerHelloProducer + implements HandshakeProducer { + + // Prevent instantiation of this class. + private T12ServerHelloProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + ClientHelloMessage clientHello = (ClientHelloMessage)message; + + // If client hasn't specified a session we can resume, start a + // new one and choose its cipher suite and compression options, + // unless new session creation is disabled for this connection! + if (!shc.isResumption || shc.resumingSession == null) { + if (!shc.sslConfig.enableSessionCreation) { + throw new SSLException( + "Not resumption, and no new session is allowed"); + } + + if (shc.localSupportedSignAlgs == null) { + shc.localSupportedSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.algorithmConstraints, shc.activeProtocols); + } + + SSLSessionImpl session = + new SSLSessionImpl(shc, CipherSuite.C_NULL); + session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize); + shc.handshakeSession = session; + + // consider the handshake extension impact + SSLExtension[] enabledExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol); + clientHello.extensions.consumeOnTrade(shc, enabledExtensions); + + // negotiate the cipher suite. + KeyExchangeProperties credentials = + chooseCipherSuite(shc, clientHello); + if (credentials == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "no cipher suites in common"); + + return null; // make the compiler happy + } + shc.negotiatedCipherSuite = credentials.cipherSuite; + shc.handshakeKeyExchange = credentials.keyExchange; + shc.handshakeSession.setSuite(credentials.cipherSuite); + shc.handshakePossessions.addAll( + Arrays.asList(credentials.possessions)); + shc.handshakeHash.determine( + shc.negotiatedProtocol, shc.negotiatedCipherSuite); + + // Check the incoming OCSP stapling extensions and attempt + // to get responses. If the resulting stapleParams is non + // null, it implies that stapling is enabled on the server side. + shc.stapleParams = StatusResponseManager.processStapling(shc); + shc.staplingActive = (shc.stapleParams != null); + + // update the responders + SSLKeyExchange ke = credentials.keyExchange; + if (ke != null) { + for (Map.Entry<Byte, HandshakeProducer> me : + ke.getHandshakeProducers(shc)) { + shc.handshakeProducers.put( + me.getKey(), me.getValue()); + } + } + + if ((ke != null) && + (shc.sslConfig.clientAuthType != + ClientAuthType.CLIENT_AUTH_NONE) && + !shc.negotiatedCipherSuite.isAnonymous()) { + for (SSLHandshake hs : + ke.getRelatedHandshakers(shc)) { + if (hs == SSLHandshake.CERTIFICATE) { + shc.handshakeProducers.put( + SSLHandshake.CERTIFICATE_REQUEST.id, + SSLHandshake.CERTIFICATE_REQUEST); + break; + } + } + } + shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id, + SSLHandshake.SERVER_HELLO_DONE); + } else { + shc.handshakeSession = shc.resumingSession; + shc.negotiatedProtocol = + shc.resumingSession.getProtocolVersion(); + shc.negotiatedCipherSuite = shc.resumingSession.getSuite(); + shc.handshakeHash.determine( + shc.negotiatedProtocol, shc.negotiatedCipherSuite); + } + + // Generate the ServerHello handshake message. + ServerHelloMessage shm = new ServerHelloMessage(shc, + shc.negotiatedProtocol, + shc.handshakeSession.getSessionId(), + shc.negotiatedCipherSuite, + new RandomCookie(shc), + clientHello); + shc.serverHelloRandom = shm.serverRandom; + + // Produce extensions for ServerHello handshake message. + SSLExtension[] serverHelloExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); + shm.extensions.produce(shc, serverHelloExtensions); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced ServerHello handshake message", shm); + } + + // Output the handshake message. + shm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + if (shc.isResumption && shc.resumingSession != null) { + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kdg == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + shc.negotiatedProtocol); + } else { + shc.handshakeKeyDerivation = kdg.createKeyDerivation( + shc, shc.resumingSession.getMasterSecret()); + } + + // update the responders + shc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + } + + // The handshake message has been delivered. + return null; + } + + private static KeyExchangeProperties chooseCipherSuite( + ServerHandshakeContext shc, + ClientHelloMessage clientHello) throws IOException { + List<CipherSuite> preferred; + List<CipherSuite> proposed; + if (shc.sslConfig.preferLocalCipherSuites) { + preferred = shc.activeCipherSuites; + proposed = clientHello.cipherSuites; + } else { + preferred = clientHello.cipherSuites; + proposed = shc.activeCipherSuites; + } + + List<CipherSuite> legacySuites = new LinkedList<>(); + for (CipherSuite cs : preferred) { + if (!HandshakeContext.isNegotiable( + proposed, shc.negotiatedProtocol, cs)) { + continue; + } + + if (shc.sslConfig.clientAuthType == + ClientAuthType.CLIENT_AUTH_REQUIRED) { + if ((cs.keyExchange == KeyExchange.K_DH_ANON) || + (cs.keyExchange == KeyExchange.K_ECDH_ANON)) { + continue; + } + } + + SSLKeyExchange ke = SSLKeyExchange.valueOf( + cs.keyExchange, shc.negotiatedProtocol); + if (ke == null) { + continue; + } + if (!ServerHandshakeContext.legacyAlgorithmConstraints.permits( + null, cs.name, null)) { + legacySuites.add(cs); + continue; + } + + SSLPossession[] hcds = ke.createPossessions(shc); + if ((hcds == null) || (hcds.length == 0)) { + continue; + } + + // The cipher suite has been negotiated. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("use cipher suite " + cs.name); + } + + return new KeyExchangeProperties(cs, ke, hcds); + } + + for (CipherSuite cs : legacySuites) { + SSLKeyExchange ke = SSLKeyExchange.valueOf( + cs.keyExchange, shc.negotiatedProtocol); + if (ke != null) { + SSLPossession[] hcds = ke.createPossessions(shc); + if ((hcds != null) && (hcds.length != 0)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "use legacy cipher suite " + cs.name); + } + return new KeyExchangeProperties(cs, ke, hcds); + } + } + } + + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "no cipher suites in common"); + + return null; // make the compiler happy. + } + + private static final class KeyExchangeProperties { + final CipherSuite cipherSuite; + final SSLKeyExchange keyExchange; + final SSLPossession[] possessions; + + private KeyExchangeProperties(CipherSuite cipherSuite, + SSLKeyExchange keyExchange, SSLPossession[] possessions) { + this.cipherSuite = cipherSuite; + this.keyExchange = keyExchange; + this.possessions = possessions; + } + } + } + + /** + * The "ServerHello" handshake message producer. + */ + private static final + class T13ServerHelloProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13ServerHelloProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + ClientHelloMessage clientHello = (ClientHelloMessage)message; + + // If client hasn't specified a session we can resume, start a + // new one and choose its cipher suite and compression options, + // unless new session creation is disabled for this connection! + if (!shc.isResumption || shc.resumingSession == null) { + if (!shc.sslConfig.enableSessionCreation) { + throw new SSLException( + "Not resumption, and no new session is allowed"); + } + + if (shc.localSupportedSignAlgs == null) { + shc.localSupportedSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.algorithmConstraints, shc.activeProtocols); + } + + SSLSessionImpl session = + new SSLSessionImpl(shc, CipherSuite.C_NULL); + session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize); + shc.handshakeSession = session; + + // consider the handshake extension impact + SSLExtension[] enabledExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol); + clientHello.extensions.consumeOnTrade(shc, enabledExtensions); + + // negotiate the cipher suite. + CipherSuite cipherSuite = chooseCipherSuite(shc, clientHello); + if (cipherSuite == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "no cipher suites in common"); + return null; // make the compiler happy + } + shc.negotiatedCipherSuite = cipherSuite; + shc.handshakeSession.setSuite(cipherSuite); + shc.handshakeHash.determine( + shc.negotiatedProtocol, shc.negotiatedCipherSuite); + } else { + shc.handshakeSession = shc.resumingSession; + + // consider the handshake extension impact + SSLExtension[] enabledExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol); + clientHello.extensions.consumeOnTrade(shc, enabledExtensions); + + shc.negotiatedProtocol = + shc.resumingSession.getProtocolVersion(); + shc.negotiatedCipherSuite = shc.resumingSession.getSuite(); + shc.handshakeHash.determine( + shc.negotiatedProtocol, shc.negotiatedCipherSuite); + + setUpPskKD(shc, + shc.resumingSession.consumePreSharedKey().get()); + + // The session can't be resumed again---remove it from cache + SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) + shc.sslContext.engineGetServerSessionContext(); + sessionCache.remove(shc.resumingSession.getSessionId()); + } + + // update the responders + shc.handshakeProducers.put(SSLHandshake.ENCRYPTED_EXTENSIONS.id, + SSLHandshake.ENCRYPTED_EXTENSIONS); + shc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + + // Generate the ServerHello handshake message. + ServerHelloMessage shm = new ServerHelloMessage(shc, + ProtocolVersion.TLS12, // use legacy version + clientHello.sessionId, // echo back + shc.negotiatedCipherSuite, + new RandomCookie(shc), + clientHello); + shc.serverHelloRandom = shm.serverRandom; + + // Produce extensions for ServerHello handshake message. + SSLExtension[] serverHelloExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol); + shm.extensions.produce(shc, serverHelloExtensions); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced ServerHello handshake message", shm); + } + + // Output the handshake message. + shm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // Change client/server handshake traffic secrets. + // Refresh handshake hash + shc.handshakeHash.update(); + + // Change client/server handshake traffic secrets. + SSLKeyExchange ke = shc.handshakeKeyExchange; + if (ke == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not negotiated key shares"); + return null; // make the compiler happy + } + + SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc); + SecretKey handshakeSecret = handshakeKD.deriveKey( + "TlsHandshakeSecret", null); + + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol); + if (kdg == null) { + // unlikely + shc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + shc.negotiatedProtocol); + return null; // make the compiler happy + } + + SSLKeyDerivation kd = + new SSLSecretDerivation(shc, handshakeSecret); + + // update the handshake traffic read keys. + SecretKey readSecret = kd.deriveKey( + "TlsClientHandshakeTrafficSecret", null); + SSLKeyDerivation readKD = + kdg.createKeyDerivation(shc, readSecret); + SecretKey readKey = readKD.deriveKey( + "TlsKey", null); + SecretKey readIvSecret = readKD.deriveKey( + "TlsIv", null); + IvParameterSpec readIv = + new IvParameterSpec(readIvSecret.getEncoded()); + SSLReadCipher readCipher; + try { + readCipher = + shc.negotiatedCipherSuite.bulkCipher.createReadCipher( + Authenticator.valueOf(shc.negotiatedProtocol), + shc.negotiatedProtocol, readKey, readIv, + shc.sslContext.getSecureRandom()); + } catch (GeneralSecurityException gse) { + // unlikely + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Missing cipher algorithm", gse); + return null; // make the compiler happy + } + + shc.baseReadSecret = readSecret; + shc.conContext.inputRecord.changeReadCiphers(readCipher); + + // update the handshake traffic write secret. + SecretKey writeSecret = kd.deriveKey( + "TlsServerHandshakeTrafficSecret", null); + SSLKeyDerivation writeKD = + kdg.createKeyDerivation(shc, writeSecret); + SecretKey writeKey = writeKD.deriveKey( + "TlsKey", null); + SecretKey writeIvSecret = writeKD.deriveKey( + "TlsIv", null); + IvParameterSpec writeIv = + new IvParameterSpec(writeIvSecret.getEncoded()); + SSLWriteCipher writeCipher; + try { + writeCipher = + shc.negotiatedCipherSuite.bulkCipher.createWriteCipher( + Authenticator.valueOf(shc.negotiatedProtocol), + shc.negotiatedProtocol, writeKey, writeIv, + shc.sslContext.getSecureRandom()); + } catch (GeneralSecurityException gse) { + // unlikely + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Missing cipher algorithm", gse); + return null; // make the compiler happy + } + + shc.baseWriteSecret = writeSecret; + shc.conContext.outputRecord.changeWriteCiphers( + writeCipher, (clientHello.sessionId.length() != 0)); + + // Update the context for master key derivation. + shc.handshakeKeyDerivation = kd; + + // The handshake message has been delivered. + return null; + } + + private static CipherSuite chooseCipherSuite( + ServerHandshakeContext shc, + ClientHelloMessage clientHello) throws IOException { + List<CipherSuite> preferred; + List<CipherSuite> proposed; + if (shc.sslConfig.preferLocalCipherSuites) { + preferred = shc.activeCipherSuites; + proposed = clientHello.cipherSuites; + } else { + preferred = clientHello.cipherSuites; + proposed = shc.activeCipherSuites; + } + + CipherSuite legacySuite = null; + AlgorithmConstraints legacyConstraints = + ServerHandshakeContext.legacyAlgorithmConstraints; + for (CipherSuite cs : preferred) { + if (!HandshakeContext.isNegotiable( + proposed, shc.negotiatedProtocol, cs)) { + continue; + } + + if ((legacySuite == null) && + !legacyConstraints.permits(null, cs.name, null)) { + legacySuite = cs; + continue; + } + + // The cipher suite has been negotiated. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("use cipher suite " + cs.name); + } + return cs; + } + + if (legacySuite != null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "use legacy cipher suite " + legacySuite.name); + } + return legacySuite; + } + + // no cipher suites in common + return null; + } + } + + /** + * The "HelloRetryRequest" handshake message producer. + */ + private static final + class T13HelloRetryRequestProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13HelloRetryRequestProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + ServerHandshakeContext shc = (ServerHandshakeContext) context; + ClientHelloMessage clientHello = (ClientHelloMessage) message; + + // negotiate the cipher suite. + CipherSuite cipherSuite = + T13ServerHelloProducer.chooseCipherSuite(shc, clientHello); + if (cipherSuite == null) { + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "no cipher suites in common for hello retry request"); + return null; // make the compiler happy + } + + ServerHelloMessage hhrm = new ServerHelloMessage(shc, + ProtocolVersion.TLS12, // use legacy version + clientHello.sessionId, // echo back + cipherSuite, + RandomCookie.hrrRandom, + clientHello + ); + + shc.negotiatedCipherSuite = cipherSuite; + shc.handshakeHash.determine( + shc.negotiatedProtocol, shc.negotiatedCipherSuite); + + // Produce extensions for HelloRetryRequest handshake message. + SSLExtension[] serverHelloExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.HELLO_RETRY_REQUEST, shc.negotiatedProtocol); + hhrm.extensions.produce(shc, serverHelloExtensions); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced HelloRetryRequest handshake message", hhrm); + } + + // Output the handshake message. + hhrm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // Stateless, shall we 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 "HelloRetryRequest" handshake message reproducer. + */ + private static final + class T13HelloRetryRequestReproducer implements HandshakeProducer { + // Prevent instantiation of this class. + private T13HelloRetryRequestReproducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + ServerHandshakeContext shc = (ServerHandshakeContext) context; + ClientHelloMessage clientHello = (ClientHelloMessage) message; + + // negotiate the cipher suite. + CipherSuite cipherSuite = shc.negotiatedCipherSuite; + ServerHelloMessage hhrm = new ServerHelloMessage(shc, + ProtocolVersion.TLS12, // use legacy version + clientHello.sessionId, // echo back + cipherSuite, + RandomCookie.hrrRandom, + clientHello + ); + + // Produce extensions for HelloRetryRequest handshake message. + SSLExtension[] serverHelloExtensions = + shc.sslConfig.getEnabledExtensions( + SSLHandshake.MESSAGE_HASH, shc.negotiatedProtocol); + hhrm.extensions.produce(shc, serverHelloExtensions); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Reproduced HelloRetryRequest handshake message", hhrm); + } + + HandshakeOutStream hos = new HandshakeOutStream(null); + hhrm.write(hos); + + return hos.toByteArray(); + } + } + + /** + * The "ServerHello" handshake message consumer. + */ + private static final + class ServerHelloConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ServerHelloConsumer() { + // 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.SERVER_HELLO.id); + if (!chc.handshakeConsumers.isEmpty()) { + // DTLS 1.0/1.2 + chc.handshakeConsumers.remove( + SSLHandshake.HELLO_VERIFY_REQUEST.id); + } + if (!chc.handshakeConsumers.isEmpty()) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "No more message expected before ServerHello is processed"); + } + + ServerHelloMessage shm = new ServerHelloMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Consuming ServerHello handshake message", shm); + } + + if (shm.serverRandom.isHelloRetryRequest()) { + onHelloRetryRequest(chc, shm); + } else { + onServerHello(chc, shm); + } + } + + private void onHelloRetryRequest(ClientHandshakeContext chc, + ServerHelloMessage helloRetryRequest) throws IOException { + // Negotiate protocol version. + // + // Check and launch SupportedVersions. + SSLExtension[] extTypes = new SSLExtension[] { + SSLExtension.HRR_SUPPORTED_VERSIONS + }; + helloRetryRequest.extensions.consumeOnLoad(chc, extTypes); + + ProtocolVersion serverVersion; + SHSupportedVersionsSpec svs = + (SHSupportedVersionsSpec)chc.handshakeExtensions.get( + SSLExtension.HRR_SUPPORTED_VERSIONS); + if (svs != null) { + serverVersion = // could be null + ProtocolVersion.valueOf(svs.selectedVersion); + } else { + serverVersion = helloRetryRequest.serverVersion; + } + + if (!chc.activeProtocols.contains(serverVersion)) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "The server selected protocol version " + serverVersion + + " is not accepted by client preferences " + + chc.activeProtocols); + } + + if (!serverVersion.useTLS13PlusSpec()) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "Unexpected HelloRetryRequest for " + serverVersion.name); + } + + chc.negotiatedProtocol = serverVersion; + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Negotiated protocol version: " + serverVersion.name); + } + + // TLS 1.3 key share extension may have produced client + // possessions for TLS 1.3 key exchanges. + // + // Clean up before producing new client key share possessions. + chc.handshakePossessions.clear(); + + if (serverVersion.isDTLS) { + d13HrrHandshakeConsumer.consume(chc, helloRetryRequest); + } else { + t13HrrHandshakeConsumer.consume(chc, helloRetryRequest); + } + } + + private void onServerHello(ClientHandshakeContext chc, + ServerHelloMessage serverHello) throws IOException { + // Negotiate protocol version. + // + // Check and launch SupportedVersions. + SSLExtension[] extTypes = new SSLExtension[] { + SSLExtension.SH_SUPPORTED_VERSIONS + }; + serverHello.extensions.consumeOnLoad(chc, extTypes); + + ProtocolVersion serverVersion; + SHSupportedVersionsSpec svs = + (SHSupportedVersionsSpec)chc.handshakeExtensions.get( + SSLExtension.SH_SUPPORTED_VERSIONS); + if (svs != null) { + serverVersion = // could be null + ProtocolVersion.valueOf(svs.selectedVersion); + } else { + serverVersion = serverHello.serverVersion; + } + + if (!chc.activeProtocols.contains(serverVersion)) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "The server selected protocol version " + serverVersion + + " is not accepted by client preferences " + + chc.activeProtocols); + } + + chc.negotiatedProtocol = serverVersion; + if (!chc.conContext.isNegotiated) { + chc.conContext.protocolVersion = chc.negotiatedProtocol; + chc.conContext.outputRecord.setVersion(chc.negotiatedProtocol); + } + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Negotiated protocol version: " + serverVersion.name); + } + + if (serverHello.serverRandom.isVersionDowngrade(chc)) { + chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "A potential protocol version downgrade attack"); + } + + // Consume the handshake message for the specific protocol version. + if (serverVersion.isDTLS) { + if (serverVersion.useTLS13PlusSpec()) { + d13HandshakeConsumer.consume(chc, serverHello); + } else { + // TLS 1.3 key share extension may have produced client + // possessions for TLS 1.3 key exchanges. + chc.handshakePossessions.clear(); + + d12HandshakeConsumer.consume(chc, serverHello); + } + } else { + if (serverVersion.useTLS13PlusSpec()) { + t13HandshakeConsumer.consume(chc, serverHello); + } else { + // TLS 1.3 key share extension may have produced client + // possessions for TLS 1.3 key exchanges. + chc.handshakePossessions.clear(); + + t12HandshakeConsumer.consume(chc, serverHello); + } + } + } + } + + private static final + class T12ServerHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private T12ServerHelloConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + ServerHelloMessage serverHello = (ServerHelloMessage)message; + if (!chc.isNegotiable(serverHello.serverVersion)) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "Server chose " + serverHello.serverVersion + + ", but that protocol version is not enabled or " + + "not supported by the client."); + } + + // chc.negotiatedProtocol = serverHello.serverVersion; + chc.negotiatedCipherSuite = serverHello.cipherSuite; + chc.handshakeHash.determine( + chc.negotiatedProtocol, chc.negotiatedCipherSuite); + chc.serverHelloRandom = serverHello.serverRandom; + if (chc.negotiatedCipherSuite.keyExchange == null) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "TLS 1.2 or prior version does not support the " + + "server cipher suite: " + chc.negotiatedCipherSuite.name); + } + + // + // validate + // + + // Check and launch the "renegotiation_info" extension. + SSLExtension[] extTypes = new SSLExtension[] { + SSLExtension.SH_RENEGOTIATION_INFO + }; + serverHello.extensions.consumeOnLoad(chc, extTypes); + + // Is it session resuming? + if (chc.resumingSession != null) { + // we tried to resume, let's see what the server decided + if (serverHello.sessionId.equals( + chc.resumingSession.getSessionId())) { + // server resumed the session, let's make sure everything + // checks out + + // Verify that the session ciphers are unchanged. + CipherSuite sessionSuite = chc.resumingSession.getSuite(); + if (chc.negotiatedCipherSuite != sessionSuite) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "Server returned wrong cipher suite for session"); + } + + // verify protocol version match + ProtocolVersion sessionVersion = + chc.resumingSession.getProtocolVersion(); + if (chc.negotiatedProtocol != sessionVersion) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "Server resumed with wrong protocol version"); + } + + // looks fine; resume it. + chc.isResumption = true; + chc.resumingSession.setAsSessionResumption(true); + chc.handshakeSession = chc.resumingSession; + } else { + // we wanted to resume, but the server refused + // + // Invalidate the session for initial handshake in case + // of reusing next time. + if (chc.resumingSession != null) { + chc.resumingSession.invalidate(); + chc.resumingSession = null; + } + chc.isResumption = false; + if (!chc.sslConfig.enableSessionCreation) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "New session creation is disabled"); + } + } + } + + // Check and launch ClientHello extensions. + extTypes = chc.sslConfig.getEnabledExtensions( + SSLHandshake.SERVER_HELLO); + serverHello.extensions.consumeOnLoad(chc, extTypes); + + if (!chc.isResumption) { + if (chc.resumingSession != null) { + // in case the resumption happens next time. + chc.resumingSession.invalidate(); + chc.resumingSession = null; + } + + if (!chc.sslConfig.enableSessionCreation) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "New session creation is disabled"); + } + chc.handshakeSession = new SSLSessionImpl(chc, + chc.negotiatedCipherSuite, + serverHello.sessionId); + chc.handshakeSession.setMaximumPacketSize( + chc.sslConfig.maximumPacketSize); + } + + // + // update + // + serverHello.extensions.consumeOnTrade(chc, extTypes); + + // update the consumers and producers + if (chc.isResumption) { + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kdg == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + } else { + chc.handshakeKeyDerivation = kdg.createKeyDerivation( + chc, chc.resumingSession.getMasterSecret()); + } + + chc.conContext.consumers.putIfAbsent( + ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t10Consumer); + chc.handshakeConsumers.put( + SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + } else { + SSLKeyExchange ke = SSLKeyExchange.valueOf( + chc.negotiatedCipherSuite.keyExchange, + chc.negotiatedProtocol); + chc.handshakeKeyExchange = ke; + if (ke != null) { + for (SSLHandshake handshake : + ke.getRelatedHandshakers(chc)) { + chc.handshakeConsumers.put(handshake.id, handshake); + } + } + + chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO_DONE.id, + SSLHandshake.SERVER_HELLO_DONE); + } + + // + // produce + // + // Need no new handshake message producers here. + } + } + + private static void setUpPskKD(HandshakeContext hc, + SecretKey psk) throws SSLHandshakeException { + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Using PSK to derive early secret"); + } + + try { + CipherSuite.HashAlg hashAlg = hc.negotiatedCipherSuite.hashAlg; + HKDF hkdf = new HKDF(hashAlg.name); + byte[] zeros = new byte[hashAlg.hashLength]; + SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret"); + hc.handshakeKeyDerivation = + new SSLSecretDerivation(hc, earlySecret); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } + + private static final + class T13ServerHelloConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private T13ServerHelloConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + ServerHelloMessage serverHello = (ServerHelloMessage)message; + if (serverHello.serverVersion != ProtocolVersion.TLS12) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "The ServerHello.legacy_version field is not TLS 1.2"); + } + + chc.negotiatedCipherSuite = serverHello.cipherSuite; + chc.handshakeHash.determine( + chc.negotiatedProtocol, chc.negotiatedCipherSuite); + chc.serverHelloRandom = serverHello.serverRandom; + + // + // validate + // + + // Check and launch ServerHello extensions. + SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions( + SSLHandshake.SERVER_HELLO); + serverHello.extensions.consumeOnLoad(chc, extTypes); + if (!chc.isResumption) { + if (chc.resumingSession != null) { + // in case the resumption happens next time. + chc.resumingSession.invalidate(); + chc.resumingSession = null; + } + + if (!chc.sslConfig.enableSessionCreation) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "New session creation is disabled"); + } + chc.handshakeSession = new SSLSessionImpl(chc, + chc.negotiatedCipherSuite, + serverHello.sessionId); + chc.handshakeSession.setMaximumPacketSize( + chc.sslConfig.maximumPacketSize); + } else { + // The PSK is consumed to allow it to be deleted + Optional<SecretKey> psk = + chc.resumingSession.consumePreSharedKey(); + if(!psk.isPresent()) { + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "No PSK available. Unable to resume."); + } + + chc.handshakeSession = chc.resumingSession; + + setUpPskKD(chc, psk.get()); + } + + // + // update + // + serverHello.extensions.consumeOnTrade(chc, extTypes); + + // Change client/server handshake traffic secrets. + // Refresh handshake hash + chc.handshakeHash.update(); + + SSLKeyExchange ke = chc.handshakeKeyExchange; + if (ke == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not negotiated key shares"); + return; // make the compiler happy + } + + SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc); + SecretKey handshakeSecret = handshakeKD.deriveKey( + "TlsHandshakeSecret", null); + SSLTrafficKeyDerivation kdg = + SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol); + if (kdg == null) { + // unlikely + chc.conContext.fatal(Alert.INTERNAL_ERROR, + "Not supported key derivation: " + + chc.negotiatedProtocol); + return; // make the compiler happy + } + + SSLKeyDerivation secretKD = + new SSLSecretDerivation(chc, handshakeSecret); + + // update the handshake traffic read keys. + SecretKey readSecret = secretKD.deriveKey( + "TlsServerHandshakeTrafficSecret", null); + + SSLKeyDerivation readKD = + kdg.createKeyDerivation(chc, readSecret); + SecretKey readKey = readKD.deriveKey( + "TlsKey", null); + SecretKey readIvSecret = readKD.deriveKey( + "TlsIv", null); + IvParameterSpec readIv = + new IvParameterSpec(readIvSecret.getEncoded()); + SSLReadCipher readCipher; + try { + readCipher = + chc.negotiatedCipherSuite.bulkCipher.createReadCipher( + Authenticator.valueOf(chc.negotiatedProtocol), + chc.negotiatedProtocol, readKey, readIv, + chc.sslContext.getSecureRandom()); + } catch (GeneralSecurityException gse) { + // unlikely + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Missing cipher algorithm", gse); + return; // make the compiler happy + } + + chc.baseReadSecret = readSecret; + chc.conContext.inputRecord.changeReadCiphers(readCipher); + + // update the handshake traffic write keys. + SecretKey writeSecret = secretKD.deriveKey( + "TlsClientHandshakeTrafficSecret", null); + SSLKeyDerivation writeKD = + kdg.createKeyDerivation(chc, writeSecret); + SecretKey writeKey = writeKD.deriveKey( + "TlsKey", null); + SecretKey writeIvSecret = writeKD.deriveKey( + "TlsIv", null); + IvParameterSpec writeIv = + new IvParameterSpec(writeIvSecret.getEncoded()); + SSLWriteCipher writeCipher; + try { + writeCipher = + chc.negotiatedCipherSuite.bulkCipher.createWriteCipher( + Authenticator.valueOf(chc.negotiatedProtocol), + chc.negotiatedProtocol, writeKey, writeIv, + chc.sslContext.getSecureRandom()); + } catch (GeneralSecurityException gse) { + // unlikely + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Missing cipher algorithm", gse); + return; // make the compiler happy + } + + chc.baseWriteSecret = writeSecret; + chc.conContext.outputRecord.changeWriteCiphers( + writeCipher, (serverHello.sessionId.length() != 0)); + + // Should use resumption_master_secret for TLS 1.3. + // chc.handshakeSession.setMasterSecret(masterSecret); + + // Update the context for master key derivation. + chc.handshakeKeyDerivation = secretKD; + + // update the consumers and producers + // + // The server sends a dummy change_cipher_spec record immediately + // after its first handshake message. This may either be after a + // ServerHello or a HelloRetryRequest. + chc.conContext.consumers.putIfAbsent( + ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t13Consumer); + + chc.handshakeConsumers.put( + SSLHandshake.ENCRYPTED_EXTENSIONS.id, + SSLHandshake.ENCRYPTED_EXTENSIONS); + + // Support cert authentication only, when not PSK. + chc.handshakeConsumers.put( + SSLHandshake.CERTIFICATE_REQUEST.id, + SSLHandshake.CERTIFICATE_REQUEST); + chc.handshakeConsumers.put( + SSLHandshake.CERTIFICATE.id, + SSLHandshake.CERTIFICATE); + chc.handshakeConsumers.put( + SSLHandshake.CERTIFICATE_VERIFY.id, + SSLHandshake.CERTIFICATE_VERIFY); + + chc.handshakeConsumers.put( + SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + + // + // produce + // + // Need no new handshake message producers here. + } + } + + private static final + class T13HelloRetryRequestConsumer implements HandshakeConsumer { + // Prevent instantiation of this class. + private T13HelloRetryRequestConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + ServerHelloMessage helloRetryRequest = (ServerHelloMessage)message; + if (helloRetryRequest.serverVersion != ProtocolVersion.TLS12) { + chc.conContext.fatal(Alert.PROTOCOL_VERSION, + "The HelloRetryRequest.legacy_version is not TLS 1.2"); + } + + chc.negotiatedCipherSuite = helloRetryRequest.cipherSuite; + + // + // validate + // + + // Check and launch ClientHello extensions. + SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions( + SSLHandshake.HELLO_RETRY_REQUEST); + helloRetryRequest.extensions.consumeOnLoad(chc, extTypes); + + // + // update + // + helloRetryRequest.extensions.consumeOnTrade(chc, extTypes); + + // Change client/server handshake traffic secrets. + // Refresh handshake hash + chc.handshakeHash.finish(); // reset the handshake hash + + // calculate the transcript hash of the 1st ClientHello message + HandshakeOutStream hos = new HandshakeOutStream(null); + try { + chc.initialClientHelloMsg.write(hos); + } catch (IOException ioe) { + // unlikely + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Failed to construct message hash", ioe); + } + chc.handshakeHash.deliver(hos.toByteArray()); + chc.handshakeHash.determine( + chc.negotiatedProtocol, chc.negotiatedCipherSuite); + byte[] clientHelloHash = chc.handshakeHash.digest(); + + // calculate the message_hash + // + // 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) + int hashLen = chc.negotiatedCipherSuite.hashAlg.hashLength; + 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(clientHelloHash, 0, + hashedClientHello, 4, hashLen); + + chc.handshakeHash.finish(); // reset the handshake hash + chc.handshakeHash.deliver(hashedClientHello); + + int hrrBodyLen = helloRetryRequest.handshakeRecord.remaining(); + byte[] hrrMessage = new byte[4 + hrrBodyLen]; + hrrMessage[0] = SSLHandshake.HELLO_RETRY_REQUEST.id; + hrrMessage[1] = (byte)((hrrBodyLen >> 16) & 0xFF); + hrrMessage[2] = (byte)((hrrBodyLen >> 8) & 0xFF); + hrrMessage[3] = (byte)(hrrBodyLen & 0xFF); + + ByteBuffer hrrBody = helloRetryRequest.handshakeRecord.duplicate(); + hrrBody.get(hrrMessage, 4, hrrBodyLen); + + chc.handshakeHash.receive(hrrMessage); + + // Update the initial ClientHello handshake message. + chc.initialClientHelloMsg.extensions.reproduce(chc, + new SSLExtension[] { + SSLExtension.CH_COOKIE, + SSLExtension.CH_KEY_SHARE, + SSLExtension.CH_PRE_SHARED_KEY + }); + + // + // produce response handshake message + // + SSLHandshake.CLIENT_HELLO.produce(context, helloRetryRequest); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ServerHelloDone.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,178 @@ +/* + * 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 ServerHelloDone handshake message. + */ +final class ServerHelloDone { + static final SSLConsumer handshakeConsumer = + new ServerHelloDoneConsumer(); + static final HandshakeProducer handshakeProducer = + new ServerHelloDoneProducer(); + + /** + * The ServerHelloDone handshake message. + */ + static final class ServerHelloDoneMessage extends HandshakeMessage { + ServerHelloDoneMessage(HandshakeContext handshakeContext) { + super(handshakeContext); + } + + ServerHelloDoneMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + if (m.hasRemaining()) { + handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, + "Error parsing ServerHelloDone message: not empty"); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.SERVER_HELLO_DONE; + } + + @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 "ServerHelloDone" handshake message producer. + */ + private static final + class ServerHelloDoneProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private ServerHelloDoneProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + ServerHelloDoneMessage shdm = new ServerHelloDoneMessage(shc); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Produced ServerHelloDone handshake message", shdm); + } + + // Output the handshake message. + shdm.write(shc.handshakeOutput); + shc.handshakeOutput.flush(); + + // + // update + // + shc.handshakeConsumers.put(SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SSLHandshake.CLIENT_KEY_EXCHANGE); + shc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id, + ChangeCipherSpec.t10Consumer); + shc.handshakeConsumers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "ServerHelloDone" handshake message consumer. + */ + private static final + class ServerHelloDoneConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ServerHelloDoneConsumer() { + // 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.clear(); + + ServerHelloDoneMessage shdm = + new ServerHelloDoneMessage(chc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming ServerHelloDone handshake message", shdm); + } + + // + // validate + // + // blank + + // + // update + // + chc.handshakeProducers.put(SSLHandshake.CLIENT_KEY_EXCHANGE.id, + SSLHandshake.CLIENT_KEY_EXCHANGE); + chc.handshakeProducers.put(SSLHandshake.FINISHED.id, + SSLHandshake.FINISHED); + // + // produce + // + SSLHandshake[] probableHandshakeMessages = new SSLHandshake[] { + // full handshake messages + SSLHandshake.CERTIFICATE, + SSLHandshake.CLIENT_KEY_EXCHANGE, + SSLHandshake.CERTIFICATE_VERIFY, + SSLHandshake.FINISHED + }; + + for (SSLHandshake hs : probableHandshakeMessages) { + HandshakeProducer handshakeProducer = + chc.handshakeProducers.remove(hs.id); + if (handshakeProducer != null) { + handshakeProducer.produce(context, null); + } + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/ServerKeyExchange.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,115 @@ +/* + * 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.Map; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +/** + * Pack of the ServerKeyExchange handshake message. + */ +final class ServerKeyExchange { + static final SSLConsumer handshakeConsumer = + new ServerKeyExchangeConsumer(); + static final HandshakeProducer handshakeProducer = + new ServerKeyExchangeProducer(); + + /** + * The "ServerKeyExchange" handshake message producer. + */ + private static final + class ServerKeyExchangeProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private ServerKeyExchangeProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + SSLKeyExchange ke = SSLKeyExchange.valueOf( + shc.negotiatedCipherSuite.keyExchange, + shc.negotiatedProtocol); + if (ke != null) { + for (Map.Entry<Byte, HandshakeProducer> hc : + ke.getHandshakeProducers(shc)) { + if (hc.getKey() == SSLHandshake.SERVER_KEY_EXCHANGE.id) { + return hc.getValue().produce(context, message); + } + } + } + + // not producer defined. + shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No ServerKeyExchange handshake message can be produced."); + return null; // make the compiler happe + } + } + + /** + * The "ServerKeyExchange" handshake message consumer. + */ + private static final + class ServerKeyExchangeConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private ServerKeyExchangeConsumer() { + // 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.SERVER_KEY_EXCHANGE.id); + + SSLKeyExchange ke = SSLKeyExchange.valueOf( + chc.negotiatedCipherSuite.keyExchange, + chc.negotiatedProtocol); + if (ke != null) { + for (Map.Entry<Byte, SSLConsumer> hc : + ke.getHandshakeConsumers(chc)) { + if (hc.getKey() == SSLHandshake.SERVER_KEY_EXCHANGE.id) { + hc.getValue().consume(context, message); + return; + } + } + } + + // no consumer defined. + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected ServerKeyExchange handshake message."); + } + } +} + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java --- a/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/ServerNameExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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,258 +26,561 @@ package sun.security.ssl; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; -import java.util.LinkedHashMap; import java.util.Map; - +import java.util.Objects; import javax.net.ssl.SNIHostName; import javax.net.ssl.SNIMatcher; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLProtocolException; import javax.net.ssl.StandardConstants; +import static sun.security.ssl.SSLExtension.CH_SERVER_NAME; +import static sun.security.ssl.SSLExtension.EE_SERVER_NAME; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import static sun.security.ssl.SSLExtension.SH_SERVER_NAME; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; -/* - * [RFC 4366/6066] To facilitate secure connections to servers that host - * multiple 'virtual' servers at a single underlying network address, clients - * MAY include an extension of type "server_name" in the (extended) client - * hello. The "extension_data" field of this extension SHALL contain - * "ServerNameList" where: - * - * struct { - * NameType name_type; - * select (name_type) { - * case host_name: HostName; - * } name; - * } ServerName; - * - * enum { - * host_name(0), (255) - * } NameType; - * - * opaque HostName<1..2^16-1>; - * - * struct { - * ServerName server_name_list<1..2^16-1> - * } ServerNameList; +/** + * Pack of the "server_name" extensions [RFC 4366/6066]. */ -final class ServerNameExtension extends HelloExtension { +final class ServerNameExtension { + static final HandshakeProducer chNetworkProducer = + new CHServerNameProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHServerNameConsumer(); + static final SSLStringizer chStringizer = + new CHServerNamesStringizer(); + + static final HandshakeProducer shNetworkProducer = + new SHServerNameProducer(); + static final ExtensionConsumer shOnLoadConsumer = + new SHServerNameConsumer(); + static final SSLStringizer shStringizer = + new SHServerNamesStringizer(); - // For backward compatibility, all future data structures associated with - // new NameTypes MUST begin with a 16-bit length field. - static final int NAME_HEADER_LENGTH = 3; // NameType: 1 byte - // Name length: 2 bytes - private Map<Integer, SNIServerName> sniMap; - private int listLength; // ServerNameList length - - // constructor for ServerHello - ServerNameExtension() throws IOException { - super(ExtensionType.EXT_SERVER_NAME); + static final HandshakeProducer eeNetworkProducer = + new EEServerNameProducer(); + static final ExtensionConsumer eeOnLoadConsumer = + new EEServerNameConsumer(); - listLength = 0; - sniMap = Collections.<Integer, SNIServerName>emptyMap(); - } - - // constructor for ClientHello - ServerNameExtension(List<SNIServerName> serverNames) - throws IOException { - super(ExtensionType.EXT_SERVER_NAME); + /** + * The "server_name" extension. + * + * See RFC 4366/6066 for the specification of the extension. + */ + static final class CHServerNamesSpec implements SSLExtensionSpec { + // For backward compatibility, all future data structures associated + // with new NameTypes MUST begin with a 16-bit length field. + static final int NAME_HEADER_LENGTH = 3; // 1: NameType + // +2: Name length + final List<SNIServerName> serverNames; - listLength = 0; - sniMap = new LinkedHashMap<>(); - for (SNIServerName serverName : serverNames) { - // check for duplicated server name type - if (sniMap.put(serverName.getType(), serverName) != null) { - // unlikely to happen, but in case ... - throw new RuntimeException( - "Duplicated server name of type " + serverName.getType()); + private CHServerNamesSpec(List<SNIServerName> serverNames) { + this.serverNames = + Collections.<SNIServerName>unmodifiableList(serverNames); + } + + private CHServerNamesSpec(ByteBuffer buffer) throws IOException { + if (buffer.remaining() < 2) { + throw new SSLProtocolException( + "Invalid server_name extension: insufficient data"); } - listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH; - } - - // This constructor is used for ClientHello only. Empty list is - // not allowed in client mode. - if (listLength == 0) { - throw new RuntimeException("The ServerNameList cannot be empty"); - } - } - - // constructor for ServerHello for parsing SNI extension - ServerNameExtension(HandshakeInStream s, int len) - throws IOException { - super(ExtensionType.EXT_SERVER_NAME); - - int remains = len; - if (len >= 2) { // "server_name" extension in ClientHello - listLength = s.getInt16(); // ServerNameList length - if (listLength == 0 || listLength + 2 != len) { + int sniLen = Record.getInt16(buffer); + if ((sniLen == 0) || sniLen != buffer.remaining()) { throw new SSLProtocolException( - "Invalid " + type + " extension"); + "Invalid server_name extension: incomplete data"); } - remains -= 2; - sniMap = new LinkedHashMap<>(); - while (remains > 0) { - int code = s.getInt8(); // NameType + Map<Integer, SNIServerName> sniMap = new LinkedHashMap<>(); + while (buffer.hasRemaining()) { + int nameType = Record.getInt8(buffer); + SNIServerName serverName; // HostName (length read in getBytes16); - byte[] encoded = s.getBytes16(); - SNIServerName serverName; - switch (code) { - case StandardConstants.SNI_HOST_NAME: - if (encoded.length == 0) { - throw new SSLProtocolException( - "Empty HostName in server name indication"); - } - try { - serverName = new SNIHostName(encoded); - } catch (IllegalArgumentException iae) { - SSLProtocolException spe = new SSLProtocolException( - "Illegal server name, type=host_name(" + - code + "), name=" + - (new String(encoded, StandardCharsets.UTF_8)) + - ", value=" + Debug.toString(encoded)); - spe.initCause(iae); - throw spe; - } - break; - default: - try { - serverName = new UnknownServerName(code, encoded); - } catch (IllegalArgumentException iae) { - SSLProtocolException spe = new SSLProtocolException( - "Illegal server name, type=(" + code + - "), value=" + Debug.toString(encoded)); - spe.initCause(iae); - throw spe; - } + // + // [RFC 6066] The data structure associated with the host_name + // NameType is a variable-length vector that begins with a + // 16-bit length. For backward compatibility, all future data + // structures associated with new NameTypes MUST begin with a + // 16-bit length field. TLS MAY treat provided server names as + // opaque data and pass the names and types to the application. + byte[] encoded = Record.getBytes16(buffer); + if (nameType == StandardConstants.SNI_HOST_NAME) { + if (encoded.length == 0) { + throw new SSLProtocolException( + "Empty HostName in server_name extension"); + } + + try { + serverName = new SNIHostName(encoded); + } catch (IllegalArgumentException iae) { + SSLProtocolException spe = new SSLProtocolException( + "Illegal server name, type=host_name(" + + nameType + "), name=" + + (new String(encoded, StandardCharsets.UTF_8)) + + ", value={" + + Utilities.toHexString(encoded) + "}"); + throw (SSLProtocolException)spe.initCause(iae); + } + } else { + try { + serverName = new UnknownServerName(nameType, encoded); + } catch (IllegalArgumentException iae) { + SSLProtocolException spe = new SSLProtocolException( + "Illegal server name, type=(" + nameType + + "), value={" + + Utilities.toHexString(encoded) + "}"); + throw (SSLProtocolException)spe.initCause(iae); + } } + // check for duplicated server name type if (sniMap.put(serverName.getType(), serverName) != null) { throw new SSLProtocolException( "Duplicated server name of type " + serverName.getType()); } - - remains -= encoded.length + NAME_HEADER_LENGTH; } - } else if (len == 0) { // "server_name" extension in ServerHello - listLength = 0; - sniMap = Collections.<Integer, SNIServerName>emptyMap(); - } - if (remains != 0) { - throw new SSLProtocolException("Invalid server_name extension"); - } - } - - List<SNIServerName> getServerNames() { - if (sniMap != null && !sniMap.isEmpty()) { - return Collections.<SNIServerName>unmodifiableList( - new ArrayList<>(sniMap.values())); + this.serverNames = new ArrayList<>(sniMap.values()); } - return Collections.<SNIServerName>emptyList(); - } + @Override + public String toString() { + if (serverNames == null || serverNames.isEmpty()) { + return "<no server name indicator specified>"; + } else { + StringBuilder builder = new StringBuilder(512); + for (SNIServerName sn : serverNames) { + builder.append(sn.toString()); + builder.append("\n"); + } - /* - * Is the extension recognized by the corresponding matcher? - * - * This method is used to check whether the server name indication can - * be recognized by the server name matchers. - * - * Per RFC 6066, if the server understood the ClientHello extension but - * does not recognize the server name, the server SHOULD take one of two - * actions: either abort the handshake by sending a fatal-level - * unrecognized_name(112) alert or continue the handshake. - * - * If there is an instance of SNIMatcher defined for a particular name - * type, it must be used to perform match operations on the server name. - */ - boolean isMatched(Collection<SNIMatcher> matchers) { - if (sniMap != null && !sniMap.isEmpty()) { - for (SNIMatcher matcher : matchers) { - SNIServerName sniName = sniMap.get(matcher.getType()); - if (sniName != null && (!matcher.matches(sniName))) { - return false; - } + return builder.toString(); } } - return true; + private static class UnknownServerName extends SNIServerName { + UnknownServerName(int code, byte[] encoded) { + super(code, encoded); + } + } } - /* - * Is the extension is identical to a server name list? - * - * This method is used to check the server name indication during session - * resumption. - * - * Per RFC 6066, when the server is deciding whether or not to accept a - * request to resume a session, the contents of a server_name extension - * MAY be used in the lookup of the session in the session cache. The - * client SHOULD include the same server_name extension in the session - * resumption request as it did in the full handshake that established - * the session. A server that implements this extension MUST NOT accept - * the request to resume the session if the server_name extension contains - * a different name. Instead, it proceeds with a full handshake to - * establish a new session. When resuming a session, the server MUST NOT - * include a server_name extension in the server hello. - */ - boolean isIdentical(List<SNIServerName> other) { - if (other.size() == sniMap.size()) { - for(SNIServerName sniInOther : other) { - SNIServerName sniName = sniMap.get(sniInOther.getType()); - if (sniName == null || !sniInOther.equals(sniName)) { - return false; - } - } - - return true; - } - - return false; - } - - @Override - int length() { - return listLength == 0 ? 4 : 6 + listLength; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt16(type.id); - if (listLength == 0) { - s.putInt16(listLength); // in ServerHello, empty extension_data - } else { - s.putInt16(listLength + 2); // length of extension_data - s.putInt16(listLength); // length of ServerNameList - - for (SNIServerName sniName : sniMap.values()) { - s.putInt8(sniName.getType()); // server name type - s.putBytes16(sniName.getEncoded()); // server name value + private static final class CHServerNamesStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new CHServerNamesSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); } } } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (SNIServerName sniName : sniMap.values()) { - sb.append("[" + sniName + "]"); + /** + * Network data producer of a "server_name" extension in the + * ClientHello handshake message. + */ + private static final + class CHServerNameProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHServerNameProducer() { + // 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_SERVER_NAME)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore unavailable server_name extension"); + } + return null; + } + + // Produce the extension. + List<SNIServerName> serverNames; + if (chc.isResumption && (chc.resumingSession != null)) { + serverNames = + chc.resumingSession.getRequestedServerNames(); + } else { + serverNames = chc.sslConfig.serverNames; + } // Shall we use host too? + + // Empty server name list is not allowed in client mode. + if ((serverNames != null) && !serverNames.isEmpty()) { + int sniLen = 0; + for (SNIServerName sniName : serverNames) { + // For backward compatibility, all future data structures + // associated with new NameTypes MUST begin with a 16-bit + // length field. The header length of server name is 3 + // bytes, including 1 byte NameType, and 2 bytes length + // of the name. + sniLen += CHServerNamesSpec.NAME_HEADER_LENGTH; + sniLen += sniName.getEncoded().length; + } + + byte[] extData = new byte[sniLen + 2]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt16(m, sniLen); + for (SNIServerName sniName : serverNames) { + Record.putInt8(m, sniName.getType()); + Record.putBytes16(m, sniName.getEncoded()); + } + + // Update the context. + chc.requestedServerNames = serverNames; + chc.handshakeExtensions.put(CH_SERVER_NAME, + new CHServerNamesSpec(serverNames)); + + return extData; + } + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning("Unable to indicate server name"); + } + return null; + } + } + + /** + * Network data consumer of a "server_name" extension in the + * ClientHello handshake message. + */ + private static final + class CHServerNameConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHServerNameConsumer() { + // blank } - return "Extension " + type + ", server_name: " + sb; - } + @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_SERVER_NAME)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + CH_SERVER_NAME.name); + } + return; // ignore the extension + } + + // Parse the extension. + CHServerNamesSpec spec; + try { + spec = new CHServerNamesSpec(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(CH_SERVER_NAME, spec); - private static class UnknownServerName extends SNIServerName { - UnknownServerName(int code, byte[] encoded) { - super(code, encoded); + // Does the server match the server name request? + SNIServerName sni = null; + if (!shc.sslConfig.sniMatchers.isEmpty()) { + sni = chooseSni(shc.sslConfig.sniMatchers, spec.serverNames); + if (sni != null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "server name indication (" + + sni + ") is accepted"); + } + } else { + // We do not reject client without SNI extension currently. + shc.conContext.fatal(Alert.UNRECOGNIZED_NAME, + "Unrecognized server name indication"); + } + } else { + // Note: Servers MAY require clients to send a valid + // "server_name" extension and respond to a ClientHello + // lacking a "server_name" extension by terminating the + // connection with a "missing_extension" alert. + // + // We do not reject client without SNI extension currently. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "no server name matchers, " + + "ignore server name indication"); + } + } + + // Impact on session resumption. + // + // Does the resuming session have the same principal? + if (shc.isResumption && shc.resumingSession != null) { + // A server that implements this extension MUST NOT accept + // the request to resume the session if the server_name + // extension contains a different name. + // + // May only need to check that the session SNI is one of + // the requested server names. + if (!Objects.equals( + sni, shc.resumingSession.serverNameIndication)) { + shc.isResumption = false; + shc.resumingSession = null; + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "abort session resumption, " + + "different server name indication used"); + } + } + } + + shc.requestedServerNames = spec.serverNames; + shc.negotiatedServerName = sni; + } + + private static SNIServerName chooseSni(Collection<SNIMatcher> matchers, + List<SNIServerName> sniNames) { + if (sniNames != null && !sniNames.isEmpty()) { + for (SNIMatcher matcher : matchers) { + int matcherType = matcher.getType(); + for (SNIServerName sniName : sniNames) { + if (sniName.getType() == matcherType) { + if (matcher.matches(sniName)) { + return sniName; + } + + // no duplicated entry in the server names list. + break; + } + } + } + } + + return null; } } + /** + * The "server_name" extension in the ServerHello handshake message. + * + * The "extension_data" field of this extension shall be empty. + */ + static final class SHServerNamesSpec implements SSLExtensionSpec { + static final SHServerNamesSpec DEFAULT = new SHServerNamesSpec(); + + private SHServerNamesSpec() { + // blank + } + + private SHServerNamesSpec(ByteBuffer buffer) throws IOException { + if (buffer.remaining() != 0) { + throw new SSLProtocolException( + "Invalid ServerHello server_name extension: not empty"); + } + } + + @Override + public String toString() { + return "<empty extension_data field>"; + } + } + + private static final class SHServerNamesStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new SHServerNamesSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "server_name" extension in the + * ServerHello handshake message. + */ + private static final + class SHServerNameProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private SHServerNameProducer() { + // 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 "server_name" extension request only + CHServerNamesSpec spec = (CHServerNamesSpec) + shc.handshakeExtensions.get(CH_SERVER_NAME); + if (spec == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable extension: " + SH_SERVER_NAME.name); + } + return null; // ignore the extension + } + + // When resuming a session, the server MUST NOT include a + // server_name extension in the server hello. + if (shc.isResumption || shc.negotiatedServerName == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "No expected server name indication response"); + } + return null; // ignore the extension + } + + // Produce the extension and update the context. + shc.handshakeExtensions.put( + SH_SERVER_NAME, SHServerNamesSpec.DEFAULT); + + return (new byte[0]); // the empty extension_data + } + } + + /** + * Network data consumer of a "server_name" extension in the + * ServerHello handshake message. + */ + private static final + class SHServerNameConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private SHServerNameConsumer() { + // 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 "server_name" extension request only + CHServerNamesSpec spec = (CHServerNamesSpec) + chc.handshakeExtensions.get(CH_SERVER_NAME); + if (spec == null) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected ServerHello server_name extension"); + } + + // Parse the extension. + if (buffer.remaining() != 0) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Invalid ServerHello server_name extension"); + } + + // Update the context. + chc.handshakeExtensions.put( + SH_SERVER_NAME, SHServerNamesSpec.DEFAULT); + // The negotiated server name is unknown in client side. Just + // use the first request name as the value is not actually used + // in the current implementation. + chc.negotiatedServerName = spec.serverNames.get(0); + } + } + + /** + * Network data producer of a "server_name" extension in the + * EncryptedExtensions handshake message. + */ + private static final + class EEServerNameProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private EEServerNameProducer() { + // 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 "server_name" extension request only + CHServerNamesSpec spec = (CHServerNamesSpec) + shc.handshakeExtensions.get(CH_SERVER_NAME); + if (spec == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "Ignore unavailable extension: " + EE_SERVER_NAME.name); + } + return null; // ignore the extension + } + + // When resuming a session, the server MUST NOT include a + // server_name extension in the server hello. + if (shc.isResumption || shc.negotiatedServerName == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest( + "No expected server name indication response"); + } + return null; // ignore the extension + } + + // Produce the extension and update the context. + shc.handshakeExtensions.put( + EE_SERVER_NAME, SHServerNamesSpec.DEFAULT); + + return (new byte[0]); // the empty extension_data + } + } + + /** + * Network data consumer of a "server_name" extension in the + * EncryptedExtensions handshake message. + */ + private static final + class EEServerNameConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private EEServerNameConsumer() { + // 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 "server_name" extension request only + CHServerNamesSpec spec = (CHServerNamesSpec) + chc.handshakeExtensions.get(CH_SERVER_NAME); + if (spec == null) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected EncryptedExtensions server_name extension"); + } + + // Parse the extension. + if (buffer.remaining() != 0) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Invalid EncryptedExtensions server_name extension"); + } + + // Update the context. + chc.handshakeExtensions.put( + EE_SERVER_NAME, SHServerNamesSpec.DEFAULT); + // The negotiated server name is unknown in client side. Just + // use the first request name as the value is not actually used + // in the current implementation. + chc.negotiatedServerName = spec.serverNames.get(0); + } + } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SessionId.java --- a/src/java.base/share/classes/sun/security/ssl/SessionId.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SessionId.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, 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,105 +23,83 @@ * questions. */ - package sun.security.ssl; import java.security.SecureRandom; +import java.util.Arrays; import javax.net.ssl.SSLProtocolException; /** - * Encapsulates an SSL session ID. SSL Session IDs are not reused by - * servers during the lifetime of any sessions it created. Sessions may - * be used by many connections, either concurrently (for example, two - * connections to a web server at the same time) or sequentially (over as - * long a time period as is allowed by a given server). + * Encapsulates an SSL session ID. * * @author Satish Dharmaraj * @author David Brownell */ -final -class SessionId -{ - static int MAX_LENGTH = 32; - private byte[] sessionId; // max 32 bytes +final class SessionId { + private static final int MAX_LENGTH = 32; + private final byte[] sessionId; // max 32 bytes - /** Constructs a new session ID ... perhaps for a rejoinable session */ - SessionId (boolean isRejoinable, SecureRandom generator) - { - if (isRejoinable) - // this will be unique, it's a timestamp plus much randomness - sessionId = new RandomCookie (generator).random_bytes; - else - sessionId = new byte [0]; + // Constructs a new session ID ... perhaps for a rejoinable session + SessionId(boolean isRejoinable, SecureRandom generator) { + if (isRejoinable && (generator != null)) { + sessionId = new RandomCookie(generator).randomBytes; + } else { + sessionId = new byte[0]; + } + } + + // Constructs a session ID from a byte array (max size 32 bytes) + SessionId(byte[] sessionId) { + this.sessionId = sessionId.clone(); } - /** Constructs a session ID from a byte array (max size 32 bytes) */ - SessionId (byte[] sessionId) - { this.sessionId = sessionId; } - - /** Returns the length of the ID, in bytes */ - int length () - { return sessionId.length; } - - /** Returns the bytes in the ID. May be an empty array. */ - byte[] getId () - { - return sessionId.clone (); + // Returns the length of the ID, in bytes + int length() { + return sessionId.length; } - /** Returns the ID as a string */ - @Override - public String toString () - { - int len = sessionId.length; - StringBuilder sb = new StringBuilder (10 + 2 * len); + // Returns the bytes in the ID. May be an empty array. + byte[] getId() { + return sessionId.clone(); + } - sb.append("{"); - for (int i = 0; i < len; i++) { - sb.append(0x0ff & sessionId[i]); - if (i != (len - 1)) - sb.append (", "); + // Returns the ID as a string + @Override + public String toString() { + if (sessionId.length == 0) { + return ""; } - sb.append("}"); - return sb.toString (); + + return Utilities.toHexString(sessionId); } - /** Returns a value which is the same for session IDs which are equal */ + // Returns a value which is the same for session IDs which are equal @Override - public int hashCode () - { - int retval = 0; - - for (int i = 0; i < sessionId.length; i++) - retval += sessionId [i]; - return retval; + public int hashCode() { + return Arrays.hashCode(sessionId); } - /** Returns true if the parameter is the same session ID */ + // Returns true if the parameter is the same session ID @Override - public boolean equals (Object obj) - { - if (!(obj instanceof SessionId)) - return false; + public boolean equals (Object obj) { + if (obj == this) { + return true; + } - SessionId s = (SessionId) obj; - byte[] b = s.getId (); + if (obj instanceof SessionId) { + SessionId that = (SessionId)obj; + return Arrays.equals(this.sessionId, that.sessionId); + } - if (b.length != sessionId.length) - return false; - for (int i = 0; i < sessionId.length; i++) { - if (b [i] != sessionId [i]) - return false; - } - return true; + return false; } /** * Checks the length of the session ID to make sure it sits within * the range called out in the specification */ - void checkLength(ProtocolVersion pv) throws SSLProtocolException { + void checkLength(int protocolVersion) throws SSLProtocolException { // As of today all versions of TLS have a 32-byte maximum length. // In the future we can do more here to support protocol versions // that may have longer max lengths. @@ -130,5 +108,4 @@ sessionId.length + " bytes)"); } } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, 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,110 +26,530 @@ package sun.security.ssl; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; +import java.nio.ByteBuffer; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +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 "signature_algorithms" extensions [RFC 5246]. + */ +final class SignatureAlgorithmsExtension { + static final HandshakeProducer chNetworkProducer = + new CHSignatureSchemesProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHSignatureSchemesConsumer(); + static final HandshakeAbsence chOnLoadAbsence = + new CHSignatureSchemesOnLoadAbsence(); + static final HandshakeConsumer chOnTradeConsumer = + new CHSignatureSchemesUpdate(); + static final HandshakeAbsence chOnTradeAbsence = + new CHSignatureSchemesOnTradeAbsence(); -import javax.net.ssl.SSLProtocolException; + static final HandshakeProducer crNetworkProducer = + new CRSignatureSchemesProducer(); + static final ExtensionConsumer crOnLoadConsumer = + new CRSignatureSchemesConsumer(); + static final HandshakeAbsence crOnLoadAbsence = + new CRSignatureSchemesAbsence(); + static final HandshakeConsumer crOnTradeConsumer = + new CRSignatureSchemesUpdate(); + + static final SSLStringizer ssStringizer = + new SignatureSchemesStringizer(); + + /** + * The "signature_algorithms" extension. + */ + static final class SignatureSchemesSpec implements SSLExtensionSpec { + final int[] signatureSchemes; + + SignatureSchemesSpec(List<SignatureScheme> schemes) { + if (schemes != null) { + signatureSchemes = new int[schemes.size()]; + int i = 0; + for (SignatureScheme scheme : schemes) { + signatureSchemes[i++] = scheme.id; + } + } else { + this.signatureSchemes = new int[0]; + } + } -/* - * [RFC5246] The client uses the "signature_algorithms" extension to - * indicate to the server which signature/hash algorithm pairs may be - * used in digital signatures. The "extension_data" field of this - * extension contains a "supported_signature_algorithms" value. - * - * enum { - * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), - * sha512(6), (255) - * } HashAlgorithm; - * - * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } - * SignatureAlgorithm; - * - * struct { - * HashAlgorithm hash; - * SignatureAlgorithm signature; - * } SignatureAndHashAlgorithm; - * - * SignatureAndHashAlgorithm - * supported_signature_algorithms<2..2^16-2>; - */ -final class SignatureAlgorithmsExtension extends HelloExtension { + SignatureSchemesSpec(ByteBuffer buffer) throws IOException { + if (buffer.remaining() < 2) { // 2: the length of the list + throw new SSLProtocolException( + "Invalid signature_algorithms: insufficient data"); + } + + byte[] algs = Record.getBytes16(buffer); + if (buffer.hasRemaining()) { + throw new SSLProtocolException( + "Invalid signature_algorithms: unknown extra data"); + } + + if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) { + throw new SSLProtocolException( + "Invalid signature_algorithms: incomplete data"); + } + + int[] schemes = new int[algs.length / 2]; + for (int i = 0, j = 0; i < algs.length;) { + byte hash = algs[i++]; + byte sign = algs[i++]; + schemes[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF); + } + + this.signatureSchemes = schemes; + } - private Collection<SignatureAndHashAlgorithm> algorithms; - private int algorithmsLen; // length of supported_signature_algorithms - - SignatureAlgorithmsExtension( - Collection<SignatureAndHashAlgorithm> signAlgs) { + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"signature schemes\": '['{0}']'", Locale.ENGLISH); - super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + if (signatureSchemes == null || signatureSchemes.length == 0) { + Object[] messageFields = { + "<no supported signature schemes specified>" + }; + return messageFormat.format(messageFields); + } else { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (int pv : signatureSchemes) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } - algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs); - algorithmsLen = - SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); + builder.append(SignatureScheme.nameOf(pv)); + } + + Object[] messageFields = { + builder.toString() + }; + + return messageFormat.format(messageFields); + } + } } - SignatureAlgorithmsExtension(HandshakeInStream s, int len) - throws IOException { - super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + private static final + class SignatureSchemesStringizer 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(); + } + } + } - algorithmsLen = s.getInt16(); - if (algorithmsLen == 0 || algorithmsLen + 2 != len) { - throw new SSLProtocolException("Invalid " + type + " extension"); + /** + * Network data producer of a "signature_algorithms" extension in + * the ClientHello handshake message. + */ + private static final + class CHSignatureSchemesProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHSignatureSchemesProducer() { + // blank } - algorithms = new ArrayList<SignatureAndHashAlgorithm>(); - int remains = algorithmsLen; - int sequence = 0; - while (remains > 1) { // needs at least two bytes - int hash = s.getInt8(); // hash algorithm - int signature = s.getInt8(); // signature algorithm + @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)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable signature_algorithms extension"); + } + return null; + } + + // Produce the extension. + if (chc.localSupportedSignAlgs == null) { + chc.localSupportedSignAlgs = + SignatureScheme.getSupportedAlgorithms( + chc.algorithmConstraints, chc.activeProtocols); + } - SignatureAndHashAlgorithm algorithm = - SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence); - algorithms.add(algorithm); - remains -= 2; // one byte for hash, one byte for signature + 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, + new SignatureSchemesSpec(chc.localSupportedSignAlgs)); + + return extData; + } + } + + /** + * Network data consumer of a "signature_algorithms" extension in + * the ClientHello handshake message. + */ + private static final + class CHSignatureSchemesConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHSignatureSchemesConsumer() { + // blank } - if (remains != 0) { - throw new SSLProtocolException("Invalid server_name extension"); + @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)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable signature_algorithms 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, spec); + + // No impact on session resumption. } } - Collection<SignatureAndHashAlgorithm> getSignAlgorithms() { - return algorithms; - } + /** + * After session creation consuming of a "signature_algorithms" + * extension in the ClientHello handshake message. + */ + private static final class CHSignatureSchemesUpdate + implements HandshakeConsumer { + // Prevent instantiation of this class. + private CHSignatureSchemesUpdate() { + // 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); + if (spec == null) { + // Ignore, no "signature_algorithms" extension requested. + return; + } - @Override - int length() { - return 6 + algorithmsLen; + // update the context + List<SignatureScheme> sss = + SignatureScheme.getSupportedAlgorithms( + shc.algorithmConstraints, shc.negotiatedProtocol, + spec.signatureSchemes); + shc.peerRequestedSignatureSchemes = sss; + + // If no "signature_algorithms_cert" extension is present, then + // the "signature_algorithms" extension also applies to + // signatures appearing in certificates. + SignatureSchemesSpec certSpec = + (SignatureSchemesSpec)shc.handshakeExtensions.get( + SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT); + if (certSpec == null) { + shc.peerRequestedCertSignSchemes = sss; + shc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss); + } + + 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); + } + } } - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt16(type.id); - s.putInt16(algorithmsLen + 2); - s.putInt16(algorithmsLen); + /** + * The absence processing if a "signature_algorithms" extension is + * not present in the ClientHello handshake message. + */ + private static final + class CHSignatureSchemesOnLoadAbsence implements HandshakeAbsence { + @Override + public void absent(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // This is a mandatory extension for certificate authentication + // in TLS 1.3. + // + // We may support the server authentication other than X.509 + // certificate later. + if (shc.negotiatedProtocol.useTLS13PlusSpec()) { + shc.conContext.fatal(Alert.MISSING_EXTENSION, + "No mandatory signature_algorithms extension in the " + + "received CertificateRequest handshake message"); + } + } + } - for (SignatureAndHashAlgorithm algorithm : algorithms) { - s.putInt8(algorithm.getHashValue()); // HashAlgorithm - s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm + /** + * The absence processing if a "signature_algorithms" extension is + * not present in the ClientHello handshake message. + */ + private static final + class CHSignatureSchemesOnTradeAbsence implements HandshakeAbsence { + @Override + public void absent(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + if (shc.negotiatedProtocol.useTLS12PlusSpec()) { + // Use default hash and signature algorithm: + // {sha1,rsa} + // {sha1,dsa} + // {sha1,ecdsa} + // Per RFC 5246, If the client supports only the default hash + // and signature algorithms, it MAY omit the + // signature_algorithms extension. If the client does not + // support the default algorithms, or supports other hash + // and signature algorithms (and it is willing to use them + // for verifying messages sent by the server, i.e., server + // certificates and server key exchange), it MUST send the + // signature_algorithms extension, listing the algorithms it + // is willing to accept. + List<SignatureScheme> shemes = Arrays.asList( + SignatureScheme.RSA_PKCS1_SHA1, + SignatureScheme.DSA_SHA1, + SignatureScheme.ECDSA_SHA1 + ); + + shc.peerRequestedSignatureSchemes = shemes; + if (shc.peerRequestedCertSignSchemes == null || + shc.peerRequestedCertSignSchemes.isEmpty()) { + shc.peerRequestedCertSignSchemes = shemes; + } + + // Use the default peer signature algorithms. + shc.handshakeSession.setUseDefaultPeerSignAlgs(); + } } } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - boolean opened = false; - for (SignatureAndHashAlgorithm signAlg : algorithms) { - if (opened) { - sb.append(", " + signAlg.getAlgorithmName()); - } else { - sb.append(signAlg.getAlgorithmName()); - opened = true; + /** + * Network data producer of a "signature_algorithms" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRSignatureSchemesProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CRSignatureSchemesProducer() { + // 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? + // + // Note that this is a mandatory extension for CertificateRequest + // handshake message in TLS 1.3. + if (!shc.sslConfig.isAvailable( + SSLExtension.CR_SIGNATURE_ALGORITHMS)) { + shc.conContext.fatal(Alert.MISSING_EXTENSION, + "No available signature_algorithms extension " + + "for client certificate authentication"); + return null; // make the compiler happy + } + + // 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, + new SignatureSchemesSpec(shc.localSupportedSignAlgs)); + + return extData; + } + } + + /** + * Network data consumer of a "signature_algorithms" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRSignatureSchemesConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CRSignatureSchemesConsumer() { + // 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? + // + // Note that this is a mandatory extension for CertificateRequest + // handshake message in TLS 1.3. + if (!chc.sslConfig.isAvailable( + SSLExtension.CR_SIGNATURE_ALGORITHMS)) { + chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No available signature_algorithms extension " + + "for client certificate authentication"); + return; // make the compiler happy + } + + // 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. + } + + List<SignatureScheme> knownSignatureSchemes = new LinkedList<>(); + for (int id : spec.signatureSchemes) { + SignatureScheme ss = SignatureScheme.valueOf(id); + if (ss != null) { + knownSignatureSchemes.add(ss); + } + } + + // Update the context. + // chc.peerRequestedSignatureSchemes = knownSignatureSchemes; + chc.handshakeExtensions.put( + SSLExtension.CR_SIGNATURE_ALGORITHMS, spec); + + // No impact on session resumption. + } + } + + /** + * After session creation consuming of a "signature_algorithms" + * extension in the CertificateRequest handshake message. + */ + private static final class CRSignatureSchemesUpdate + implements HandshakeConsumer { + // Prevent instantiation of this class. + private CRSignatureSchemesUpdate() { + // 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); + if (spec == null) { + // Ignore, no "signature_algorithms" extension requested. + return; + } + + // update the context + List<SignatureScheme> sss = + SignatureScheme.getSupportedAlgorithms( + chc.algorithmConstraints, chc.negotiatedProtocol, + spec.signatureSchemes); + chc.peerRequestedSignatureSchemes = sss; + + // If no "signature_algorithms_cert" extension is present, then + // the "signature_algorithms" extension also applies to + // signatures appearing in certificates. + SignatureSchemesSpec certSpec = + (SignatureSchemesSpec)chc.handshakeExtensions.get( + SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT); + if (certSpec == null) { + chc.peerRequestedCertSignSchemes = sss; + chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss); } } + } - return "Extension " + type + ", signature_algorithms: " + sb; + /** + * The absence processing if a "signature_algorithms" extension is + * not present in the CertificateRequest handshake message. + */ + private static final + class CRSignatureSchemesAbsence implements HandshakeAbsence { + @Override + public void absent(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // This is a mandatory extension for CertificateRequest handshake + // message in TLS 1.3. + chc.conContext.fatal(Alert.MISSING_EXTENSION, + "No mandatory signature_algorithms extension in the " + + "received CertificateRequest handshake message"); + } } } - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java --- a/src/java.base/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -/* - * Copyright (c) 2010, 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.security.AlgorithmConstraints; -import java.security.CryptoPrimitive; -import java.security.PrivateKey; -import java.security.Security; - -import java.util.Set; -import java.util.HashSet; -import java.util.Map; -import java.util.EnumSet; -import java.util.TreeMap; -import java.util.Collection; -import java.util.Collections; -import java.util.ArrayList; - -import sun.security.util.KeyUtil; - -/** - * Signature and hash algorithm. - * - * [RFC5246] The client uses the "signature_algorithms" extension to - * indicate to the server which signature/hash algorithm pairs may be - * used in digital signatures. The "extension_data" field of this - * extension contains a "supported_signature_algorithms" value. - * - * enum { - * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), - * sha512(6), (255) - * } HashAlgorithm; - * - * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } - * SignatureAlgorithm; - * - * struct { - * HashAlgorithm hash; - * SignatureAlgorithm signature; - * } SignatureAndHashAlgorithm; - */ -final class SignatureAndHashAlgorithm { - - // minimum priority for default enabled algorithms - static final int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0; - - // performance optimization - private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = - Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); - - // supported pairs of signature and hash algorithm - private static final Map<Integer, SignatureAndHashAlgorithm> supportedMap; - private static final Map<Integer, SignatureAndHashAlgorithm> priorityMap; - - // the hash algorithm - private HashAlgorithm hash; - - // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA - private int id; - - // the standard algorithm name, for example "SHA512withECDSA" - private String algorithm; - - // Priority for the preference order. The lower the better. - // - // If the algorithm is unsupported, its priority should be bigger - // than SUPPORTED_ALG_PRIORITY_MAX_NUM. - private int priority; - - // constructor for supported algorithm - private SignatureAndHashAlgorithm(HashAlgorithm hash, - SignatureAlgorithm signature, String algorithm, int priority) { - this.hash = hash; - this.algorithm = algorithm; - this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF); - this.priority = priority; - } - - // constructor for unsupported algorithm - private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) { - this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF); - this.algorithm = algorithm; - this.id = id; - - // add one more to the sequence number, in case that the number is zero - this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1; - } - - // Note that we do not use the sequence argument for supported algorithms, - // so please don't sort by comparing the objects read from handshake - // messages. - static SignatureAndHashAlgorithm valueOf(int hash, - int signature, int sequence) { - hash &= 0xFF; - signature &= 0xFF; - - int id = (hash << 8) | signature; - SignatureAndHashAlgorithm signAlg = supportedMap.get(id); - if (signAlg == null) { - // unsupported algorithm - signAlg = new SignatureAndHashAlgorithm( - "Unknown (hash:0x" + Integer.toString(hash, 16) + - ", signature:0x" + Integer.toString(signature, 16) + ")", - id, sequence); - } - - return signAlg; - } - - int getHashValue() { - return (id >> 8) & 0xFF; - } - - int getSignatureValue() { - return id & 0xFF; - } - - String getAlgorithmName() { - return algorithm; - } - - // return the size of a SignatureAndHashAlgorithm structure in TLS record - static int sizeInRecord() { - return 2; - } - - // Get local supported algorithm collection complying to - // algorithm constraints - static Collection<SignatureAndHashAlgorithm> - getSupportedAlgorithms(AlgorithmConstraints constraints) { - - Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>(); - for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) { - if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM && - constraints.permits(SIGNATURE_PRIMITIVE_SET, - sigAlg.algorithm, null)) { - supported.add(sigAlg); - } - } - - return supported; - } - - // Get supported algorithm collection from an untrusted collection - static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms( - AlgorithmConstraints constraints, - Collection<SignatureAndHashAlgorithm> algorithms ) { - Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>(); - for (SignatureAndHashAlgorithm sigAlg : algorithms) { - if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM && - constraints.permits(SIGNATURE_PRIMITIVE_SET, - sigAlg.algorithm, null)) { - supported.add(sigAlg); - } - } - - return supported; - } - - static String[] getAlgorithmNames( - Collection<SignatureAndHashAlgorithm> algorithms) { - ArrayList<String> algorithmNames = new ArrayList<>(); - if (algorithms != null) { - for (SignatureAndHashAlgorithm sigAlg : algorithms) { - algorithmNames.add(sigAlg.algorithm); - } - } - - String[] array = new String[algorithmNames.size()]; - return algorithmNames.toArray(array); - } - - static Set<String> getHashAlgorithmNames( - Collection<SignatureAndHashAlgorithm> algorithms) { - Set<String> algorithmNames = new HashSet<>(); - if (algorithms != null) { - for (SignatureAndHashAlgorithm sigAlg : algorithms) { - if (sigAlg.hash.value > 0) { - algorithmNames.add(sigAlg.hash.standardName); - } - } - } - - return algorithmNames; - } - - static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) { - return algorithm.hash.standardName; - } - - private static void supports(HashAlgorithm hash, - SignatureAlgorithm signature, String algorithm, int priority) { - - SignatureAndHashAlgorithm pair = - new SignatureAndHashAlgorithm(hash, signature, algorithm, priority); - if (supportedMap.put(pair.id, pair) != null) { - throw new RuntimeException( - "Duplicate SignatureAndHashAlgorithm definition, id: " + - pair.id); - } - if (priorityMap.put(pair.priority, pair) != null) { - throw new RuntimeException( - "Duplicate SignatureAndHashAlgorithm definition, priority: " + - pair.priority); - } - } - - static SignatureAndHashAlgorithm getPreferableAlgorithm( - Collection<SignatureAndHashAlgorithm> algorithms, String expected) { - - return SignatureAndHashAlgorithm.getPreferableAlgorithm( - algorithms, expected, null); - } - - static SignatureAndHashAlgorithm getPreferableAlgorithm( - Collection<SignatureAndHashAlgorithm> algorithms, - String expected, PrivateKey signingKey) { - - int maxDigestLength = getMaxDigestLength(signingKey); - for (SignatureAndHashAlgorithm algorithm : algorithms) { - int signValue = algorithm.id & 0xFF; - if ((expected == null) || - (expected.equalsIgnoreCase("rsa") && - signValue == SignatureAlgorithm.RSA.value) || - (expected.equalsIgnoreCase("dsa") && - signValue == SignatureAlgorithm.DSA.value) || - (expected.equalsIgnoreCase("ecdsa") && - signValue == SignatureAlgorithm.ECDSA.value) || - (expected.equalsIgnoreCase("ec") && - signValue == SignatureAlgorithm.ECDSA.value)) { - - if (algorithm.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM && - algorithm.hash.length <= maxDigestLength) { - - return algorithm; - } - } - } - - return null; - } - - /* - * Need to check key length to match the length of hash value - */ - private static int getMaxDigestLength(PrivateKey signingKey) { - int maxDigestLength = Integer.MAX_VALUE; - - // only need to check RSA algorithm at present. - if (signingKey != null && - "rsa".equalsIgnoreCase(signingKey.getAlgorithm())) { - /* - * RSA keys of 512 bits have been shown to be practically - * breakable, it does not make much sense to use the strong - * hash algorithm for keys whose key size less than 512 bits. - * So it is not necessary to caculate the required max digest - * length exactly. - * - * If key size is greater than or equals to 768, there is no max - * digest length limitation in currect implementation. - * - * If key size is greater than or equals to 512, but less than - * 768, the digest length should be less than or equal to 32 bytes. - * - * If key size is less than 512, the digest length should be - * less than or equal to 20 bytes. - */ - int keySize = KeyUtil.getKeySize(signingKey); - if (keySize >= 768) { - maxDigestLength = HashAlgorithm.SHA512.length; - } else if ((keySize >= 512) && (keySize < 768)) { - maxDigestLength = HashAlgorithm.SHA256.length; - } else if ((keySize > 0) && (keySize < 512)) { - maxDigestLength = HashAlgorithm.SHA1.length; - } // Otherwise, cannot determine the key size, prefer the most - // preferable hash algorithm. - } - - return maxDigestLength; - } - - static enum HashAlgorithm { - UNDEFINED("undefined", "", -1, -1), - NONE( "none", "NONE", 0, -1), - MD5( "md5", "MD5", 1, 16), - SHA1( "sha1", "SHA-1", 2, 20), - SHA224( "sha224", "SHA-224", 3, 28), - SHA256( "sha256", "SHA-256", 4, 32), - SHA384( "sha384", "SHA-384", 5, 48), - SHA512( "sha512", "SHA-512", 6, 64); - - final String name; // not the standard signature algorithm name - // except the UNDEFINED, other names are defined - // by TLS 1.2 protocol - final String standardName; // the standard MessageDigest algorithm name - final int value; - final int length; // digest length in bytes, -1 means not applicable - - private HashAlgorithm(String name, String standardName, - int value, int length) { - this.name = name; - this.standardName = standardName; - this.value = value; - this.length = length; - } - - static HashAlgorithm valueOf(int value) { - HashAlgorithm algorithm = UNDEFINED; - switch (value) { - case 0: - algorithm = NONE; - break; - case 1: - algorithm = MD5; - break; - case 2: - algorithm = SHA1; - break; - case 3: - algorithm = SHA224; - break; - case 4: - algorithm = SHA256; - break; - case 5: - algorithm = SHA384; - break; - case 6: - algorithm = SHA512; - break; - } - - return algorithm; - } - } - - static enum SignatureAlgorithm { - UNDEFINED("undefined", -1), - ANONYMOUS("anonymous", 0), - RSA( "rsa", 1), - DSA( "dsa", 2), - ECDSA( "ecdsa", 3); - - final String name; // not the standard signature algorithm name - // except the UNDEFINED, other names are defined - // by TLS 1.2 protocol - final int value; - - private SignatureAlgorithm(String name, int value) { - this.name = name; - this.value = value; - } - - static SignatureAlgorithm valueOf(int value) { - SignatureAlgorithm algorithm = UNDEFINED; - switch (value) { - case 0: - algorithm = ANONYMOUS; - break; - case 1: - algorithm = RSA; - break; - case 2: - algorithm = DSA; - break; - case 3: - algorithm = ECDSA; - break; - } - - return algorithm; - } - } - - static { - supportedMap = Collections.synchronizedSortedMap( - new TreeMap<Integer, SignatureAndHashAlgorithm>()); - priorityMap = Collections.synchronizedSortedMap( - new TreeMap<Integer, SignatureAndHashAlgorithm>()); - - synchronized (supportedMap) { - int p = SUPPORTED_ALG_PRIORITY_MAX_NUM; - supports(HashAlgorithm.MD5, SignatureAlgorithm.RSA, - "MD5withRSA", --p); - supports(HashAlgorithm.SHA1, SignatureAlgorithm.DSA, - "SHA1withDSA", --p); - supports(HashAlgorithm.SHA1, SignatureAlgorithm.RSA, - "SHA1withRSA", --p); - supports(HashAlgorithm.SHA1, SignatureAlgorithm.ECDSA, - "SHA1withECDSA", --p); - - if (Security.getProvider("SunMSCAPI") == null) { - supports(HashAlgorithm.SHA224, SignatureAlgorithm.DSA, - "SHA224withDSA", --p); - supports(HashAlgorithm.SHA224, SignatureAlgorithm.RSA, - "SHA224withRSA", --p); - supports(HashAlgorithm.SHA224, SignatureAlgorithm.ECDSA, - "SHA224withECDSA", --p); - } - - supports(HashAlgorithm.SHA256, SignatureAlgorithm.DSA, - "SHA256withDSA", --p); - supports(HashAlgorithm.SHA256, SignatureAlgorithm.RSA, - "SHA256withRSA", --p); - supports(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA, - "SHA256withECDSA", --p); - supports(HashAlgorithm.SHA384, SignatureAlgorithm.RSA, - "SHA384withRSA", --p); - supports(HashAlgorithm.SHA384, SignatureAlgorithm.ECDSA, - "SHA384withECDSA", --p); - supports(HashAlgorithm.SHA512, SignatureAlgorithm.RSA, - "SHA512withRSA", --p); - supports(HashAlgorithm.SHA512, SignatureAlgorithm.ECDSA, - "SHA512withECDSA", --p); - } - } -} - diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SignatureScheme.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,485 @@ +/* + * 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.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import sun.security.ssl.SupportedGroupsExtension.NamedGroup; +import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.util.KeyUtil; + +enum SignatureScheme { + // EdDSA algorithms + ED25519 (0x0807, "ed25519", "ed25519", + "ed25519", + ProtocolVersion.PROTOCOLS_OF_13), + ED448 (0x0808, "ed448", "ed448", + "ed448", + ProtocolVersion.PROTOCOLS_OF_13), + + // ECDSA algorithms + ECDSA_SECP256R1_SHA256 (0x0403, "ecdsa_secp256r1_sha256", + "SHA256withECDSA", + "EC", + NamedGroup.SECP256_R1, + ProtocolVersion.PROTOCOLS_TO_13), + ECDSA_SECP384R1_SHA384 (0x0503, "ecdsa_secp384r1_sha384", + "SHA384withECDSA", + "EC", + NamedGroup.SECP384_R1, + ProtocolVersion.PROTOCOLS_TO_13), + ECDSA_SECP512R1_SHA512 (0x0603, "ecdsa_secp512r1_sha512", + "SHA512withECDSA", + "EC", + NamedGroup.SECP521_R1, + ProtocolVersion.PROTOCOLS_TO_13), + + // RSASSA-PSS algorithms with public key OID rsaEncryption + // + // The minimalKeySize is calculated as (See RFC 8017 for details): + // hash length + salt length + 16 + RSA_PSS_RSAE_SHA256 (0x0804, "rsa_pss_rsae_sha256", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA256, 528, + ProtocolVersion.PROTOCOLS_12_13), + RSA_PSS_RSAE_SHA384 (0x0805, "rsa_pss_rsae_sha384", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA384, 784, + ProtocolVersion.PROTOCOLS_12_13), + RSA_PSS_RSAE_SHA512 (0x0806, "rsa_pss_rsae_sha512", + "RSASSA-PSS", "RSA", + SigAlgParamSpec.RSA_PSS_SHA512, 1040, + ProtocolVersion.PROTOCOLS_12_13), + + // RSASSA-PSS algorithms with public key OID RSASSA-PSS + // + // The minimalKeySize is calculated as (See RFC 8017 for details): + // hash length + salt length + 16 + RSA_PSS_PSS_SHA256 (0x0809, "rsa_pss_pss_sha256", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA256, 528, + ProtocolVersion.PROTOCOLS_12_13), + RSA_PSS_PSS_SHA384 (0x080A, "rsa_pss_pss_sha384", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA384, 784, + ProtocolVersion.PROTOCOLS_12_13), + RSA_PSS_PSS_SHA512 (0x080B, "rsa_pss_pss_sha512", + "RSASSA-PSS", "RSASSA-PSS", + SigAlgParamSpec.RSA_PSS_SHA512, 1040, + ProtocolVersion.PROTOCOLS_12_13), + + // RSASSA-PKCS1-v1_5 algorithms + RSA_PKCS1_SHA256 (0x0401, "rsa_pkcs1_sha256", "SHA256withRSA", + "RSA", null, null, 511, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + RSA_PKCS1_SHA384 (0x0501, "rsa_pkcs1_sha384", "SHA384withRSA", + "RSA", null, null, 768, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + RSA_PKCS1_SHA512 (0x0601, "rsa_pkcs1_sha512", "SHA512withRSA", + "RSA", null, null, 768, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + + // Legacy algorithms + DSA_SHA256 (0x0402, "dsa_sha256", "SHA256withDSA", + "DSA", + ProtocolVersion.PROTOCOLS_TO_12), + ECDSA_SHA224 (0x0303, "ecdsa_sha224", "SHA224withECDSA", + "EC", + ProtocolVersion.PROTOCOLS_TO_12), + RSA_SHA224 (0x0301, "rsa_sha224", "SHA224withRSA", + "RSA", 511, + ProtocolVersion.PROTOCOLS_TO_12), + DSA_SHA224 (0x0302, "dsa_sha224", "SHA224withDSA", + "DSA", + ProtocolVersion.PROTOCOLS_TO_12), + ECDSA_SHA1 (0x0203, "ecdsa_sha1", "SHA1withECDSA", + "EC", + ProtocolVersion.PROTOCOLS_TO_13), + RSA_PKCS1_SHA1 (0x0201, "rsa_pkcs1_sha1", "SHA1withRSA", + "RSA", null, null, 511, + ProtocolVersion.PROTOCOLS_TO_13, + ProtocolVersion.PROTOCOLS_TO_12), + DSA_SHA1 (0x0202, "dsa_sha1", "SHA1withDSA", + "DSA", + ProtocolVersion.PROTOCOLS_TO_12), + RSA_MD5 (0x0101, "rsa_md5", "MD5withRSA", + "RSA", 511, + ProtocolVersion.PROTOCOLS_TO_12); + + final int id; // hash + signature + final String name; // literal name + private final String algorithm; // signature algorithm + final String keyAlgorithm; // signature key algorithm + private final AlgorithmParameterSpec signAlgParameter; + private final NamedGroup namedGroup; // associated named group + + // The minimal required key size in bits. + // + // Only need to check RSA algorithm at present. RSA keys of 512 bits + // have been shown to be practically breakable, it does not make much + // sense to use the strong hash algorithm for keys whose key size less + // than 512 bits. So it is not necessary to calculate the minimal + // required key size exactly for a hash algorithm. + // + // Note that some provider may use 511 bits for 512-bit strength RSA keys. + final int minimalKeySize; + final List<ProtocolVersion> supportedProtocols; + + // Some signature schemes are supported in different versions for handshake + // messages and certificates. This field holds the supported protocols + // for handshake messages. + final List<ProtocolVersion> handshakeSupportedProtocols; + final boolean isAvailable; + + private static final String[] hashAlgorithms = new String[] { + "none", "md5", "sha1", "sha224", + "sha256", "sha384", "sha512" + }; + + private static final String[] signatureAlgorithms = new String[] { + "anonymous", "rsa", "dsa", "ecdsa", + }; + + static enum SigAlgParamSpec { // support RSASSA-PSS only now + RSA_PSS_SHA256 ("SHA-256", 32), + RSA_PSS_SHA384 ("SHA-384", 48), + RSA_PSS_SHA512 ("SHA-512", 64); + + final private AlgorithmParameterSpec parameterSpec; + final boolean isAvailable; + + SigAlgParamSpec(String hash, int saltLength) { + // See RFC 8017 + PSSParameterSpec pssParamSpec = + new PSSParameterSpec(hash, "MGF1", + new MGF1ParameterSpec(hash), saltLength, 1); + + boolean mediator = true; + try { + Signature signer = JsseJce.getSignature("RSASSA-PSS"); + signer.setParameter(pssParamSpec); + } catch (InvalidAlgorithmParameterException | + NoSuchAlgorithmException exp) { + mediator = false; + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "RSASSA-PSS signature with " + hash + + " is not supported by the underlying providers", exp); + } + } + + this.isAvailable = mediator; + this.parameterSpec = mediator ? pssParamSpec : null; + } + + AlgorithmParameterSpec getParameterSpec() { + return parameterSpec; + } + } + + // performance optimization + private static final Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = + Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); + + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + ProtocolVersion[] supportedProtocols) { + this(id, name, algorithm, keyAlgorithm, -1, supportedProtocols); + } + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + int minimalKeySize, + ProtocolVersion[] supportedProtocols) { + this(id, name, algorithm, keyAlgorithm, + null, minimalKeySize, supportedProtocols); + } + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + SigAlgParamSpec signAlgParamSpec, int minimalKeySize, + ProtocolVersion[] supportedProtocols) { + this(id, name, algorithm, keyAlgorithm, + signAlgParamSpec, null, minimalKeySize, + supportedProtocols, supportedProtocols); + } + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + NamedGroup namedGroup, + ProtocolVersion[] supportedProtocols) { + this(id, name, algorithm, keyAlgorithm, + null, namedGroup, -1, + supportedProtocols, supportedProtocols); + } + + private SignatureScheme(int id, String name, + String algorithm, String keyAlgorithm, + SigAlgParamSpec signAlgParamSpec, + NamedGroup namedGroup, int minimalKeySize, + ProtocolVersion[] supportedProtocols, + ProtocolVersion[] handshakeSupportedProtocols) { + this.id = id; + this.name = name; + this.algorithm = algorithm; + this.keyAlgorithm = keyAlgorithm; + this.signAlgParameter = + signAlgParamSpec != null ? signAlgParamSpec.parameterSpec : null; + this.namedGroup = namedGroup; + this.minimalKeySize = minimalKeySize; + this.supportedProtocols = Arrays.asList(supportedProtocols); + this.handshakeSupportedProtocols = + Arrays.asList(handshakeSupportedProtocols); + + boolean mediator = true; + if (signAlgParamSpec != null) { + mediator = signAlgParamSpec.isAvailable; + } else { + try { + JsseJce.getSignature(algorithm); + } catch (Exception e) { + mediator = false; + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Signature algorithm, " + algorithm + + ", is not supported by the underlying providers"); + } + } + } + + if (mediator && ((id >> 8) & 0xFF) == 0x03) { // SHA224 + // There are some problems to use SHA224 on Windows. + if (Security.getProvider("SunMSCAPI") != null) { + mediator = false; + } + } + + this.isAvailable = mediator; + } + + static SignatureScheme valueOf(int id) { + for (SignatureScheme ss: SignatureScheme.values()) { + if (ss.id == id) { + return ss; + } + } + + return null; + } + + static String nameOf(int id) { + for (SignatureScheme ss: SignatureScheme.values()) { + if (ss.id == id) { + return ss.name; + } + } + + // Use TLS 1.2 style name for unknown signature scheme. + int hashId = ((id >> 8) & 0xFF); + int signId = (id & 0xFF); + String hashName = (hashId >= hashAlgorithms.length) ? + "UNDEFINED-HASH(" + hashId + ")" : hashAlgorithms[hashId]; + String signName = (signId >= signatureAlgorithms.length) ? + "UNDEFINED-SIGNATURE(" + signId + ")" : + signatureAlgorithms[signId]; + + return signName + "_" + hashName; + } + + // Return the size of a SignatureScheme structure in TLS record + static int sizeInRecord() { + return 2; + } + + // Get local supported algorithm collection complying to algorithm + // constraints. + static List<SignatureScheme> getSupportedAlgorithms( + AlgorithmConstraints constraints, + List<ProtocolVersion> activeProtocols) { + List<SignatureScheme> supported = new LinkedList<>(); + for (SignatureScheme ss: SignatureScheme.values()) { + if (!ss.isAvailable) { + continue; + } + + boolean isMatch = false; + for (ProtocolVersion pv : activeProtocols) { + if (ss.supportedProtocols.contains(pv)) { + isMatch = true; + break; + } + } + + if (isMatch) { + if (constraints.permits( + SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) { + supported.add(ss); + } else if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Ignore disabled signature sheme: " + ss.name); + } + } else if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Ignore inactive signature sheme: " + ss.name); + } + } + + return supported; + } + + static List<SignatureScheme> getSupportedAlgorithms( + AlgorithmConstraints constraints, + ProtocolVersion protocolVersion, int[] algorithmIds) { + List<SignatureScheme> supported = new LinkedList<>(); + for (int ssid : algorithmIds) { + SignatureScheme ss = SignatureScheme.valueOf(ssid); + if (ss == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unsupported signature scheme: " + + SignatureScheme.nameOf(ssid)); + } + } else if (ss.isAvailable && + ss.supportedProtocols.contains(protocolVersion) && + constraints.permits(SIGNATURE_PRIMITIVE_SET, + ss.algorithm, null)) { + supported.add(ss); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Unsupported signature scheme: " + ss.name); + } + } + } + + return supported; + } + + static SignatureScheme getPreferableAlgorithm( + List<SignatureScheme> schemes, + SignatureScheme certScheme, + ProtocolVersion version) { + + for (SignatureScheme ss : schemes) { + if (ss.isAvailable && + ss.handshakeSupportedProtocols.contains(version) && + certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { + + return ss; + } + } + + return null; + } + + static SignatureScheme getPreferableAlgorithm( + List<SignatureScheme> schemes, + PrivateKey signingKey, + ProtocolVersion version) { + + String keyAlgorithm = signingKey.getAlgorithm(); + int keySize; + // Only need to check RSA algorithm at present. + if (keyAlgorithm.equalsIgnoreCase("RSA") || + keyAlgorithm.equalsIgnoreCase("RSASSA-PSS")) { + keySize = KeyUtil.getKeySize(signingKey); + } else { + keySize = Integer.MAX_VALUE; + } + for (SignatureScheme ss : schemes) { + if (ss.isAvailable && (keySize >= ss.minimalKeySize) && + ss.handshakeSupportedProtocols.contains(version) && + keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { + if (ss.namedGroup != null && + ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { + ECParameterSpec params = + ((ECPrivateKey)signingKey).getParams(); + if (ss.namedGroup == NamedGroup.valueOf(params)) { + return ss; + } + } else { + return ss; + } + } + } + + return null; + } + + static String[] getAlgorithmNames(Collection<SignatureScheme> schemes) { + if (schemes != null) { + ArrayList<String> names = new ArrayList<>(schemes.size()); + for (SignatureScheme scheme : schemes) { + names.add(scheme.algorithm); + } + + return names.toArray(new String[0]); + } + + return new String[0]; + } + + Signature getSignature(Key key) throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException { + if (!isAvailable) { + return null; + } + + Signature signer = JsseJce.getSignature(algorithm); + if (key instanceof PublicKey) { + signer.initVerify((PublicKey)(key)); + } else { + signer.initSign((PrivateKey)key); + } + + // Important note: Please don't set the parameters before signature + // or verification initialization, so that the crypto provider can + // be selected properly. + if (signAlgParameter != null) { + signer.setParameter(signAlgParameter); + } + + return signer; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/StatusRequest.java --- a/src/java.base/share/classes/sun/security/ssl/StatusRequest.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +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; - -/* - * RFC 6066 defines the TLS extension,"status_request" (type 0x5), - * which allows the client to request that the server perform OCSP - * on the client's behalf. - * - * This class is an interface for multiple types of StatusRequests - * (e.g. OCSPStatusRequest). - */ -interface StatusRequest { - - /** - * Obtain the length of the {@code StatusRequest} object in encoded form - * - * @return the length of the {@code StatusRequest} object in encoded form - */ - int length(); - - /** - * Place the encoded {@code StatusRequest} bytes into the - * {@code HandshakeOutputStream} - * - * @param s the target {@code HandshakeOutputStream} - * - * @throws IOException if any encoding error occurs - */ - void send(HandshakeOutStream s) throws IOException; -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/StatusRequestType.java --- a/src/java.base/share/classes/sun/security/ssl/StatusRequestType.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +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.ArrayList; -import java.util.List; - -final class StatusRequestType { - - final int id; - final String name; - static List<StatusRequestType> knownTypes = new ArrayList<>(4); - - private StatusRequestType(int id, String name) { - this.id = id; - this.name = name; - } - - static StatusRequestType get(int id) { - for (StatusRequestType ext : knownTypes) { - if (ext.id == id) { - return ext; - } - } - return new StatusRequestType(id, "type_" + id); - } - - private static StatusRequestType e(int id, String name) { - StatusRequestType ext = new StatusRequestType(id, name); - knownTypes.add(ext); - return ext; - } - - @Override - public String toString() { - return (name == null || name.isEmpty()) ? - String.format("Unknown (0x%04X", id) : name; - } - - // Status request types defined in RFC 6066 and 6961 - static final StatusRequestType OCSP = e(0x01, "ocsp"); - static final StatusRequestType OCSP_MULTI = e(0x02, "ocsp_multi"); -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java --- a/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -22,18 +22,32 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package sun.security.ssl; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.security.AccessController; +import java.security.cert.Extension; import java.security.cert.X509Certificate; -import java.security.cert.Extension; -import java.util.*; -import java.util.concurrent.*; - +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import sun.security.action.GetBooleanAction; +import sun.security.action.GetIntegerAction; +import sun.security.action.GetPropertyAction; import sun.security.provider.certpath.CertId; import sun.security.provider.certpath.OCSP; import sun.security.provider.certpath.OCSPResponse; @@ -41,15 +55,13 @@ import sun.security.util.Cache; import sun.security.x509.PKIXExtensions; import sun.security.x509.SerialNumber; -import sun.security.action.GetBooleanAction; -import sun.security.action.GetIntegerAction; -import sun.security.action.GetPropertyAction; +import sun.security.ssl.X509Authentication.X509Possession; +import static sun.security.ssl.CertStatusExtension.*; final class StatusResponseManager { private static final int DEFAULT_CORE_THREADS = 8; private static final int DEFAULT_CACHE_SIZE = 256; - private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds - private static final Debug debug = Debug.getInstance("ssl"); + private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds private final ScheduledThreadPoolExecutor threadMgr; private final Cache<CertId, ResponseCacheEntry> responseCache; @@ -99,10 +111,12 @@ } }, new ThreadPoolExecutor.DiscardPolicy()); threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); - threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); + threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy( + false); threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS); threadMgr.allowCoreThreadTimeOut(true); - responseCache = Cache.newSoftMemoryCache(cacheCapacity, cacheLifetime); + responseCache = Cache.newSoftMemoryCache( + cacheCapacity, cacheLifetime); } /** @@ -147,8 +161,8 @@ * Get the ignore extensions setting. * * @return {@code true} if the {@code StatusResponseManager} will not - * pass OCSP Extensions in the TLS {@code status_request[_v2]} extensions, - * {@code false} if extensions will be passed (the default). + * pass OCSP Extensions in the TLS {@code status_request[_v2]} + * extensions, {@code false} if extensions will be passed (the default). */ boolean getIgnoreExtensions() { return ignoreExtensions; @@ -158,7 +172,9 @@ * Clear the status response cache */ void clear() { - debugLog("Clearing response cache"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Clearing response cache"); + } responseCache.clear(); } @@ -172,16 +188,18 @@ } /** - * Obtain the URI use by the {@code StatusResponseManager} during lookups. + * Obtain the URI use by the {@code StatusResponseManager} during + * lookups. + * * This method takes into account not only the AIA extension from a * certificate to be checked, but also any default URI and possible * override settings for the response manager. * * @param cert the subject to get the responder URI from * - * @return a {@code URI} containing the address to the OCSP responder, or - * {@code null} if no AIA extension exists in the certificate and no - * default responder has been configured. + * @return a {@code URI} containing the address to the OCSP responder, + * or {@code null} if no AIA extension exists in the certificate + * and no default responder has been configured. * * @throws NullPointerException if {@code cert} is {@code null}. */ @@ -190,10 +208,16 @@ if (cert.getExtensionValue( PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { - debugLog("OCSP NoCheck extension found. OCSP will be skipped"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "OCSP NoCheck extension found. OCSP will be skipped"); + } return null; } else if (defaultResponder != null && respOverride) { - debugLog("Responder override: URI is " + defaultResponder); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Responder override: URI is " + defaultResponder); + } return defaultResponder; } else { URI certURI = OCSP.getResponderURI(cert); @@ -205,25 +229,29 @@ * Shutdown the thread pool */ void shutdown() { - debugLog("Shutting down " + threadMgr.getActiveCount() + - " active threads"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Shutting down " + threadMgr.getActiveCount() + + " active threads"); + } threadMgr.shutdown(); } /** * Get a list of responses for a chain of certificates. - * This will find OCSP responses from the cache, or failing that, directly - * contact the OCSP responder. It is assumed that the certificates in - * the provided chain are in their proper order (from end-entity to - * trust anchor). + * + * This will find OCSP responses from the cache, or failing that, + * directly contact the OCSP responder. It is assumed that the + * certificates in the provided chain are in their proper order + * (from end-entity to trust anchor). * * @param type the type of request being made of the * {@code StatusResponseManager} - * @param request the {@code StatusRequest} from the status_request or - * status_request_v2 ClientHello extension. A value of {@code null} - * is interpreted as providing no responder IDs or extensions. - * @param chain an array of 2 or more certificates. Each certificate must - * be issued by the next certificate in the chain. + * @param request the {@code CertStatusRequest} from the + * status_request or status_request_v2 ClientHello extension. + * A value of {@code null} is interpreted as providing no + * responder IDs or extensions. + * @param chain an array of 2 or more certificates. Each certificate + * must be issued by the next certificate in the chain. * @param delay the number of time units to delay before returning * responses. * @param unit the unit of time applied to the {@code delay} parameter @@ -231,17 +259,20 @@ * @return an unmodifiable {@code Map} containing the certificate and * its usually * - * @throws SSLHandshakeException if an unsupported {@code StatusRequest} - * is provided. + * @throws SSLHandshakeException if an unsupported + * {@code CertStatusRequest} is provided. */ - Map<X509Certificate, byte[]> get(StatusRequestType type, - StatusRequest request, X509Certificate[] chain, long delay, + Map<X509Certificate, byte[]> get(CertStatusRequestType type, + CertStatusRequest request, X509Certificate[] chain, long delay, TimeUnit unit) { Map<X509Certificate, byte[]> responseMap = new HashMap<>(); List<OCSPFetchCall> requestList = new ArrayList<>(); - debugLog("Beginning check: Type = " + type + ", Chain length = " + + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Beginning check: Type = " + type + ", Chain length = " + chain.length); + } // It is assumed that the caller has ordered the certs in the chain // in the proper order (each certificate is issued by the next entry @@ -250,7 +281,7 @@ return Collections.emptyMap(); } - if (type == StatusRequestType.OCSP) { + if (type == CertStatusRequestType.OCSP) { try { // For type OCSP, we only check the end-entity certificate OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; @@ -264,22 +295,26 @@ requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - debugLog("Exception during CertId creation: " + exc); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Exception during CertId creation: ", exc); + } } - } else if (type == StatusRequestType.OCSP_MULTI) { + } else if (type == CertStatusRequestType.OCSP_MULTI) { // For type OCSP_MULTI, we check every cert in the chain that - // has a direct issuer at the next index. We won't have an issuer - // certificate for the last certificate in the chain and will - // not be able to create a CertId because of that. + // has a direct issuer at the next index. We won't have an + // issuer certificate for the last certificate in the chain + // and will not be able to create a CertId because of that. OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; int ctr; for (ctr = 0; ctr < chain.length - 1; ctr++) { try { - // The cert at "ctr" is the subject cert, "ctr + 1" is the - // issuer certificate. + // The cert at "ctr" is the subject cert, "ctr + 1" + // is the issuer certificate. CertId cid = new CertId(chain[ctr + 1], - new SerialNumber(chain[ctr].getSerialNumber())); - ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); + new SerialNumber(chain[ctr].getSerialNumber())); + ResponseCacheEntry cacheEntry = + getFromCache(cid, ocspReq); if (cacheEntry != null) { responseMap.put(chain[ctr], cacheEntry.ocspBytes); } else { @@ -287,17 +322,22 @@ requestList.add(new OCSPFetchCall(sInfo, ocspReq)); } } catch (IOException exc) { - debugLog("Exception during CertId creation: " + exc); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Exception during CertId creation: ", exc); + } } } } else { - debugLog("Unsupported status request type: " + type); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Unsupported status request type: " + type); + } } // If we were able to create one or more Fetches, go and run all // of them in separate threads. For all the threads that completed - // in the allotted time, put those status responses into the returned - // Map. + // in the allotted time, put those status responses into the + // returned Map. if (!requestList.isEmpty()) { try { // Set a bunch of threads to go do the fetching @@ -307,23 +347,31 @@ // Go through the Futures and from any non-cancelled task, // get the bytes and attach them to the responseMap. for (Future<StatusInfo> task : resultList) { - if (task.isDone()) { - if (!task.isCancelled()) { - StatusInfo info = task.get(); - if (info != null && info.responseData != null) { - responseMap.put(info.cert, - info.responseData.ocspBytes); - } else { - debugLog("Completed task had no response data"); - } - } else { - debugLog("Found cancelled task"); + if (!task.isDone()) { + continue; + } + + if (!task.isCancelled()) { + StatusInfo info = task.get(); + if (info != null && info.responseData != null) { + responseMap.put(info.cert, + info.responseData.ocspBytes); + } else if (SSLLogger.isOn && + SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Completed task had no response data"); + } + } else { + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Found cancelled task"); } } } } catch (InterruptedException | ExecutionException exc) { // Not sure what else to do here - debugLog("Exception when getting data: " + exc); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Exception when getting data: ", exc); + } } } @@ -345,9 +393,13 @@ OCSPStatusRequest ocspRequest) { // Determine if the nonce extension is present in the request. If // so, then do not attempt to retrieve the response from the cache. - for (Extension ext : ocspRequest.getExtensions()) { - if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { - debugLog("Nonce extension found, skipping cache check"); + for (Extension ext : ocspRequest.extensions) { + if (ext.getId().equals( + PKIXExtensions.OCSPNonce_Id.toString())) { + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Nonce extension found, skipping cache check"); + } return null; } } @@ -359,12 +411,18 @@ // and do not return it as a cache hit. if (respEntry != null && respEntry.nextUpdate != null && respEntry.nextUpdate.before(new Date())) { - debugLog("nextUpdate threshold exceeded, purging from cache"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "nextUpdate threshold exceeded, purging from cache"); + } respEntry = null; } - debugLog("Check cache for SN" + cid.getSerialNumber() + ": " + - (respEntry != null ? "HIT" : "MISS")); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Check cache for SN" + cid.getSerialNumber() + ": " + + (respEntry != null ? "HIT" : "MISS")); + } return respEntry; } @@ -398,20 +456,6 @@ } /** - * Log messages through the SSL Debug facility. - * - * @param message the message to be displayed - */ - static void debugLog(String message) { - if (debug != null && Debug.isOn("respmgr")) { - StringBuilder sb = new StringBuilder(); - sb.append("[").append(Thread.currentThread().getName()); - sb.append("] ").append(message); - System.out.println(sb.toString()); - } - } - - /** * Inner class used to group request and response data. */ class StatusInfo { @@ -426,7 +470,7 @@ * @param subjectCert the certificate to be checked for revocation * @param issuerCert the issuer of the {@code subjectCert} * - * @throws IOException if CertId creation from the certificates fails + * @throws IOException if CertId creation from the certificate fails */ StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert) throws IOException { @@ -471,11 +515,14 @@ @Override public String toString() { StringBuilder sb = new StringBuilder("StatusInfo:"); - sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal()); + sb.append("\n\tCert: ").append( + this.cert.getSubjectX500Principal()); sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); sb.append("\n\tResponder: ").append(this.responder); - sb.append("\n\tResponse data: ").append(this.responseData != null ? - (this.responseData.ocspBytes.length + " bytes") : "<NULL>"); + sb.append("\n\tResponse data: ").append( + this.responseData != null ? + (this.responseData.ocspBytes.length + " bytes") : + "<NULL>"); return sb.toString(); } } @@ -483,7 +530,7 @@ /** * Static nested class used as the data kept in the response cache. */ - static class ResponseCacheEntry { + class ResponseCacheEntry { final OCSPResponse.ResponseStatus status; final byte[] ocspBytes; final Date nextUpdate; @@ -495,8 +542,8 @@ * * @param responseBytes the DER encoding for the OCSP response * - * @throws IOException if an {@code OCSPResponse} cannot be created from - * the encoded bytes. + * @throws IOException if an {@code OCSPResponse} cannot be + * created from the encoded bytes. */ ResponseCacheEntry(byte[] responseBytes, CertId cid) throws IOException { @@ -515,8 +562,9 @@ // Date is cloned. nextUpdate = singleResp.getNextUpdate(); } else { - throw new IOException("Unable to find SingleResponse for " + - "SN " + cid.getSerialNumber()); + throw new IOException( + "Unable to find SingleResponse for SN " + + cid.getSerialNumber()); } } else { nextUpdate = null; @@ -537,7 +585,8 @@ /** * A constructor that builds the OCSPFetchCall from the provided - * StatusInfo and information from the status_request[_v2] extension. + * StatusInfo and information from the status_request[_v2] + * extension. * * @param info the {@code StatusInfo} containing the subject * certificate, CertId, and other supplemental info. @@ -549,37 +598,54 @@ "Null StatusInfo not allowed"); ocspRequest = Objects.requireNonNull(request, "Null OCSPStatusRequest not allowed"); - extensions = ocspRequest.getExtensions(); - responderIds = ocspRequest.getResponderIds(); + extensions = ocspRequest.extensions; + responderIds = ocspRequest.responderIds; } /** * Get an OCSP response, either from the cache or from a responder. * - * @return The StatusInfo object passed into the {@code OCSPFetchCall} - * constructor, with the {@code responseData} field filled in with the - * response or {@code null} if no response can be obtained. + * @return The StatusInfo object passed into the + * {@code OCSPFetchCall} constructor, with the + * {@code responseData} field filled in with the response + * or {@code null} if no response can be obtained. */ @Override public StatusInfo call() { - debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber()); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Starting fetch for SN " + + statInfo.cid.getSerialNumber()); + } try { ResponseCacheEntry cacheEntry; List<Extension> extsToSend; if (statInfo.responder == null) { - // If we have no URI then there's nothing to do but return - debugLog("Null URI detected, OCSP fetch aborted."); + // If we have no URI then there's nothing to do + // but return. + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Null URI detected, OCSP fetch aborted"); + } return statInfo; } else { - debugLog("Attempting fetch from " + statInfo.responder); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Attempting fetch from " + statInfo.responder); + } } // If the StatusResponseManager has been configured to not - // forward extensions, then set extensions to an empty list. - // We will forward the extensions unless one of two conditions - // occur: (1) The jdk.tls.stapling.ignoreExtensions property is - // true or (2) There is a non-empty ResponderId list. + // forward extensions, then set extensions to an empty + // list. + // + // We will forward the extensions unless one of two + // conditions occur: + // (1) The jdk.tls.stapling.ignoreExtensions property is + // true, or + // (2) There is a non-empty ResponderId list. + // // ResponderId selection is a feature that will be // supported in the future. extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? @@ -595,8 +661,10 @@ statInfo.cid); // Get the response status and act on it appropriately - debugLog("OCSP Status: " + cacheEntry.status + + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("OCSP Status: " + cacheEntry.status + " (" + respBytes.length + " bytes)"); + } if (cacheEntry.status == OCSPResponse.ResponseStatus.SUCCESSFUL) { // Set the response in the returned StatusInfo @@ -606,10 +674,15 @@ addToCache(statInfo.cid, cacheEntry); } } else { - debugLog("No data returned from OCSP Responder"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "No data returned from OCSP Responder"); + } } } catch (IOException ioe) { - debugLog("Caught exception: " + ioe); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Caught exception: ", ioe); + } } return statInfo; @@ -626,22 +699,28 @@ // If no cache lifetime has been set on entries then // don't cache this response if there is no nextUpdate field if (entry.nextUpdate == null && cacheLifetime == 0) { - debugLog("Not caching this OCSP response"); + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine("Not caching this OCSP response"); + } } else { responseCache.put(certId, entry); - debugLog("Added response for SN " + certId.getSerialNumber() + + if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { + SSLLogger.fine( + "Added response for SN " + + certId.getSerialNumber() + " to cache"); + } } } /** * Determine the delay to use when scheduling the task that will * update the OCSP response. This is the shorter time between the - * cache lifetime and the nextUpdate. If no nextUpdate is present in - * the response, then only the cache lifetime is used. + * cache lifetime and the nextUpdate. If no nextUpdate is present + * in the response, then only the cache lifetime is used. * If cache timeouts are disabled (a zero value) and there's no - * nextUpdate, then the entry is not cached and no rescheduling will - * take place. + * nextUpdate, then the entry is not cached and no rescheduling + * will take place. * * @param nextUpdate a {@code Date} object corresponding to the * next update time from a SingleResponse. @@ -667,4 +746,218 @@ return delaySec; } } + + static final StaplingParameters processStapling( + ServerHandshakeContext shc) { + StaplingParameters params = null; + SSLExtension ext = null; + CertStatusRequestType type = null; + CertStatusRequest req = null; + Map<X509Certificate, byte[]> responses; + + // If this feature has not been enabled, then no more processing + // is necessary. Also we will only staple if we're doing a full + // handshake. + if (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Staping disabled or is a resumed session"); + } + return null; + } + + // Check if the client has asserted the status_request[_v2] extension(s) + Map<SSLExtension, SSLExtension.SSLExtensionSpec> exts = + shc.handshakeExtensions; + CertStatusRequestSpec statReq = (CertStatusRequestSpec)exts.get( + SSLExtension.CH_STATUS_REQUEST); + CertStatusRequestV2Spec statReqV2 = (CertStatusRequestV2Spec) + exts.get(SSLExtension.CH_STATUS_REQUEST_V2); + + // Determine which type of stapling we are doing and assert the + // proper extension in the server hello. + // Favor status_request_v2 over status_request and ocsp_multi + // over ocsp. + // If multiple ocsp or ocsp_multi types exist, select the first + // instance of a given type. Also since we don't support ResponderId + // selection yet, only accept a request if the ResponderId field + // is empty. Finally, we'll only do this in (D)TLS 1.2 or earlier. + if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.fine("SH Processing status_request_v2 extension"); + } + // RFC 6961 stapling + ext = SSLExtension.CH_STATUS_REQUEST_V2; + int ocspIdx = -1; + int ocspMultiIdx = -1; + CertStatusRequest[] reqItems = statReqV2.certStatusRequests; + for (int pos = 0; (pos < reqItems.length && + (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) { + CertStatusRequest item = reqItems[pos]; + CertStatusRequestType curType = + CertStatusRequestType.valueOf(item.statusType); + if (ocspIdx < 0 && curType == CertStatusRequestType.OCSP) { + OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; + // We currently only accept empty responder ID lists + // but may support them in the future + if (ocspReq.responderIds.isEmpty()) { + ocspIdx = pos; + } + } else if (ocspMultiIdx < 0 && + curType == CertStatusRequestType.OCSP_MULTI) { + OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; + // We currently only accept empty responder ID lists + // but may support them in the future + if (ocspReq.responderIds.isEmpty()) { + ocspMultiIdx = pos; + } + } + } + if (ocspMultiIdx >= 0) { + req = reqItems[ocspMultiIdx]; + type = CertStatusRequestType.valueOf(req.statusType); + } else if (ocspIdx >= 0) { + req = reqItems[ocspIdx]; + type = CertStatusRequestType.valueOf(req.statusType); + } else { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: No suitable request " + + "found in the status_request_v2 extension."); + } + } + } + + // Only attempt to process a status_request extension if: + // * The status_request extension is set AND + // * either the status_request_v2 extension is not present OR + // * none of the underlying OCSPStatusRequest structures is + // suitable for stapling. + // If either of the latter two bullet items is true the ext, + // type and req variables should all be null. If any are null + // we will try processing an asserted status_request. + if ((statReq != null) && + (ext == null || type == null || req == null)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.fine("SH Processing status_request extension"); + } + ext = SSLExtension.CH_STATUS_REQUEST; + type = CertStatusRequestType.valueOf( + statReq.statusRequest.statusType); + if (type == CertStatusRequestType.OCSP) { + // If the type is OCSP, then the request is guaranteed + // to be OCSPStatusRequest + OCSPStatusRequest ocspReq = + (OCSPStatusRequest)statReq.statusRequest; + if (ocspReq.responderIds.isEmpty()) { + req = ocspReq; + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: No suitable request " + + "found in the status_request extension."); + } + } + } + } + + // If, after walking through the extensions we were unable to + // find a suitable StatusRequest, then stapling is disabled. + // The ext, type and req variables must have been set to continue. + if (type == null || req == null || ext == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("No suitable status_request or " + + "status_request_v2, stapling is disabled"); + } + return null; + } + + // Get the cert chain since we'll need it for OCSP checking + X509Possession x509Possession = null; + for (SSLPossession possession : shc.handshakePossessions) { + if (possession instanceof X509Possession) { + x509Possession = (X509Possession)possession; + break; + } + } + + if (x509Possession == null) { // unlikely + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: no X.509 certificates found. " + + "Stapling is disabled."); + } + return null; + } + + // Get the OCSP responses from the StatusResponseManager + X509Certificate[] certs = x509Possession.popCerts; + StatusResponseManager statRespMgr = + shc.sslContext.getStatusResponseManager(); + if (statRespMgr != null) { + // For the purposes of the fetch from the SRM, override the + // type when it is TLS 1.3 so it always gets responses for + // all certs it can. This should not change the type field + // in the StaplingParameters though. + CertStatusRequestType fetchType = + shc.negotiatedProtocol.useTLS13PlusSpec() ? + CertStatusRequestType.OCSP_MULTI : type; + responses = statRespMgr.get(fetchType, req, certs, + shc.statusRespTimeout, TimeUnit.MILLISECONDS); + if (!responses.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Response manager returned " + + responses.size() + " entries."); + } + // If this RFC 6066-style stapling (SSL cert only) then the + // response cannot be zero length + if (type == CertStatusRequestType.OCSP) { + byte[] respDER = responses.get(certs[0]); + if (respDER == null || respDER.length <= 0) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: Null or zero-length " + + "response found for leaf certificate. " + + "Stapling is disabled."); + } + return null; + } + } + params = new StaplingParameters(ext, type, req, responses); + } else { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: no OCSP responses obtained. " + + "Stapling is disabled."); + } + } + } else { + // This should not happen, but if lazy initialization of the + // StatusResponseManager doesn't occur we should turn off stapling. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Warning: lazy initialization " + + "of the StatusResponseManager failed. " + + "Stapling is disabled."); + } + params = null; + } + + return params; + } + + /** + * Inner class used to hold stapling parameters needed by the handshaker + * when stapling is active. + */ + static final class StaplingParameters { + final SSLExtension statusRespExt; + final CertStatusRequestType statReqType; + final CertStatusRequest statReqData; + final Map<X509Certificate, byte[]> responseMap; + + StaplingParameters(SSLExtension ext, CertStatusRequestType type, + CertStatusRequest req, Map<X509Certificate, byte[]> responses) { + statusRespExt = ext; + statReqType = type; + statReqData = req; + responseMap = responses; + } + } } + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SunJSSE.java --- a/src/java.base/share/classes/sun/security/ssl/SunJSSE.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SunJSSE.java Mon Jun 25 13:41:39 2018 -0700 @@ -23,7 +23,6 @@ * questions. */ - package sun.security.ssl; import java.security.*; @@ -62,7 +61,7 @@ private static String info = "Sun JSSE provider" + "(PKCS12, SunX509/PKIX key/trust factories, " + - "SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)"; + "SSLv3/TLSv1/TLSv1.1/TLSv1.2/TLSv1.3/DTLSv1.0/DTLSv1.2)"; private static String fipsInfo = "Sun JSSE provider (FIPS mode, crypto provider "; @@ -149,7 +148,7 @@ } private void registerAlgorithms(final boolean isfips) { - AccessController.doPrivileged(new PrivilegedAction<>() { + AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { doRegister(isfips); @@ -214,6 +213,8 @@ "sun.security.ssl.SSLContextImpl$TLS11Context"); put("SSLContext.TLSv1.2", "sun.security.ssl.SSLContextImpl$TLS12Context"); + put("SSLContext.TLSv1.3", + "sun.security.ssl.SSLContextImpl$TLS13Context"); put("SSLContext.TLS", "sun.security.ssl.SSLContextImpl$TLSContext"); if (isfips == false) { diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java --- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, 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 @@ -25,13 +25,27 @@ package sun.security.ssl; -import javax.net.ssl.*; -import java.security.*; -import java.security.cert.*; +import java.net.Socket; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; -import java.util.*; -import java.net.Socket; - +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedKeyManager; import javax.security.auth.x500.X500Principal; @@ -67,8 +81,6 @@ */ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { - private static final Debug debug = Debug.getInstance("ssl"); - private static final String[] STRING0 = new String[0]; /* @@ -148,14 +160,8 @@ X509Credentials cred = new X509Credentials((PrivateKey)key, (X509Certificate[])certs); credentialsMap.put(alias, cred); - if (debug != null && Debug.isOn("keymanager")) { - System.out.println("***"); - System.out.println("found key for : " + alias); - for (int i = 0; i < certs.length; i++) { - System.out.println("chain [" + i + "] = " - + certs[i]); - } - System.out.println("***"); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("found key for : " + alias, (Object[])certs); } } } @@ -382,8 +388,8 @@ if (issuers.length == 0) { // no issuer specified, match all aliases.add(alias); - if (debug != null && Debug.isOn("keymanager")) { - System.out.println("matching alias: " + alias); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("matching alias: " + alias); } } else { Set<X500Principal> certIssuers = @@ -391,8 +397,8 @@ for (int i = 0; i < x500Issuers.length; i++) { if (certIssuers.contains(issuers[i])) { aliases.add(alias); - if (debug != null && Debug.isOn("keymanager")) { - System.out.println("matching alias: " + alias); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("matching alias: " + alias); } break; } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2017, 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,466 +26,1012 @@ package sun.security.ssl; import java.io.IOException; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.InvalidParameterSpecException; -import java.security.AlgorithmParameters; +import java.nio.ByteBuffer; +import java.security.AccessController; import java.security.AlgorithmConstraints; +import java.security.AlgorithmParameters; import java.security.CryptoPrimitive; -import java.security.AccessController; +import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; -import javax.crypto.spec.DHParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.ArrayList; +import javax.crypto.spec.DHParameterSpec; import javax.net.ssl.SSLProtocolException; - import sun.security.action.GetPropertyAction; - -// -// Note: Since RFC 7919, the extension's semantics are expanded from -// "Supported Elliptic Curves" to "Supported Groups". The enum datatype -// used in the extension has been renamed from NamedCurve to NamedGroup. -// Its semantics are likewise expanded from "named curve" to "named group". -// -final class SupportedGroupsExtension extends HelloExtension { - - /* Class and subclass dynamic debugging support */ - private static final Debug debug = Debug.getInstance("ssl"); - - private static final int ARBITRARY_PRIME = 0xff01; - private static final int ARBITRARY_CHAR2 = 0xff02; - - // cache to speed up the parameters construction - private static final Map<NamedGroup, - AlgorithmParameters> namedGroupParams = new HashMap<>(); +import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS; +import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; - // the supported named groups - private static final NamedGroup[] supportedNamedGroups; +/** + * Pack of the "supported_groups" extensions [RFC 4492/7919]. + */ +final class SupportedGroupsExtension { + static final HandshakeProducer chNetworkProducer = + new CHSupportedGroupsProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHSupportedGroupsConsumer(); + static final SSLStringizer sgsStringizer = + new SupportedGroupsStringizer(); - // the named group presented in the extension - private final int[] requestedNamedGroupIds; - - static { - boolean requireFips = SunJSSE.isFIPS(); + static final HandshakeProducer eeNetworkProducer = + new EESupportedGroupsProducer(); + static final ExtensionConsumer eeOnLoadConsumer = + new EESupportedGroupsConsumer(); - // The value of the System Property defines a list of enabled named - // groups in preference order, separated with comma. For example: - // - // jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048" - // - // If the System Property is not defined or the value is empty, the - // default groups and preferences will be used. - String property = AccessController.doPrivileged( - new GetPropertyAction("jdk.tls.namedGroups")); - if (property != null && property.length() != 0) { - // remove double quote marks from beginning/end of the property - if (property.length() > 1 && property.charAt(0) == '"' && - property.charAt(property.length() - 1) == '"') { - property = property.substring(1, property.length() - 1); + /** + * The "supported_groups" extension. + */ + static final class SupportedGroupsSpec implements SSLExtensionSpec { + final int[] namedGroupsIds; + + private SupportedGroupsSpec(int[] namedGroupsIds) { + this.namedGroupsIds = namedGroupsIds; + } + + private SupportedGroupsSpec(List<NamedGroup> namedGroups) { + this.namedGroupsIds = new int[namedGroups.size()]; + int i = 0; + for (NamedGroup ng : namedGroups) { + namedGroupsIds[i++] = ng.id; } } - ArrayList<NamedGroup> groupList; - if (property != null && property.length() != 0) { // customized groups - String[] groups = property.split(","); - groupList = new ArrayList<>(groups.length); - for (String group : groups) { - group = group.trim(); - if (!group.isEmpty()) { - NamedGroup namedGroup = NamedGroup.nameOf(group); - if (namedGroup != null && - (!requireFips || namedGroup.isFips)) { - if (isAvailableGroup(namedGroup)) { - groupList.add(namedGroup); - } - } // ignore unknown groups + private SupportedGroupsSpec(ByteBuffer m) throws IOException { + if (m.remaining() < 2) { // 2: the length of the list + throw new SSLProtocolException( + "Invalid supported_groups extension: insufficient data"); + } + + byte[] ngs = Record.getBytes16(m); + if (m.hasRemaining()) { + throw new SSLProtocolException( + "Invalid supported_groups extension: unknown extra data"); + } + + if ((ngs == null) || (ngs.length == 0) || (ngs.length % 2 != 0)) { + throw new SSLProtocolException( + "Invalid supported_groups extension: incomplete data"); + } + + int[] ids = new int[ngs.length / 2]; + for (int i = 0, j = 0; i < ngs.length;) { + ids[j++] = ((ngs[i++] & 0xFF) << 8) | (ngs[i++] & 0xFF); + } + + this.namedGroupsIds = ids; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"versions\": '['{0}']'", Locale.ENGLISH); + + if (namedGroupsIds == null || namedGroupsIds.length == 0) { + Object[] messageFields = { + "<no supported named group specified>" + }; + return messageFormat.format(messageFields); + } else { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (int ngid : namedGroupsIds) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } + + builder.append(NamedGroup.nameOf(ngid)); + } + + Object[] messageFields = { + builder.toString() + }; + + return messageFormat.format(messageFields); + } + } + } + + private static final + class SupportedGroupsStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new SupportedGroupsSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + static enum NamedGroupType { + NAMED_GROUP_ECDHE, // Elliptic Curve Groups (ECDHE) + NAMED_GROUP_FFDHE, // Finite Field Groups (DHE) + NAMED_GROUP_XDH, // Finite Field Groups (XDH) + NAMED_GROUP_ARBITRARY, // arbitrary prime and curves (ECDHE) + NAMED_GROUP_NONE; // Not predefined named group + + boolean isSupported(List<CipherSuite> cipherSuites) { + for (CipherSuite cs : cipherSuites) { + if (cs.keyExchange == null || cs.keyExchange.groupType == this) { + return true; + } + } + + return false; + } + } + + static enum NamedGroup { + // Elliptic Curves (RFC 4492) + // + // See sun.security.util.CurveDB for the OIDs + // NIST K-163 + SECT163_K1 (0x0001, "sect163k1", "1.3.132.0.1", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECT163_R1 (0x0002, "sect163r1", "1.3.132.0.2", false, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-163 + SECT163_R2 (0x0003, "sect163r2", "1.3.132.0.15", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECT193_R1 (0x0004, "sect193r1", "1.3.132.0.24", false, + ProtocolVersion.PROTOCOLS_TO_12), + SECT193_R2 (0x0005, "sect193r2", "1.3.132.0.25", false, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-233 + SECT233_K1 (0x0006, "sect233k1", "1.3.132.0.26", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-233 + SECT233_R1 (0x0007, "sect233r1", "1.3.132.0.27", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECT239_K1 (0x0008, "sect239k1", "1.3.132.0.3", false, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-283 + SECT283_K1 (0x0009, "sect283k1", "1.3.132.0.16", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-283 + SECT283_R1 (0x000A, "sect283r1", "1.3.132.0.17", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-409 + SECT409_K1 (0x000B, "sect409k1", "1.3.132.0.36", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-409 + SECT409_R1 (0x000C, "sect409r1", "1.3.132.0.37", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-571 + SECT571_K1 (0x000D, "sect571k1", "1.3.132.0.38", true, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-571 + SECT571_R1 (0x000E, "sect571r1", "1.3.132.0.39", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_K1 (0x000F, "secp160k1", "1.3.132.0.9", false, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_R1 (0x0010, "secp160r1", "1.3.132.0.8", false, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_R2 (0x0011, "secp160r2", "1.3.132.0.30", false, + ProtocolVersion.PROTOCOLS_TO_12), + SECP192_K1 (0x0012, "secp192k1", "1.3.132.0.31", false, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST P-192 + SECP192_R1 (0x0013, "secp192r1", "1.2.840.10045.3.1.1", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECP224_K1 (0x0014, "secp224k1", "1.3.132.0.32", false, + ProtocolVersion.PROTOCOLS_TO_12), + // NIST P-224 + SECP224_R1 (0x0015, "secp224r1", "1.3.132.0.33", true, + ProtocolVersion.PROTOCOLS_TO_12), + SECP256_K1 (0x0016, "secp256k1", "1.3.132.0.10", false, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST P-256 + SECP256_R1 (0x0017, "secp256r1", "1.2.840.10045.3.1.7", true, + ProtocolVersion.PROTOCOLS_TO_13), + + // NIST P-384 + SECP384_R1 (0x0018, "secp384r1", "1.3.132.0.34", true, + ProtocolVersion.PROTOCOLS_TO_13), + + // NIST P-521 + SECP521_R1 (0x0019, "secp521r1", "1.3.132.0.35", true, + ProtocolVersion.PROTOCOLS_TO_13), + + // x25519 and x448 + X25519 (0x001D, "x25519", true, "x25519", + ProtocolVersion.PROTOCOLS_TO_13), + X448 (0x001E, "x448", true, "x448", + ProtocolVersion.PROTOCOLS_TO_13), + + // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919) + FFDHE_2048 (0x0100, "ffdhe2048", true, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_3072 (0x0101, "ffdhe3072", true, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_4096 (0x0102, "ffdhe4096", true, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_6144 (0x0103, "ffdhe6144", true, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_8192 (0x0104, "ffdhe8192", true, + ProtocolVersion.PROTOCOLS_TO_13), + + // Elliptic Curves (RFC 4492) + // + // arbitrary prime and characteristic-2 curves + ARBITRARY_PRIME (0xFF01, "arbitrary_explicit_prime_curves", + ProtocolVersion.PROTOCOLS_TO_12), + ARBITRARY_CHAR2 (0xFF02, "arbitrary_explicit_char2_curves", + ProtocolVersion.PROTOCOLS_TO_12); + + final int id; // hash + signature + final NamedGroupType type; // group type + final String name; // literal name + final String oid; // object identifier of the named group + final String algorithm; // signature algorithm + final boolean isFips; // can be used in FIPS mode? + final ProtocolVersion[] supportedProtocols; + + // Constructor used for Elliptic Curve Groups (ECDHE) + private NamedGroup(int id, String name, String oid, boolean isFips, + ProtocolVersion[] supportedProtocols) { + this.id = id; + this.type = NamedGroupType.NAMED_GROUP_ECDHE; + this.name = name; + this.oid = oid; + this.algorithm = "EC"; + this.isFips = isFips; + this.supportedProtocols = supportedProtocols; + } + + // Constructor used for Elliptic Curve Groups (XDH) + private NamedGroup(int id, String name, + boolean isFips, String algorithm, + ProtocolVersion[] supportedProtocols) { + this.id = id; + this.type = NamedGroupType.NAMED_GROUP_XDH; + this.name = name; + this.oid = null; + this.algorithm = algorithm; + this.isFips = isFips; + this.supportedProtocols = supportedProtocols; + } + + // Constructor used for Finite Field Diffie-Hellman Groups (FFDHE) + private NamedGroup(int id, String name, boolean isFips, + ProtocolVersion[] supportedProtocols) { + this.id = id; + this.type = NamedGroupType.NAMED_GROUP_FFDHE; + this.name = name; + this.oid = null; + this.algorithm = "DiffieHellman"; + this.isFips = isFips; + this.supportedProtocols = supportedProtocols; + } + + // Constructor used for arbitrary prime and curves (ECDHE) + private NamedGroup(int id, String name, + ProtocolVersion[] supportedProtocols) { + this.id = id; + this.type = NamedGroupType.NAMED_GROUP_ARBITRARY; + this.name = name; + this.oid = null; + this.algorithm = "EC"; + this.isFips = false; + this.supportedProtocols = supportedProtocols; + } + + static NamedGroup valueOf(int id) { + for (NamedGroup group : NamedGroup.values()) { + if (group.id == id) { + 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 ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) && + oid.equals(group.oid)) { + return group; + } } } - if (groupList.isEmpty() && JsseJce.isEcAvailable()) { - throw new IllegalArgumentException( - "System property jdk.tls.namedGroups(" + property + ") " + - "contains no supported elliptic curves"); + return null; + } + + static NamedGroup valueOf(DHParameterSpec params) { + for (Map.Entry<NamedGroup, AlgorithmParameters> me : + SupportedGroups.namedGroupParams.entrySet()) { + NamedGroup ng = me.getKey(); + if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) { + continue; + } + + DHParameterSpec ngParams = null; + AlgorithmParameters aps = me.getValue(); + try { + ngParams = aps.getParameterSpec(DHParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + } + + if (ngParams == null) { + continue; + } + + if (ngParams.getP().equals(params.getP()) && + ngParams.getG().equals(params.getG())) { + return ng; + } } - } else { // default groups - NamedGroup[] groups; - if (requireFips) { - groups = new NamedGroup[] { - // only NIST curves in FIPS mode - NamedGroup.SECP256_R1, - NamedGroup.SECP384_R1, - NamedGroup.SECP521_R1, - NamedGroup.SECT283_K1, - NamedGroup.SECT283_R1, - NamedGroup.SECT409_K1, - NamedGroup.SECT409_R1, - NamedGroup.SECT571_K1, - NamedGroup.SECT571_R1, + + return null; + } + + static NamedGroup nameOf(String name) { + for (NamedGroup group : NamedGroup.values()) { + if (group.name.equals(name)) { + return group; + } + } + + return null; + } - // FFDHE 2048 - NamedGroup.FFDHE_2048, - NamedGroup.FFDHE_3072, - NamedGroup.FFDHE_4096, - NamedGroup.FFDHE_6144, - NamedGroup.FFDHE_8192, - }; - } else { - groups = new NamedGroup[] { - // NIST curves first - NamedGroup.SECP256_R1, - NamedGroup.SECP384_R1, - NamedGroup.SECP521_R1, - NamedGroup.SECT283_K1, - NamedGroup.SECT283_R1, - NamedGroup.SECT409_K1, - NamedGroup.SECT409_R1, - NamedGroup.SECT571_K1, - NamedGroup.SECT571_R1, + static String nameOf(int id) { + for (NamedGroup group : NamedGroup.values()) { + if (group.id == id) { + return group.name; + } + } + + return "UNDEFINED-NAMED-GROUP(" + id + ")"; + } + + boolean isAvailable(List<ProtocolVersion> protocolVersions) { + for (ProtocolVersion pv : supportedProtocols) { + if (protocolVersions.contains(pv)) { + return true; + } + } + return false; + } - // non-NIST curves - NamedGroup.SECP256_K1, + boolean isAvailable(ProtocolVersion protocolVersion) { + for (ProtocolVersion pv : supportedProtocols) { + if (protocolVersion == pv) { + return true; + } + } + return false; + } - // FFDHE 2048 - NamedGroup.FFDHE_2048, - NamedGroup.FFDHE_3072, - NamedGroup.FFDHE_4096, - NamedGroup.FFDHE_6144, - NamedGroup.FFDHE_8192, - }; + boolean isSupported(List<CipherSuite> cipherSuites) { + for (CipherSuite cs : cipherSuites) { + boolean isMatch = isAvailable(cs.supportedProtocols); + if (isMatch && (cs.keyExchange == null || + cs.keyExchange.groupType == type)) { + return true; + } + } + return false; + } + + // lazy loading of parameters + AlgorithmParameters getParameters() { + return SupportedGroups.namedGroupParams.get(this); + } + + AlgorithmParameterSpec getParameterSpec() { + if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) { + return SupportedGroups.getECGenParamSpec(this); + } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) { + return SupportedGroups.getDHParameterSpec(this); } - groupList = new ArrayList<>(groups.length); - for (NamedGroup group : groups) { - if (isAvailableGroup(group)) { - groupList.add(group); + return null; + } + } + + static class SupportedGroups { + // To switch off the supported_groups extension for DHE cipher suite. + static final boolean enableFFDHE = + Utilities.getBooleanProperty("jsse.enableFFDHE", true); + + // cache to speed up the parameters construction + static final Map<NamedGroup, + AlgorithmParameters> namedGroupParams = new HashMap<>(); + + // the supported named groups + static final NamedGroup[] supportedNamedGroups; + + static { + boolean requireFips = SunJSSE.isFIPS(); + + // The value of the System Property defines a list of enabled named + // groups in preference order, separated with comma. For example: + // + // jdk.tls.namedGroups="secp521r1, secp256r1, ffdhe2048" + // + // If the System Property is not defined or the value is empty, the + // default groups and preferences will be used. + String property = AccessController.doPrivileged( + new GetPropertyAction("jdk.tls.namedGroups")); + if (property != null && property.length() != 0) { + // remove double quote marks from beginning/end of the property + if (property.length() > 1 && property.charAt(0) == '"' && + property.charAt(property.length() - 1) == '"') { + property = property.substring(1, property.length() - 1); + } + } + + ArrayList<NamedGroup> groupList; + if (property != null && property.length() != 0) { + String[] groups = property.split(","); + groupList = new ArrayList<>(groups.length); + for (String group : groups) { + group = group.trim(); + if (!group.isEmpty()) { + NamedGroup namedGroup = NamedGroup.nameOf(group); + if (namedGroup != null && + (!requireFips || namedGroup.isFips)) { + if (isAvailableGroup(namedGroup)) { + groupList.add(namedGroup); + } + } // ignore unknown groups + } + } + + if (groupList.isEmpty()) { + throw new IllegalArgumentException( + "System property jdk.tls.namedGroups(" + + property + ") contains no supported named groups"); } + } else { // default groups + NamedGroup[] groups; + if (requireFips) { + groups = new NamedGroup[] { + // only NIST curves in FIPS mode + NamedGroup.SECP256_R1, + NamedGroup.SECP384_R1, + NamedGroup.SECP521_R1, + NamedGroup.SECT283_K1, + NamedGroup.SECT283_R1, + NamedGroup.SECT409_K1, + NamedGroup.SECT409_R1, + NamedGroup.SECT571_K1, + NamedGroup.SECT571_R1, + + // FFDHE 2048 + NamedGroup.FFDHE_2048, + NamedGroup.FFDHE_3072, + NamedGroup.FFDHE_4096, + NamedGroup.FFDHE_6144, + NamedGroup.FFDHE_8192, + }; + } else { + groups = new NamedGroup[] { + // NIST curves first + NamedGroup.SECP256_R1, + NamedGroup.SECP384_R1, + NamedGroup.SECP521_R1, + NamedGroup.SECT283_K1, + NamedGroup.SECT283_R1, + NamedGroup.SECT409_K1, + NamedGroup.SECT409_R1, + NamedGroup.SECT571_K1, + NamedGroup.SECT571_R1, + + // non-NIST curves + NamedGroup.SECP256_K1, + + // FFDHE 2048 + NamedGroup.FFDHE_2048, + NamedGroup.FFDHE_3072, + NamedGroup.FFDHE_4096, + NamedGroup.FFDHE_6144, + NamedGroup.FFDHE_8192, + }; + } + + groupList = new ArrayList<>(groups.length); + for (NamedGroup group : groups) { + if (isAvailableGroup(group)) { + groupList.add(group); + } + } + + if (groupList.isEmpty() && + SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("No default named groups"); + } + } + + supportedNamedGroups = new NamedGroup[groupList.size()]; + int i = 0; + for (NamedGroup namedGroup : groupList) { + supportedNamedGroups[i++] = namedGroup; } } - if (debug != null && groupList.isEmpty()) { - Debug.log( - "Initialized [jdk.tls.namedGroups|default] list contains " + - "no available elliptic curves. " + - (property != null ? "(" + property + ")" : "[Default]")); + // check whether the group is supported by the underlying providers + private static boolean isAvailableGroup(NamedGroup namedGroup) { + AlgorithmParameters params = null; + AlgorithmParameterSpec spec = null; + if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { + if (namedGroup.oid != null) { + try { + params = JsseJce.getAlgorithmParameters("EC"); + spec = new ECGenParameterSpec(namedGroup.oid); + } catch (NoSuchAlgorithmException e) { + return false; + } + } + } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { + try { + params = JsseJce.getAlgorithmParameters("DiffieHellman"); + spec = getFFDHEDHParameterSpec(namedGroup); + } catch (NoSuchAlgorithmException e) { + return false; + } + } // Otherwise, unsupported. + + if ((params != null) && (spec != null)) { + try { + params.init(spec); + } catch (InvalidParameterSpecException e) { + return false; + } + + // cache the parameters + namedGroupParams.put(namedGroup, params); + + return true; + } + + return false; } - supportedNamedGroups = new NamedGroup[groupList.size()]; - int i = 0; - for (NamedGroup namedGroup : groupList) { - supportedNamedGroups[i++] = namedGroup; + private static DHParameterSpec getFFDHEDHParameterSpec( + NamedGroup namedGroup) { + DHParameterSpec spec = null; + switch (namedGroup) { + case FFDHE_2048: + spec = PredefinedDHParameterSpecs.ffdheParams.get(2048); + break; + case FFDHE_3072: + spec = PredefinedDHParameterSpecs.ffdheParams.get(3072); + break; + case FFDHE_4096: + spec = PredefinedDHParameterSpecs.ffdheParams.get(4096); + break; + case FFDHE_6144: + spec = PredefinedDHParameterSpecs.ffdheParams.get(6144); + break; + case FFDHE_8192: + spec = PredefinedDHParameterSpecs.ffdheParams.get(8192); + } + + return spec; } - } - // check whether the group is supported by the underlying providers - private static boolean isAvailableGroup(NamedGroup namedGroup) { - AlgorithmParameters params = null; - AlgorithmParameterSpec spec = null; - if ("EC".equals(namedGroup.algorithm)) { - if (namedGroup.oid != null) { - try { - params = JsseJce.getAlgorithmParameters("EC"); - spec = new ECGenParameterSpec(namedGroup.oid); - } catch (Exception e) { - return false; - } + private static DHParameterSpec getPredefinedDHParameterSpec( + NamedGroup namedGroup) { + DHParameterSpec spec = null; + switch (namedGroup) { + case FFDHE_2048: + spec = PredefinedDHParameterSpecs.definedParams.get(2048); + break; + case FFDHE_3072: + spec = PredefinedDHParameterSpecs.definedParams.get(3072); + break; + case FFDHE_4096: + spec = PredefinedDHParameterSpecs.definedParams.get(4096); + break; + case FFDHE_6144: + spec = PredefinedDHParameterSpecs.definedParams.get(6144); + break; + case FFDHE_8192: + spec = PredefinedDHParameterSpecs.definedParams.get(8192); } - } else if ("DiffieHellman".equals(namedGroup.algorithm)) { + + return spec; + } + + static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) { + if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) { + throw new RuntimeException( + "Not a named EC group: " + namedGroup); + } + + AlgorithmParameters params = namedGroupParams.get(namedGroup); try { - params = JsseJce.getAlgorithmParameters("DiffieHellman"); - spec = getFFDHEDHParameterSpec(namedGroup); - } catch (Exception e) { - return false; + return params.getParameterSpec(ECGenParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + return new ECGenParameterSpec(namedGroup.oid); } } - if ((params != null) && (spec != null)) { + static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) { + if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) { + throw new RuntimeException( + "Not a named DH group: " + namedGroup); + } + + AlgorithmParameters params = namedGroupParams.get(namedGroup); try { - params.init(spec); - } catch (Exception e) { + return params.getParameterSpec(DHParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + return getPredefinedDHParameterSpec(namedGroup); + } + } + + // Is there any supported group permitted by the constraints? + static boolean isActivatable( + AlgorithmConstraints constraints, NamedGroupType type) { + + boolean hasFFDHEGroups = false; + for (NamedGroup namedGroup : supportedNamedGroups) { + if (namedGroup.type == type) { + if (constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroup.algorithm, + namedGroupParams.get(namedGroup))) { + + return true; + } + + if (!hasFFDHEGroups && + (type == NamedGroupType.NAMED_GROUP_FFDHE)) { + hasFFDHEGroups = true; + } + } + } + + // For compatibility, if no FFDHE groups are defined, the non-FFDHE + // compatible mode (using DHE cipher suite without FFDHE extension) + // is allowed. + // + // Note that the constraints checking on DHE parameters will be + // performed during key exchanging in a handshake. + return !hasFFDHEGroups && type == NamedGroupType.NAMED_GROUP_FFDHE; + } + + // Is the named group permitted by the constraints? + static boolean isActivatable( + AlgorithmConstraints constraints, NamedGroup namedGroup) { + if (!isSupported(namedGroup)) { return false; } - // cache the parameters - namedGroupParams.put(namedGroup, params); + return constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroup.algorithm, + namedGroupParams.get(namedGroup)); + } - return true; + // Is there any supported group permitted by the constraints? + static boolean isSupported(NamedGroup namedGroup) { + for (NamedGroup group : supportedNamedGroups) { + if (namedGroup.id == group.id) { + return true; + } + } + + return false; } - return false; - } + static NamedGroup getPreferredGroup( + ProtocolVersion negotiatedProtocol, + AlgorithmConstraints constraints, NamedGroupType type, + List<NamedGroup> requestedNamedGroups) { + for (NamedGroup namedGroup : requestedNamedGroups) { + if ((namedGroup.type == type) && + namedGroup.isAvailable(negotiatedProtocol) && + constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroup.algorithm, + namedGroupParams.get(namedGroup))) { + return namedGroup; + } + } - private static DHParameterSpec getFFDHEDHParameterSpec( - NamedGroup namedGroup) { - DHParameterSpec spec = null; - switch (namedGroup) { - case FFDHE_2048: - spec = PredefinedDHParameterSpecs.ffdheParams.get(2048); - break; - case FFDHE_3072: - spec = PredefinedDHParameterSpecs.ffdheParams.get(3072); - break; - case FFDHE_4096: - spec = PredefinedDHParameterSpecs.ffdheParams.get(4096); - break; - case FFDHE_6144: - spec = PredefinedDHParameterSpecs.ffdheParams.get(6144); - break; - case FFDHE_8192: - spec = PredefinedDHParameterSpecs.ffdheParams.get(8192); + return null; } - return spec; + static NamedGroup getPreferredGroup( + ProtocolVersion negotiatedProtocol, + AlgorithmConstraints constraints, NamedGroupType type) { + for (NamedGroup namedGroup : supportedNamedGroups) { + if ((namedGroup.type == type) && + namedGroup.isAvailable(negotiatedProtocol) && + constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + namedGroup.algorithm, + namedGroupParams.get(namedGroup))) { + return namedGroup; + } + } + + return null; + } } - private static DHParameterSpec getPredefinedDHParameterSpec( - NamedGroup namedGroup) { - DHParameterSpec spec = null; - switch (namedGroup) { - case FFDHE_2048: - spec = PredefinedDHParameterSpecs.definedParams.get(2048); - break; - case FFDHE_3072: - spec = PredefinedDHParameterSpecs.definedParams.get(3072); - break; - case FFDHE_4096: - spec = PredefinedDHParameterSpecs.definedParams.get(4096); - break; - case FFDHE_6144: - spec = PredefinedDHParameterSpecs.definedParams.get(6144); - break; - case FFDHE_8192: - spec = PredefinedDHParameterSpecs.definedParams.get(8192); + /** + * Network data producer of a "supported_groups" extension in + * the ClientHello handshake message. + */ + private static final class CHSupportedGroupsProducer + extends SupportedGroups implements HandshakeProducer { + // Prevent instantiation of this class. + private CHSupportedGroupsProducer() { + // blank } - return spec; - } + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; - private SupportedGroupsExtension(int[] requestedNamedGroupIds) { - super(ExtensionType.EXT_SUPPORTED_GROUPS); + // Is it a supported and enabled extension? + if (!chc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable supported_groups extension"); + } + return null; + } - this.requestedNamedGroupIds = requestedNamedGroupIds; - } + // Produce the extension. + ArrayList<NamedGroup> namedGroups = + new ArrayList<>(SupportedGroups.supportedNamedGroups.length); + for (NamedGroup ng : SupportedGroups.supportedNamedGroups) { + if ((!SupportedGroups.enableFFDHE) && + (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) { + continue; + } - SupportedGroupsExtension(HandshakeInStream s, int len) throws IOException { - super(ExtensionType.EXT_SUPPORTED_GROUPS); + if (ng.isAvailable(chc.activeProtocols) && + ng.isSupported(chc.activeCipherSuites) && + chc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + ng.algorithm, namedGroupParams.get(ng))) { + namedGroups.add(ng); + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore inactive or disabled named group: " + ng.name); + } + } + + if (namedGroups.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning("no available named group"); + } - int k = s.getInt16(); - if (((len & 1) != 0) || (k == 0) || (k + 2 != len)) { - throw new SSLProtocolException("Invalid " + type + " extension"); - } + return null; + } - // Note: unknown named group will be ignored later. - requestedNamedGroupIds = new int[k >> 1]; - for (int i = 0; i < requestedNamedGroupIds.length; i++) { - requestedNamedGroupIds[i] = s.getInt16(); + int vectorLen = namedGroups.size() << 1; + byte[] extData = new byte[vectorLen + 2]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt16(m, vectorLen); + for (NamedGroup namedGroup : namedGroups) { + Record.putInt16(m, namedGroup.id); + } + + // Update the context. + chc.clientRequestedNamedGroups = + Collections.<NamedGroup>unmodifiableList(namedGroups); + chc.handshakeExtensions.put(CH_SUPPORTED_GROUPS, + new SupportedGroupsSpec(namedGroups)); + + return extData; } } - // Get a local preferred supported ECDHE group permitted by the constraints. - static NamedGroup getPreferredECGroup(AlgorithmConstraints constraints) { - for (NamedGroup namedGroup : supportedNamedGroups) { - if ((namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) && - constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroup.algorithm, namedGroupParams.get(namedGroup))) { - - return namedGroup; - } + /** + * Network data producer of a "supported_groups" extension in + * the ClientHello handshake message. + */ + private static final + class CHSupportedGroupsConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHSupportedGroupsConsumer() { + // blank } - return null; - } - - // Is there any supported group permitted by the constraints? - static boolean isActivatable( - AlgorithmConstraints constraints, NamedGroupType type) { + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; - boolean hasFFDHEGroups = false; - for (NamedGroup namedGroup : supportedNamedGroups) { - if (namedGroup.type == type) { - if (constraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroup.algorithm, - namedGroupParams.get(namedGroup))) { - - return true; - } - - if (!hasFFDHEGroups && - (type == NamedGroupType.NAMED_GROUP_FFDHE)) { - - hasFFDHEGroups = true; + // Is it a supported and enabled extension? + if (!shc.sslConfig.isAvailable(CH_SUPPORTED_GROUPS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable supported_groups extension"); } - } - } - - // For compatibility, if no FFDHE groups are defined, the non-FFDHE - // compatible mode (using DHE cipher suite without FFDHE extension) - // is allowed. - // - // Note that the constraints checking on DHE parameters will be - // performed during key exchanging in a handshake. - if (!hasFFDHEGroups && (type == NamedGroupType.NAMED_GROUP_FFDHE)) { - return true; - } - - return false; - } - - // Create the default supported groups extension. - static SupportedGroupsExtension createExtension( - AlgorithmConstraints constraints, - CipherSuiteList cipherSuites, boolean enableFFDHE) { - - ArrayList<Integer> groupList = - new ArrayList<>(supportedNamedGroups.length); - for (NamedGroup namedGroup : supportedNamedGroups) { - if ((!enableFFDHE) && - (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE)) { - continue; + return; // ignore the extension } - if (cipherSuites.contains(namedGroup.type) && - constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroup.algorithm, namedGroupParams.get(namedGroup))) { - - groupList.add(namedGroup.id); - } - } - - if (!groupList.isEmpty()) { - int[] ids = new int[groupList.size()]; - int i = 0; - for (Integer id : groupList) { - ids[i++] = id; + // Parse the extension. + SupportedGroupsSpec spec; + try { + spec = new SupportedGroupsSpec(buffer); + } catch (IOException ioe) { + shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + return; // fatal() always throws, make the compiler happy. } - return new SupportedGroupsExtension(ids); - } - - return null; - } - - // get the preferred activated named group - NamedGroup getPreferredGroup( - AlgorithmConstraints constraints, NamedGroupType type) { - - for (int groupId : requestedNamedGroupIds) { - NamedGroup namedGroup = NamedGroup.valueOf(groupId); - if ((namedGroup != null) && (namedGroup.type == type) && - SupportedGroupsExtension.supports(namedGroup) && - constraints.permits(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - namedGroup.algorithm, namedGroupParams.get(namedGroup))) { - - return namedGroup; + // Update the context. + List<NamedGroup> knownNamedGroups = new LinkedList<>(); + for (int id : spec.namedGroupsIds) { + NamedGroup ng = NamedGroup.valueOf(id); + if (ng != null) { + knownNamedGroups.add(ng); + } } - } - - return null; - } - boolean hasFFDHEGroup() { - for (int groupId : requestedNamedGroupIds) { - /* - * [RFC 7919] Codepoints in the "Supported Groups Registry" - * with a high byte of 0x01 (that is, between 256 and 511, - * inclusive) are set aside for FFDHE groups. - */ - if ((groupId >= 256) && (groupId <= 511)) { - return true; - } - } - - return false; - } + shc.clientRequestedNamedGroups = knownNamedGroups; + shc.handshakeExtensions.put(CH_SUPPORTED_GROUPS, spec); - boolean contains(int index) { - for (int groupId : requestedNamedGroupIds) { - if (index == groupId) { - return true; - } - } - return false; - } - - @Override - int length() { - return 6 + (requestedNamedGroupIds.length << 1); - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt16(type.id); - int k = requestedNamedGroupIds.length << 1; - s.putInt16(k + 2); - s.putInt16(k); - for (int groupId : requestedNamedGroupIds) { - s.putInt16(groupId); + // No impact on session resumption. } } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Extension " + type + ", group names: {"); - boolean first = true; - for (int groupId : requestedNamedGroupIds) { - if (first) { - first = false; - } else { - sb.append(", "); - } - // first check if it is a known named group, then try other cases. - NamedGroup namedGroup = NamedGroup.valueOf(groupId); - if (namedGroup != null) { - sb.append(namedGroup.name); - } else if (groupId == ARBITRARY_PRIME) { - sb.append("arbitrary_explicit_prime_curves"); - } else if (groupId == ARBITRARY_CHAR2) { - sb.append("arbitrary_explicit_char2_curves"); - } else { - sb.append("unknown named group " + groupId); - } - } - sb.append("}"); - return sb.toString(); - } + /** + * Network data producer of a "supported_groups" extension in + * the EncryptedExtensions handshake message. + */ + private static final class EESupportedGroupsProducer + extends SupportedGroups implements HandshakeProducer { - static boolean supports(NamedGroup namedGroup) { - for (NamedGroup group : supportedNamedGroups) { - if (namedGroup.id == group.id) { - return true; - } + // Prevent instantiation of this class. + private EESupportedGroupsProducer() { + // blank } - return false; - } + @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(EE_SUPPORTED_GROUPS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable supported_groups extension"); + } + return null; + } + + // Produce the extension. + // + // Contains all groups the server supports, regardless of whether + // they are currently supported by the client. + ArrayList<NamedGroup> namedGroups = new ArrayList<>( + SupportedGroups.supportedNamedGroups.length); + for (NamedGroup ng : SupportedGroups.supportedNamedGroups) { + if ((!SupportedGroups.enableFFDHE) && + (ng.type == NamedGroupType.NAMED_GROUP_FFDHE)) { + continue; + } - static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) { - if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) { - throw new RuntimeException("Not a named EC group: " + namedGroup); - } + if (ng.isAvailable(shc.activeProtocols) && + ng.isSupported(shc.activeCipherSuites) && + shc.algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + ng.algorithm, namedGroupParams.get(ng))) { + namedGroups.add(ng); + } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore inactive or disabled named group: " + ng.name); + } + } + + if (namedGroups.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning("no available named group"); + } - AlgorithmParameters params = namedGroupParams.get(namedGroup); - try { - return params.getParameterSpec(ECGenParameterSpec.class); - } catch (InvalidParameterSpecException ipse) { - // should be unlikely - return new ECGenParameterSpec(namedGroup.oid); + return null; + } + + int vectorLen = namedGroups.size() << 1; + byte[] extData = new byte[vectorLen + 2]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt16(m, vectorLen); + for (NamedGroup namedGroup : namedGroups) { + Record.putInt16(m, namedGroup.id); + } + + // Update the context. + shc.conContext.serverRequestedNamedGroups = + Collections.<NamedGroup>unmodifiableList(namedGroups); + SupportedGroupsSpec spec = new SupportedGroupsSpec(namedGroups); + shc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec); + + return extData; } } - static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) { - if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) { - throw new RuntimeException("Not a named DH group: " + namedGroup); + private static final + class EESupportedGroupsConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private EESupportedGroupsConsumer() { + // blank } - AlgorithmParameters params = namedGroupParams.get(namedGroup); - try { - return params.getParameterSpec(DHParameterSpec.class); - } catch (InvalidParameterSpecException ipse) { - // should be unlikely - return getPredefinedDHParameterSpec(namedGroup); + @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(EE_SUPPORTED_GROUPS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable supported_groups extension"); + } + return; // ignore the extension + } + + // Parse the extension. + SupportedGroupsSpec spec; + try { + spec = new SupportedGroupsSpec(buffer); + } catch (IOException ioe) { + chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); + return; // fatal() always throws, make the compiler happy. + } + + // Update the context. + List<NamedGroup> knownNamedGroups = + new ArrayList<>(spec.namedGroupsIds.length); + for (int id : spec.namedGroupsIds) { + NamedGroup ng = NamedGroup.valueOf(id); + if (ng != null) { + knownNamedGroups.add(ng); + } + } + + chc.conContext.serverRequestedNamedGroups = knownNamedGroups; + chc.handshakeExtensions.put(EE_SUPPORTED_GROUPS, spec); + + // No impact on session resumption. } } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/SupportedVersionsExtension.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,511 @@ +/* + * 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_SUPPORTED_VERSIONS; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import static sun.security.ssl.SSLExtension.HRR_SUPPORTED_VERSIONS; +import static sun.security.ssl.SSLExtension.SH_SUPPORTED_VERSIONS; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +/** + * Pack of the "supported_versions" extensions. + */ +final class SupportedVersionsExtension { + static final HandshakeProducer chNetworkProducer = + new CHSupportedVersionsProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHSupportedVersionsConsumer(); + static final SSLStringizer chStringizer = + new CHSupportedVersionsStringizer(); + + static final HandshakeProducer shNetworkProducer = + new SHSupportedVersionsProducer(); + static final ExtensionConsumer shOnLoadConsumer = + new SHSupportedVersionsConsumer(); + static final SSLStringizer shStringizer = + new SHSupportedVersionsStringizer(); + + static final HandshakeProducer hrrNetworkProducer = + new HRRSupportedVersionsProducer(); + static final ExtensionConsumer hrrOnLoadConsumer = + new HRRSupportedVersionsConsumer(); + static final HandshakeProducer hrrReproducer = + new HRRSupportedVersionsReproducer(); + static final SSLStringizer hrrStringizer = + new SHSupportedVersionsStringizer(); + /** + * The "supported_versions" extension in ClientHello. + */ + static final class CHSupportedVersionsSpec implements SSLExtensionSpec { + final int[] requestedProtocols; + + private CHSupportedVersionsSpec(int[] requestedProtocols) { + this.requestedProtocols = requestedProtocols; + } + + private CHSupportedVersionsSpec(ByteBuffer m) throws IOException { + if (m.remaining() < 3) { // 1: the length of the list + // +2: one version at least + throw new SSLProtocolException( + "Invalid supported_versions extension: insufficient data"); + } + + byte[] vbs = Record.getBytes8(m); // Get the version bytes. + if (m.hasRemaining()) { + throw new SSLProtocolException( + "Invalid supported_versions extension: unknown extra data"); + } + + if (vbs == null || vbs.length == 0 || (vbs.length & 0x01) != 0) { + throw new SSLProtocolException( + "Invalid supported_versions extension: incomplete data"); + } + + int[] protocols = new int[vbs.length >> 1]; + for (int i = 0, j = 0; i < vbs.length;) { + byte major = vbs[i++]; + byte minor = vbs[i++]; + protocols[j++] = ((major & 0xFF) << 8) | (minor & 0xFF); + } + + this.requestedProtocols = protocols; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"versions\": '['{0}']'", Locale.ENGLISH); + + if (requestedProtocols == null || requestedProtocols.length == 0) { + Object[] messageFields = { + "<no supported version specified>" + }; + return messageFormat.format(messageFields); + } else { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (int pv : requestedProtocols) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } + + builder.append(ProtocolVersion.nameOf(pv)); + } + + Object[] messageFields = { + builder.toString() + }; + + return messageFormat.format(messageFields); + } + } + } + + private static final + class CHSupportedVersionsStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new CHSupportedVersionsSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "supported_versions" extension in ClientHello. + */ + private static final + class CHSupportedVersionsProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHSupportedVersionsProducer() { + // 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_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + CH_SUPPORTED_VERSIONS.name); + } + return null; + } + + // Produce the extension. + // + // The activated protocols are used as the supported versions. + int[] protocols = new int[chc.activeProtocols.size()]; + int verLen = protocols.length * 2; + byte[] extData = new byte[verLen + 1]; // 1: versions length + extData[0] = (byte)(verLen & 0xFF); + int i = 0, j = 1; + for (ProtocolVersion pv : chc.activeProtocols) { + protocols[i++] = pv.id; + extData[j++] = pv.major; + extData[j++] = pv.minor; + } + + // Update the context. + chc.handshakeExtensions.put(CH_SUPPORTED_VERSIONS, + new CHSupportedVersionsSpec(protocols)); + + return extData; + } + } + + /** + * Network data consumer of a "supported_versions" extension in ClientHello. + */ + private static final + class CHSupportedVersionsConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHSupportedVersionsConsumer() { + // 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_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + CH_SUPPORTED_VERSIONS.name); + } + return; // ignore the extension + } + + // Parse the extension. + CHSupportedVersionsSpec spec; + try { + spec = new CHSupportedVersionsSpec(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(CH_SUPPORTED_VERSIONS, 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. + } + } + + /** + * The "supported_versions" extension in ServerHello and HelloRetryRequest. + */ + static final class SHSupportedVersionsSpec implements SSLExtensionSpec { + final int selectedVersion; + + private SHSupportedVersionsSpec(ProtocolVersion selectedVersion) { + this.selectedVersion = selectedVersion.id; + } + + private SHSupportedVersionsSpec(ByteBuffer m) throws IOException { + if (m.remaining() != 2) { // 2: the selected version + throw new SSLProtocolException( + "Invalid supported_versions: insufficient data"); + } + + byte major = m.get(); + byte minor = m.get(); + this.selectedVersion = ((major & 0xFF) << 8) | (minor & 0xFF); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"selected version\": '['{0}']'", Locale.ENGLISH); + + Object[] messageFields = { + ProtocolVersion.nameOf(selectedVersion) + }; + return messageFormat.format(messageFields); + } + } + + private static final + class SHSupportedVersionsStringizer implements SSLStringizer { + @Override + public String toString(ByteBuffer buffer) { + try { + return (new SHSupportedVersionsSpec(buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "supported_versions" extension in ServerHello. + */ + private static final + class SHSupportedVersionsProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private SHSupportedVersionsProducer() { + // 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 supported_versions request only + CHSupportedVersionsSpec svs = (CHSupportedVersionsSpec) + shc.handshakeExtensions.get(CH_SUPPORTED_VERSIONS); + if (svs == null) { + // Unlikely, no key_share extension requested. + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Ignore unavailable supported_versions extension"); + } + return null; + } + + // Is it a supported and enabled extension? + if (!shc.sslConfig.isAvailable(SH_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + SH_SUPPORTED_VERSIONS.name); + } + return null; + } + + // Produce the extension. + byte[] extData = new byte[2]; + extData[0] = shc.negotiatedProtocol.major; + extData[1] = shc.negotiatedProtocol.minor; + + // Update the context. + shc.handshakeExtensions.put(SH_SUPPORTED_VERSIONS, + new SHSupportedVersionsSpec(shc.negotiatedProtocol)); + + return extData; + } + } + + /** + * Network data consumer of a "supported_versions" extension in ServerHello. + */ + private static final + class SHSupportedVersionsConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private SHSupportedVersionsConsumer() { + // 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(SH_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + SH_SUPPORTED_VERSIONS.name); + } + return; // ignore the extension + } + + // Parse the extension. + SHSupportedVersionsSpec spec; + try { + spec = new SHSupportedVersionsSpec(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(SH_SUPPORTED_VERSIONS, 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. + } + } + + /** + * Network data producer of a "supported_versions" extension in + * HelloRetryRequest. + */ + private static final + class HRRSupportedVersionsProducer implements HandshakeProducer { + + // Prevent instantiation of this class. + private HRRSupportedVersionsProducer() { + // 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(HRR_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + HRR_SUPPORTED_VERSIONS.name); + } + return null; + } + + // Produce the extension. + byte[] extData = new byte[2]; + extData[0] = shc.negotiatedProtocol.major; + extData[1] = shc.negotiatedProtocol.minor; + + // Update the context. + shc.handshakeExtensions.put(HRR_SUPPORTED_VERSIONS, + new SHSupportedVersionsSpec(shc.negotiatedProtocol)); + + return extData; + } + } + + /** + * Network data consumer of a "supported_versions" extension in + * HelloRetryRequest. + */ + private static final + class HRRSupportedVersionsConsumer implements ExtensionConsumer { + + // Prevent instantiation of this class. + private HRRSupportedVersionsConsumer() { + // 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(HRR_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable extension: " + + HRR_SUPPORTED_VERSIONS.name); + } + return; // ignore the extension + } + + // Parse the extension. + SHSupportedVersionsSpec spec; + try { + spec = new SHSupportedVersionsSpec(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(HRR_SUPPORTED_VERSIONS, 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. + } + } + + /** + * Network data producer of a "supported_versions" extension for stateless + * HelloRetryRequest reconstruction. + */ + private static final + class HRRSupportedVersionsReproducer implements HandshakeProducer { + // Prevent instantiation of this class. + private HRRSupportedVersionsReproducer() { + // 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(HRR_SUPPORTED_VERSIONS)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "[Reproduce] Ignore unavailable extension: " + + HRR_SUPPORTED_VERSIONS.name); + } + return null; + } + + // Produce the extension. + byte[] extData = new byte[2]; + extData[0] = shc.negotiatedProtocol.major; + extData[1] = shc.negotiatedProtocol.minor; + + return extData; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/TransportContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,678 @@ +/* + * 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.Closeable; +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.net.ssl.HandshakeCompletedEvent; +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSocket; +import sun.security.ssl.SupportedGroupsExtension.NamedGroup; + +/** + * SSL/(D)TLS transportation context. + */ +class TransportContext implements ConnectionContext, Closeable { + final SSLTransport transport; + + // registered plaintext consumers + final Map<Byte, SSLConsumer> consumers; + final AccessControlContext acc; + + final SSLContextImpl sslContext; + final SSLConfiguration sslConfig; + final InputRecord inputRecord; + final OutputRecord outputRecord; + + // connection status + boolean isUnsureMode; + boolean isNegotiated = false; + boolean isBroken = false; + boolean isInputCloseNotified = false; + boolean isOutputCloseNotified = false; + Exception closeReason = null; + + // negotiated security parameters + SSLSessionImpl conSession; + ProtocolVersion protocolVersion; + String applicationProtocol= null; + + // handshake context + HandshakeContext handshakeContext = null; + + // connection reserved status for handshake. + boolean secureRenegotiation = false; + byte[] clientVerifyData; + byte[] serverVerifyData; + + // connection sensitive configuration + List<NamedGroup> serverRequestedNamedGroups; + + CipherSuite cipherSuite; + private static final byte[] emptyByteArray = new byte[0]; + + // Please never use the transport parameter other than storing a + // reference to this object. + // + // Called by SSLEngineImpl + TransportContext(SSLContextImpl sslContext, SSLTransport transport, + InputRecord inputRecord, OutputRecord outputRecord) { + this(sslContext, transport, new SSLConfiguration(sslContext, true), + inputRecord, outputRecord, true); + } + + // Please never use the transport parameter other than storing a + // reference to this object. + // + // Called by SSLSocketImpl + TransportContext(SSLContextImpl sslContext, SSLTransport transport, + InputRecord inputRecord, OutputRecord outputRecord, + boolean isClientMode) { + this(sslContext, transport, + new SSLConfiguration(sslContext, isClientMode), + inputRecord, outputRecord, false); + } + + // Please never use the transport parameter other than storing a + // reference to this object. + // + // Called by SSLSocketImpl with an existing SSLConfig + TransportContext(SSLContextImpl sslContext, SSLTransport transport, + SSLConfiguration sslConfig, + InputRecord inputRecord, OutputRecord outputRecord) { + this(sslContext, transport, (SSLConfiguration)sslConfig.clone(), + inputRecord, outputRecord, false); + } + + private TransportContext(SSLContextImpl sslContext, SSLTransport transport, + SSLConfiguration sslConfig, InputRecord inputRecord, + OutputRecord outputRecord, boolean isUnsureMode) { + this.transport = transport; + this.sslContext = sslContext; + this.inputRecord = inputRecord; + this.outputRecord = outputRecord; + this.sslConfig = sslConfig; + if (this.sslConfig.maximumPacketSize == 0) { + this.sslConfig.maximumPacketSize = outputRecord.getMaxPacketSize(); + } + this.isUnsureMode = isUnsureMode; + + // initial security parameters + this.conSession = SSLSessionImpl.nullSession; + this.protocolVersion = this.sslConfig.maximumProtocolVersion; + this.clientVerifyData = emptyByteArray; + this.serverVerifyData = emptyByteArray; + + this.acc = AccessController.getContext(); + this.consumers = new HashMap<>(); + } + + // Dispatch plaintext to a specific consumer. + void dispatch(Plaintext plaintext) throws IOException { + if (plaintext == null) { + return; + } + + ContentType ct = ContentType.valueOf(plaintext.contentType); + if (ct == null) { + fatal(Alert.UNEXPECTED_MESSAGE, + "Unknown content type: " + plaintext.contentType); + return; + } + + switch (ct) { + case HANDSHAKE: + byte type = HandshakeContext.getHandshakeType(this, + plaintext); + if (handshakeContext == null) { + if (type == SSLHandshake.KEY_UPDATE.id || + type == SSLHandshake.NEW_SESSION_TICKET.id) { + if (isNegotiated && + protocolVersion.useTLS13PlusSpec()) { + handshakeContext = new PostHandshakeContext(this); + } else { + fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected post-handshake message: " + + SSLHandshake.nameOf(type)); + } + } else { + handshakeContext = sslConfig.isClientMode ? + new ClientHandshakeContext(sslContext, this) : + new ServerHandshakeContext(sslContext, this); + outputRecord.initHandshaker(); + } + } + handshakeContext.dispatch(type, plaintext); + break; + case ALERT: + Alert.alertConsumer.consume(this, plaintext.fragment); + break; + default: + SSLConsumer consumer = consumers.get(plaintext.contentType); + if (consumer != null) { + consumer.consume(this, plaintext.fragment); + } else { + fatal(Alert.UNEXPECTED_MESSAGE, + "Unexpected content: " + plaintext.contentType); + } + } + } + + void kickstart() throws IOException { + if (isUnsureMode) { + throw new IllegalStateException("Client/Server mode not yet set."); + } + + if (outputRecord.isClosed() || inputRecord.isClosed() || isBroken) { + if (closeReason != null) { + throw new SSLException( + "Cannot kickstart, the connection is broken or closed", + closeReason); + } else { + throw new SSLException( + "Cannot kickstart, the connection is broken or closed"); + } + } + + // initialize the handshaker if necessary + if (handshakeContext == null) { + // TLS1.3 post-handshake + if (isNegotiated && protocolVersion.useTLS13PlusSpec()) { + handshakeContext = new PostHandshakeContext(this); + } else { + handshakeContext = sslConfig.isClientMode ? + new ClientHandshakeContext(sslContext, this) : + new ServerHandshakeContext(sslContext, this); + outputRecord.initHandshaker(); + } + } + + // kickstart the handshake if needed + // + // Need no kickstart message on server side unless the connection + // has been established. + if(isNegotiated || sslConfig.isClientMode) { + handshakeContext.kickstart(); + } + } + + void keyUpdate() throws IOException { + kickstart(); + } + + boolean isPostHandshakeContext() { + return handshakeContext != null && + (handshakeContext instanceof PostHandshakeContext); + } + + // Note: close_notify is delivered as a warning alert. + void warning(Alert alert) { + // For initial handshaking, don't send a warning alert message to peer + // if handshaker has not started. + if (isNegotiated || handshakeContext != null) { + try { + outputRecord.encodeAlert(Alert.Level.WARNING.level, alert.id); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Warning: failed to send warning alert " + alert, ioe); + } + } + } + } + + void fatal(Alert alert, + String diagnostic) throws SSLException { + fatal(alert, diagnostic, null); + } + + void fatal(Alert alert, Throwable cause) throws SSLException { + fatal(alert, null, cause); + } + + void fatal(Alert alert, + String diagnostic, Throwable cause) throws SSLException { + fatal(alert, diagnostic, false, cause); + } + + // Note: close_notify is not delivered via fatal() methods. + void fatal(Alert alert, String diagnostic, + boolean recvFatalAlert, Throwable cause) throws SSLException { + // If we've already shutdown because of an error, there is nothing we + // can do except rethrow the exception. + // + // Most exceptions seen here will be SSLExceptions. We may find the + // occasional Exception which hasn't been converted to a SSLException, + // so we'll do it here. + if (closeReason != null) { + if (cause == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Closed transport, general or untracked problem"); + } + throw alert.createSSLException( + "Closed transport, general or untracked problem"); + } + + if (cause instanceof SSLException) { + throw (SSLException)cause; + } else { // unlikely, but just in case. + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Closed transport, unexpected rethrowing", cause); + } + throw alert.createSSLException("Unexpected rethrowing", cause); + } + } + + // If we have no further information, make a general-purpose + // message for folks to see. We generally have one or the other. + if (diagnostic == null) { + if (cause == null) { + diagnostic = "General/Untracked problem"; + } else { + diagnostic = cause.getMessage(); + } + } + + if (cause == null) { + cause = alert.createSSLException(diagnostic); + } + + // shutdown the transport + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.severe("Fatal (" + alert + "): " + diagnostic, cause); + } + + // remember the close reason + if (cause instanceof SSLException) { + closeReason = (SSLException)cause; + } else { + // Including RuntimeException, but we'll throw those down below. + closeReason = alert.createSSLException(diagnostic, cause); + } + + // close inbound + try { + inputRecord.close(); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("Fatal: input record closure failed", ioe); + } + } + + // invalidate the session + if (conSession != null) { + conSession.invalidate(); + } + + if (handshakeContext != null && + handshakeContext.handshakeSession != null) { + handshakeContext.handshakeSession.invalidate(); + } + + // send fatal alert + // + // If we haven't even started handshaking yet, or we are the recipient + // of a fatal alert, no need to generate a fatal close alert. + if (!recvFatalAlert && !isOutboundDone() && !isBroken && + (isNegotiated || handshakeContext != null)) { + try { + outputRecord.encodeAlert(Alert.Level.FATAL.level, alert.id); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Fatal: failed to send fatal alert " + alert, ioe); + } + } + } + + // close outbound + try { + outputRecord.close(); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("Fatal: output record closure failed", ioe); + } + } + + // terminal handshake context + if (handshakeContext != null) { + handshakeContext = null; + } + + // terminal the transport + try { + transport.shutdown(); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("Fatal: transport closure failed", ioe); + } + } finally { + isBroken = true; + } + + if (closeReason instanceof SSLException) { + throw (SSLException)closeReason; + } else { + throw (RuntimeException)closeReason; + } + } + + void setUseClientMode(boolean useClientMode) { + /* + * If we need to change the client mode and the enabled + * protocols and cipher suites haven't specifically been + * set by the user, change them to the corresponding + * default ones. + */ + if (sslConfig.isClientMode != useClientMode) { + // Once handshaking has begun, the mode can not be reset for the + // life of this engine. + if (handshakeContext != null || isNegotiated) { + throw new IllegalArgumentException( + "Cannot change mode after SSL traffic has started"); + } + + if (sslContext.isDefaultProtocolVesions( + sslConfig.enabledProtocols)) { + sslConfig.enabledProtocols = + sslContext.getDefaultProtocolVersions(!useClientMode); + } + + if (sslContext.isDefaultCipherSuiteList( + sslConfig.enabledCipherSuites)) { + sslConfig.enabledCipherSuites = + sslContext.getDefaultCipherSuites(!useClientMode); + } + + sslConfig.isClientMode = useClientMode; + } + + isUnsureMode = false; + } + + // The OutputRecord is closed and not buffered output record. + boolean isOutboundDone() { + return outputRecord.isClosed() && outputRecord.isEmpty(); + } + + // The OutputRecord is closed, but buffered output record may be still + // waiting for delivery to the underlying connection. + boolean isOutboundClosed() { + return outputRecord.isClosed(); + } + + boolean isInboundDone() { + return inputRecord.isClosed(); + } + + boolean isClosed() { + return isOutboundClosed() && isInboundDone(); + } + + @Override + public void close() throws IOException { + if (!isOutboundDone()) { + closeOutbound(); + } + + if (!isInboundDone()) { + closeInbound(); + } + } + + void closeInbound() { + if (isInboundDone()) { + return; + } + + try { + if (isInputCloseNotified) { // passive close + passiveInboundClose(); + } else { // initiative close + initiateInboundClose(); + } + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("inbound closure failed", ioe); + } + } + } + + void closeOutbound() { + if (isOutboundDone()) { + return; + } + + try { + initiateOutboundClose(); + } catch (IOException ioe) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning("outbound closure failed", ioe); + } + } + } + + // Close the connection passively. The closure could be kickoff by + // receiving a close_notify alert or reaching end_of_file of the socket. + private void passiveInboundClose() throws IOException { + if (!isInboundDone()) { + inputRecord.close(); + } + + // For TLS 1.2 and prior version, it is required to respond with + // a close_notify alert of its own and close down the connection + // immediately, discarding any pending writes. + if (!isOutboundDone() && !isOutputCloseNotified) { + try { + // send a close_notify alert + warning(Alert.CLOSE_NOTIFY); + } finally { + // any data received after a closure alert is ignored. + isOutputCloseNotified = true; + outputRecord.close(); + } + } + + transport.shutdown(); + } + + // Initiate a close by sending a close_notify alert. + private void initiateInboundClose() throws IOException { + // TLS 1.3 does not define how to initiate and close a TLS connection + // gracefully. We will always send a close_notify alert, and close + // the underlying transportation layer if needed. + if (!isInboundDone() && !isInputCloseNotified) { + try { + // send a close_notify alert + warning(Alert.CLOSE_NOTIFY); + } finally { + // any data received after a closure alert is ignored. + isInputCloseNotified = true; + inputRecord.close(); + } + } + + // For TLS 1.3, input closure is independent from output closure. Both + // parties need not wait to receive a "close_notify" alert before + // closing their read side of the connection. + // + // For TLS 1.2 and prior version, it is not required for the initiator + // of the close to wait for the responding close_notify alert before + // closing the read side of the connection. + try { + transport.shutdown(); + } finally { + if (!isOutboundDone()) { + outputRecord.close(); + } + } + } + + // Initiate a close by sending a close_notify alert. + private void initiateOutboundClose() throws IOException { + if (!isOutboundDone() && !isOutputCloseNotified) { + try { // close outputRecord + // send a close_notify alert + warning(Alert.CLOSE_NOTIFY); + } finally { + // any data received after a closure alert is ignored. + isOutputCloseNotified = true; + outputRecord.close(); + } + } + + // It is not required for the initiator of the close to wait for the + // responding close_notify alert before closing the read side of the + // connection. However, if the application protocol using TLS + // provides that any data may be carried over the underlying transport + // after the TLS connection is closed, the TLS implementation MUST + // receive a "close_notify" alert before indicating end-of-data to the + // application-layer. + try { + transport.shutdown(); + } finally { + if (!isInboundDone()) { + inputRecord.close(); + } + } + } + + // Note; HandshakeStatus.FINISHED status is retrieved in other places. + HandshakeStatus getHandshakeStatus() { + if (!outputRecord.isEmpty()) { + // If no handshaking, special case to wrap alters or + // post-handshake messages. + return HandshakeStatus.NEED_WRAP; + } else if (handshakeContext != null) { + if (!handshakeContext.delegatedActions.isEmpty()) { + return HandshakeStatus.NEED_TASK; + } else if (sslContext.isDTLS() && + !inputRecord.isEmpty()) { + return HandshakeStatus.NEED_UNWRAP_AGAIN; + } else { + return HandshakeStatus.NEED_UNWRAP; + } + } else if (isOutboundDone() && !isInboundDone()) { + /* + * Special case where we're closing, but + * still need the close_notify before we + * can officially be closed. + * + * Note isOutboundDone is taken care of by + * hasOutboundData() above. + */ + return HandshakeStatus.NEED_UNWRAP; + } + + return HandshakeStatus.NOT_HANDSHAKING; + } + + HandshakeStatus finishHandshake() { + if (protocolVersion.useTLS13PlusSpec()) { + outputRecord.tc = this; + inputRecord.tc = this; + cipherSuite = handshakeContext.negotiatedCipherSuite; + inputRecord.readCipher.baseSecret = handshakeContext.baseReadSecret; + outputRecord.writeCipher.baseSecret = handshakeContext.baseWriteSecret; + } + + handshakeContext = null; + outputRecord.handshakeHash.finish(); + inputRecord.finishHandshake(); + outputRecord.finishHandshake(); + isNegotiated = true; + + // Tell folk about handshake completion, but do it in a separate thread. + if (transport instanceof SSLSocket && + sslConfig.handshakeListeners != null && + !sslConfig.handshakeListeners.isEmpty()) { + HandshakeCompletedEvent hce = + new HandshakeCompletedEvent((SSLSocket)transport, conSession); + Thread thread = new Thread( + null, + new NotifyHandshake(sslConfig.handshakeListeners, hce), + "HandshakeCompletedNotify-Thread", + 0, + false); + thread.start(); + } + + return HandshakeStatus.FINISHED; + } + + HandshakeStatus finishPostHandshake() { + handshakeContext = null; + + // Note: May need trigger handshake completion even for post-handshake + // authentication in the future. + + return HandshakeStatus.FINISHED; + } + + // A separate thread is allocated to deliver handshake completion + // events. + private static class NotifyHandshake implements Runnable { + private final Set<Map.Entry<HandshakeCompletedListener, + AccessControlContext>> targets; // who gets notified + private final HandshakeCompletedEvent event; // the notification + + NotifyHandshake( + Map<HandshakeCompletedListener,AccessControlContext> listeners, + HandshakeCompletedEvent event) { + this.targets = new HashSet<>(listeners.entrySet()); // clone + this.event = event; + } + + @Override + public void run() { + // Don't need to synchronize, as it only runs in one thread. + for (Map.Entry<HandshakeCompletedListener, + AccessControlContext> entry : targets) { + final HandshakeCompletedListener listener = entry.getKey(); + AccessControlContext acc = entry.getValue(); + AccessController.doPrivileged(new PrivilegedAction<Void>() { + @Override + public Void run() { + listener.handshakeCompleted(event); + return null; + } + }, acc); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java --- a/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -25,18 +25,16 @@ package sun.security.ssl; -import java.util.*; import java.io.*; import java.security.*; import java.security.cert.*; +import java.util.*; import javax.net.ssl.*; - +import sun.security.validator.TrustStoreUtil; import sun.security.validator.Validator; -import sun.security.validator.TrustStoreUtil; abstract class TrustManagerFactoryImpl extends TrustManagerFactorySpi { - private static final Debug debug = Debug.getInstance("ssl"); private X509TrustManager trustManager = null; private boolean isInitialized = false; @@ -51,26 +49,26 @@ trustManager = getInstance(TrustStoreManager.getTrustedCerts()); } catch (SecurityException se) { // eat security exceptions but report other throwables - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( - "SunX509: skip default keystore: " + se); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( + "SunX509: skip default keystore", se); } } catch (Error err) { - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( - "SunX509: skip default keystore: " + err); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( + "SunX509: skip default keystore", err); } throw err; } catch (RuntimeException re) { - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( - "SunX509: skip default keystore: " + re); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( + "SunX509: skip default keystor", re); } throw re; } catch (Exception e) { - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( - "SunX509: skip default keystore: " + e); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( + "SunX509: skip default keystore", e); } throw new KeyStoreException( "problem accessing trust store", e); diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java --- a/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/TrustStoreManager.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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,14 +25,11 @@ package sun.security.ssl; +import java.io.*; import java.lang.ref.WeakReference; -import java.io.*; -import java.util.*; - import java.security.*; import java.security.cert.*; -import java.security.cert.Certificate; - +import java.util.*; import sun.security.action.*; import sun.security.validator.TrustStoreUtil; @@ -41,7 +38,6 @@ * effectively. */ final class TrustStoreManager { - private static final Debug debug = Debug.getInstance("ssl"); // A singleton service to manage the default trusted KeyStores effectively. private static final TrustAnchorManager tam = new TrustAnchorManager(); @@ -112,8 +108,8 @@ this.storeFile = storeFile; this.lastModified = lastModified; - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( "trustStore is: " + storeName + "\n" + "trustStore type is: " + storeType + "\n" + "trustStore provider is: " + storeProvider + "\n" + @@ -125,8 +121,10 @@ * Create an instance of TrustStoreDescriptor for the default * trusted KeyStore. */ + @SuppressWarnings("Convert2Lambda") static TrustStoreDescriptor createInstance() { - return AccessController.doPrivileged(new PrivilegedAction<>() { + return AccessController.doPrivileged( + new PrivilegedAction<TrustStoreDescriptor>() { @Override public TrustStoreDescriptor run() { @@ -158,11 +156,11 @@ } // Not break, the file is inaccessible. - if (debug != null && - Debug.isOn("trustmanager")) { - System.out.println( - "Inaccessible trust store: " + - storePropName); + if (SSLLogger.isOn && + SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( + "Inaccessible trust store: " + + storePropName); } } } else { @@ -267,8 +265,8 @@ } // Reload a new key store. - if ((debug != null) && Debug.isOn("trustmanager")) { - System.out.println("Reload the trust store"); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine("Reload the trust store"); } ks = loadKeyStore(descriptor); @@ -309,20 +307,20 @@ // Reload the trust store if needed. if (ks == null) { - if ((debug != null) && Debug.isOn("trustmanager")) { - System.out.println("Reload the trust store"); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine("Reload the trust store"); } ks = loadKeyStore(descriptor); } // Reload trust certs from the key store. - if ((debug != null) && Debug.isOn("trustmanager")) { - System.out.println("Reload trust certs"); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine("Reload trust certs"); } certs = loadTrustedCerts(ks); - if ((debug != null) && Debug.isOn("trustmanager")) { - System.out.println("Reloaded " + certs.size() + " trust certs"); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine("Reloaded " + certs.size() + " trust certs"); } // Note that as ks is a local variable, it is not @@ -333,7 +331,7 @@ } /** - * Load the KeyStore as described in the specified descriptor. + * Load the the KeyStore as described in the specified descriptor. */ private static KeyStore loadKeyStore( TrustStoreDescriptor descriptor) throws Exception { @@ -341,8 +339,8 @@ descriptor.storeFile == null) { // No file available, no KeyStore available. - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println("No available key store"); + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine("No available key store"); } return null; @@ -367,8 +365,8 @@ ks.load(fis, password); } catch (FileNotFoundException fnfe) { // No file available, no KeyStore available. - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println( + if (SSLLogger.isOn && SSLLogger.isOn("trustmanager")) { + SSLLogger.fine( "Not available key store: " + descriptor.storeName); } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/UnknownExtension.java --- a/src/java.base/share/classes/sun/security/ssl/UnknownExtension.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +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; - -final class UnknownExtension extends HelloExtension { - - private final byte[] data; - - UnknownExtension(HandshakeInStream s, int len, ExtensionType type) - throws IOException { - super(type); - data = new byte[len]; - // s.read() does not handle 0-length arrays. - if (len != 0) { - s.read(data); - } - } - - @Override - int length() { - return 4 + data.length; - } - - @Override - void send(HandshakeOutStream s) throws IOException { - s.putInt16(type.id); - s.putBytes16(data); - } - - @Override - public String toString() { - return "Unsupported extension " + type + ", data: " + - Debug.toString(data); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java --- a/src/java.base/share/classes/sun/security/ssl/UnknownStatusRequest.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +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; - -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); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/Utilities.java --- a/src/java.base/share/classes/sun/security/ssl/Utilities.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, 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,18 +25,21 @@ package sun.security.ssl; -import javax.net.ssl.*; +import java.math.BigInteger; import java.util.*; +import java.util.regex.Pattern; +import javax.net.ssl.*; import sun.net.util.IPAddressUtil; +import sun.security.action.GetPropertyAction; /** * A utility class to share the static methods. */ final class Utilities { - /** - * hex digits - */ static final char[] hexDigits = "0123456789ABCDEF".toCharArray(); + private static final String indent = " "; + private static final Pattern lineBreakPatern = + Pattern.compile("\\r\\n|\\n|\\r"); /** * Puts {@code hostname} into the {@code serverNames} list. @@ -66,9 +69,9 @@ SNIServerName serverName = sniList.get(i); if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { sniList.set(i, sniHostName); - if (Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", the previous server name in SNI (" + serverName + + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + "the previous server name in SNI (" + serverName + ") was replaced with (" + sniHostName + ")"); } reset = true; @@ -107,9 +110,8 @@ sniHostName = new SNIHostName(hostname); } catch (IllegalArgumentException iae) { // don't bother to handle illegal host_name - if (Debug.isOn("ssl")) { - System.out.println(Thread.currentThread().getName() + - ", \"" + hostname + "\" " + + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine(hostname + "\" " + "is not a legal HostName for server name indication"); } } @@ -117,4 +119,117 @@ return sniHostName; } + + /** + * 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 indent(String source) { + return Utilities.indent(source, indent); + } + + static String indent(String source, String prefix) { + StringBuilder builder = new StringBuilder(); + if (source == null) { + builder.append("\n" + prefix + "<blank message>"); + } else { + String[] lines = lineBreakPatern.split(source); + boolean isFirst = true; + for (String line : lines) { + if (isFirst) { + isFirst = false; + } else { + builder.append("\n"); + } + builder.append(prefix).append(line); + } + } + + return builder.toString(); + } + + static String toHexString(byte b) { + return String.valueOf(hexDigits[(b >> 4) & 0x0F]) + + String.valueOf(hexDigits[b & 0x0F]); + } + + static String byte16HexString(int id) { + return "0x" + + hexDigits[(id >> 12) & 0x0F] + hexDigits[(id >> 8) & 0x0F] + + hexDigits[(id >> 4) & 0x0F] + hexDigits[id & 0x0F]; + } + + static String toHexString(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return ""; + } + + StringBuilder builder = new StringBuilder(bytes.length * 3); + boolean isFirst = true; + for (byte b : bytes) { + if (isFirst) { + isFirst = false; + } else { + builder.append(' '); + } + + builder.append(hexDigits[(b >> 4) & 0x0F]); + builder.append(hexDigits[b & 0x0F]); + } + return builder.toString(); + } + + static String toHexString(long lv) { + StringBuilder builder = new StringBuilder(128); + + boolean isFirst = true; + do { + if (isFirst) { + isFirst = false; + } else { + builder.append(' '); + } + + builder.append(hexDigits[(int)(lv & 0x0F)]); + lv >>>= 4; + builder.append(hexDigits[(int)(lv & 0x0F)]); + lv >>>= 4; + } while (lv != 0); + builder.reverse(); + + return builder.toString(); + } + + /** + * Utility method to convert a BigInteger to a byte array in unsigned + * format as needed in the handshake messages. BigInteger uses + * 2's complement format, i.e. it prepends an extra zero if the MSB + * is set. We remove that. + */ + static byte[] toByteArray(BigInteger bi) { + byte[] b = bi.toByteArray(); + if ((b.length > 1) && (b[0] == 0)) { + int n = b.length - 1; + byte[] newarray = new byte[n]; + System.arraycopy(b, 1, newarray, 0, n); + b = newarray; + } + return b; + } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/X509Authentication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,311 @@ +/* + * 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.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Map; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedKeyManager; +import sun.security.ssl.SupportedGroupsExtension.NamedGroup; +import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; + +enum X509Authentication implements SSLAuthentication { + // Require rsaEncryption public key + RSA ("RSA", new X509PossessionGenerator( + new String[]{"RSA"})), + + // Require RSASSA-PSS public key + RSASSA_PSS ("RSASSA-PSS", new X509PossessionGenerator( + new String[] {"RSASSA-PSS"})), + + // Require rsaEncryption or RSASSA-PSS public key + // + // Note that this is a specifical scheme for TLS 1.2. (EC)DHE_RSA cipher + // suites of TLS 1.2 can use either rsaEncryption or RSASSA-PSS public + // key for authentication and handshake. + RSA_OR_PSS ("RSA_OR_PSS", new X509PossessionGenerator( + new String[] {"RSA", "RSASSA-PSS"})), + + // Require DSA public key + DSA ("DSA", new X509PossessionGenerator( + new String[] {"DSA"})), + + // Require EC public key + EC ("EC", new X509PossessionGenerator( + new String[] {"EC"})); + + final String keyType; + final SSLPossessionGenerator possessionGenerator; + + X509Authentication(String keyType, + SSLPossessionGenerator possessionGenerator) { + this.keyType = keyType; + this.possessionGenerator = possessionGenerator; + } + + static X509Authentication valueOf(SignatureScheme signatureScheme) { + for (X509Authentication au: X509Authentication.values()) { + if (au.keyType.equals(signatureScheme.keyAlgorithm)) { + return au; + } + } + + return null; + } + + @Override + public SSLPossession createPossession(HandshakeContext handshakeContext) { + return possessionGenerator.createPossession(handshakeContext); + } + + @Override + public SSLHandshake[] getRelatedHandshakers( + HandshakeContext handshakeContext) { + if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return new SSLHandshake[] { + SSLHandshake.CERTIFICATE, + SSLHandshake.CERTIFICATE_REQUEST + }; + } // Otherwise, TLS 1.3 does not use this method. + + return new SSLHandshake[0]; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers( + HandshakeContext handshakeContext) { + if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) { + return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[] { + new SimpleImmutableEntry<Byte, HandshakeProducer>( + SSLHandshake.CERTIFICATE.id, + SSLHandshake.CERTIFICATE + ) + }); + } // Otherwise, TLS 1.3 does not use this method. + + return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]); + } + + static final class X509Possession implements SSLPossession { + // Proof of possession of the private key corresponding to the public + // key for which a certificate is being provided for authentication. + final X509Certificate[] popCerts; + final PrivateKey popPrivateKey; + + X509Possession(PrivateKey popPrivateKey, + X509Certificate[] popCerts) { + this.popCerts = popCerts; + this.popPrivateKey = popPrivateKey; + } + } + + static final class X509Credentials implements SSLCredentials { + final X509Certificate[] popCerts; + final PublicKey popPublicKey; + + X509Credentials(PublicKey popPublicKey, X509Certificate[] popCerts) { + this.popCerts = popCerts; + this.popPublicKey = popPublicKey; + } + } + + private static final + class X509PossessionGenerator implements SSLPossessionGenerator { + private final String[] keyTypes; + + private X509PossessionGenerator(String[] keyTypes) { + this.keyTypes = keyTypes; + } + + @Override + public SSLPossession createPossession(HandshakeContext context) { + if (context.sslConfig.isClientMode) { + for (String keyType : keyTypes) { + SSLPossession poss = createClientPossession( + (ClientHandshakeContext)context, keyType); + if (poss != null) { + return poss; + } + } + } else { + for (String keyType : keyTypes) { + SSLPossession poss = createServerPossession( + (ServerHandshakeContext)context, keyType); + if (poss != null) { + return poss; + } + } + } + + return null; + } + + // Used by TLS 1.3 only. + private SSLPossession createClientPossession( + ClientHandshakeContext chc, String keyType) { + X509ExtendedKeyManager km = chc.sslContext.getX509KeyManager(); + String clientAlias = null; + if (chc.conContext.transport instanceof SSLSocketImpl) { + clientAlias = km.chooseClientAlias( + new String[] { keyType }, + null, (SSLSocket)chc.conContext.transport); + } else if (chc.conContext.transport instanceof SSLEngineImpl) { + clientAlias = km.chooseEngineClientAlias( + new String[] { keyType }, + null, (SSLEngine)chc.conContext.transport); + } + + if (clientAlias == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("No X.509 cert selected for " + keyType); + } + return null; + } + + PrivateKey clientPrivateKey = km.getPrivateKey(clientAlias); + if (clientPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + clientAlias + " is not a private key entry"); + } + return null; + } + + X509Certificate[] clientCerts = km.getCertificateChain(clientAlias); + if ((clientCerts == null) || (clientCerts.length == 0)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest(clientAlias + + " is a private key entry with no cert chain stored"); + } + return null; + } + + PublicKey clientPublicKey = clientCerts[0].getPublicKey(); + if ((!clientPrivateKey.getAlgorithm().equals(keyType)) + || (!clientPublicKey.getAlgorithm().equals(keyType))) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + clientAlias + " private or public key is not of " + + keyType + " algorithm"); + } + return null; + } + + return new X509Possession(clientPrivateKey, clientCerts); + } + + private SSLPossession createServerPossession( + ServerHandshakeContext shc, String keyType) { + X509ExtendedKeyManager km = shc.sslContext.getX509KeyManager(); + String serverAlias = null; + if (shc.conContext.transport instanceof SSLSocketImpl) { + serverAlias = km.chooseServerAlias(keyType, + null, (SSLSocket)shc.conContext.transport); + } else if (shc.conContext.transport instanceof SSLEngineImpl) { + serverAlias = km.chooseEngineServerAlias(keyType, + null, (SSLEngine)shc.conContext.transport); + } + + if (serverAlias == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest("No X.509 cert selected for " + keyType); + } + return null; + } + + PrivateKey serverPrivateKey = km.getPrivateKey(serverAlias); + if (serverPrivateKey == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + serverAlias + " is not a private key entry"); + } + return null; + } + + X509Certificate[] serverCerts = km.getCertificateChain(serverAlias); + if ((serverCerts == null) || (serverCerts.length == 0)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.finest( + serverAlias + " is not a certificate entry"); + } + return null; + } + + PublicKey serverPublicKey = serverCerts[0].getPublicKey(); + if ((!serverPrivateKey.getAlgorithm().equals(keyType)) + || (!serverPublicKey.getAlgorithm().equals(keyType))) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.fine( + serverAlias + " private or public key is not of " + + keyType + " algorithm"); + } + return null; + } + + // For ECC certs, check whether we support the EC domain + // parameters. If the client sent a SupportedEllipticCurves + // ClientHello extension, check against that too. + if (keyType.equals("EC")) { + if (!(serverPublicKey instanceof ECPublicKey)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning(serverAlias + + " public key is not an instance of ECPublicKey"); + } + return null; + } + + // For ECC certs, check whether we support the EC domain + // parameters. If the client sent a SupportedEllipticCurves + // ClientHello extension, check against that too. + ECParameterSpec params = + ((ECPublicKey)serverPublicKey).getParams(); + NamedGroup namedGroup = NamedGroup.valueOf(params); + if ((namedGroup == null) || + (!SupportedGroups.isSupported(namedGroup)) || + ((shc.clientRequestedNamedGroups != null) && + !shc.clientRequestedNamedGroups.contains(namedGroup))) { + + if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { + SSLLogger.warning( + "Unsupported named group (" + namedGroup + + ") used in the " + serverAlias + " certificate"); + } + + return null; + } + } + + return new X509Possession(serverPrivateKey, serverCerts); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java --- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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,21 @@ package sun.security.ssl; import java.lang.ref.*; +import java.net.Socket; +import java.security.AlgorithmConstraints; +import java.security.KeyStore; +import java.security.KeyStore.Builder; +import java.security.KeyStore.Entry; +import java.security.KeyStore.PrivateKeyEntry; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.util.*; -import static java.util.Locale.ENGLISH; import java.util.concurrent.atomic.AtomicLong; -import java.net.Socket; - -import java.security.*; -import java.security.KeyStore.*; -import java.security.cert.*; -import java.security.cert.Certificate; - import javax.net.ssl.*; - import sun.security.provider.certpath.AlgorithmChecker; import sun.security.validator.Validator; @@ -61,11 +64,6 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager implements X509KeyManager { - private static final Debug debug = Debug.getInstance("ssl"); - - private static final boolean useDebug = - (debug != null) && Debug.isOn("keymanager"); - // for unit testing only, set via privileged reflection private static Date verificationDate; @@ -189,9 +187,7 @@ SSLSession session = sslSocket.getHandshakeSession(); if (session != null) { - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); - if (protocolVersion.useTLS12PlusSpec()) { + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { String[] peerSupportedSignAlgs = null; if (session instanceof ExtendedSSLSession) { @@ -217,9 +213,7 @@ if (engine != null) { SSLSession session = engine.getHandshakeSession(); if (session != null) { - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); - if (protocolVersion.useTLS12PlusSpec()) { + if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { String[] peerSupportedSignAlgs = null; if (session instanceof ExtendedSSLSession) { @@ -297,6 +291,7 @@ // In TLS 1.2, the signature algorithm has been obsoleted by the // supported_signature_algorithms, and the certificate type no longer // restricts the algorithm used to sign the certificate. + // // However, because we don't support certificate type checking other // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the // protocol version here. @@ -328,8 +323,10 @@ // Check the signature algorithm of the certificate itself. // Look for the "withRSA" in "SHA1withRSA", etc. X509Certificate issuer = (X509Certificate)chain[0]; - String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH); - String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH); + String sigAlgName = + issuer.getSigAlgName().toUpperCase(Locale.ENGLISH); + String pattern = + "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH); return sigAlgName.contains(pattern); } } @@ -388,8 +385,8 @@ // if it's a perfect match, return immediately EntryStatus status = results.get(0); if (status.checkResult == CheckResult.OK) { - if (useDebug) { - debug.println("KeyMgr: choosing key: " + status); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("KeyMgr: choosing key: " + status); } return makeAlias(status); } @@ -403,16 +400,16 @@ } } if (allResults == null) { - if (useDebug) { - debug.println("KeyMgr: no matching key found"); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("KeyMgr: no matching key found"); } return null; } Collections.sort(allResults); - if (useDebug) { - debug.println("KeyMgr: no good matching key found, " - + "returning best match out of:"); - debug.println(allResults.toString()); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "KeyMgr: no good matching key found, " + + "returning best match out of", allResults); } return makeAlias(allResults.get(0)); } @@ -439,7 +436,7 @@ null, null); if (results != null) { if (allResults == null) { - allResults = new ArrayList<EntryStatus>(); + allResults = new ArrayList<>(); } allResults.addAll(results); } @@ -448,14 +445,14 @@ } } if (allResults == null || allResults.isEmpty()) { - if (useDebug) { - debug.println("KeyMgr: no matching alias found"); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("KeyMgr: no matching alias found"); } return null; } Collections.sort(allResults); - if (useDebug) { - debug.println("KeyMgr: getting aliases: " + allResults); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("KeyMgr: getting aliases", allResults); } return toAliases(allResults); } @@ -544,9 +541,10 @@ return (bit < keyUsage.length) && keyUsage[bit]; } - // check if this certificate is appropriate for this type of use - // first check extensions, if they match, check expiration - // note: we may want to move this code into the sun.security.validator + // Check if this certificate is appropriate for this type of use + // first check extensions, if they match, check expiration. + // + // Note: we may want to move this code into the sun.security.validator // package CheckResult check(X509Certificate cert, Date date, List<SNIServerName> serverNames, String idAlgorithm) { @@ -570,20 +568,25 @@ boolean[] ku = cert.getKeyUsage(); if (ku != null) { String algorithm = cert.getPublicKey().getAlgorithm(); - boolean kuSignature = getBit(ku, 0); + boolean supportsDigitalSignature = getBit(ku, 0); switch (algorithm) { case "RSA": // require either signature bit // or if server also allow key encipherment bit - if (kuSignature == false) { - if ((this == CLIENT) || (getBit(ku, 2) == false)) { + if (!supportsDigitalSignature) { + if (this == CLIENT || getBit(ku, 2) == false) { return CheckResult.EXTENSION_MISMATCH; } } break; + case "RSASSA-PSS": + if (!supportsDigitalSignature && (this == SERVER)) { + return CheckResult.EXTENSION_MISMATCH; + } + break; case "DSA": // require signature bit - if (kuSignature == false) { + if (!supportsDigitalSignature) { return CheckResult.EXTENSION_MISMATCH; } break; @@ -595,7 +598,7 @@ break; case "EC": // require signature bit - if (kuSignature == false) { + if (!supportsDigitalSignature) { return CheckResult.EXTENSION_MISMATCH; } // For servers, also require key agreement. @@ -631,8 +634,9 @@ new SNIHostName(serverName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if (useDebug) { - debug.println( + if (SSLLogger.isOn && + SSLLogger.isOn("keymanager")) { + SSLLogger.fine( "Illegal server name: " + serverName); } @@ -646,11 +650,12 @@ X509TrustManagerImpl.checkIdentity(hostname, cert, idAlgorithm); } catch (CertificateException e) { - if (useDebug) { - debug.println( - "Certificate identity does not match " + - "Server Name Inidication (SNI): " + - hostname); + if (SSLLogger.isOn && + SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Certificate identity does not match " + + "Server Name Inidication (SNI): " + + hostname); } return CheckResult.INSENSITIVE; } @@ -724,7 +729,7 @@ for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); // check if it is a key entry (private key or secret key) - if (ks.isKeyEntry(alias) == false) { + if (!ks.isKeyEntry(alias)) { continue; } @@ -757,8 +762,8 @@ j++; } if (keyIndex == -1) { - if (useDebug) { - debug.println("Ignoring alias " + alias + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Ignore alias " + alias + ": key algorithm does not match"); } continue; @@ -774,9 +779,10 @@ } } if (found == false) { - if (useDebug) { - debug.println("Ignoring alias " + alias - + ": issuers do not match"); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Ignore alias " + alias + + ": issuers do not match"); } continue; } @@ -787,8 +793,8 @@ !conformsToAlgorithmConstraints(constraints, chain, checkType.getValidator())) { - if (useDebug) { - debug.println("Ignoring alias " + alias + + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Ignore alias " + alias + ": certificate list does not conform to " + "algorithm constraints"); } @@ -813,7 +819,7 @@ return Collections.singletonList(status); } else { if (results == null) { - results = new ArrayList<EntryStatus>(); + results = new ArrayList<>(); } results.add(status); } @@ -825,14 +831,15 @@ AlgorithmConstraints constraints, Certificate[] chain, String variant) { - AlgorithmChecker checker = new AlgorithmChecker(constraints, null, variant); + AlgorithmChecker checker = + new AlgorithmChecker(constraints, null, variant); try { checker.init(false); } catch (CertPathValidatorException cpve) { // unlikely to happen - if (useDebug) { - debug.println( - "Cannot initialize algorithm constraints checker: " + cpve); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine( + "Cannot initialize algorithm constraints checker", cpve); } return false; @@ -845,9 +852,9 @@ // We don't care about the unresolved critical extensions. checker.check(cert, Collections.<String>emptySet()); } catch (CertPathValidatorException cpve) { - if (useDebug) { - debug.println("Certificate (" + cert + - ") does not conform to algorithm constraints: " + cpve); + if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { + SSLLogger.fine("Certificate does not conform to " + + "algorithm constraints", cert, cpve); } return false; @@ -856,5 +863,4 @@ return true; } - } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java --- a/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/ssl/X509TrustManagerImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,20 +23,16 @@ * questions. */ - package sun.security.ssl; import java.net.Socket; -import javax.net.ssl.SSLSession; - -import java.util.*; import java.security.*; import java.security.cert.*; +import java.util.*; import javax.net.ssl.*; - -import sun.security.validator.*; import sun.security.util.AnchorCertificates; import sun.security.util.HostnameChecker; +import sun.security.validator.*; /** * This class implements the SunJSSE X.509 trust manager using the internal @@ -67,8 +63,6 @@ // the different extension checks. They are initialized lazily on demand. private volatile Validator clientValidator, serverValidator; - private static final Debug debug = Debug.getInstance("ssl"); - X509TrustManagerImpl(String validatorType, Collection<X509Certificate> trustedCerts) { @@ -81,8 +75,9 @@ this.trustedCerts = trustedCerts; - if (debug != null && Debug.isOn("trustmanager")) { - showTrustedCerts(); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + SSLLogger.fine("adding as trusted certificates", + (Object[])trustedCerts.toArray(new X509Certificate[0])); } } @@ -97,8 +92,9 @@ trustedCerts = v.getTrustedCertificates(); serverValidator = v; - if (debug != null && Debug.isOn("trustmanager")) { - showTrustedCerts(); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + SSLLogger.fine("adding as trusted certificates", + (Object[])trustedCerts.toArray(new X509Certificate[0])); } } @@ -202,11 +198,10 @@ } // create the algorithm constraints - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); boolean isExtSession = (session instanceof ExtendedSSLSession); - AlgorithmConstraints constraints = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) { + AlgorithmConstraints constraints; + if (isExtSession && + ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] localSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); @@ -228,8 +223,8 @@ // check if EE certificate chains to a public root CA (as // pre-installed in cacerts) - boolean chainsToPublicCA = - AnchorCertificates.contains(trustedChain[trustedChain.length-1]); + boolean chainsToPublicCA = AnchorCertificates.contains( + trustedChain[trustedChain.length-1]); // check endpoint identity String identityAlg = sslSocket.getSSLParameters(). @@ -242,9 +237,10 @@ trustedChain = validate(v, chain, Collections.emptyList(), null, isClient ? null : authType); } - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println("Found trusted certificate:"); - System.out.println(trustedChain[trustedChain.length - 1]); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + SSLLogger.fine("Found trusted certificate", + trustedChain[trustedChain.length - 1]); } } @@ -260,11 +256,10 @@ } // create the algorithm constraints - ProtocolVersion protocolVersion = - ProtocolVersion.valueOf(session.getProtocol()); boolean isExtSession = (session instanceof ExtendedSSLSession); - AlgorithmConstraints constraints = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v && isExtSession) { + AlgorithmConstraints constraints; + if (isExtSession && + ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] localSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); @@ -286,8 +281,8 @@ // check if EE certificate chains to a public root CA (as // pre-installed in cacerts) - boolean chainsToPublicCA = - AnchorCertificates.contains(trustedChain[trustedChain.length-1]); + boolean chainsToPublicCA = AnchorCertificates.contains( + trustedChain[trustedChain.length-1]); // check endpoint identity String identityAlg = engine.getSSLParameters(). @@ -300,27 +295,10 @@ trustedChain = validate(v, chain, Collections.emptyList(), null, isClient ? null : authType); } - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println("Found trusted certificate:"); - System.out.println(trustedChain[trustedChain.length - 1]); - } - } - private void showTrustedCerts() { - for (X509Certificate cert : trustedCerts) { - System.out.println("adding as trusted cert:"); - System.out.println(" Subject: " - + cert.getSubjectX500Principal()); - System.out.println(" Issuer: " - + cert.getIssuerX500Principal()); - System.out.println(" Algorithm: " - + cert.getPublicKey().getAlgorithm() - + "; Serial number: 0x" - + cert.getSerialNumber().toString(16)); - System.out.println(" Valid from " - + cert.getNotBefore() + " until " - + cert.getNotAfter()); - System.out.println(); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + SSLLogger.fine("Found trusted certificate", + trustedChain[trustedChain.length - 1]); } } @@ -364,8 +342,8 @@ hostname = new SNIHostName(sniName.getEncoded()); } catch (IllegalArgumentException iae) { // unlikely to happen, just in case ... - if ((debug != null) && Debug.isOn("trustmanager")) { - System.out.println("Illegal server name: " + sniName); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { + SSLLogger.fine("Illegal server name: " + sniName); } } } @@ -491,3 +469,4 @@ } } } + diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/classes/sun/security/util/HostnameChecker.java --- a/src/java.base/share/classes/sun/security/util/HostnameChecker.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/classes/sun/security/util/HostnameChecker.java Mon Jun 25 13:41:39 2018 -0700 @@ -35,9 +35,8 @@ import javax.net.ssl.SNIHostName; import sun.net.util.IPAddressUtil; -import sun.security.ssl.ClientKeyExchangeService; -import sun.security.ssl.Debug; import sun.security.x509.X500Name; +import sun.security.ssl.SSLLogger; /** * Class to check hostnames against the names specified in a certificate as @@ -60,8 +59,6 @@ private static final int ALTNAME_DNS = 2; private static final int ALTNAME_IP = 7; - private static final Debug debug = Debug.getInstance("ssl"); - // the algorithm to follow to perform the check. Currently unused. private final byte checkType; @@ -107,26 +104,6 @@ } /** - * Perform the check for Kerberos. - */ - public static boolean match(String expectedName, Principal principal) { - String hostName = getServerName(principal); - return (expectedName.equalsIgnoreCase(hostName)); - } - - /** - * Return the Server name from Kerberos principal. - */ - public static String getServerName(Principal principal) { - ClientKeyExchangeService p = - ClientKeyExchangeService.find("KRB5"); - if (p == null) { - throw new AssertionError("Kerberos should have been available"); - } - return p.getServiceHostName(principal); - } - - /** * Test whether the given hostname looks like a literal IPv4 or IPv6 * address. The hostname does not need to be a fully qualified name. * @@ -316,9 +293,10 @@ boolean chainsToPublicCA) { // not ok if it is a single wildcard character or "*." if (template.equals("*") || template.equals("*.")) { - if (debug != null) { - debug.println("Certificate domain name has illegal single " + - "wildcard character: " + template); + if (SSLLogger.isOn) { + SSLLogger.fine( + "Certificate domain name has illegal single " + + "wildcard character: " + template); } return true; } @@ -335,9 +313,10 @@ // not ok if there is no dot after wildcard (ex: "*com") if (firstDotIndex == -1) { - if (debug != null) { - debug.println("Certificate domain name has illegal wildcard, " + - "no dot after wildcard character: " + template); + if (SSLLogger.isOn) { + SSLLogger.fine( + "Certificate domain name has illegal wildcard, " + + "no dot after wildcard character: " + template); } return true; } @@ -354,9 +333,10 @@ if (rd.isPresent()) { String wDomain = afterWildcard.substring(firstDotIndex + 1); if (rd.get().publicSuffix().equalsIgnoreCase(wDomain)) { - if (debug != null) { - debug.println("Certificate domain name has illegal " + - "wildcard for public suffix: " + template); + if (SSLLogger.isOn) { + SSLLogger.fine( + "Certificate domain name has illegal " + + "wildcard for public suffix: " + template); } return true; } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.base/share/conf/security/java.security --- a/src/java.base/share/conf/security/java.security Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.base/share/conf/security/java.security Mon Jun 25 13:41:39 2018 -0700 @@ -800,6 +800,40 @@ # FFFFFFFF FFFFFFFF, 2} # +# TLS key limits on symmetric cryptographic algorithms +# +# This security property sets limits on algorithms key usage in TLS 1.3. +# When the amount of data encrypted exceeds the algorithm value listed below, +# a KeyUpdate message will trigger a key change. This is for symmetric ciphers +# with TLS 1.3 only. +# +# The syntax for the property is described below: +# KeyLimits: +# " KeyLimit { , KeyLimit } " +# +# WeakKeyLimit: +# AlgorithmName Action Length +# +# AlgorithmName: +# A full algorithm transformation. +# +# Action: +# KeyUpdate +# +# Length: +# The amount of encrypted data in a session before the Action occurs +# This value may be an integer value in bytes, or as a power of two, 2^29. +# +# KeyUpdate: +# The TLS 1.3 KeyUpdate handshake process begins when the Length amount +# is fulfilled. +# +# Note: This property is currently used by OpenJDK's JSSE implementation. It +# is not guaranteed to be examined and used by other implementations. +# +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 + +# # Cryptographic Jurisdiction Policy defaults # # Import and export control rules on cryptographic software vary from diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java --- a/src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -404,27 +404,16 @@ try { HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker.TYPE_LDAP); - // Use ciphersuite to determine whether Kerberos is active. - if (session.getCipherSuite().startsWith("TLS_KRB5")) { - Principal principal = getPeerPrincipal(session); - if (!HostnameChecker.match(hostname, principal)) { - throw new SSLPeerUnverifiedException( - "hostname of the kerberos principal:" + principal + - " does not match the hostname:" + hostname); - } - } else { // X.509 - - // get the subject's certificate - certs = session.getPeerCertificates(); - X509Certificate peerCert; - if (certs[0] instanceof java.security.cert.X509Certificate) { - peerCert = (java.security.cert.X509Certificate) certs[0]; - } else { - throw new SSLPeerUnverifiedException( - "Received a non X509Certificate from the server"); - } - checker.match(hostname, peerCert); + // get the subject's certificate + certs = session.getPeerCertificates(); + X509Certificate peerCert; + if (certs[0] instanceof java.security.cert.X509Certificate) { + peerCert = (java.security.cert.X509Certificate) certs[0]; + } else { + throw new SSLPeerUnverifiedException( + "Received a non X509Certificate from the server"); } + checker.match(hostname, peerCert); // no exception means verification passed return true; diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/module-info.java --- a/src/java.security.jgss/share/classes/module-info.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.security.jgss/share/classes/module-info.java Mon Jun 25 13:41:39 2018 -0700 @@ -55,7 +55,5 @@ provides java.security.Provider with sun.security.jgss.SunProvider; - provides sun.security.ssl.ClientKeyExchangeService with - sun.security.krb5.internal.ssl.Krb5KeyExchangeService; } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/GSSCaller.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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,8 +34,6 @@ public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN"); public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE"); public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT"); - public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT"); - public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER"); private String name; GSSCaller(String s) { diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/LoginConfigImpl.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -109,16 +109,6 @@ "com.sun.security.jgss.krb5.accept", "com.sun.security.jgss.accept", }; - } else if (caller == GSSCaller.CALLER_SSL_CLIENT) { - alts = new String[] { - "com.sun.security.jgss.krb5.initiate", - "com.sun.net.ssl.client", - }; - } else if (caller == GSSCaller.CALLER_SSL_SERVER) { - alts = new String[] { - "com.sun.security.jgss.krb5.accept", - "com.sun.net.ssl.server", - }; } else if (caller instanceof HttpCaller) { alts = new String[] { "com.sun.security.jgss.krb5.initiate", @@ -132,14 +122,12 @@ /* switch (caller) { case GSSUtil.CALLER_INITIATE: - case GSSUtil.CALLER_SSL_CLIENT: case GSSUtil.CALLER_HTTP_NEGOTIATE: alts = new String[] { "com.sun.security.jgss." + mechName + ".initiate", }; break; case GSSUtil.CALLER_ACCEPT: - case GSSUtil.CALLER_SSL_SERVER: alts = new String[] { "com.sun.security.jgss." + mechName + ".accept", }; @@ -207,7 +195,6 @@ } private static boolean isServerSide (GSSCaller caller) { - return GSSCaller.CALLER_ACCEPT == caller || - GSSCaller.CALLER_SSL_SERVER == caller; + return GSSCaller.CALLER_ACCEPT == caller; } } diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java Mon Jun 25 21:22:16 2018 +0300 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptedData.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -103,7 +103,7 @@ return new_encryptedData; } - // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) + // Used by test public EncryptedData( int new_eType, Integer new_kvno, @@ -126,8 +126,7 @@ } */ - // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv - // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) + // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv public EncryptedData( EncryptionKey key, byte[] plaintext, diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2003, 2010, 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.krb5.internal.ssl; - -import java.io.*; -import java.security.*; -import java.util.Arrays; - -import javax.net.ssl.*; - -import sun.security.krb5.EncryptionKey; -import sun.security.krb5.EncryptedData; -import sun.security.krb5.KrbException; -import sun.security.krb5.internal.crypto.KeyUsage; - -import sun.security.ssl.Debug; -import sun.security.ssl.HandshakeInStream; -import sun.security.ssl.HandshakeMessage; -import sun.security.ssl.ProtocolVersion; - -/** - * This is the Kerberos premaster secret in the Kerberos client key - * exchange message (CLIENT --> SERVER); it holds the - * Kerberos-encrypted pre-master secret. The secret is encrypted using the - * Kerberos session key. The padding and size of the resulting message - * depends on the session key type, but the pre-master secret is - * always exactly 48 bytes. - * - */ -final class KerberosPreMasterSecret { - - private ProtocolVersion protocolVersion; // preMaster [0,1] - private byte[] preMaster; // 48 bytes - private byte[] encrypted; - - /** - * Constructor used by client to generate premaster secret. - * - * Client randomly creates a pre-master secret and encrypts it - * using the Kerberos session key; only the server can decrypt - * it, using the session key available in the service ticket. - * - * @param protocolVersion used to set preMaster[0,1] - * @param generator random number generator for generating premaster secret - * @param sessionKey Kerberos session key for encrypting premaster secret - */ - KerberosPreMasterSecret(ProtocolVersion protocolVersion, - SecureRandom generator, EncryptionKey sessionKey) throws IOException { - - if (sessionKey.getEType() == - EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { - throw new IOException( - "session keys with des3-cbc-hmac-sha1-kd encryption type " + - "are not supported for TLS Kerberos cipher suites"); - } - - this.protocolVersion = protocolVersion; - preMaster = generatePreMaster(generator, protocolVersion); - - // Encrypt premaster secret - try { - EncryptedData eData = new EncryptedData(sessionKey, preMaster, - KeyUsage.KU_UNKNOWN); - encrypted = eData.getBytes(); // not ASN.1 encoded. - - } catch (KrbException e) { - throw (SSLKeyException)new SSLKeyException - ("Kerberos premaster secret error").initCause(e); - } - } - - /* - * Constructor used by server to decrypt encrypted premaster secret. - * The protocol version in preMaster[0,1] must match either currentVersion - * or clientVersion, otherwise, the premaster secret is set to - * a random one to foil possible attack. - * - * @param currentVersion version of protocol being used - * @param clientVersion version requested by client - * @param generator random number generator used to generate - * bogus premaster secret if premaster secret verification fails - * @param input input stream from which to read the encrypted - * premaster secret - * @param sessionKey Kerberos session key to be used for decryption - */ - KerberosPreMasterSecret(ProtocolVersion currentVersion, - ProtocolVersion clientVersion, - SecureRandom generator, byte[] encrypted, - EncryptionKey sessionKey) throws IOException { - - if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - if (encrypted != null) { - Debug.println(System.out, - "encrypted premaster secret", encrypted); - } - } - - if (sessionKey.getEType() == - EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { - throw new IOException( - "session keys with des3-cbc-hmac-sha1-kd encryption type " + - "are not supported for TLS Kerberos cipher suites"); - } - - // Decrypt premaster secret - try { - EncryptedData data = new EncryptedData(sessionKey.getEType(), - null /* optional kvno */, encrypted); - - byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN); - if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - if (encrypted != null) { - Debug.println(System.out, - "decrypted premaster secret", temp); - } - } - - // Remove padding bytes after decryption. Only DES and DES3 have - // paddings and we don't support DES3 in TLS (see above) - - if (temp.length == 52 && - data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) { - // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00. - if (paddingByteIs(temp, 52, (byte)4) || - paddingByteIs(temp, 52, (byte)0)) { - temp = Arrays.copyOf(temp, 48); - } - } else if (temp.length == 56 && - data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) { - // For des-cbc-md5, 8 paddings with 0x08, or no padding - if (paddingByteIs(temp, 56, (byte)8)) { - temp = Arrays.copyOf(temp, 48); - } - } - - preMaster = temp; - - protocolVersion = ProtocolVersion.valueOf(preMaster[0], - preMaster[1]); - if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - System.out.println("Kerberos PreMasterSecret version: " - + protocolVersion); - } - } catch (Exception e) { - // catch exception & process below - preMaster = null; - protocolVersion = currentVersion; - } - - // check if the premaster secret version is ok - // the specification says that it must be the maximum version supported - // by the client from its ClientHello message. However, many - // old implementations send the negotiated version, so accept both - // for SSL v3.0 and TLS v1.0. - // NOTE that we may be comparing two unsupported version numbers in - // the second case, which is why we cannot use object references - // equality in this special case - boolean versionMismatch = (protocolVersion.v != clientVersion.v); - - /* - * we never checked the client_version in server side - * for TLS v1.0 and SSL v3.0. For compatibility, we - * maintain this behavior. - */ - if (versionMismatch && (clientVersion.v <= 0x0301)) { - versionMismatch = (protocolVersion.v != currentVersion.v); - } - - /* - * Bogus decrypted ClientKeyExchange? If so, conjure a - * a random preMaster secret that will fail later during - * Finished message processing. This is a countermeasure against - * the "interactive RSA PKCS#1 encryption envelop attack" reported - * in June 1998. Preserving the executation path will - * mitigate timing attacks and force consistent error handling - * that will prevent an attacking client from differentiating - * different kinds of decrypted ClientKeyExchange bogosities. - */ - if ((preMaster == null) || (preMaster.length != 48) - || versionMismatch) { - if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - System.out.println("Kerberos PreMasterSecret error, " - + "generating random secret"); - if (preMaster != null) { - Debug.println(System.out, "Invalid secret", preMaster); - } - } - - /* - * Randomize the preMaster secret with the - * ClientHello.client_version, as will produce invalid master - * secret to prevent the attacks. - */ - preMaster = generatePreMaster(generator, clientVersion); - protocolVersion = clientVersion; - } - } - - /** - * Checks if all paddings of data are b - * @param data the block with padding - * @param len length of data, >= 48 - * @param b expected padding byte - */ - private static boolean paddingByteIs(byte[] data, int len, byte b) { - for (int i=48; i<len; i++) { - if (data[i] != b) return false; - } - return true; - } - - /* - * Used by server to generate premaster secret in case of - * problem decoding ticket. - * - * @param protocolVersion used for preMaster[0,1] - * @param generator random number generator to use for generating secret. - */ - KerberosPreMasterSecret(ProtocolVersion protocolVersion, - SecureRandom generator) { - - this.protocolVersion = protocolVersion; - preMaster = generatePreMaster(generator, protocolVersion); - } - - private static byte[] generatePreMaster(SecureRandom rand, - ProtocolVersion ver) { - - byte[] pm = new byte[48]; - rand.nextBytes(pm); - pm[0] = ver.major; - pm[1] = ver.minor; - - return pm; - } - - // Clone not needed; internal use only - byte[] getUnencrypted() { - return preMaster; - } - - // Clone not needed; internal use only - byte[] getEncrypted() { - return encrypted; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,563 +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.krb5.internal.ssl; - -import sun.security.ssl.ClientKeyExchange; -import sun.security.ssl.Debug; -import sun.security.ssl.ClientKeyExchangeService; -import sun.security.ssl.HandshakeOutStream; - -import sun.security.jgss.GSSCaller; -import sun.security.jgss.krb5.Krb5Util; -import sun.security.jgss.krb5.ServiceCreds; -import sun.security.krb5.EncryptedData; -import sun.security.krb5.EncryptionKey; -import sun.security.krb5.KrbException; -import sun.security.krb5.PrincipalName; -import sun.security.krb5.internal.EncTicketPart; -import sun.security.krb5.internal.Ticket; -import sun.security.krb5.internal.crypto.KeyUsage; -import sun.security.ssl.ProtocolVersion; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosKey; -import javax.security.auth.kerberos.KerberosPrincipal; -import javax.security.auth.kerberos.KerberosTicket; -import javax.security.auth.kerberos.KeyTab; -import javax.security.auth.kerberos.ServicePermission; -import java.io.IOException; -import java.io.PrintStream; -import java.net.InetAddress; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Principal; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.security.SecureRandom; -import java.util.Set; - -/** - * The provider for TLS_KRB_ cipher suites. - * - * @since 9 - */ -public class Krb5KeyExchangeService implements ClientKeyExchangeService { - - public static final Debug debug = Debug.getInstance("ssl"); - - @Override - public String[] supported() { - return new String[] { "KRB5", "KRB5_EXPORT" }; - } - - @Override - public Object getServiceCreds(AccessControlContext acc) { - try { - ServiceCreds serviceCreds = AccessController.doPrivileged( - (PrivilegedExceptionAction<ServiceCreds>) - () -> Krb5Util.getServiceCreds( - GSSCaller.CALLER_SSL_SERVER, null, acc)); - if (serviceCreds == null) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Kerberos serviceCreds not available"); - } - return null; - } - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Using Kerberos creds"); - } - String serverPrincipal = serviceCreds.getName(); - if (serverPrincipal != null) { - // When service is bound, we check ASAP. Otherwise, - // will check after client request is received - // in in Kerberos ClientKeyExchange - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) { - // Eliminate dependency on ServicePermission - sm.checkPermission(new ServicePermission( - serverPrincipal, "accept"), acc); - } - } catch (SecurityException se) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Permission to access Kerberos" - + " secret key denied"); - } - return null; - } - } - return serviceCreds; - } catch (PrivilegedActionException e) { - // Likely exception here is LoginException - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Attempt to obtain Kerberos key failed: " - + e.toString()); - } - return null; - } - } - - @Override - public String getServiceHostName(Principal principal) { - if (principal == null) { - return null; - } - String hostName = null; - try { - PrincipalName princName = - new PrincipalName(principal.getName(), - PrincipalName.KRB_NT_SRV_HST); - String[] nameParts = princName.getNameStrings(); - if (nameParts.length >= 2) { - hostName = nameParts[1]; - } - } catch (Exception e) { - // ignore - } - return hostName; - } - - - @Override - public boolean isRelated(boolean isClient, - AccessControlContext acc, Principal p) { - - if (p == null) return false; - try { - Subject subject = AccessController.doPrivileged( - (PrivilegedExceptionAction<Subject>) - () -> Krb5Util.getSubject( - isClient ? GSSCaller.CALLER_SSL_CLIENT - : GSSCaller.CALLER_SSL_SERVER, - acc)); - if (subject == null) { - if (debug != null && Debug.isOn("session")) { - System.out.println("Kerberos credentials are" + - " not present in the current Subject;" + - " check if " + - " javax.security.auth.useSubjectAsCreds" + - " system property has been set to false"); - } - return false; - } - Set<Principal> principals = - subject.getPrincipals(Principal.class); - if (principals.contains(p)) { - // bound to this principal - return true; - } else { - if (isClient) { - return false; - } else { - for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) { - if (!pc.isBound()) { - return true; - } - } - return false; - } - } - } catch (PrivilegedActionException pae) { - if (debug != null && Debug.isOn("session")) { - System.out.println("Attempt to obtain" + - " subject failed! " + pae); - } - return false; - } - - } - - public ClientKeyExchange createClientExchange( - String serverName, AccessControlContext acc, - ProtocolVersion protocolVerson, SecureRandom rand) throws IOException { - return new ExchangerImpl(serverName, acc, protocolVerson, rand); - } - - public ClientKeyExchange createServerExchange( - ProtocolVersion protocolVersion, ProtocolVersion clientVersion, - SecureRandom rand, byte[] encodedTicket, byte[] encrypted, - AccessControlContext acc, Object serviceCreds) throws IOException { - return new ExchangerImpl(protocolVersion, clientVersion, rand, - encodedTicket, encrypted, acc, serviceCreds); - } - - static class ExchangerImpl extends ClientKeyExchange { - - final private KerberosPreMasterSecret preMaster; - final private byte[] encodedTicket; - final private KerberosPrincipal peerPrincipal; - final private KerberosPrincipal localPrincipal; - - @Override - public int messageLength() { - return encodedTicket.length + preMaster.getEncrypted().length + 6; - } - - @Override - public void send(HandshakeOutStream s) throws IOException { - s.putBytes16(encodedTicket); - s.putBytes16(null); - s.putBytes16(preMaster.getEncrypted()); - } - - @Override - public void print(PrintStream s) throws IOException { - s.println("*** ClientKeyExchange, Kerberos"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "Kerberos service ticket", encodedTicket); - Debug.println(s, "Random Secret", preMaster.getUnencrypted()); - Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted()); - } - } - - ExchangerImpl(String serverName, AccessControlContext acc, - ProtocolVersion protocolVersion, SecureRandom rand) throws IOException { - - // Get service ticket - KerberosTicket ticket = getServiceTicket(serverName, acc); - encodedTicket = ticket.getEncoded(); - - // Record the Kerberos principals - peerPrincipal = ticket.getServer(); - localPrincipal = ticket.getClient(); - - // Optional authenticator, encrypted using session key, - // currently ignored - - // Generate premaster secret and encrypt it using session key - EncryptionKey sessionKey = new EncryptionKey( - ticket.getSessionKeyType(), - ticket.getSessionKey().getEncoded()); - - preMaster = new KerberosPreMasterSecret(protocolVersion, - rand, sessionKey); - } - - ExchangerImpl( - ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand, - byte[] encodedTicket, byte[] encrypted, - AccessControlContext acc, Object serviceCreds) throws IOException { - - // Read ticket - this.encodedTicket = encodedTicket; - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(System.out, - "encoded Kerberos service ticket", encodedTicket); - } - - EncryptionKey sessionKey = null; - KerberosPrincipal tmpPeer = null; - KerberosPrincipal tmpLocal = null; - - try { - Ticket t = new Ticket(encodedTicket); - - EncryptedData encPart = t.encPart; - PrincipalName ticketSname = t.sname; - - final ServiceCreds creds = (ServiceCreds)serviceCreds; - final KerberosPrincipal princ = - new KerberosPrincipal(ticketSname.toString()); - - // For bound service, permission already checked at setup - if (creds.getName() == null) { - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) { - // Eliminate dependency on ServicePermission - sm.checkPermission(new ServicePermission( - ticketSname.toString(), "accept"), acc); - } - } catch (SecurityException se) { - serviceCreds = null; - // Do not destroy keys. Will affect Subject - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Permission to access Kerberos" - + " secret key denied"); - se.printStackTrace(System.out); - } - throw new IOException("Kerberos service not allowedy"); - } - } - KerberosKey[] serverKeys = AccessController.doPrivileged( - new PrivilegedAction<KerberosKey[]>() { - @Override - public KerberosKey[] run() { - return creds.getKKeys(princ); - } - }); - if (serverKeys.length == 0) { - throw new IOException("Found no key for " + princ + - (creds.getName() == null ? "" : - (", this keytab is for " + creds.getName() + " only"))); - } - - /* - * permission to access and use the secret key of the Kerberized - * "host" service is done in ServerHandshaker.getKerberosKeys() - * to ensure server has the permission to use the secret key - * before promising the client - */ - - // See if we have the right key to decrypt the ticket to get - // the session key. - int encPartKeyType = encPart.getEType(); - Integer encPartKeyVersion = encPart.getKeyVersionNumber(); - KerberosKey dkey = null; - try { - dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys); - } catch (KrbException ke) { // a kvno mismatch - throw new IOException( - "Cannot find key matching version number", ke); - } - if (dkey == null) { - // %%% Should print string repr of etype - throw new IOException("Cannot find key of appropriate type" + - " to decrypt ticket - need etype " + encPartKeyType); - } - - EncryptionKey secretKey = new EncryptionKey( - encPartKeyType, - dkey.getEncoded()); - - // Decrypt encPart using server's secret key - byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET); - - // Reset data stream after decryption, remove redundant bytes - byte[] temp = encPart.reset(bytes); - EncTicketPart encTicketPart = new EncTicketPart(temp); - - // Record the Kerberos Principals - tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName()); - tmpLocal = new KerberosPrincipal(ticketSname.getName()); - - sessionKey = encTicketPart.key; - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("server principal: " + ticketSname); - System.out.println("cname: " + encTicketPart.cname.toString()); - } - } catch (IOException e) { - throw e; - } catch (Exception e) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("KerberosWrapper error getting session key," - + " generating random secret (" + e.getMessage() + ")"); - } - sessionKey = null; - } - - //input.getBytes16(); // XXX Read and ignore authenticator - - if (sessionKey != null) { - preMaster = new KerberosPreMasterSecret(protocolVersion, - clientVersion, rand, encrypted, sessionKey); - } else { - // Generate bogus premaster secret - preMaster = new KerberosPreMasterSecret(clientVersion, rand); - } - - peerPrincipal = tmpPeer; - localPrincipal = tmpLocal; - } - - // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context - private static KerberosTicket getServiceTicket(String serverName, - final AccessControlContext acc) throws IOException { - - if ("localhost".equals(serverName) || - "localhost.localdomain".equals(serverName)) { - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Get the local hostname"); - } - String localHost = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction<String>() { - public String run() { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (java.net.UnknownHostException e) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning," - + " cannot get the local hostname: " - + e.getMessage()); - } - return null; - } - } - }); - if (localHost != null) { - serverName = localHost; - } - } - - // Resolve serverName (possibly in IP addr form) to Kerberos principal - // name for service with hostname - String serviceName = "host/" + serverName; - PrincipalName principal; - try { - principal = new PrincipalName(serviceName, - PrincipalName.KRB_NT_SRV_HST); - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - IOException ioe = new IOException("Invalid service principal" + - " name: " + serviceName); - ioe.initCause(e); - throw ioe; - } - String realm = principal.getRealmAsString(); - - final String serverPrincipal = principal.toString(); - final String tgsPrincipal = "krbtgt/" + realm + "@" + realm; - final String clientPrincipal = null; // use default - - - // check permission to obtain a service ticket to initiate a - // context with the "host" service - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new ServicePermission(serverPrincipal, - "initiate"), acc); - } - - try { - KerberosTicket ticket = AccessController.doPrivileged( - new PrivilegedExceptionAction<KerberosTicket>() { - public KerberosTicket run() throws Exception { - return Krb5Util.getTicketFromSubjectAndTgs( - GSSCaller.CALLER_SSL_CLIENT, - clientPrincipal, serverPrincipal, - tgsPrincipal, acc); - }}); - - if (ticket == null) { - throw new IOException("Failed to find any kerberos service" + - " ticket for " + serverPrincipal); - } - return ticket; - } catch (PrivilegedActionException e) { - IOException ioe = new IOException( - "Attempt to obtain kerberos service ticket for " + - serverPrincipal + " failed!"); - ioe.initCause(e); - throw ioe; - } - } - - @Override - public SecretKey clientKeyExchange() { - byte[] secretBytes = preMaster.getUnencrypted(); - return new SecretKeySpec(secretBytes, "TlsPremasterSecret"); - } - - @Override - public Principal getPeerPrincipal() { - return peerPrincipal; - } - - @Override - public Principal getLocalPrincipal() { - return localPrincipal; - } - - /** - * Determines if a kvno matches another kvno. Used in the method - * findKey(etype, version, keys). Always returns true if either input - * is null or zero, in case any side does not have kvno info available. - * - * Note: zero is included because N/A is not a legal value for kvno - * in javax.security.auth.kerberos.KerberosKey. Therefore, the info - * that the kvno is N/A might be lost when converting between - * EncryptionKey and KerberosKey. - */ - private static boolean versionMatches(Integer v1, int v2) { - if (v1 == null || v1 == 0 || v2 == 0) { - return true; - } - return v1.equals(v2); - } - - private static KerberosKey findKey(int etype, Integer version, - KerberosKey[] keys) throws KrbException { - int ktype; - boolean etypeFound = false; - - // When no matched kvno is found, returns tke key of the same - // etype with the highest kvno - int kvno_found = 0; - KerberosKey key_found = null; - - for (int i = 0; i < keys.length; i++) { - ktype = keys[i].getKeyType(); - if (etype == ktype) { - int kv = keys[i].getVersionNumber(); - etypeFound = true; - if (versionMatches(version, kv)) { - return keys[i]; - } else if (kv > kvno_found) { - key_found = keys[i]; - kvno_found = kv; - } - } - } - // Key not found. - // %%% kludge to allow DES keys to be used for diff etypes - if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || - etype == EncryptedData.ETYPE_DES_CBC_MD5)) { - for (int i = 0; i < keys.length; i++) { - ktype = keys[i].getKeyType(); - if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || - ktype == EncryptedData.ETYPE_DES_CBC_MD5) { - int kv = keys[i].getVersionNumber(); - etypeFound = true; - if (versionMatches(version, kv)) { - return new KerberosKey(keys[i].getPrincipal(), - keys[i].getEncoded(), - etype, - kv); - } else if (kv > kvno_found) { - key_found = new KerberosKey(keys[i].getPrincipal(), - keys[i].getEncoded(), - etype, - kv); - kvno_found = kv; - } - } - } - } - if (etypeFound) { - return key_found; - } - return null; - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/ProblemList.txt --- a/test/jdk/ProblemList.txt Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/ProblemList.txt Mon Jun 25 13:41:39 2018 -0700 @@ -561,6 +561,22 @@ java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-all +java/net/httpclient/ConcurrentResponses.java 8204977 generic-all +java/net/httpclient/DigestEchoClientSSL.java 8204977 generic-all +java/net/httpclient/EncodedCharsInURI.java 8204977 generic-all +java/net/httpclient/FlowAdapterSubscriberTest.java 8204977 generic-all +java/net/httpclient/ImmutableFlowItems.java 8204977 generic-all +java/net/httpclient/ManyRequests.java 8204977 generic-all +java/net/httpclient/ManyRequests2.java 8204977 generic-all +java/net/httpclient/SplitResponseSSL.java 8204977 generic-all +java/net/httpclient/http2/BasicTest.java 8204977 generic-all +java/net/httpclient/http2/ContinuationFrameTest.java 8204977 generic-all +java/net/httpclient/ProxyAuthDisabledSchemesSSL.java 8204977 generic-all +java/net/httpclient/RequestBodyTest.java 8204977 generic-all +java/net/httpclient/SmokeTest.java 8204977 generic-all +java/net/httpclient/CancelledResponse.java 8204977 solaris-all +java/net/httpclient/InvalidSSLContextTest.java 8204980 generic-all + ############################################################################ # jdk_nio diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java --- a/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -67,7 +67,8 @@ public void performOp(InitialContext ctx) throws NamingException {} public void handleNamingException(NamingException e, long start, long end) { - if (e.getCause() instanceof SocketTimeoutException) { + if (e.getCause() instanceof SocketTimeoutException + || e.getCause().getCause() instanceof SocketTimeoutException) { // SSL connect will timeout via readReply using // SocketTimeoutException e.printStackTrace(); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/java/net/httpclient/MockServer.java --- a/test/jdk/java/net/httpclient/MockServer.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/java/net/httpclient/MockServer.java Mon Jun 25 13:41:39 2018 -0700 @@ -183,7 +183,8 @@ } catch (IOException |InterruptedException e1) { cleanup(); } catch (Throwable t) { - System.out.println("X: " + t); + System.out.println("Exception: " + t); + t.printStackTrace(); cleanup(); } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/DTLS/InvalidRecords.java --- a/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/DTLS/InvalidRecords.java Mon Jun 25 13:41:39 2018 -0700 @@ -55,7 +55,7 @@ @Override DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) { - if (needInvalidRecords && (ba.length >= 60) && + if ((ba.length >= 60) && (ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) && (ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) { @@ -64,6 +64,16 @@ // ba[0x3B]: length of session ID // ba[0x3C]: length of cookie + if (!needInvalidRecords) { + // The 2nd ClientHello with cookie. The 1st one should be + // rejected as expected. + // + // This may happen if the last few bytes of the packet are + // for supported_version extension. + throw new RuntimeException( + "the crashed handshake message was rejected as expected"); + } + // ClientHello with cookie needInvalidRecords = false; System.out.println("invalidate ClientHello message"); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java --- a/test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/CheckStatus.java Mon Jun 25 13:41:39 2018 -0700 @@ -25,15 +25,16 @@ * @test * @bug 4948079 * @summary SSLEngineResult needs updating [none yet] - * - * This is a simple hack to test a bunch of conditions and check - * their return codes. - * + * @ignore the dependent implementation details are changed * @run main/othervm -Djsse.enableCBCProtection=false CheckStatus * * @author Brad Wetmore */ +/* + * This is a simple hack to test a bunch of conditions and check + * their return codes. + */ import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.*; import java.io.*; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java --- a/test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/ConnectionTest.java Mon Jun 25 13:41:39 2018 -0700 @@ -26,17 +26,18 @@ * @bug 4495742 * @summary Add non-blocking SSL/TLS functionality, usable with any * I/O abstraction - * - * This is a bit hacky, meant to test various conditions. The main - * thing I wanted to do with this was to do buffer reads/writes - * when buffers were not empty. (buffer.position() = 10) - * The code could certainly be tightened up a lot. - * + * @ignore the dependent implementation details are changed * @author Brad Wetmore * * @run main/othervm ConnectionTest */ +/* + * This is a bit hacky, meant to test various conditions. The main + * thing I wanted to do with this was to do buffer reads/writes + * when buffers were not empty. (buffer.position() = 10) + * The code could certainly be tightened up a lot. + */ import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.*; import java.io.*; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java --- a/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/EngineCloseOnAlert.java Mon Jun 25 13:41:39 2018 -0700 @@ -25,7 +25,8 @@ * @test * @bug 8133632 * @summary javax.net.ssl.SSLEngine does not properly handle received - * SSL fatal alerts + * SSL fatal alerts + * @ignore the dependent implementation details are changed * @run main/othervm EngineCloseOnAlert */ diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java --- a/test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/IllegalHandshakeMessage.java Mon Jun 25 13:41:39 2018 -0700 @@ -30,7 +30,7 @@ * @test * @bug 8180643 * @summary Illegal handshake message - * + * @ignore the dependent implementation details are changed * @run main/othervm IllegalHandshakeMessage */ diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java --- a/test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/IllegalRecordVersion.java Mon Jun 25 13:41:39 2018 -0700 @@ -28,7 +28,7 @@ * @test * @bug 8042449 * @summary Issue for negative byte major record version - * + * @ignore the dependent implementation details are changed * @run main/othervm IllegalRecordVersion */ diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java --- a/test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/LargeBufs.java Mon Jun 25 13:41:39 2018 -0700 @@ -45,7 +45,7 @@ public class LargeBufs { - private static boolean debug = false; + private static boolean debug = true; private SSLContext sslc; static private SSLEngine ssle1; // client @@ -297,6 +297,11 @@ } else { log("Data transferred cleanly"); } + + a.position(a.limit()); + b.position(b.limit()); + a.limit(a.capacity()); + b.limit(b.capacity()); } private static void log(String str) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java --- a/test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java Mon Jun 25 13:41:39 2018 -0700 @@ -21,15 +21,19 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 4495742 * @summary Demonstrate SSLEngine switch from no client auth to client auth. - * @run main/othervm NoAuthClientAuth - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. - * + * @run main/othervm NoAuthClientAuth SSLv3 + * @run main/othervm NoAuthClientAuth TLSv1 + * @run main/othervm NoAuthClientAuth TLSv1.1 + * @run main/othervm NoAuthClientAuth TLSv1.2 * @author Brad R. Wetmore */ @@ -78,6 +82,7 @@ import java.security.*; import java.nio.*; +// Note that this test case depends on JSSE provider implementation details. public class NoAuthClientAuth { /* @@ -94,7 +99,7 @@ * including specific handshake messages, and might be best examined * after gaining some familiarity with this application. */ - private static boolean debug = false; + private static boolean debug = true; private SSLContext sslc; @@ -128,15 +133,21 @@ private static String trustFilename = System.getProperty("test.src", ".") + "/" + pathToStores + "/" + trustStoreFile; + // the specified protocol + private static String tlsProtocol; /* * Main entry point for this test. */ public static void main(String args[]) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + if (debug) { System.setProperty("javax.net.debug", "all"); } + tlsProtocol = args[0]; + NoAuthClientAuth test = new NoAuthClientAuth(); test.runTest(); @@ -243,8 +254,8 @@ for (java.security.cert.Certificate c : certs) { System.out.println(c); } - log("Closing server."); - serverEngine.closeOutbound(); +// log("Closing server."); +// serverEngine.closeOutbound(); } // nothing. } @@ -253,18 +264,30 @@ log("----"); - clientResult = clientEngine.unwrap(sTOc, clientIn); - log("client unwrap: ", clientResult); - runDelegatedTasks(clientResult, clientEngine); - clientIn.clear(); + if (!clientEngine.isInboundDone()) { + clientResult = clientEngine.unwrap(sTOc, clientIn); + log("client unwrap: ", clientResult); + runDelegatedTasks(clientResult, clientEngine); + clientIn.clear(); + sTOc.compact(); + } else { + sTOc.clear(); + } - serverResult = serverEngine.unwrap(cTOs, serverIn); - log("server unwrap: ", serverResult); - runDelegatedTasks(serverResult, serverEngine); - serverIn.clear(); + if (!serverEngine.isInboundDone()) { + serverResult = serverEngine.unwrap(cTOs, serverIn); + log("server unwrap: ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + serverIn.clear(); + cTOs.compact(); + } else { + cTOs.clear(); + } - cTOs.compact(); - sTOc.compact(); + if (hsCompleted == 2) { + log("Closing server."); + serverEngine.closeOutbound(); + } } } @@ -286,6 +309,7 @@ */ clientEngine = sslc.createSSLEngine("client", 80); clientEngine.setUseClientMode(true); + clientEngine.setEnabledProtocols(new String[] { tlsProtocol }); } /* diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLSession/RenegotiateTLS13.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/javax/net/ssl/SSLSession/RenegotiateTLS13.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,292 @@ +/* + * 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. + * + * 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. + */ + +/* + * @test + * @run main/othervm -Djavax.net.debug=ssl RenegotiateTLS13 + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.security.KeyStore; +import java.security.SecureRandom; + +public class RenegotiateTLS13 { + + static final String dataString = "This is a test"; + + // Run the server as a thread instead of the client + static boolean separateServerThread = false; + + static String pathToStores = "../etc"; + static String keyStoreFile = "keystore"; + static String trustStoreFile = "truststore"; + static String passwd = "passphrase"; + + // Server ready flag + volatile static boolean serverReady = false; + // Turn on SSL debugging + static boolean debug = false; + // Server done flag + static boolean done = false; + + // Main server code + + void doServerSide() throws Exception { + SSLServerSocketFactory sslssf; + sslssf = initContext().getServerSocketFactory(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket(serverPort); + serverPort = sslServerSocket.getLocalPort(); + + serverReady = true; + + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + + DataInputStream sslIS = + new DataInputStream(sslSocket.getInputStream()); + String s = ""; + while (s.compareTo("done") != 0) { + try { + s = sslIS.readUTF(); + System.out.println("Received: " + s); + } catch (IOException e) { + throw e; + } + } + done = true; + sslSocket.close(); + } + + // Main client code + void doClientSide() throws Exception { + + while (!serverReady) { + Thread.sleep(5); + } + + SSLSocketFactory sslsf; + sslsf = initContext().getSocketFactory(); + + SSLSocket sslSocket = (SSLSocket) + sslsf.createSocket("localhost", serverPort); + + DataOutputStream sslOS = + new DataOutputStream(sslSocket.getOutputStream()); + + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + + sslSocket.startHandshake(); + + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + + sslSocket.startHandshake(); + + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("With " + dataString); + sslOS.writeUTF("done"); + + while (!done) { + Thread.sleep(5); + } + sslSocket.close(); + } + + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + String keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + String trustFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + trustStoreFile; + + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + System.setProperty("javax.net.ssl.trustStore", trustFilename); + System.setProperty("javax.net.ssl.trustStorePassword", passwd); + + if (debug) + System.setProperty("javax.net.debug", "ssl"); + + new RenegotiateTLS13(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + RenegotiateTLS13() throws Exception { + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + // swallow for now. Show later + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + String whichRemote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + whichRemote = "server"; + } else { + remote = clientException; + local = serverException; + whichRemote = "client"; + } + + /* + * If both failed, return the curthread's exception, but also + * print the remote side Exception + */ + if ((local != null) && (remote != null)) { + System.out.println(whichRemote + " also threw:"); + remote.printStackTrace(); + System.out.println(); + throw local; + } + + if (remote != null) { + throw remote; + } + + if (local != null) { + throw local; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..."); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + serverException = e; + } finally { + serverReady = true; + } + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + clientException = e; + } + } + } + + // Initialize context for TLS 1.3 + SSLContext initContext() throws Exception { + System.out.println("Using TLS13"); + SSLContext sc = SSLContext.getInstance("TLSv1.3"); + KeyStore ks = KeyStore.getInstance( + new File(System.getProperty("javax.net.ssl.keyStore")), + passwd.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, passwd.toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + return sc; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java --- a/test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/SSLSession/TestEnabledProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -234,6 +234,10 @@ } catch (java.lang.InterruptedException ie) { // must have been interrupted, no harm break; + } catch (SSLException ssle) { + // The client side may have closed the socket. + System.out.println("Server SSLException:"); + ssle.printStackTrace(System.out); } catch (Exception e) { System.out.println("Server exception:"); e.printStackTrace(System.out); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java --- a/test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/ServerName/SSLSocketExplorerFailure.java Mon Jun 25 13:41:39 2018 -0700 @@ -45,6 +45,7 @@ import java.util.*; import java.net.*; import javax.net.ssl.*; +import java.security.Security; public class SSLSocketExplorerFailure { @@ -232,6 +233,9 @@ volatile Exception clientException = null; public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + Security.setProperty("jdk.certpath.disabledAlgorithms", ""); + String keyFilename = System.getProperty("test.src", ".") + "/" + pathToStores + "/" + keyStoreFile; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java --- a/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/ServerName/SSLSocketSNISensitive.java Mon Jun 25 13:41:39 2018 -0700 @@ -54,7 +54,8 @@ import java.security.interfaces.*; import java.util.Base64; - +// Note: this test case works only on TLS 1.2 and prior versions because of +// the use of MD5withRSA signed certificate. public class SSLSocketSNISensitive { /* @@ -415,7 +416,7 @@ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); tmf.init(ks); - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); kmf.init(ks, passphrase); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java --- a/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -109,6 +109,11 @@ static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder static int intOcspPort; // Port number for intermed. OCSP + // Extra configuration parameters and constants + static final String[] TLS13ONLY = new String[] { "TLSv1.3" }; + static final String[] TLS12MAX = + new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }; + private static final String SIMPLE_WEB_PAGE = "<HTML>\n" + "<HEAD><Title>Web Page!</Title></HEAD>\n" + "<BODY><H1>Web Page!</H1></BODY>\n</HTML>"; @@ -124,7 +129,7 @@ */ public static void main(String[] args) throws Exception { if (debug) { - System.setProperty("javax.net.debug", "ssl"); + System.setProperty("javax.net.debug", "ssl:handshake"); } System.setProperty("javax.net.ssl.keyStore", ""); @@ -136,7 +141,8 @@ createPKI(); utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT")); - testPKIXParametersRevEnabled(); + testPKIXParametersRevEnabled(TLS12MAX); + testPKIXParametersRevEnabled(TLS13ONLY); // shut down the OCSP responders before finishing the test intOcsp.stop(); @@ -148,8 +154,10 @@ * enabled and client-side OCSP disabled. It will only pass if all * stapled responses are present, valid and have a GOOD status. */ - static void testPKIXParametersRevEnabled() throws Exception { + static void testPKIXParametersRevEnabled(String[] allowedProts) + throws Exception { ClientParameters cliParams = new ClientParameters(); + cliParams.protocols = allowedProts; ServerParameters servParams = new ServerParameters(); serverReady = false; @@ -194,7 +202,7 @@ // because of the client alert if (tr.serverExc instanceof SSLHandshakeException) { if (!tr.serverExc.getMessage().contains( - "alert: bad_certificate_status_response")) { + "bad_certificate_status_response")) { throw tr.serverExc; } } @@ -333,7 +341,8 @@ if (contentLength == -1) { contentLength = Integer.MAX_VALUE; } - byte[] response = new byte[contentLength > 2048 ? 2048 : contentLength]; + byte[] response = new byte[contentLength > 2048 ? 2048 : + contentLength]; int total = 0; while (total < contentLength) { int count = in.read(response, total, response.length - total); @@ -391,8 +400,8 @@ /** * Checks a validation failure to see if it failed for the reason we think * it should. This comes in as an SSLException of some sort, but it - * encapsulates a ValidatorException which in turn encapsulates the - * CertPathValidatorException we are interested in. + * encapsulates a CertPathValidatorException at some point in the + * exception stack. * * @param e the exception thrown at the top level * @param reason the underlying CertPathValidatorException BasicReason @@ -404,19 +413,31 @@ BasicReason reason) { boolean result = false; - if (e instanceof SSLException) { - Throwable valExc = e.getCause(); - if (valExc instanceof sun.security.validator.ValidatorException) { - Throwable cause = valExc.getCause(); - if (cause instanceof CertPathValidatorException) { - CertPathValidatorException cpve = - (CertPathValidatorException)cause; - if (cpve.getReason() == reason) { - result = true; - } - } + // Locate the CertPathValidatorException. If one + // Does not exist, then it's an automatic failure of + // the test. + Throwable curExc = e; + CertPathValidatorException cpve = null; + while (curExc != null) { + if (curExc instanceof CertPathValidatorException) { + cpve = (CertPathValidatorException)curExc; } + curExc = curExc.getCause(); } + + // If we get through the loop and cpve is null then we + // we didn't find CPVE and this is a failure + if (cpve != null) { + if (cpve.getReason() == reason) { + result = true; + } else { + System.out.println("CPVE Reason Mismatch: Expected = " + + reason + ", Actual = " + cpve.getReason()); + } + } else { + System.out.println("Failed to find an expected CPVE"); + } + return result; } @@ -698,6 +719,8 @@ boolean enabled = true; PKIXBuilderParameters pkixParams = null; PKIXRevocationChecker revChecker = null; + String[] protocols = null; + String[] cipherSuites = null; ClientParameters() { } } @@ -717,9 +740,19 @@ static class TestResult { Exception serverExc = null; Exception clientExc = null; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Test Result:\n"). + append("\tServer Exc = ").append(serverExc).append("\n"). + append("\tClient Exc = ").append(clientExc).append("\n"); + return sb.toString(); + } } static class HtucSSLSocketFactory extends SSLSocketFactory { + ClientParameters params; SSLContext sslc = SSLContext.getInstance("TLS"); HtucSSLSocketFactory(ClientParameters cliParams) @@ -747,6 +780,7 @@ } sslc.init(null, tmf.getTrustManagers(), null); + params = cliParams; } @Override @@ -754,7 +788,7 @@ boolean autoClose) throws IOException { Socket sock = sslc.getSocketFactory().createSocket(s, host, port, autoClose); - setCiphers(sock); + customizeSocket(sock); return sock; } @@ -762,7 +796,7 @@ public Socket createSocket(InetAddress host, int port) throws IOException { Socket sock = sslc.getSocketFactory().createSocket(host, port); - setCiphers(sock); + customizeSocket(sock); return sock; } @@ -771,7 +805,7 @@ InetAddress localAddress, int localPort) throws IOException { Socket sock = sslc.getSocketFactory().createSocket(host, port, localAddress, localPort); - setCiphers(sock); + customizeSocket(sock); return sock; } @@ -779,7 +813,7 @@ public Socket createSocket(String host, int port) throws IOException { Socket sock = sslc.getSocketFactory().createSocket(host, port); - setCiphers(sock); + customizeSocket(sock); return sock; } @@ -789,7 +823,7 @@ throws IOException { Socket sock = sslc.getSocketFactory().createSocket(host, port, localAddress, localPort); - setCiphers(sock); + customizeSocket(sock); return sock; } @@ -803,10 +837,15 @@ return sslc.getSupportedSSLParameters().getCipherSuites(); } - private static void setCiphers(Socket sock) { + private void customizeSocket(Socket sock) { if (sock instanceof SSLSocket) { - String[] ciphers = { "TLS_RSA_WITH_AES_128_CBC_SHA" }; - ((SSLSocket)sock).setEnabledCipherSuites(ciphers); + SSLSocket sslSock = (SSLSocket)sock; + if (params.protocols != null) { + sslSock.setEnabledProtocols(params.protocols); + } + if (params.cipherSuites != null) { + sslSock.setEnabledCipherSuites(params.cipherSuites); + } } } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java --- a/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/Stapling/SSLEngineWithStapling.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -106,7 +106,7 @@ * including specific handshake messages, and might be best examined * after gaining some familiarity with this application. */ - private static final boolean debug = false; + private static final boolean debug = true; private SSLEngine clientEngine; // client Engine private ByteBuffer clientOut; // write side of clientEngine @@ -142,12 +142,17 @@ static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder static int intOcspPort; // Port number for intermed. OCSP + // Extra configuration parameters and constants + static final String[] TLS13ONLY = new String[] { "TLSv1.3" }; + static final String[] TLS12MAX = + new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }; + /* * Main entry point for this test. */ public static void main(String args[]) throws Exception { if (debug) { - System.setProperty("javax.net.debug", "ssl"); + System.setProperty("javax.net.debug", "ssl:handshake"); } // Create the PKI we will use for the test and start the OCSP servers @@ -166,16 +171,23 @@ TimeUnit.HOURS.toMillis(8)))); intOcsp.updateStatusDb(revInfo); - SSLEngineWithStapling test = new SSLEngineWithStapling(); - try { - test.runTest(); - throw new RuntimeException("Expected failure due to revocation " + - "did not occur"); - } catch (Exception e) { - if (!checkClientValidationFailure(e, - CertPathValidatorException.BasicReason.REVOKED)) { - System.out.println("*** Didn't find the exception we wanted"); - throw e; + // Create a list of TLS protocol configurations we can use to + // drive tests with different handshaking models. + List<String[]> allowedProtList = List.of(TLS12MAX, TLS13ONLY); + + for (String[] protocols : allowedProtList) { + SSLEngineWithStapling test = new SSLEngineWithStapling(); + try { + test.runTest(protocols); + throw new RuntimeException("Expected failure due to " + + "revocation did not occur"); + } catch (Exception e) { + if (!checkClientValidationFailure(e, + CertPathValidatorException.BasicReason.REVOKED)) { + System.out.println( + "*** Didn't find the exception we wanted"); + throw e; + } } } @@ -218,10 +230,10 @@ * One could easily separate these phases into separate * sections of code. */ - private void runTest() throws Exception { + private void runTest(String[] protocols) throws Exception { boolean dataDone = false; - createSSLEngines(); + createSSLEngines(protocols); createBuffers(); SSLEngineResult clientResult; // results from client's last operation @@ -290,7 +302,7 @@ * Using the SSLContext created during object creation, * create/configure the SSLEngines we'll use for this test. */ - private void createSSLEngines() throws Exception { + private void createSSLEngines(String[] protocols) throws Exception { // Initialize the KeyManager and TrustManager for the server KeyManagerFactory servKmf = KeyManagerFactory.getInstance("PKIX"); servKmf.init(serverKeystore, passwd.toCharArray()); @@ -321,6 +333,7 @@ * handshake. */ serverEngine = servCtx.createSSLEngine(); + serverEngine.setEnabledProtocols(protocols); serverEngine.setUseClientMode(false); serverEngine.setNeedClientAuth(false); @@ -328,6 +341,7 @@ * Similar to above, but using client mode instead. */ clientEngine = cliCtx.createSSLEngine("client", 80); + clientEngine.setEnabledProtocols(protocols); clientEngine.setUseClientMode(true); } @@ -637,8 +651,8 @@ /** * Checks a validation failure to see if it failed for the reason we think * it should. This comes in as an SSLException of some sort, but it - * encapsulates a ValidatorException which in turn encapsulates the - * CertPathValidatorException we are interested in. + * encapsulates a CertPathValidatorException at some point in the + * exception stack. * * @param e the exception thrown at the top level * @param reason the underlying CertPathValidatorException BasicReason @@ -650,22 +664,31 @@ CertPathValidatorException.BasicReason reason) { boolean result = false; - if (e instanceof SSLException) { - Throwable sslhe = e.getCause(); - if (sslhe instanceof SSLHandshakeException) { - Throwable valExc = sslhe.getCause(); - if (valExc instanceof sun.security.validator.ValidatorException) { - Throwable cause = valExc.getCause(); - if (cause instanceof CertPathValidatorException) { - CertPathValidatorException cpve = - (CertPathValidatorException)cause; - if (cpve.getReason() == reason) { - result = true; - } - } - } + // Locate the CertPathValidatorException. If one + // Does not exist, then it's an automatic failure of + // the test. + Throwable curExc = e; + CertPathValidatorException cpve = null; + while (curExc != null) { + if (curExc instanceof CertPathValidatorException) { + cpve = (CertPathValidatorException)curExc; } + curExc = curExc.getCause(); } + + // If we get through the loop and cpve is null then we + // we didn't find CPVE and this is a failure + if (cpve != null) { + if (cpve.getReason() == reason) { + result = true; + } else { + System.out.println("CPVE Reason Mismatch: Expected = " + + reason + ", Actual = " + cpve.getReason()); + } + } else { + System.out.println("Failed to find an expected CPVE"); + } + return result; } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java --- a/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/Stapling/SSLSocketWithStapling.java Mon Jun 25 13:41:39 2018 -0700 @@ -35,6 +35,10 @@ import java.io.*; import java.math.BigInteger; +import java.net.InetAddress; +import java.net.Socket; +import java.net.ServerSocket; +import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import javax.net.ssl.*; @@ -71,7 +75,7 @@ */ // Turn on TLS debugging - static boolean debug = false; + static final boolean debug = false; /* * Should we run the client or server in a separate thread? @@ -106,6 +110,11 @@ static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder static int intOcspPort; // Port number for intermed. OCSP + // Extra configuration parameters and constants + static final String[] TLS13ONLY = new String[] { "TLSv1.3" }; + static final String[] TLS12MAX = + new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }; + /* * If the client or server is doing some kind of object creation * that the other side depends on, and that thread prematurely @@ -116,20 +125,31 @@ */ public static void main(String[] args) throws Exception { if (debug) { - System.setProperty("javax.net.debug", "ssl"); + System.setProperty("javax.net.debug", "ssl:handshake"); } try { // Create the PKI we will use for the test and start the OCSP servers createPKI(); - testAllDefault(); - testPKIXParametersRevEnabled(); - testRevokedCertificate(); - testHardFailFallback(); - testSoftFailFallback(); - testLatencyNoStaple(false); - testLatencyNoStaple(true); + testAllDefault(false); + testAllDefault(true); + testPKIXParametersRevEnabled(false); + testPKIXParametersRevEnabled(true); + testRevokedCertificate(false); + testRevokedCertificate(true); + testRevokedIntermediate(false); + testRevokedIntermediate(true); + testMissingIntermediate(false); + testMissingIntermediate(true); + testHardFailFallback(false); + testHardFailFallback(true); + testSoftFailFallback(false); + testSoftFailFallback(true); + testLatencyNoStaple(false, false); + testLatencyNoStaple(false, true); + testLatencyNoStaple(true, false); + testLatencyNoStaple(true, true); } finally { // shut down the OCSP responders before finishing the test intOcsp.stop(); @@ -140,9 +160,16 @@ /** * Default test using no externally-configured PKIXBuilderParameters */ - static void testAllDefault() throws Exception { + static void testAllDefault(boolean isTls13) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = new HashMap<>(); @@ -188,9 +215,16 @@ * enabled and client-side OCSP disabled. It will only pass if all * stapled responses are present, valid and have a GOOD status. */ - static void testPKIXParametersRevEnabled() throws Exception { + static void testPKIXParametersRevEnabled(boolean isTls13) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; System.out.println("====================================="); @@ -222,9 +256,16 @@ * pass if the OCSP response is found, since we will check the * CertPathValidatorException reason for revoked status. */ - static void testRevokedCertificate() throws Exception { + static void testRevokedCertificate(boolean isTls13) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = new HashMap<>(); @@ -242,9 +283,9 @@ fiveMinsAgo)); intOcsp.updateStatusDb(revInfo); - System.out.println("======================================="); - System.out.println("Stapling enabled, default configuration"); - System.out.println("======================================="); + System.out.println("============================================"); + System.out.println("Stapling enabled, detect revoked certificate"); + System.out.println("============================================"); cliParams.pkixParams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); @@ -274,13 +315,146 @@ } /** + * Perform a test where the intermediate CA certificate is revoked and + * placed in the TLS handshake. Client-side OCSP is disabled, so this + * test will only pass if the OCSP response for the intermediate CA is + * found and placed into the CertificateStatus or Certificate message + * (depending on the protocol version) since we will check + * the CertPathValidatorException reason for revoked status. + */ + static void testRevokedIntermediate(boolean isTls13) throws Exception { + ClientParameters cliParams = new ClientParameters(); + ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } + serverReady = false; + Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = + new HashMap<>(); + + // We will prove revocation checking is disabled by marking the SSL + // certificate as revoked. The test would only pass if revocation + // checking did not happen. + X509Certificate intCACert = + (X509Certificate)intKeystore.getCertificate(INT_ALIAS); + Date fiveMinsAgo = new Date(System.currentTimeMillis() - + TimeUnit.MINUTES.toMillis(5)); + revInfo.put(intCACert.getSerialNumber(), + new SimpleOCSPServer.CertStatusInfo( + SimpleOCSPServer.CertStatus.CERT_STATUS_REVOKED, + fiveMinsAgo)); + rootOcsp.updateStatusDb(revInfo); + + System.out.println("==============================================="); + System.out.println("Stapling enabled, detect revoked CA certificate"); + System.out.println("==============================================="); + + cliParams.pkixParams = new PKIXBuilderParameters(trustStore, + new X509CertSelector()); + cliParams.pkixParams.setRevocationEnabled(true); + Security.setProperty("ocsp.enable", "false"); + + SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, + servParams); + TestResult tr = sslTest.getResult(); + if (!checkClientValidationFailure(tr.clientExc, BasicReason.REVOKED)) { + if (tr.clientExc != null) { + throw tr.clientExc; + } else { + throw new RuntimeException( + "Expected client failure, but the client succeeded"); + } + } + + // Return the ssl certificate to non-revoked status + revInfo.put(intCACert.getSerialNumber(), + new SimpleOCSPServer.CertStatusInfo( + SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); + rootOcsp.updateStatusDb(revInfo); + + System.out.println(" PASS"); + System.out.println("=======================================\n"); + } + + /** + * Test a case where OCSP stapling is attempted, but partially occurs + * because the root OCSP responder is unreachable. This should use a + * default hard-fail behavior. + */ + static void testMissingIntermediate(boolean isTls13) throws Exception { + ClientParameters cliParams = new ClientParameters(); + ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } + serverReady = false; + + // Make the OCSP responder reject connections + rootOcsp.rejectConnections(); + + System.out.println("======================================="); + System.out.println("Stapling enbled in client and server,"); + System.out.println("but root OCSP responder disabled."); + System.out.println("PKIXParameters with Revocation checking"); + System.out.println("enabled."); + System.out.println("======================================="); + + Security.setProperty("ocsp.enable", "false"); + cliParams.pkixParams = new PKIXBuilderParameters(trustStore, + new X509CertSelector()); + cliParams.pkixParams.setRevocationEnabled(true); + + SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams, + servParams); + TestResult tr = sslTest.getResult(); + if (!checkClientValidationFailure(tr.clientExc, + BasicReason.UNDETERMINED_REVOCATION_STATUS)) { + if (tr.clientExc != null) { + throw tr.clientExc; + } else { + throw new RuntimeException( + "Expected client failure, but the client succeeded"); + } + } + + System.out.println(" PASS"); + System.out.println("=======================================\n"); + + // Make root OCSP responder accept connections + rootOcsp.acceptConnections(); + + // Wait 5 seconds for server ready + for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { + Thread.sleep(50); + } + if (!rootOcsp.isServerReady()) { + throw new RuntimeException("Root OCSP responder not ready yet"); + } + } + + /** * Test a case where client-side stapling is attempted, but does not * occur because OCSP responders are unreachable. This should use a * default hard-fail behavior. */ - static void testHardFailFallback() throws Exception { + static void testHardFailFallback(boolean isTls13) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; // make OCSP responders reject connections @@ -320,7 +494,8 @@ rootOcsp.acceptConnections(); // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { + for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || + !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { @@ -333,9 +508,16 @@ * occur because OCSP responders are unreachable. Client-side OCSP * checking is enabled for this, with SOFT_FAIL. */ - static void testSoftFailFallback() throws Exception { + static void testSoftFailFallback(boolean isTls13) throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; // make OCSP responders reject connections @@ -381,7 +563,8 @@ rootOcsp.acceptConnections(); // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { + for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || + !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { @@ -400,9 +583,17 @@ * will change the result from the client failing with CPVE (no fallback) * to a pass (fallback active). */ - static void testLatencyNoStaple(Boolean fallback) throws Exception { + static void testLatencyNoStaple(Boolean fallback, boolean isTls13) + throws Exception { ClientParameters cliParams = new ClientParameters(); ServerParameters servParams = new ServerParameters(); + if (isTls13) { + cliParams.protocols = TLS13ONLY; + servParams.protocols = TLS13ONLY; + } else { + cliParams.protocols = TLS12MAX; + servParams.protocols = TLS12MAX; + } serverReady = false; // Give a 1 second delay before running the test. @@ -411,7 +602,8 @@ Thread.sleep(1000); // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { + for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || + !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { @@ -462,7 +654,8 @@ Thread.sleep(1000); // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || !rootOcsp.isServerReady())); i++) { + for (int i = 0; (i < 100 && (!intOcsp.isServerReady() || + !rootOcsp.isServerReady())); i++) { Thread.sleep(50); } if (!intOcsp.isServerReady() || !rootOcsp.isServerReady()) { @@ -504,7 +697,8 @@ SSLContext sslc = SSLContext.getInstance("TLS"); sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - SSLServerSocketFactory sslssf = sslc.getServerSocketFactory(); + SSLServerSocketFactory sslssf = new CustomizedServerSocketFactory(sslc, + servParams.protocols, servParams.ciphers); try (SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(serverPort)) { @@ -570,7 +764,8 @@ SSLContext sslc = SSLContext.getInstance("TLS"); sslc.init(null, tmf.getTrustManagers(), null); - SSLSocketFactory sslsf = sslc.getSocketFactory(); + SSLSocketFactory sslsf = new CustomizedSocketFactory(sslc, + cliParams.protocols, cliParams.ciphers); try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost", serverPort); InputStream sslIS = sslSocket.getInputStream(); @@ -927,6 +1122,8 @@ boolean enabled = true; PKIXBuilderParameters pkixParams = null; PKIXRevocationChecker revChecker = null; + String[] protocols = null; + String[] ciphers = null; ClientParameters() { } } @@ -939,10 +1136,161 @@ String respUri = ""; boolean respOverride = false; boolean ignoreExts = false; + String[] protocols = null; + String[] ciphers = null; ServerParameters() { } } + static class CustomizedSocketFactory extends SSLSocketFactory { + final SSLContext sslc; + final String[] protocols; + final String[] cipherSuites; + + CustomizedSocketFactory(SSLContext ctx, String[] prots, String[] suites) + throws GeneralSecurityException { + super(); + sslc = (ctx != null) ? ctx : SSLContext.getDefault(); + protocols = prots; + cipherSuites = suites; + + // Create the Trust Manager Factory using the PKIX variant + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + } + + @Override + public Socket createSocket(Socket s, String host, int port, + boolean autoClose) throws IOException { + Socket sock = sslc.getSocketFactory().createSocket(s, host, port, + autoClose); + customizeSocket(sock); + return sock; + } + + @Override + public Socket createSocket(InetAddress host, int port) + throws IOException { + Socket sock = sslc.getSocketFactory().createSocket(host, port); + customizeSocket(sock); + return sock; + } + + @Override + public Socket createSocket(InetAddress host, int port, + InetAddress localAddress, int localPort) throws IOException { + Socket sock = sslc.getSocketFactory().createSocket(host, port, + localAddress, localPort); + customizeSocket(sock); + return sock; + } + + @Override + public Socket createSocket(String host, int port) + throws IOException { + Socket sock = sslc.getSocketFactory().createSocket(host, port); + customizeSocket(sock); + return sock; + } + + @Override + public Socket createSocket(String host, int port, + InetAddress localAddress, int localPort) + throws IOException { + Socket sock = sslc.getSocketFactory().createSocket(host, port, + localAddress, localPort); + customizeSocket(sock); + return sock; + } + + @Override + public String[] getDefaultCipherSuites() { + return sslc.getDefaultSSLParameters().getCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return sslc.getSupportedSSLParameters().getCipherSuites(); + } + + private void customizeSocket(Socket sock) { + if (sock instanceof SSLSocket) { + if (protocols != null) { + ((SSLSocket)sock).setEnabledProtocols(protocols); + } + if (cipherSuites != null) { + ((SSLSocket)sock).setEnabledCipherSuites(cipherSuites); + } + } + } + } + + static class CustomizedServerSocketFactory extends SSLServerSocketFactory { + final SSLContext sslc; + final String[] protocols; + final String[] cipherSuites; + + CustomizedServerSocketFactory(SSLContext ctx, String[] prots, String[] suites) + throws GeneralSecurityException { + super(); + sslc = (ctx != null) ? ctx : SSLContext.getDefault(); + protocols = prots; + cipherSuites = suites; + + // Create the Trust Manager Factory using the PKIX variant + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException { + ServerSocket sock = + sslc.getServerSocketFactory().createServerSocket(port); + customizeSocket(sock); + return sock; + } + + @Override + public ServerSocket createServerSocket(int port, int backlog) + throws IOException { + ServerSocket sock = + sslc.getServerSocketFactory().createServerSocket(port, + backlog); + customizeSocket(sock); + return sock; + } + + @Override + public ServerSocket createServerSocket(int port, int backlog, + InetAddress ifAddress) throws IOException { + ServerSocket sock = + sslc.getServerSocketFactory().createServerSocket(port, + backlog, ifAddress); + customizeSocket(sock); + return sock; + } + + @Override + public String[] getDefaultCipherSuites() { + return sslc.getDefaultSSLParameters().getCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return sslc.getSupportedSSLParameters().getCipherSuites(); + } + + private void customizeSocket(ServerSocket sock) { + if (sock instanceof SSLServerSocket) { + if (protocols != null) { + ((SSLServerSocket)sock).setEnabledProtocols(protocols); + } + if (cipherSuites != null) { + ((SSLServerSocket)sock).setEnabledCipherSuites(cipherSuites); + } + } + } + } + + static class TestResult { Exception serverExc = null; Exception clientExc = null; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java --- a/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/Stapling/StapleEnableProps.java Mon Jun 25 13:41:39 2018 -0700 @@ -70,11 +70,16 @@ */ private static final boolean debug = false; - // These two ByteBuffer references will be used to hang onto ClientHello + // These four ByteBuffer references will be used to hang onto ClientHello // messages with and without the status_request[_v2] extensions. These - // will be used in the server-side stapling tests. - private static ByteBuffer cHelloStaple; - private static ByteBuffer cHelloNoStaple; + // will be used in the server-side stapling tests. There are two sets, + // one for 1.2 and earlier versions of the protocol and one for 1.3 + // and later versions, since the handshake and extension sets differ + // between the two sets. + private static ByteBuffer cHello12Staple; + private static ByteBuffer cHello12NoStaple; + private static ByteBuffer cHello13Staple; + private static ByteBuffer cHello13NoStaple; // The following items are used to set up the keystores. private static final String passwd = "passphrase"; @@ -94,6 +99,11 @@ private static SimpleOCSPServer intOcsp; // Intermediate CA OCSP server private static int intOcspPort; // Port for intermediate OCSP + // Extra configuration parameters and constants + static final String[] TLS13ONLY = new String[] { "TLSv1.3" }; + static final String[] TLS12MAX = + new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }; + // A few helpful TLS definitions to make it easier private static final int HELLO_EXT_STATUS_REQ = 5; private static final int HELLO_EXT_STATUS_REQ_V2 = 17; @@ -103,7 +113,7 @@ */ public static void main(String args[]) throws Exception { if (debug) { - System.setProperty("javax.net.debug", "ssl"); + System.setProperty("javax.net.debug", "ssl:handshake,verbose"); } // Create the PKI we will use for the test and start the OCSP servers @@ -128,6 +138,7 @@ System.out.println("========================================="); System.out.println("Client Test 1: " + "jdk.tls.client.enableStatusRequestExtension = true"); + System.out.println("Version = TLS 1.2"); System.out.println("========================================="); System.setProperty("jdk.tls.client.enableStatusRequestExtension", @@ -136,6 +147,7 @@ ctxStaple.init(null, tmf.getTrustManagers(), null); SSLEngine engine = ctxStaple.createSSLEngine(); engine.setUseClientMode(true); + engine.setEnabledProtocols(TLS12MAX); SSLSession session = engine.getSession(); ByteBuffer clientOut = ByteBuffer.wrap("I'm a Client".getBytes()); ByteBuffer cTOs = @@ -151,12 +163,13 @@ cTOs.flip(); System.out.println(dumpHexBytes(cTOs)); checkClientHello(cTOs, true, true); - cHelloStaple = cTOs; + cHello12Staple = cTOs; // Test with the property set to false System.out.println("========================================="); System.out.println("Client Test 2: " + "jdk.tls.client.enableStatusRequestExtension = false"); + System.out.println("Version = TLS 1.2"); System.out.println("========================================="); System.setProperty("jdk.tls.client.enableStatusRequestExtension", @@ -165,6 +178,7 @@ ctxNoStaple.init(null, tmf.getTrustManagers(), null); engine = ctxNoStaple.createSSLEngine(); engine.setUseClientMode(true); + engine.setEnabledProtocols(TLS12MAX); session = engine.getSession(); cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize()); @@ -178,7 +192,95 @@ cTOs.flip(); System.out.println(dumpHexBytes(cTOs)); checkClientHello(cTOs, false, false); - cHelloNoStaple = cTOs; + cHello12NoStaple = cTOs; + + // Turn the property back on to true and test using TLS 1.3 + System.out.println("========================================="); + System.out.println("Client Test 3: " + + "jdk.tls.client.enableStatusRequestExtension = true"); + System.out.println("Version = TLS 1.3"); + System.out.println("========================================="); + + System.setProperty("jdk.tls.client.enableStatusRequestExtension", + "true"); + ctxStaple = SSLContext.getInstance("TLS"); + ctxStaple.init(null, tmf.getTrustManagers(), null); + engine = ctxStaple.createSSLEngine(); + engine.setUseClientMode(true); + engine.setEnabledProtocols(TLS13ONLY); + session = engine.getSession(); + cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize()); + + // Create and check the ClientHello message + clientResult = engine.wrap(clientOut, cTOs); + log("client wrap: ", clientResult); + if (clientResult.getStatus() != SSLEngineResult.Status.OK) { + throw new SSLException("Client wrap got status: " + + clientResult.getStatus()); + } + cTOs.flip(); + System.out.println(dumpHexBytes(cTOs)); + checkClientHello(cTOs, true, false); + cHello13Staple = cTOs; + + // Turn the property off again and test in a TLS 1.3 handshake + System.out.println("========================================="); + System.out.println("Client Test 4: " + + "jdk.tls.client.enableStatusRequestExtension = false"); + System.out.println("Version = TLS 1.3"); + System.out.println("========================================="); + + System.setProperty("jdk.tls.client.enableStatusRequestExtension", + "false"); + ctxNoStaple = SSLContext.getInstance("TLS"); + ctxNoStaple.init(null, tmf.getTrustManagers(), null); + engine = ctxNoStaple.createSSLEngine(); + engine.setUseClientMode(true); + engine.setEnabledProtocols(TLS13ONLY); + session = engine.getSession(); + cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize()); + + // Create and check the ClientHello message + clientResult = engine.wrap(clientOut, cTOs); + log("client wrap: ", clientResult); + if (clientResult.getStatus() != SSLEngineResult.Status.OK) { + throw new SSLException("Client wrap got status: " + + clientResult.getStatus()); + } + cTOs.flip(); + System.out.println(dumpHexBytes(cTOs)); + checkClientHello(cTOs, false, false); + cHello13NoStaple = cTOs; + + // A TLS 1.3-capable hello, one that is not strictly limited to + // the TLS 1.3 protocol should have both status_request and + // status_request_v2 + System.out.println("========================================="); + System.out.println("Client Test 5: " + + "jdk.tls.client.enableStatusRequestExtension = true"); + System.out.println("Version = TLS 1.3 capable [default hello]"); + System.out.println("========================================="); + + System.setProperty("jdk.tls.client.enableStatusRequestExtension", + "true"); + ctxStaple = SSLContext.getInstance("TLS"); + ctxStaple.init(null, tmf.getTrustManagers(), null); + engine = ctxStaple.createSSLEngine(); + engine.setUseClientMode(true); + // Note: Unlike the other tests, there is no explicit protocol setting + session = engine.getSession(); + cTOs = ByteBuffer.allocateDirect(session.getPacketBufferSize()); + + // Create and check the ClientHello message + clientResult = engine.wrap(clientOut, cTOs); + log("client wrap: ", clientResult); + if (clientResult.getStatus() != SSLEngineResult.Status.OK) { + throw new SSLException("Client wrap got status: " + + clientResult.getStatus()); + } + cTOs.flip(); + System.out.println(dumpHexBytes(cTOs)); + checkClientHello(cTOs, true, true); } private static void testServerProp() throws Exception { @@ -189,6 +291,7 @@ System.out.println("========================================="); System.out.println("Server Test 1: " + "jdk.tls.server.enableStatusRequestExtension = true"); + System.out.println("Version = TLS 1.2"); System.out.println("========================================="); System.setProperty("jdk.tls.server.enableStatusRequestExtension", @@ -197,6 +300,7 @@ ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLEngine engine = ctxStaple.createSSLEngine(); engine.setUseClientMode(false); + engine.setEnabledProtocols(TLS12MAX); SSLSession session = engine.getSession(); ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes()); ByteBuffer serverIn = @@ -205,7 +309,7 @@ ByteBuffer.allocateDirect(session.getPacketBufferSize()); // Consume the client hello - serverResult = engine.unwrap(cHelloStaple, serverIn); + serverResult = engine.unwrap(cHello12Staple, serverIn); log("server unwrap: ", serverResult); if (serverResult.getStatus() != SSLEngineResult.Status.OK) { throw new SSLException("Server unwrap got status: " + @@ -234,12 +338,13 @@ checkServerHello(sTOc, false, true); // Flip the client hello so we can reuse it in the next test. - cHelloStaple.flip(); + cHello12Staple.flip(); // Test with the server-side enable property set to false System.out.println("========================================="); System.out.println("Server Test 2: " + "jdk.tls.server.enableStatusRequestExtension = false"); + System.out.println("Version = TLS 1.2"); System.out.println("========================================="); System.setProperty("jdk.tls.server.enableStatusRequestExtension", @@ -248,12 +353,13 @@ ctxNoStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); engine = ctxNoStaple.createSSLEngine(); engine.setUseClientMode(false); + engine.setEnabledProtocols(TLS12MAX); session = engine.getSession(); serverIn = ByteBuffer.allocate(session.getApplicationBufferSize() + 50); sTOc = ByteBuffer.allocateDirect(session.getPacketBufferSize()); // Consume the client hello - serverResult = engine.unwrap(cHelloStaple, serverIn); + serverResult = engine.unwrap(cHello12Staple, serverIn); log("server unwrap: ", serverResult); if (serverResult.getStatus() != SSLEngineResult.Status.OK) { throw new SSLException("Server unwrap got status: " + diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/TLSCommon/CipherSuite.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/javax/net/ssl/TLSCommon/CipherSuite.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,238 @@ +/* + * 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. + * + * 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. + */ + +public enum CipherSuite { + + TLS_AES_256_GCM_SHA384( + 0x1302, Protocol.TLSV1_3, Protocol.TLSV1_3), + TLS_AES_128_GCM_SHA256( + 0x1301, Protocol.TLSV1_3, Protocol.TLSV1_3), + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256( + 0xCCAA, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256( + 0xCCA9, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256( + 0xCCA8, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384( + 0xC032, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256( + 0xC031, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384( + 0xC030, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256( + 0xC02F, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384( + 0xC02E, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256( + 0xC02D, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384( + 0xC02C, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256( + 0xC02B, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384( + 0xC02A, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256( + 0xC029, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384( + 0xC028, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256( + 0xC027, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384( + 0xC026, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA( + 0xC025, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256( + 0xC025, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384( + 0xC024, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256( + 0xC023, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_anon_WITH_AES_256_CBC_SHA( + 0xC019, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_anon_WITH_AES_128_CBC_SHA( + 0xC018, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA( + 0xC017, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_anon_WITH_RC4_128_SHA( + 0xC016, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_anon_WITH_NULL_SHA( + 0xC015, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA( + 0xC014, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA( + 0xC013, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA( + 0xC012, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_RC4_128_SHA( + 0xC011, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_RSA_WITH_NULL_SHA( + 0xC010, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA( + 0xC00F, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA( + 0xC00E, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA( + 0xC00D, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_RC4_128_SHA( + 0xC00C, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_RSA_WITH_NULL_SHA( + 0xC00B, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA( + 0xC00A, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA( + 0xC009, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA( + 0xC008, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA( + 0xC007, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDHE_ECDSA_WITH_NULL_SHA( + 0xC006, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA( + 0xC003, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_RC4_128_SHA( + 0xC002, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_NULL_SHA( + 0xC001, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_EMPTY_RENEGOTIATION_INFO_SCSV( + 0x00FF, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_256_GCM_SHA384( + 0x00A7, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_128_GCM_SHA256( + 0x00A6, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384( + 0x00A3, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256( + 0x00A2, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384( + 0x009F, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256( + 0x009E, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_256_GCM_SHA384( + 0x009D, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_128_GCM_SHA256( + 0x009C, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_256_CBC_SHA256( + 0x006D, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_128_CBC_SHA256( + 0x006C, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256( + 0x006B, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256( + 0x006A, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256( + 0x0067, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA( + 0x004C, Protocol.TLSV1, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256( + 0x0040, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_256_CBC_SHA256( + 0x003D, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_128_CBC_SHA256( + 0x003C, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_NULL_SHA256( + 0x003B, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_256_CBC_SHA( + 0x003A, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_256_CBC_SHA( + 0x0039, Protocol.TLSV1, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_256_CBC_SHA( + 0x0038, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_256_CBC_SHA( + 0x0035, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_DH_anon_WITH_AES_128_CBC_SHA( + 0x0034, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_DHE_RSA_WITH_AES_128_CBC_SHA( + 0x0033, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_DHE_DSS_WITH_AES_128_CBC_SHA( + 0x0032, Protocol.TLSV1_2, Protocol.TLSV1_2), + TLS_RSA_WITH_AES_128_CBC_SHA( + 0x002F, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_KRB5_WITH_3DES_EDE_CBC_MD5( + 0x0023, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_KRB5_WITH_DES_CBC_MD5( + 0x0022, Protocol.SSLV3, Protocol.TLSV1_1), + TLS_KRB5_WITH_3DES_EDE_CBC_SHA( + 0x001F, Protocol.SSLV3, Protocol.TLSV1_2), + TLS_KRB5_WITH_DES_CBC_SHA( + 0x001E, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_DH_anon_WITH_3DES_EDE_CBC_SHA( + 0x001B, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_DH_anon_WITH_DES_CBC_SHA( + 0x001A, Protocol.SSLV3, Protocol.TLSV1_1), + SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA( + 0x0019, Protocol.SSLV3, Protocol.TLSV1), + SSL_DH_anon_WITH_RC4_128_MD5( + 0x0018, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_DH_anon_EXPORT_WITH_RC4_40_MD5( + 0x0017, Protocol.SSLV3, Protocol.TLSV1), + SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA( + 0x0016, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_DHE_RSA_WITH_DES_CBC_SHA( + 0x0015, Protocol.SSLV3, Protocol.TLSV1_1), + SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA( + 0x0014, Protocol.SSLV3, Protocol.TLSV1), + SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA( + 0x0013, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_DHE_DSS_WITH_DES_CBC_SHA( + 0x0012, Protocol.SSLV3, Protocol.TLSV1_1), + SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA( + 0x0011, Protocol.SSLV3, Protocol.TLSV1), + SSL_RSA_WITH_3DES_EDE_CBC_SHA( + 0x000A, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_RSA_WITH_DES_CBC_SHA( + 0x0009, Protocol.SSLV3, Protocol.TLSV1_1), + SSL_RSA_EXPORT_WITH_DES40_CBC_SHA( + 0x0008, Protocol.SSLV3, Protocol.TLSV1), + SSL_RSA_WITH_RC4_128_SHA( + 0x0005, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_RSA_WITH_RC4_128_MD5( + 0x0004, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_RSA_EXPORT_WITH_RC4_40_MD5( + 0x0003, Protocol.SSLV3, Protocol.TLSV1), + SSL_RSA_WITH_NULL_SHA( + 0x0002, Protocol.SSLV3, Protocol.TLSV1_2), + SSL_RSA_WITH_NULL_MD5( + 0x0001, Protocol.SSLV3, Protocol.TLSV1_2); + + public final int id; + public final Protocol startProtocol; + public final Protocol endProtocol; + + private CipherSuite( + int id, + Protocol startProtocol, + Protocol endProtocol) { + this.id = id; + this.startProtocol = startProtocol; + this.endProtocol = endProtocol; + } + + public boolean supportedByProtocol(Protocol protocol) { + return startProtocol.id <= protocol.id + && protocol.id <= endProtocol.id; + } + + public static CipherSuite cipherSuite(String name) { + return CipherSuite.valueOf(CipherSuite.class, name); + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/TLSCommon/Protocol.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/javax/net/ssl/TLSCommon/Protocol.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,58 @@ +/* + * 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. + * + * 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. + */ + +public enum Protocol { + + SSLV2HELLO(0x0002, "SSLv2Hello"), + SSLV3 (0x0300, "SSLv3"), + TLSV1 (0x0301, "TLSv1"), + TLSV1_1 (0x0302, "TLSv1.1"), + TLSV1_2 (0x0303, "TLSv1.2"), + TLSV1_3 (0x0304, "TLSv1.3"), + + DTLS1_3 (0xFEFC, "DTLSv1.3"), + DTLS1_2 (0xFEFD, "DTLSv1.2"), + DTLS1_0 (0xFEFF, "DTLSv1.0"); + + public final int id; + public final String name; + + private Protocol(int id, String name) { + this.id = id; + this.name = name; + } + + public String toString() { + return name; + } + + public static Protocol protocol(String name) { + for (Protocol protocol : values()) { + if (protocol.name.equals(name)) { + return protocol; + } + } + + return null; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java --- a/test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/TLSCommon/SSLEngineTestCase.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -85,6 +85,12 @@ SSLEngineTestCase.ENABLED_NON_KRB_NOT_ANON_CIPHERS, "Enabled by default non kerberos not anonymous"), /** + * Ciphers supported by TLS 1.3 only. + */ + TLS13_CIPHERS( + SSLEngineTestCase.TLS13_CIPHERS, + "Supported by TLS 1.3 only"), + /** * Ciphers unsupported by the tested SSLEngine. */ UNSUPPORTED_CIPHERS(SSLEngineTestCase.UNSUPPORTED_CIPHERS, @@ -174,6 +180,11 @@ private static final String SERVER_NAME = "service.localhost"; private static final String SNI_PATTERN = ".*"; + private static final String[] TLS13_CIPHERS = { + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_GCM_SHA256" + }; + private static final String[] SUPPORTED_NON_KRB_CIPHERS; static { @@ -183,8 +194,8 @@ List<String> supportedCiphersList = new LinkedList<>(); for (String cipher : allSupportedCiphers) { if (!cipher.contains("KRB5") - && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { - + && !isTLS13Cipher(cipher) + && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { supportedCiphersList.add(cipher); } } @@ -204,6 +215,7 @@ List<String> supportedCiphersList = new LinkedList<>(); for (String cipher : allSupportedCiphers) { if (!cipher.contains("KRB5") + && !isTLS13Cipher(cipher) && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV") && !cipher.endsWith("_SHA256") && !cipher.endsWith("_SHA384")) { @@ -226,7 +238,8 @@ List<String> supportedCiphersList = new LinkedList<>(); for (String cipher : allSupportedCiphers) { if (cipher.contains("KRB5") - && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + && !isTLS13Cipher(cipher) + && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { supportedCiphersList.add(cipher); } } @@ -246,7 +259,8 @@ List<String> enabledCiphersList = new LinkedList<>(); for (String cipher : enabledCiphers) { if (!cipher.contains("anon") && !cipher.contains("KRB5") - && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { + && !isTLS13Cipher(cipher) + && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) { enabledCiphersList.add(cipher); } } @@ -303,6 +317,16 @@ this.maxPacketSize = 0; } + private static boolean isTLS13Cipher(String cipher) { + for (String cipherSuite : TLS13_CIPHERS) { + if (cipherSuite.equals(cipher)) { + return true; + } + } + + return false; + } + /** * Wraps data with the specified engine. * @@ -392,7 +416,7 @@ int length = net.remaining(); System.out.println(wrapper + " wrapped " + length + " bytes."); System.out.println(wrapper + " handshake status is " - + engine.getHandshakeStatus()); + + engine.getHandshakeStatus() + " Result is " + r.getStatus()); if (maxPacketSize < length && maxPacketSize != 0) { throw new AssertionError("Handshake wrapped net buffer length " + length + " exceeds maximum packet size " @@ -480,7 +504,7 @@ SSLEngineResult r = engine.unwrap(net, app); app.flip(); System.out.println(unwrapper + " handshake status is " - + engine.getHandshakeStatus()); + + engine.getHandshakeStatus() + " Result is " + r.getStatus()); checkResult(r, wantedStatus); if (result != null && result.length > 0) { result[0] = r; @@ -713,8 +737,13 @@ case "TLSv1.1": runTests(Ciphers.SUPPORTED_NON_KRB_NON_SHA_CIPHERS); break; - default: + case "DTLSv1.1": + case "TLSv1.2": runTests(Ciphers.SUPPORTED_NON_KRB_CIPHERS); + break; + case "TLSv1.3": + runTests(Ciphers.TLS13_CIPHERS); + break; } break; case "krb": diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java --- a/test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/TLSv12/DisabledShortDSAKeys.java Mon Jun 25 13:41:39 2018 -0700 @@ -289,8 +289,14 @@ volatile Exception clientException = null; public static void main(String[] args) throws Exception { - if (debug) + Security.setProperty("jdk.certpath.disabledAlgorithms", + "DSA keySize < 1024"); + Security.setProperty("jdk.tls.disabledAlgorithms", + "DSA keySize < 1024"); + + if (debug) { System.setProperty("javax.net.debug", "all"); + } /* * Get the customized arguments. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java --- a/test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/TLSv12/DisabledShortRSAKeys.java Mon Jun 25 13:41:39 2018 -0700 @@ -238,8 +238,14 @@ } public static void main(String[] args) throws Exception { - if (debug) + Security.setProperty("jdk.certpath.disabledAlgorithms", + "RSA keySize < 1024"); + Security.setProperty("jdk.tls.disabledAlgorithms", + "RSA keySize < 1024"); + + if (debug) { System.setProperty("javax.net.debug", "all"); + } /* * Get the customized arguments. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java --- a/test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/ciphersuites/ECCurvesconstraints.java Mon Jun 25 13:41:39 2018 -0700 @@ -40,6 +40,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; +import java.io.IOException; import java.security.KeyStore; import java.security.KeyFactory; import java.security.cert.Certificate; @@ -49,7 +50,6 @@ import java.util.Base64; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; @@ -141,7 +141,7 @@ sslOS.flush(); throw new Exception("EC curve secp224k1 should be disabled"); - } catch (SSLHandshakeException she) { + } catch (IOException she) { // expected exception: no cipher suites in common System.out.println("Expected exception: " + she); } finally { @@ -183,7 +183,7 @@ sslIS.read(); throw new Exception("EC curve secp224k1 should be disabled"); - } catch (SSLHandshakeException she) { + } catch (IOException she) { // expected exception: Received fatal alert System.out.println("Expected exception: " + she); } finally { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/etc/README --- a/test/jdk/javax/net/ssl/etc/README Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/etc/README Mon Jun 25 13:41:39 2018 -0700 @@ -80,11 +80,34 @@ -dname "CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US" \ -validity 3652 -keypass passphrase -keystore keystore -storepass passphrase +Alias name: dummyecrsa +-------------------- +Creation date: Apr 13, 2018 +Entry type: PrivateKeyEntry +Certificate chain length: 2 +Certificate[1]: +Owner: CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US +Issuer: CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US +Serial number: 6f2d1faa +Valid from: Fri Apr 13 16:20:55 CST 2018 until: Wed Apr 12 16:20:55 CST 2028 +Version: 3 + +This can be generated by using keytool command: +% keytool -genkeypair -alias dummyecrsa -keyalg EC -keysize 256 \ + -keypass passphrase -storepass passphrase -keystore keystore \ + -dname "CN=dummy.example.com, OU=Dummy, O=Dummy, L=Cupertino, ST=CA, C=US" +% keytool -certreq -alias dummyecrsa -storepass passphrase -keystore keystore \ + -file ecrsa.csr +% keytool -gencert -alias dummy -storepass passphrase -keystore keystore \ + -validity 3652 -infile ecrsa.csr -outfile ecrsa.cer +% keytool -importcert -alias dummyecrsa -storepass passphrase -keystore keystore \ + -file ecrsa.cer + truststore entries ================== -This key store contains only trusted certificate entries. The same -certificates are used in both keystore and truststore. +This key store contains only trusted certificate entries. The same +certificates, except dummyecrsa, are used in both keystore and truststore. unknown_keystore diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/etc/keystore Binary file test/jdk/javax/net/ssl/etc/keystore has changed diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/etc/truststore Binary file test/jdk/javax/net/ssl/etc/truststore has changed diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java --- a/test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/sanity/ciphersuites/CipherSuitesInOrder.java Mon Jun 25 13:41:39 2018 -0700 @@ -30,6 +30,7 @@ * @test * @bug 7174244 * @summary NPE in Krb5ProxyImpl.getServerKeys() + * @ignore the dependent implementation details are changed * @run main/othervm CipherSuitesInOrder */ diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/sanity/interop/CipherTest.java --- a/test/jdk/javax/net/ssl/sanity/interop/CipherTest.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/sanity/interop/CipherTest.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, 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 @@ -21,7 +21,6 @@ * questions. */ - import java.io.*; import java.net.*; import java.util.*; @@ -29,7 +28,6 @@ import java.security.*; import java.security.cert.*; -import java.security.cert.Certificate; import javax.net.ssl.*; @@ -103,11 +101,11 @@ public static class TestParameters { - String cipherSuite; - String protocol; + CipherSuite cipherSuite; + Protocol protocol; String clientAuth; - TestParameters(String cipherSuite, String protocol, + TestParameters(CipherSuite cipherSuite, Protocol protocol, String clientAuth) { this.cipherSuite = cipherSuite; this.protocol = protocol; @@ -115,7 +113,7 @@ } boolean isEnabled() { - return TLSCipherStatus.isEnabled(cipherSuite, protocol); + return cipherSuite.supportedByProtocol(protocol); } public String toString() { @@ -125,134 +123,6 @@ } return s; } - - static enum TLSCipherStatus { - // cipher suites supported since TLS 1.2 - CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - - CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - - CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), - - CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - - CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - - CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - - // cipher suites obsoleted since TLS 1.2 - CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_53("SSL_DH_anon_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_54("TLS_KRB5_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_55("TLS_KRB5_WITH_DES_CBC_MD5", 0x0000, 0x0303), - - // cipher suites obsoleted since TLS 1.1 - CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0000, 0x0302), - CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0000, 0x0302), - CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0000, 0x0302), - - // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always - CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0xFFFF, 0x0000); - - // the cipher suite name - final String cipherSuite; - - // supported since protocol version - final int supportedSince; - - // obsoleted since protocol version - final int obsoletedSince; - - TLSCipherStatus(String cipherSuite, - int supportedSince, int obsoletedSince) { - this.cipherSuite = cipherSuite; - this.supportedSince = supportedSince; - this.obsoletedSince = obsoletedSince; - } - - static boolean isEnabled(String cipherSuite, String protocol) { - int versionNumber = toVersionNumber(protocol); - - if (versionNumber < 0) { - return true; // unlikely to happen - } - - for (TLSCipherStatus status : TLSCipherStatus.values()) { - if (cipherSuite.equals(status.cipherSuite)) { - if ((versionNumber < status.supportedSince) || - (versionNumber >= status.obsoletedSince)) { - return false; - } - - return true; - } - } - - return true; - } - - private static int toVersionNumber(String protocol) { - int versionNumber = -1; - - switch (protocol) { - case "SSLv2Hello": - versionNumber = 0x0002; - break; - case "SSLv3": - versionNumber = 0x0300; - break; - case "TLSv1": - versionNumber = 0x0301; - break; - case "TLSv1.1": - versionNumber = 0x0302; - break; - case "TLSv1.2": - versionNumber = 0x0303; - break; - default: - // unlikely to happen - } - - return versionNumber; - } - } } private List<TestParameters> tests; @@ -269,11 +139,23 @@ String[] clientAuths = {null, "RSA", "DSA"}; tests = new ArrayList<TestParameters>( cipherSuites.length * protocols.length * clientAuths.length); - for (int i = 0; i < cipherSuites.length; i++) { - String cipherSuite = cipherSuites[i]; + for (int j = 0; j < protocols.length; j++) { + String protocol = protocols[j]; + if (protocol.equals(Protocol.SSLV2HELLO.name)) { + System.out.println("Skipping SSLv2Hello protocol"); + continue; + } - for (int j = 0; j < protocols.length; j++) { - String protocol = protocols[j]; + for (int i = 0; i < cipherSuites.length; i++) { + String cipherSuite = cipherSuites[i]; + + // skip kerberos cipher suites and TLS_EMPTY_RENEGOTIATION_INFO_SCSV + if (cipherSuite.startsWith("TLS_KRB5") || cipherSuite.equals( + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.name())) { + System.out.println("Skipping unsupported test for " + + cipherSuite + " of " + protocol); + continue; + } if (!peerFactory.isSupported(cipherSuite, protocol)) { continue; @@ -281,13 +163,16 @@ for (int k = 0; k < clientAuths.length; k++) { String clientAuth = clientAuths[k]; - if ((clientAuth != null) && - (cipherSuite.indexOf("DH_anon") != -1)) { - // no client with anonymous ciphersuites + // no client with anonymous cipher suites; + // TLS 1.3 doesn't support DSA + if ((clientAuth != null && cipherSuite.contains("DH_anon")) + || ("DSA".equals(clientAuth) && "TLSv1.3".equals(protocol))) { continue; } - tests.add(new TestParameters(cipherSuite, protocol, - clientAuth)); + tests.add(new TestParameters( + CipherSuite.cipherSuite(cipherSuite), + Protocol.protocol(protocol), + clientAuth)); } } } @@ -356,7 +241,7 @@ // no more tests break; } - if (params.isEnabled() == false) { + if (!params.isEnabled()) { System.out.println("Skipping disabled test " + params); continue; } @@ -422,7 +307,7 @@ } PATH = new File(System.getProperty("test.src", "."), relPath); CipherTest.peerFactory = peerFactory; - System.out.print( + System.out.println( "Initializing test '" + peerFactory.getName() + "'..."); secureRandom = new SecureRandom(); secureRandom.nextInt(); @@ -455,27 +340,12 @@ abstract Server newServer(CipherTest cipherTest) throws Exception; boolean isSupported(String cipherSuite, String protocol) { - // skip kerberos cipher suites - if (cipherSuite.startsWith("TLS_KRB5")) { - System.out.println("Skipping unsupported test for " + - cipherSuite + " of " + protocol); - return false; - } - - // skip SSLv2Hello protocol - if (protocol.equals("SSLv2Hello")) { - System.out.println("Skipping unsupported test for " + - cipherSuite + " of " + protocol); - return false; - } - // ignore exportable cipher suite for TLSv1.1 - if (protocol.equals("TLSv1.1")) { - if (cipherSuite.indexOf("_EXPORT_WITH") != -1) { + if (protocol.equals("TLSv1.1") + && (cipherSuite.indexOf("_EXPORT_WITH") != -1)) { System.out.println("Skipping obsoleted test for " + cipherSuite + " of " + protocol); return false; - } } // ignore obsoleted cipher suite for the specified protocol diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java --- a/test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/sanity/interop/ClientJSSEServerJSSE.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -26,6 +26,7 @@ * @bug 4496785 * @summary Verify that all ciphersuites work in all configurations * @author Andreas Sterbenz + * @library ../../TLSCommon * @run main/othervm/timeout=300 ClientJSSEServerJSSE */ import java.security.Security; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java --- a/test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/sanity/interop/JSSEClient.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, 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 @@ -21,16 +21,16 @@ * questions. */ - -import java.io.*; -import java.net.*; -import java.util.*; - -import java.security.*; -import java.security.cert.*; +import java.io.InputStream; +import java.io.OutputStream; import java.security.cert.Certificate; -import javax.net.ssl.*; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; class JSSEClient extends CipherTest.Client { @@ -47,12 +47,15 @@ SSLSocket socket = null; try { keyManager.setAuthType(params.clientAuth); - sslContext.init(new KeyManager[] {keyManager}, new TrustManager[] {cipherTest.trustManager}, cipherTest.secureRandom); + sslContext.init( + new KeyManager[] { keyManager }, + new TrustManager[] { CipherTest.trustManager }, + CipherTest.secureRandom); SSLSocketFactory factory = (SSLSocketFactory)sslContext.getSocketFactory(); - socket = (SSLSocket)factory.createSocket("127.0.0.1", cipherTest.serverPort); - socket.setSoTimeout(cipherTest.TIMEOUT); - socket.setEnabledCipherSuites(new String[] {params.cipherSuite}); - socket.setEnabledProtocols(new String[] {params.protocol}); + socket = (SSLSocket)factory.createSocket("127.0.0.1", CipherTest.serverPort); + socket.setSoTimeout(CipherTest.TIMEOUT); + socket.setEnabledCipherSuites(new String[] { params.cipherSuite.name() }); + socket.setEnabledProtocols(new String[] { params.protocol.name }); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); sendRequest(in, out); @@ -60,12 +63,14 @@ SSLSession session = socket.getSession(); session.invalidate(); String cipherSuite = session.getCipherSuite(); - if (params.cipherSuite.equals(cipherSuite) == false) { - throw new Exception("Negotiated ciphersuite mismatch: " + cipherSuite + " != " + params.cipherSuite); + if (!params.cipherSuite.name().equals(cipherSuite)) { + throw new Exception("Negotiated ciphersuite mismatch: " + + cipherSuite + " != " + params.cipherSuite); } String protocol = session.getProtocol(); - if (params.protocol.equals(protocol) == false) { - throw new Exception("Negotiated protocol mismatch: " + protocol + " != " + params.protocol); + if (!params.protocol.name.equals(protocol)) { + throw new Exception("Negotiated protocol mismatch: " + protocol + + " != " + params.protocol); } if (cipherSuite.indexOf("DH_anon") == -1) { session.getPeerCertificates(); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java --- a/test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/sanity/interop/JSSEServer.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2005, 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 @@ -21,17 +21,18 @@ * questions. */ - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; -import java.security.*; -import java.security.cert.*; -import java.security.cert.Certificate; - -import javax.net.ssl.*; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; class JSSEServer extends CipherTest.Server { @@ -40,23 +41,28 @@ JSSEServer(CipherTest cipherTest) throws Exception { super(cipherTest); SSLContext serverContext = SSLContext.getInstance("TLS"); - serverContext.init(new KeyManager[] {cipherTest.keyManager}, new TrustManager[] {cipherTest.trustManager}, cipherTest.secureRandom); + serverContext.init( + new KeyManager[] { CipherTest.keyManager }, + new TrustManager[] { CipherTest.trustManager }, + CipherTest.secureRandom); - SSLServerSocketFactory factory = (SSLServerSocketFactory)serverContext.getServerSocketFactory(); - serverSocket = (SSLServerSocket)factory.createServerSocket(cipherTest.serverPort); - cipherTest.serverPort = serverSocket.getLocalPort(); + SSLServerSocketFactory factory + = (SSLServerSocketFactory) serverContext.getServerSocketFactory(); + serverSocket + = (SSLServerSocket) factory.createServerSocket(CipherTest.serverPort); + CipherTest.serverPort = serverSocket.getLocalPort(); serverSocket.setEnabledCipherSuites(factory.getSupportedCipherSuites()); serverSocket.setWantClientAuth(true); } public void run() { - System.out.println("JSSE Server listening on port " + cipherTest.serverPort); + System.out.println("JSSE Server listening on port " + CipherTest.serverPort); Executor exec = Executors.newFixedThreadPool (cipherTest.THREADS, DaemonThreadFactory.INSTANCE); try { while (true) { final SSLSocket socket = (SSLSocket)serverSocket.accept(); - socket.setSoTimeout(cipherTest.TIMEOUT); + socket.setSoTimeout(CipherTest.TIMEOUT); Runnable r = new Runnable() { public void run() { try { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java --- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java Mon Jun 25 13:41:39 2018 -0700 @@ -245,6 +245,8 @@ log("\tClosing clientEngine's *OUTBOUND*..."); clientEngine.closeOutbound(); dataDone = true; + log("\tClosing serverEngine's *OUTBOUND*..."); + serverEngine.closeOutbound(); } } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java --- a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -133,6 +133,7 @@ protected SSLContext createClientSSLContext() throws Exception { return createSSLContext(trustedCertStrs, endEntityCertStrs, endEntityPrivateKeys, + endEntityPrivateKeyAlgs, endEntityPrivateKeyNames, getClientContextParameters()); } @@ -143,6 +144,7 @@ protected SSLContext createServerSSLContext() throws Exception { return createSSLContext(trustedCertStrs, endEntityCertStrs, endEntityPrivateKeys, + endEntityPrivateKeyAlgs, endEntityPrivateKeyNames, getServerContextParameters()); } @@ -357,173 +359,235 @@ private final static String[] trustedCertStrs = { // SHA256withECDSA, curve prime256v1 // Validity - // Not Before: Nov 25 04:19:51 2016 GMT - // Not After : Nov 5 04:19:51 2037 GMT + // Not Before: May 22 07:18:16 2018 GMT + // Not After : May 17 07:18:16 2038 GMT // Subject Key Identifier: - // CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C + // 60:CF:BD:73:FF:FA:1A:30:D2:A4:EC:D3:49:71:46:EF:1A:35:A0:86 "-----BEGIN CERTIFICATE-----\n" + - "MIICHDCCAcGgAwIBAgIJAJtKs6ZEcVjxMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" + - "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + - "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNzExMDUwNDE5NTFaMDsxCzAJBgNVBAYTAlVT\n" + - "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZTBZ\n" + - "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABKMO/AFDHZia65RaqMIBX7WBdtzFj8fz\n" + - "ggqMADLJhoszS6qfTUDYskETw3uHfB3KAOENsoKX446qFFPuVjxS1aejga0wgaow\n" + - "HQYDVR0OBBYEFMpI6ADBQr1Zm3nZtLTOP2gMyMQMMGsGA1UdIwRkMGKAFMpI6ADB\n" + - "Qr1Zm3nZtLTOP2gMyMQMoT+kPTA7MQswCQYDVQQGEwJVUzENMAsGA1UEChMESmF2\n" + - "YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2WCCQCbSrOmRHFY8TAPBgNV\n" + - "HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDAgNJADBGAiEA5cJ/\n" + - "jirBbXxzpZ6kdp/Zb/yrIBnr4jiPGJTLgRTb8s4CIQChUDfP1Zqg0qJVfqFNaL4V\n" + - "a0EAeJHXGZnvCGGqHzoxkg==\n" + + "MIIBvjCCAWOgAwIBAgIJAIvFG6GbTroCMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" + + "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + + "ZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMDsxCzAJBgNVBAYTAlVT\n" + + "MQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZjZTBZ\n" + + "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBz1WeVb6gM2mh85z3QlvaB/l11b5h0v\n" + + "LIzmkC3DKlVukZT+ltH2Eq1oEkpXuf7QmbM0ibrUgtjsWH3mULfmcWmjUDBOMB0G\n" + + "A1UdDgQWBBRgz71z//oaMNKk7NNJcUbvGjWghjAfBgNVHSMEGDAWgBRgz71z//oa\n" + + "MNKk7NNJcUbvGjWghjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQCG\n" + + "6wluh1r2/T6L31mZXRKf9JxeSf9pIzoLj+8xQeUChQIhAJ09wAi1kV8yePLh2FD9\n" + + "2YEHlSQUAbwwqCDEVB5KxaqP\n" + "-----END CERTIFICATE-----", + // -----BEGIN PRIVATE KEY----- + // MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/HcHdoLJCdq3haVd + // XZTSKP00YzM3xX97l98vGL/RI1KhRANCAAQc9VnlW+oDNpofOc90Jb2gf5ddW+Yd + // LyyM5pAtwypVbpGU/pbR9hKtaBJKV7n+0JmzNIm61ILY7Fh95lC35nFp + // -----END PRIVATE KEY----- // SHA256withRSA, 2048 bits // Validity - // Not Before: Nov 25 04:20:02 2016 GMT - // Not After : Nov 5 04:20:02 2037 GMT + // Not Before: May 22 07:18:16 2018 GMT + // Not After : May 17 07:18:16 2038 GMT // Subject Key Identifier: - // A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E + // 0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C "-----BEGIN CERTIFICATE-----\n" + - "MIIDpzCCAo+gAwIBAgIJAO586A+hYNXaMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" + - "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" + - "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNzExMDUwNDIwMDJaMDsxCzAJBgNVBAYT\n" + - "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + - "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMm3veDSU4zKXO0aAHos\n" + - "cFRXGLBTe+MUJXAtlkNyx7VKaMZNt5wrUuqzyi/r0LFUdRfNCzZf3X8s8HPHQVii\n" + - "29tK0y/yeTn4sJTATSmGaAysMJQpKQcfAQ79ItcEGQ721TFQZ3kOBdgp3t/yUYAP\n" + - "K2tFze/QbIw72LE52SBnPPPTzyimNw7Ai2MLl4eQlyMkTs7JS07zIiAO5QYbS8s+\n" + - "1NW0A3Y+d0B0q8wYEoHGq7QVjOKlSAksfO0tzi4l0Zu6Uf+J5kMAyZ4ZFgEJvGvw\n" + - "y/OKJ+soRFH/5cy1SL8B6AWD1y7WXugeeHTHRW1eOjTxqfG1rZqTVd2lfOMER8y1\n" + - "bXcCAwEAAaOBrTCBqjAdBgNVHQ4EFgQUotxVOORHfIvT4Mr6rTrISt0SoI4wawYD\n" + - "VR0jBGQwYoAUotxVOORHfIvT4Mr6rTrISt0SoI6hP6Q9MDsxCzAJBgNVBAYTAlVT\n" + - "MQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZjZYIJ\n" + - "AO586A+hYNXaMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3\n" + - "DQEBCwUAA4IBAQAtNZJSFeFU6Yid0WSCs2qLAVaTyHsUNSUPUFIIhFAKxdP4DFS0\n" + - "+aeOFwdqizAU3kReAYULsfwEBgO51lPBSpB+9coUNQwu7cc8Q5Xqw/llRB0PrINS\n" + - "pZl7PW6Ur2ExTBocnUT9A/nhm8iO4PFD/Ly11sf5OdZihhX69NJ2h3a3gcrLjIpO\n" + - "L/ewTOgSi5xs+AGGQa+huN3YVL7dh+/rCUvMZVSBX5PloxWS5MMJi0Ui6YjwCFGO\n" + - "J4G9m7pI+gJs/x1UG0AgplMF2UCFfiY1SAeE2nKAeOOXAXEwEjFy0ToVTmqXx7fg\n" + - "m9YjhuePxlBrd2DF/YW0pc8gmLnrtm4rKPLz\n" + + "MIIDSTCCAjGgAwIBAgIJAI4ZF3iy8zG+MA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" + + "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" + + "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMDsxCzAJBgNVBAYT\n" + + "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + + "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALpMcY7aWieXDEM1/YJf\n" + + "JW27b4nRIFZyEYhEloyGsKTuQiiQjc8cqRZFNXe2vwziDB4IyTEl0Hjl5QF6ZaQE\n" + + "huPzzwvQm1pv64KrRXrmj3FisQK8B5OWLty9xp6xDqsaMRoyObLK+oIb20T5fSlE\n" + + "evmo1vYjnh8CX0Yzx5Gr5ye6YSEHQvYOWEws8ad17OlyToR2KMeC8w4qo6rs59pW\n" + + "g7Mxn9vo22ImDzrtAbTbXbCias3xlE0Bp0h5luyf+5U4UgksoL9B9r2oP4GrLNEV\n" + + "oJk57t8lwaR0upiv3CnS8LcJELpegZub5ggqLY8ZPYFQPjlK6IzLOm6rXPgZiZ3m\n" + + "RL0CAwEAAaNQME4wHQYDVR0OBBYEFA3dk8n+S701t+iZeJD721o92xVMMB8GA1Ud\n" + + "IwQYMBaAFA3dk8n+S701t+iZeJD721o92xVMMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" + + "hvcNAQELBQADggEBAJTRC3rKUUhVH07/1+stUungSYgpM08dY4utJq0BDk36BbmO\n" + + "0AnLDMbkwFdHEoqF6hQIfpm7SQTmXk0Fss6Eejm8ynYr6+EXiRAsaXOGOBCzF918\n" + + "/RuKOzqABfgSU4UBKECLM5bMfQTL60qx+HdbdVIpnikHZOFfmjCDVxoHsGyXc1LW\n" + + "Jhkht8IGOgc4PMGvyzTtRFjz01kvrVQZ75aN2E0GQv6dCxaEY0i3ypSzjUWAKqDh\n" + + "3e2OLwUSvumcdaxyCdZAOUsN6pDBQ+8VRG7KxnlRlY1SMEk46QgQYLbPDe/+W/yH\n" + + "ca4PejicPeh+9xRAwoTpiE2gulfT7Lm+fVM7Ruc=\n" + "-----END CERTIFICATE-----", + // -----BEGIN PRIVATE KEY----- + // MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6THGO2lonlwxD + // Nf2CXyVtu2+J0SBWchGIRJaMhrCk7kIokI3PHKkWRTV3tr8M4gweCMkxJdB45eUB + // emWkBIbj888L0Jtab+uCq0V65o9xYrECvAeTli7cvcaesQ6rGjEaMjmyyvqCG9tE + // +X0pRHr5qNb2I54fAl9GM8eRq+cnumEhB0L2DlhMLPGndezpck6EdijHgvMOKqOq + // 7OfaVoOzMZ/b6NtiJg867QG0212womrN8ZRNAadIeZbsn/uVOFIJLKC/Qfa9qD+B + // qyzRFaCZOe7fJcGkdLqYr9wp0vC3CRC6XoGbm+YIKi2PGT2BUD45SuiMyzpuq1z4 + // GYmd5kS9AgMBAAECggEAFHSoU2MuWwJ+2jJnb5U66t2V1bAcuOE1g5zkWvG/G5z9 + // rq6Qo5kmB8f5ovdx6tw3MGUOklLwnRXBG3RxDJ1iokz3AvkY1clMNsDPlDsUrQKF + // JSO4QUBQTPSZhnsyfR8XHSU+qJ8Y+ohMfzpVv95BEoCzebtXdVgxVegBlcEmVHo2 + // kMmkRN+bYNsr8eb2r+b0EpyumS39ZgKYh09+cFb78y3T6IFMGcVJTP6nlGBFkmA/ + // 25pYeCF2tSki08qtMJZQAvKfw0Kviibk7ZxRbJqmc7B1yfnOEHP6ftjuvKl2+RP/ + // +5P5f8CfIP6gtA0LwSzAqQX/hfIKrGV5j0pCqrD0kQKBgQDeNR6Xi4sXVq79lihO + // a1bSeV7r8yoQrS8x951uO+ox+UIZ1MsAULadl7zB/P0er92p198I9M/0Jth3KBuS + // zj45mucvpiiGvmQlMKMEfNq4nN7WHOu55kufPswQB2mR4J3xmwI+4fM/nl1zc82h + // De8JSazRldJXNhfx0RGFPmgzbwKBgQDWoVXrXLbCAn41oVnWB8vwY9wjt92ztDqJ + // HMFA/SUohjePep9UDq6ooHyAf/Lz6oE5NgeVpPfTDkgvrCFVKnaWdwALbYoKXT2W + // 9FlyJox6eQzrtHAacj3HJooXWuXlphKSizntfxj3LtMR9BmrmRJOfK+SxNOVJzW2 + // +MowT20EkwKBgHmpB8jdZBgxI7o//m2BI5Y1UZ1KE5vx1kc7VXzHXSBjYqeV9FeF + // 2ZZLP9POWh/1Fh4pzTmwIDODGT2UPhSQy0zq3O0fwkyT7WzXRknsuiwd53u/dejg + // iEL2NPAJvulZ2+AuiHo5Z99LK8tMeidV46xoJDDUIMgTG+UQHNGhK5gNAoGAZn/S + // Cn7SgMC0CWSvBHnguULXZO9wH1wZAFYNLL44OqwuaIUFBh2k578M9kkke7woTmwx + // HxQTjmWpr6qimIuY6q6WBN8hJ2Xz/d1fwhYKzIp20zHuv5KDUlJjbFfqpsuy3u1C + // kts5zwI7pr1ObRbDGVyOdKcu7HI3QtR5qqyjwaUCgYABo7Wq6oHva/9V34+G3Goh + // 63bYGUnRw2l5BD11yhQv8XzGGZFqZVincD8gltNThB0Dc/BI+qu3ky4YdgdZJZ7K + // z51GQGtaHEbrHS5caV79yQ8QGY5mUVH3E+VXSxuIqb6pZq2DH4sTAEFHyncddmOH + // zoXBInYwRG9KE/Bw5elhUw== + // -----END PRIVATE KEY----- // SHA256withDSA, 2048 bits // Validity - // Not Before: Nov 25 04:19:56 2016 GMT - // Not After : Nov 5 04:19:56 2037 GMT + // Not Before: May 22 07:18:18 2018 GMT + // Not After : May 17 07:18:18 2038 GMT // Subject Key Identifier: - // 19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5 + // 76:66:9E:F7:3B:DD:45:E5:3B:D9:72:3C:3F:F0:54:39:86:31:26:53 "-----BEGIN CERTIFICATE-----\n" + - "MIIFCzCCBLGgAwIBAgIJAOnEn6YZD/sAMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + - "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + - "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzcxMTA1MDQxOTU2WjA7MQswCQYDVQQGEwJV\n" + - "UzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2Y2Uw\n" + - "ggNGMIICOQYHKoZIzjgEATCCAiwCggEBAJa17ZYdIChv5yeYuPK3zXxgUEGGsdUD\n" + - "AzfQUxtMryCtc3aNgWLxsN1/QYvp9v+hh4twnG20VemCEH9Qlx06Pxg74DwSOA83\n" + - "SecO2y7cdgmrHpep9drxKbXVZafwBhbTSwhV+IDO7EO6+LaRvZuya/YOqNIE9ENx\n" + - "FVk0NrNsDB6pfDEXZsCZALMN2mcl8KGn1q7vdzJQUEV7F6uLGP33znVfmQyWJH3Y\n" + - "W09WVCFXHvDzZHGXDO2O2QwIU1B5AsXnOGeLnKgXzErCoNKwUbVFP0W0OVeJo4pc\n" + - "ZfL/8TVELGG90AuuH1V3Gsq6PdzCDPw4Uv/v5m7/6kwXqBQxAJA7jhMCIQCORIGV\n" + - "mHy5nBLRhIP4vC7vsTxb4CTApPVmZeL5gTIwtQKCAQB2VZLY22k2+IQM6deRGK3L\n" + - "l7tPChGrKnGmTbtUThIza70Sp9DmTBiLzMEY+IgG8kYuT5STVxWjs0cxXCKZGMQW\n" + - "tioMtiXPA2M3HA0/8E0mDLSmzb0RAd2xxnDyGsuqo1eVmx7PLjN3bn3EjhD/+j3d\n" + - "Jx3ZVScMGyq7sVWppUvpudEXi+2etf6GUHjrcX27juo7u4zQ1ezC/HYG1H+jEFqG\n" + - "hdQ6b7H+LBHZH9LegOyIZTMrzAY/TwIr77sXrNJWRoxmDErKB+8bRDybYhNJswlZ\n" + - "m0N5YYUlPmepgbl6XzwCv0y0d81h3bayqIPLXEUtRAl9GuM0hNAlA1Y+qSn9xLFY\n" + - "A4IBBQACggEAZgWC0uflwqQQP1GRU1tolmFZwyVtKre7SjYgCeQBrOa0Xnj/SLaD\n" + - "g1HZ1oH0hccaR/45YouJiCretbbsQ77KouldGSGqTHJgRL75Y2z5uvxa60+YxZ0Z\n" + - "v8xvZnj4seyOjgJLxSSYSPl5n/F70RaNiCLVz/kGe6OQ8KoAeQjdDTOHXCegO9KX\n" + - "tvhM7EaYc8CII9OIR7S7PXJW0hgLKynZcu/Unh02aM0ABh/uLmw1+tvo8e8KTp98\n" + - "NKYSVf6kV3/ya58n4h64UbIYL08JoKUM/5SFETcKAZTU0YKZbpWTM79oJMr8oYVk\n" + - "P9jKitNsXq0Xkzt5dSO0kfu/kM7zpnaFsqOBrTCBqjAdBgNVHQ4EFgQUGUYQQyRq\n" + - "pRS+4pIBefBMX+GugbUwawYDVR0jBGQwYoAUGUYQQyRqpRS+4pIBefBMX+GugbWh\n" + - "P6Q9MDsxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5K\n" + - "U1NFIFRlc3QgU2VyaXZjZYIJAOnEn6YZD/sAMA8GA1UdEwEB/wQFMAMBAf8wCwYD\n" + - "VR0PBAQDAgEGMAsGCWCGSAFlAwQDAgNHADBEAiAwBafz5RRR9nc4cCYoYuBlT/D9\n" + - "9eayhkjhBY/zYunypwIgNp/JnFR88/T4hh36QfSKBGXId9RBCM6uaOkOKnEGkps=\n" + + "MIIErjCCBFSgAwIBAgIJAOktYLNCbr02MAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTgwNTIyMDcxODE4WhcNMzgwNTE3MDcxODE4WjA7MQswCQYDVQQGEwJV\n" + + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Uw\n" + + "ggNHMIICOQYHKoZIzjgEATCCAiwCggEBAO5GyPhSm0ze3LSu+gicdULLj05iOfTL\n" + + "UvZQ29sYz41zmqrLBQbdKiHqgJu2Re9sgTb5suLNjF047TOLPnU3jhPtWm2X8Xzi\n" + + "VGIcHym/Q/MeZxStt/88seqroI3WOKzIML2GcrishT+lcGrtH36Tf1+ue2Snn3PS\n" + + "WyxygNqPjllP5uUjYmFLvAf4QLMldkd/D2VxcwsHjB8y5iUZsXezc/LEhRZS/02m\n" + + "ivqlRw3AMkq/OVe/ZtxFWsP0nsfxEGdZuaUFpppGfixxFvymrB3+J51cTt+pZBDq\n" + + "D2y0DYfc+88iCs4jwHTfcDIpLb538HBjBj2rEgtQESQmB0ooD/+wsPsCIQC1bYch\n" + + "gElNtDYL3FgpLgNSUYp7gIWv9ehaC7LO2z7biQKCAQBitvFOnDkUja8NAF7lDpOV\n" + + "b5ipQ8SicBLW3kQamxhyuyxgZyy/PojZ/oPorkqW/T/A0rhnG6MssEpAtdiwVB+c\n" + + "rBYGo3bcwmExJhdOJ6dYuKFppPWhCwKMHs9npK+lqBMl8l5j58xlcFeC7ZfGf8GY\n" + + "GkhFW0c44vEQhMMbac6ZTTP4mw+1t7xJfmDMlLEyIpTXaAAk8uoVLWzQWnR40sHi\n" + + "ybvS0u3JxQkb7/y8tOOZu8qlz/YOS7lQ6UxUGX27Ce1E0+agfPphetoRAlS1cezq\n" + + "Wa7r64Ga0nkj1kwkcRqjgTiJx0NwnUXr78VAXFhVF95+O3lfqhvdtEGtkhDGPg7N\n" + + "A4IBBgACggEBAMmSHQK0w2i+iqUjOPzn0yNEZrzepLlLeQ1tqtn0xnlv5vBAeefD\n" + + "Pm9dd3tZOjufVWP7hhEz8xPobb1CS4e3vuQiv5UBfhdPL3f3l9T7JMAKPH6C9Vve\n" + + "OQXE5eGqbjsySbcmseHoYUt1WCSnSda1opX8zchX04e7DhGfE2/L9flpYEoSt8lI\n" + + "vMNjgOwvKdW3yvPt1/eBBHYNFG5gWPv/Q5KoyCtHS03uqGm4rNc/wZTIEEfd66C+\n" + + "QRaUltjOaHmtwOdDHaNqwhYZSVOip+Mo+TfyzHFREcdHLapo7ZXqbdYkRGxRR3d+\n" + + "3DfHaraJO0OKoYlPkr3JMvM/MSGR9AnZOcejUDBOMB0GA1UdDgQWBBR2Zp73O91F\n" + + "5TvZcjw/8FQ5hjEmUzAfBgNVHSMEGDAWgBR2Zp73O91F5TvZcjw/8FQ5hjEmUzAM\n" + + "BgNVHRMEBTADAQH/MAsGCWCGSAFlAwQDAgNHADBEAiBzriYE41M2y9Hy5ppkL0Qn\n" + + "dIlNc8JhXT/PHW7GDtViagIgMko8Qoj9gDGPK3+O9E8DC3wGiiF9CObM4LN387ok\n" + + "J+g=\n" + "-----END CERTIFICATE-----" + // -----BEGIN PRIVATE KEY----- + // MIICZQIBADCCAjkGByqGSM44BAEwggIsAoIBAQDuRsj4UptM3ty0rvoInHVCy49O + // Yjn0y1L2UNvbGM+Nc5qqywUG3Soh6oCbtkXvbIE2+bLizYxdOO0ziz51N44T7Vpt + // l/F84lRiHB8pv0PzHmcUrbf/PLHqq6CN1jisyDC9hnK4rIU/pXBq7R9+k39frntk + // p59z0lsscoDaj45ZT+blI2JhS7wH+ECzJXZHfw9lcXMLB4wfMuYlGbF3s3PyxIUW + // Uv9Npor6pUcNwDJKvzlXv2bcRVrD9J7H8RBnWbmlBaaaRn4scRb8pqwd/iedXE7f + // qWQQ6g9stA2H3PvPIgrOI8B033AyKS2+d/BwYwY9qxILUBEkJgdKKA//sLD7AiEA + // tW2HIYBJTbQ2C9xYKS4DUlGKe4CFr/XoWguyzts+24kCggEAYrbxTpw5FI2vDQBe + // 5Q6TlW+YqUPEonAS1t5EGpsYcrssYGcsvz6I2f6D6K5Klv0/wNK4ZxujLLBKQLXY + // sFQfnKwWBqN23MJhMSYXTienWLihaaT1oQsCjB7PZ6SvpagTJfJeY+fMZXBXgu2X + // xn/BmBpIRVtHOOLxEITDG2nOmU0z+JsPtbe8SX5gzJSxMiKU12gAJPLqFS1s0Fp0 + // eNLB4sm70tLtycUJG+/8vLTjmbvKpc/2Dku5UOlMVBl9uwntRNPmoHz6YXraEQJU + // tXHs6lmu6+uBmtJ5I9ZMJHEao4E4icdDcJ1F6+/FQFxYVRfefjt5X6ob3bRBrZIQ + // xj4OzQQjAiEAsceWOM8do4etxp2zgnoNXV8PUUyqWhz1+0srcKV7FR4= + // -----END PRIVATE KEY----- }; // End entity certificate. private final static String[] endEntityCertStrs = { // SHA256withECDSA, curve prime256v1 // Validity - // Not Before: Nov 25 04:19:51 2016 GMT - // Not After : Aug 12 04:19:51 2036 GMT + // Not Before: May 22 07:18:16 2018 GMT + // Not After : May 17 07:18:16 2038 GMT // Authority Key Identifier: - // CA:48:E8:00:C1:42:BD:59:9B:79:D9:B4:B4:CE:3F:68:0C:C8:C4:0C + // 60:CF:BD:73:FF:FA:1A:30:D2:A4:EC:D3:49:71:46:EF:1A:35:A0:86 "-----BEGIN CERTIFICATE-----\n" + - "MIIB1zCCAXygAwIBAgIJAPFq2QL/nUNZMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" + - "AlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + - "ZTAeFw0xNjExMjUwNDE5NTFaFw0zNjA4MTIwNDE5NTFaMFUxCzAJBgNVBAYTAlVT\n" + + "MIIBqjCCAVCgAwIBAgIJAPLY8qZjgNRAMAoGCCqGSM49BAMCMDsxCzAJBgNVBAYT\n" + + "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + + "ZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMFUxCzAJBgNVBAYTAlVT\n" + "MQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZjZTEY\n" + "MBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" + - "QgAE4yvRGVvy9iVATyuHPJVdX6+lh/GLm/sRJ5qLT/3PVFOoNIvlEVNiARo7xhyj\n" + - "2p6bnf32gNg5Ye+QCw20VUv9E6NPME0wCwYDVR0PBAQDAgPoMB0GA1UdDgQWBBSO\n" + - "hHlHZQp9hyBfSGTSQWeszqMXejAfBgNVHSMEGDAWgBTKSOgAwUK9WZt52bS0zj9o\n" + - "DMjEDDAKBggqhkjOPQQDAgNJADBGAiEAu3t6cvFglBAZfkhZlEwB04ZjUFqyfiRj\n" + - "4Hr275E4ZoQCIQDUEonJHlmA19J6oobfR5lYsmoqPm1r0DPm/IiNNKGKKA==\n" + + "QgAEb+9n05qfXnfHUb0xtQJNS4JeSi6IjOfW5NqchvKnfJey9VkJzR7QHLuOESdf\n" + + "xlR7q8YIWgih3iWLGfB+wxHiOqMjMCEwHwYDVR0jBBgwFoAUYM+9c//6GjDSpOzT\n" + + "SXFG7xo1oIYwCgYIKoZIzj0EAwIDSAAwRQIgWpRegWXMheiD3qFdd8kMdrkLxRbq\n" + + "1zj8nQMEwFTUjjQCIQDRIrAjZX+YXHN9b0SoWWLPUq0HmiFIi8RwMnO//wJIGQ==\n" + "-----END CERTIFICATE-----", // SHA256withRSA, 2048 bits // Validity - // Not Before: Nov 25 04:20:02 2016 GMT - // Not After : Aug 12 04:20:02 2036 GMT + // Not Before: May 22 07:18:16 2018 GMT + // Not After : May 17 07:18:16 2038 GMT // Authority Key Identifier: - // A2:DC:55:38:E4:47:7C:8B:D3:E0:CA:FA:AD:3A:C8:4A:DD:12:A0:8E + // 0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C "-----BEGIN CERTIFICATE-----\n" + - "MIIDdjCCAl6gAwIBAgIJAJDcIGOlAmBmMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" + - "BAYTAlVTMQ0wCwYDVQQKEwRKYXZhMR0wGwYDVQQLExRTdW5KU1NFIFRlc3QgU2Vy\n" + - "aXZjZTAeFw0xNjExMjUwNDIwMDJaFw0zNjA4MTIwNDIwMDJaMFUxCzAJBgNVBAYT\n" + + "MIIDNjCCAh6gAwIBAgIJAO2+yPcFryUTMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" + + "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" + + "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0zODA1MTcwNzE4MTZaMFUxCzAJBgNVBAYT\n" + "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + "ZTEYMBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC\n" + - "AQ8AMIIBCgKCAQEAp0dHrifTg2aY0sH03f2JjK2fW4DL6gLDKq0YirsNE07z54LF\n" + - "IeeDio49XwPjB7OpbUTC1hf/YKZ7XiRWyPa1rYozZ88emhZt+cpkyKz+nmW4avlA\n" + - "WnrV+gx4+bU9T+WuBWdAraBcq27Y1I26yfCEtC8k3+O0sdlHbhasF+BUDmX/n4+n\n" + - "ifJdbNm5eSDx8eFYHFTdjhAud3An2X6QD9WWSoJpPdDi4utHhFAlxW6osjJxsAPv\n" + - "zo8YsqmpCMjZcEks4ZsqiZKKiWUWUAjCcbMerDPDX29fyeo348uztvJsmNRzfcwl\n" + - "FtwxpYdxuZqwHi2QoNaQTGXjjbZFmjn7qEkjXwIDAQABo2MwYTALBgNVHQ8EBAMC\n" + - "A+gwHQYDVR0OBBYEFP+henfufE6Znr60lRkmayadVdxnMB8GA1UdIwQYMBaAFKLc\n" + - "VTjkR3yL0+DK+q06yErdEqCOMBIGA1UdEQEB/wQIMAaHBH8AAAEwDQYJKoZIhvcN\n" + - "AQELBQADggEBAK56pV2XoAIkrHFTCkWtYX518nuvkzN6a6BqPKALQlmlbJnq/lhV\n" + - "tPQx79b0j7tow28l2ze/3M0hRb5Ft/d/7mITZNMR+0owk4U51AU2NacRt7fpoxu5\n" + - "wX3hTa4VgX2+BAXeoWF+Yzy6Jj5gAVmSLzBnkTUH0d+EyL1pp+DFE3QdvZqf3+nP\n" + - "zkxz15h3iW8FwI+7/19MX2j2XB/sG8mJpqoszWw8lM4qCa2eWyCbqSHhPi+/+rGg\n" + - "dDG5uzZeOC845GEH2T3tHDA+F3WwcZG/W+4RR6ZaaHlqPKKMcwFL73YbsqdCiKBv\n" + - "p6sXrhIiP0oXImRBRLDlidj5TIOLfAtNM9A=\n" + + "AQ8AMIIBCgKCAQEAszfBobWfZIp8AgC6PiWDDavP65mSvgCXUGxACbxVNAfkLhNR\n" + + "QOsHriRB3X1Q3nvO9PetC6wKlvE9jlnDDj7D+1j1r1CHO7ms1fq8rfcQYdkanDtu\n" + + "4AlHo8v+SSWX16MIXFRYDj2VVHmyPtgbltcg4zGAuwT746FdLI94uXjJjq1IOr/v\n" + + "0VIlwE5ORWH5Xc+5Tj+oFWK0E4a4GHDgtKKhn2m72hN56/GkPKGkguP5NRS1qYYV\n" + + "/EFkdyQMOV8J1M7HaicSft4OL6eKjTrgo93+kHk+tv0Dc6cpVBnalX3TorG8QI6B\n" + + "cHj1XQd78oAlAC+/jF4pc0mwi0un49kdK9gRfQIDAQABoyMwITAfBgNVHSMEGDAW\n" + + "gBQN3ZPJ/ku9NbfomXiQ+9taPdsVTDANBgkqhkiG9w0BAQsFAAOCAQEApXS0nKwm\n" + + "Kp8gpmO2yG1rpd1+2wBABiMU4JZaTqmma24DQ3RzyS+V2TeRb29dl5oTUEm98uc0\n" + + "GPZvhK8z5RFr4YE17dc04nI/VaNDCw4y1NALXGs+AHkjoPjLyGbWpi1S+gfq2sNB\n" + + "Ekkjp6COb/cb9yiFXOGVls7UOIjnVZVd0r7KaPFjZhYh82/f4PA/A1SnIKd1+nfH\n" + + "2yk7mSJNC7Z3qIVDL8MM/jBVwiC3uNe5GPB2uwhd7k5LGAVN3j4HQQGB0Sz+VC1h\n" + + "92oi6xDa+YBva2fvHuCd8P50DDjxmp9CemC7rnZ5j8egj88w14X44Xjb/Fd/ApG9\n" + + "e57NnbT7KM+Grw==\n" + + "-----END CERTIFICATE-----", + + // SHA256withRSA, curv prime256v1 + // Validity + // Not Before: May 22 07:18:16 2018 GMT + // Not After : May 21 07:18:16 2028 GMT + // Authority Key Identifier: + // 0D:DD:93:C9:FE:4B:BD:35:B7:E8:99:78:90:FB:DB:5A:3D:DB:15:4C + "-----BEGIN CERTIFICATE-----\n" + + "MIICazCCAVOgAwIBAgIJAO2+yPcFryUUMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNV\n" + + "BAYTAlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2Vy\n" + + "aXZjZTAeFw0xODA1MjIwNzE4MTZaFw0yODA1MjEwNzE4MTZaMFUxCzAJBgNVBAYT\n" + + "AlVTMQ0wCwYDVQQKDARKYXZhMR0wGwYDVQQLDBRTdW5KU1NFIFRlc3QgU2VyaXZj\n" + + "ZTEYMBYGA1UEAwwPUmVncmVzc2lvbiBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" + + "AQcDQgAE59MERNTlVZ1eeps8Z3Oue5ZkgQdPtD+WIE6tj3PbIKpxGPDxvfNP959A\n" + + "yQjEK/ehWQVrCMmNoEkIzY+IIBgB06MjMCEwHwYDVR0jBBgwFoAUDd2Tyf5LvTW3\n" + + "6Jl4kPvbWj3bFUwwDQYJKoZIhvcNAQELBQADggEBAFOTVEqs70ykhZiIdrEsF1Ra\n" + + "I3B2rLvwXZk52uSltk2/bzVvewA577ZCoxQ1pL7ynkisPfBN1uVYtHjM1VA3RC+4\n" + + "+TAK78dnI7otYjWoHp5rvs4l6c/IbOspS290IlNuDUxMErEm5wxIwj+Aukx/1y68\n" + + "hOyCvHBLMY2c1LskH1MMBbDuS1aI+lnGpToi+MoYObxGcV458vxuT8+wwV8Fkpvd\n" + + "ll8IIFmeNPRv+1E+lXbES6CSNCVaZ/lFhPgdgYKleN7sfspiz50DG4dqafuEAaX5\n" + + "xaK1NWXJxTRz0ROH/IUziyuDW6jphrlgit4+3NCzp6vP9hAJQ8Vhcj0n15BKHIQ=\n" + "-----END CERTIFICATE-----", // SHA256withDSA, 2048 bits // Validity - // Not Before: Nov 25 04:19:56 2016 GMT - // Not After : Aug 12 04:19:56 2036 GMT + // Not Before: May 22 07:18:20 2018 GMT + // Not After : May 17 07:18:20 2038 GMT // Authority Key Identifier: - // 19:46:10:43:24:6A:A5:14:BE:E2:92:01:79:F0:4C:5F:E1:AE:81:B5 + // 76:66:9E:F7:3B:DD:45:E5:3B:D9:72:3C:3F:F0:54:39:86:31:26:53 "-----BEGIN CERTIFICATE-----\n" + - "MIIE2jCCBICgAwIBAgIJAONcI1oba9V9MAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + - "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + - "Y2UwHhcNMTYxMTI1MDQxOTU2WhcNMzYwODEyMDQxOTU2WjBVMQswCQYDVQQGEwJV\n" + + "MIIEnDCCBEGgAwIBAgIJAP/jh1qVhNVjMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTgwNTIyMDcxODIwWhcNMzgwNTE3MDcxODIwWjBVMQswCQYDVQQGEwJV\n" + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" + - "GDAWBgNVBAMMD1JlZ3Jlc3Npb24gVGVzdDCCA0YwggI5BgcqhkjOOAQBMIICLAKC\n" + - "AQEAlrXtlh0gKG/nJ5i48rfNfGBQQYax1QMDN9BTG0yvIK1zdo2BYvGw3X9Bi+n2\n" + - "/6GHi3CcbbRV6YIQf1CXHTo/GDvgPBI4DzdJ5w7bLtx2Casel6n12vEptdVlp/AG\n" + - "FtNLCFX4gM7sQ7r4tpG9m7Jr9g6o0gT0Q3EVWTQ2s2wMHql8MRdmwJkAsw3aZyXw\n" + - "oafWru93MlBQRXsXq4sY/ffOdV+ZDJYkfdhbT1ZUIVce8PNkcZcM7Y7ZDAhTUHkC\n" + - "xec4Z4ucqBfMSsKg0rBRtUU/RbQ5V4mjilxl8v/xNUQsYb3QC64fVXcayro93MIM\n" + - "/DhS/+/mbv/qTBeoFDEAkDuOEwIhAI5EgZWYfLmcEtGEg/i8Lu+xPFvgJMCk9WZl\n" + - "4vmBMjC1AoIBAHZVktjbaTb4hAzp15EYrcuXu08KEasqcaZNu1ROEjNrvRKn0OZM\n" + - "GIvMwRj4iAbyRi5PlJNXFaOzRzFcIpkYxBa2Kgy2Jc8DYzccDT/wTSYMtKbNvREB\n" + - "3bHGcPIay6qjV5WbHs8uM3dufcSOEP/6Pd0nHdlVJwwbKruxVamlS+m50ReL7Z61\n" + - "/oZQeOtxfbuO6ju7jNDV7ML8dgbUf6MQWoaF1Dpvsf4sEdkf0t6A7IhlMyvMBj9P\n" + - "Aivvuxes0lZGjGYMSsoH7xtEPJtiE0mzCVmbQ3lhhSU+Z6mBuXpfPAK/TLR3zWHd\n" + - "trKog8tcRS1ECX0a4zSE0CUDVj6pKf3EsVgDggEFAAKCAQBEGmdP55PyE3M+Q3fU\n" + - "dCGq0sbKw/04xPVhaNYRnRKNR82n+wb8bMCI1vvFqXy1BB6svti4mTHbQZ8+bQXm\n" + - "gyce67uYMwIa5BIk6omNGCeW/kd4ruPgyFxeb6O/Y/7w6AWyRmQttlxRA5M5OhSC\n" + - "tVS4oVC1KK1EfHAUh7mu8S8GrWJoJAWA3PM97Oy/HSGCEUl6HGEu1m7FHPhOKeYG\n" + - "cLkSaov5cbCYO76smHchI+tdUciVqeL3YKQdS+KAzsQoeAZIu/WpbaI1V+5/rSG1\n" + - "I94uBITLCjlJfJZ1aredCDrRXOFH7qgSBhM8/WzwFpFCnnpbSKMgrcrKubsFmW9E\n" + - "jQhXo2MwYTALBgNVHQ8EBAMCA+gwHQYDVR0OBBYEFNA9PhQOjB+05fxxXPNqe0OT\n" + - "doCjMB8GA1UdIwQYMBaAFBlGEEMkaqUUvuKSAXnwTF/hroG1MBIGA1UdEQEB/wQI\n" + - "MAaHBH8AAAEwCwYJYIZIAWUDBAMCA0cAMEQCIE0LM2sZi+L8tjH9sgjLEwJmYZvO\n" + - "yqNfQnXrkTCb+MLMAiBZLaRTVJrOW3edQjum+SonKKuiN22bRclO6pGuNRCtng==\n" + + "GDAWBgNVBAMMD1JlZ3Jlc3Npb24gVGVzdDCCA0cwggI6BgcqhkjOOAQBMIICLQKC\n" + + "AQEAmlavgoJrMcjqWRVcDE2dmWAPREgnzQvneEDef68cprDzjSwvOs5QeFyx75ib\n" + + "ado1e6jO/rW1prCGWHDD1oA/Tn4Pk3vu0nUxzvl1qATc+aJbpUU5Op0bvp6LbCsQ\n" + + "QslV9FeRh7Eb7bP6gpc/kHCBzEgC1VCK7prccXWy+t6SMOHbND3h+UbckfSaUuaV\n" + + "sVJNTD1D6GElfRj4Nmz1BGPfSYvKorwNZEU3gXwFgtDoAcGx7tcyClLpDHfqRfw/\n" + + "7yiqLyeiP7D4hl5lMNouJWDlAdMFp0FMgS3s9VDFinIcr6VtBWMTG7+4+czHAB+3\n" + + "fvrwlqNzhBn3uFHrekN/w8fNxwIhAJo7Sae1za7IMW0Q6hE5B4b+s2B/FaKPoA4E\n" + + "jtZu13B9AoIBAQCOZqLMKfvqZWUgT0PQ3QjR7dAFdd06I9Y3+TOQzZk1+j+vw/6E\n" + + "X4vFItX4gihb/u5Q9CdmpwhVGi7bvo+7+/IKeTgoQ6f5+PSug7SrWWUQ5sPwaZui\n" + + "zXZJ5nTeZDucFc2yFx0wgnjbPwiUxZklOT7xGiOMtzOTa2koCz5KuIBL+/wPKKxm\n" + + "ypo9VoY9xfbdU6LMXZv/lpD5XTM9rYHr/vUTNkukvV6Hpm0YMEWhVZKUJiqCqTqG\n" + + "XHaleOxSw6uQWB/+TznifcC7gB48UOQjCqOKf5VuwQneJLhlhU/jhRV3xtr+hLZa\n" + + "hW1wYhVi8cjLDrZFKlgEQqhB4crnJU0mJY+tA4IBBQACggEAID0ezl00/X8mv7eb\n" + + "bzovum1+DEEP7FM57k6HZEG2N3ve4CW+0m9Cd+cWPz8wkZ+M0j/Eqa6F0IdbkXEc\n" + + "Q7CuzvUyJ57xQ3L/WCgXsiS+Bh8O4Mz7GwW22CGmHqafbVv+hKBfr8MkskO6GJUt\n" + + "SUF/CVLzB4gMIvZMH26tBP2xK+i7FeEK9kT+nGdzQSZBAhFYpEVCBplHZO24/OYq\n" + + "1DNoU327nUuXIhmsfA8N0PjiWbIZIjTPwBGr9H0LpATI7DIDNcvRRvtROP+pBU9y\n" + + "fuykPkptg9C0rCM9t06bukpOSaEz/2VIQdLE8fHYFA6pHZ6CIc2+5cfvMgTPhcjz\n" + + "W2jCt6MjMCEwHwYDVR0jBBgwFoAUdmae9zvdReU72XI8P/BUOYYxJlMwCwYJYIZI\n" + + "AWUDBAMCA0gAMEUCIQCeI5fN08b9BpOaHdc3zQNGjp24FOL/RxlBLeBAorswJgIg\n" + + "JEZ8DhYxQy1O7mmZ2UIT7op6epWMB4dENjs0qWPmcKo=\n" + "-----END CERTIFICATE-----" }; @@ -532,63 +596,79 @@ // // EC private key related to cert endEntityCertStrs[0]. // - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGAy4Pxrd2keM7AdP\n" + - "VNUMEO5iO681v4/tstVGfdXkCTuhRANCAATjK9EZW/L2JUBPK4c8lV1fr6WH8Yub\n" + - "+xEnmotP/c9UU6g0i+URU2IBGjvGHKPanpud/faA2Dlh75ALDbRVS/0T", + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgn5K03bpTLjEtFQRa\n" + + "JUtx22gtmGEvvSUSQdimhGthdtihRANCAARv72fTmp9ed8dRvTG1Ak1Lgl5KLoiM\n" + + "59bk2pyG8qd8l7L1WQnNHtAcu44RJ1/GVHurxghaCKHeJYsZ8H7DEeI6", // // RSA private key related to cert endEntityCertStrs[1]. // - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnR0euJ9ODZpjS\n" + - "wfTd/YmMrZ9bgMvqAsMqrRiKuw0TTvPngsUh54OKjj1fA+MHs6ltRMLWF/9gpnte\n" + - "JFbI9rWtijNnzx6aFm35ymTIrP6eZbhq+UBaetX6DHj5tT1P5a4FZ0CtoFyrbtjU\n" + - "jbrJ8IS0LyTf47Sx2UduFqwX4FQOZf+fj6eJ8l1s2bl5IPHx4VgcVN2OEC53cCfZ\n" + - "fpAP1ZZKgmk90OLi60eEUCXFbqiyMnGwA+/OjxiyqakIyNlwSSzhmyqJkoqJZRZQ\n" + - "CMJxsx6sM8Nfb1/J6jfjy7O28myY1HN9zCUW3DGlh3G5mrAeLZCg1pBMZeONtkWa\n" + - "OfuoSSNfAgMBAAECggEAWnAHKPkPObN2XDvQj1RL0WrtBSOVG2dy7Ne4tQh8ATxm\n" + - "UXw56CKq03YjaANJ8xgHObQ7QlSnFTHs8PDkmrIHd1OIh09LVDNcMfhilLwyzKBi\n" + - "HDO1vzU6Cn5DyX1bMJ8UfodcSIKyl1zOjdwyaItIs8HpRcJuJtk57SME18PIrh9H\n" + - "EWchWSxTvPvKDY2bhb4vBMgVPfTQO3yc8gY/1J5vKPqDpyEuCGjV13zd/AoL/u5A\n" + - "sG10hQ2veJ9KAn1xwPwEoAkCdNLL8vPB1rCbeMZNamqHZRihfoOgDnnJIf3FtUFF\n" + - "8bS2FM2kGQR+05SZdhBmJl0obPrbBWil/2vxqeFrgQKBgQDZl1yQsFss2BKK2VAh\n" + - "9PKc8tU1v6RpHQZhJEDSct2slHQS5DF6bWl5kJiPevXSvcnjgoPamZf7Joii+Rds\n" + - "3xtPQmRw2vQmXBQzQS1YoRuh4hUlkdFWCjUNNg1kvipue6FO4PVg3ViP7llN8PXK\n" + - "rSpVxn0a36UiN9jN2zMOUs6tJwKBgQDEzlqa7DghMli7TUdL44uvD2/ceiHqHMMw\n" + - "5eLsEHeRVn/EVU99beKw/dAOGwRssTpCd9h7fwzQ2503/Qb/Goe0nKE7+xvt3/sE\n" + - "n2Y8Qfv1W1+hGb2qU2jhQaR5bZrLZp0+BgRuQ4ttpYvzopYe4FLZWhDBA0zsGyu0\n" + - "nCi7lUSrCQKBgGeGYW8hyS9r2l6fiEWvsiLEUnbRKFsuiRN82S6HojpzI0q9sWDL\n" + - "X6yMBFn3qa/LxpttRGikPTAsJERN+Tw+ZlLuhrU/J3x8wMumDfomJOx/kYofd5bV\n" + - "ImqXtgWhiLSqM5RA6d5dUb6hK3Iu2/LDMuo+ltVLZNkD8y32RbNh6J1vAoGAbLqQ\n" + - "pgyhSf3Vtc0Q+aVB87p0k3tKJ1wynl4zSzYhyMLgHakAHIzL8/qVqmVUwXP8euJZ\n" + - "UIk1nGHobxk0d1XB6Y+rKEcn+/iFZt1ljx7pQ3ly0L824NXqGKC6bHeYUI1li/Gp\n" + - "Gv3oFvCh7D1D8NUAEKLIpMndAohUUhkAC/qAkHkCgYEAzSIarDNquayV+umC1SXm\n" + - "Zo6XLuzWjudLxPd2lyCfwR2aRKlrb+5OFYErX+RSLyCJmaqVZMyXP09PBIvNXu2Z\n" + - "+gbx5WUC+kA+6zdKEPXowei6i6EHMXYT2AL7395ZbPajZjsCduE3WuUztuHrhtMm\n" + - "JI+k1o4rCnSLlX4gWdN1oTs=", + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzN8GhtZ9kinwC\n" + + "ALo+JYMNq8/rmZK+AJdQbEAJvFU0B+QuE1FA6weuJEHdfVDee870960LrAqW8T2O\n" + + "WcMOPsP7WPWvUIc7uazV+ryt9xBh2RqcO27gCUejy/5JJZfXowhcVFgOPZVUebI+\n" + + "2BuW1yDjMYC7BPvjoV0sj3i5eMmOrUg6v+/RUiXATk5FYfldz7lOP6gVYrQThrgY\n" + + "cOC0oqGfabvaE3nr8aQ8oaSC4/k1FLWphhX8QWR3JAw5XwnUzsdqJxJ+3g4vp4qN\n" + + "OuCj3f6QeT62/QNzpylUGdqVfdOisbxAjoFwePVdB3vygCUAL7+MXilzSbCLS6fj\n" + + "2R0r2BF9AgMBAAECggEASIkPkMCuw4WdTT44IwERus3IOIYOs2IP3BgEDyyvm4B6\n" + + "JP/iihDWKfA4zEl1Gqcni1RXMHswSglXra682J4kui02Ov+vzEeJIY37Ibn2YnP5\n" + + "ZjRT2s9GtI/S2o4hl8A/mQb2IMViFC+xKehTukhV4j5d6NPKk0XzLR7gcMjnYxwn\n" + + "l21fS6D2oM1xRG/di7sL+uLF8EXLRzfiWDNi12uQv4nwtxPKvuKhH6yzHt7YqMH0\n" + + "46pmDKDaxV4w1JdycjCb6NrCJOYZygoQobuZqOQ30UZoZsPJrtovkncFr1e+lNcO\n" + + "+aWDfOLCtTH046dEQh5oCShyXMybNlry/QHsOtHOwQKBgQDh2iIjs+FPpQy7Z3EX\n" + + "DGEvHYqPjrYO9an2KSRr1m9gzRlWYxKY46WmPKwjMerYtra0GP+TBHrgxsfO8tD2\n" + + "wUAII6sd1qup0a/Sutgf2JxVilLykd0+Ge4/Cs51tCdJ8EqDV2B6WhTewOY2EGvg\n" + + "JiKYkeNwgRX/9M9CFSAMAk0hUQKBgQDLJAartL3DoGUPjYtpJnfgGM23yAGl6G5r\n" + + "NSXDn80BiYIC1p0bG3N0xm3yAjqOtJAUj9jZbvDNbCe3GJfLARMr23legX4tRrgZ\n" + + "nEdKnAFKAKL01oM+A5/lHdkwaZI9yyv+hgSVdYzUjB8rDmzeVQzo1BT7vXypt2yV\n" + + "6O1OnUpCbQKBgA/0rzDChopv6KRcvHqaX0tK1P0rYeVQqb9ATNhpf9jg5Idb3HZ8\n" + + "rrk91BNwdVz2G5ZBpdynFl9G69rNAMJOCM4KZw5mmh4XOEq09Ivba8AHU7DbaTv3\n" + + "7QL7KnbaUWRB26HHzIMYVh0el6T+KADf8NXCiMTr+bfpfbL3dxoiF3zhAoGAbCJD\n" + + "Qse1dBs/cKYCHfkSOsI5T6kx52Tw0jS6Y4X/FOBjyqr/elyEexbdk8PH9Ar931Qr\n" + + "NKMvn8oA4iA/PRrXX7M2yi3YQrWwbkGYWYjtzrzEAdzmg+5eARKAeJrZ8/bg9l3U\n" + + "ttKaItJsDPlizn8rngy3FsJpR9aSAMK6/+wOiYkCgYEA1tZkI1rD1W9NYZtbI9BE\n" + + "qlJVFi2PBOJMKNuWdouPX3HLQ72GJSQff2BFzLTELjweVVJ0SvY4IipzpQOHQOBy\n" + + "5qh/p6izXJZh3IHtvwVBjHoEVplg1b2+I5e3jDCfqnwcQw82dW5SxOJMg1h/BD0I\n" + + "qAL3go42DYeYhu/WnECMeis=", // - // DSA private key related to cert endEntityCertStrs[2]. + // EC private key related to cert endEntityCertStrs[2]. + // + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGVc7hICpmp91jbYe\n" + + "nrr8nYHD37RZP3VENY+szuA7WjuhRANCAATn0wRE1OVVnV56mzxnc657lmSBB0+0\n" + + "P5YgTq2Pc9sgqnEY8PG980/3n0DJCMQr96FZBWsIyY2gSQjNj4ggGAHT", + + // + // DSA private key related to cert endEntityCertStrs[3]. // - "MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQCWte2WHSAob+cnmLjyt818YFBB\n" + - "hrHVAwM30FMbTK8grXN2jYFi8bDdf0GL6fb/oYeLcJxttFXpghB/UJcdOj8YO+A8\n" + - "EjgPN0nnDtsu3HYJqx6XqfXa8Sm11WWn8AYW00sIVfiAzuxDuvi2kb2bsmv2DqjS\n" + - "BPRDcRVZNDazbAweqXwxF2bAmQCzDdpnJfChp9au73cyUFBFexerixj99851X5kM\n" + - "liR92FtPVlQhVx7w82RxlwztjtkMCFNQeQLF5zhni5yoF8xKwqDSsFG1RT9FtDlX\n" + - "iaOKXGXy//E1RCxhvdALrh9VdxrKuj3cwgz8OFL/7+Zu/+pMF6gUMQCQO44TAiEA\n" + - "jkSBlZh8uZwS0YSD+Lwu77E8W+AkwKT1ZmXi+YEyMLUCggEAdlWS2NtpNviEDOnX\n" + - "kRity5e7TwoRqypxpk27VE4SM2u9EqfQ5kwYi8zBGPiIBvJGLk+Uk1cVo7NHMVwi\n" + - "mRjEFrYqDLYlzwNjNxwNP/BNJgy0ps29EQHdscZw8hrLqqNXlZsezy4zd259xI4Q\n" + - "//o93Scd2VUnDBsqu7FVqaVL6bnRF4vtnrX+hlB463F9u47qO7uM0NXswvx2BtR/\n" + - "oxBahoXUOm+x/iwR2R/S3oDsiGUzK8wGP08CK++7F6zSVkaMZgxKygfvG0Q8m2IT\n" + - "SbMJWZtDeWGFJT5nqYG5el88Ar9MtHfNYd22sqiDy1xFLUQJfRrjNITQJQNWPqkp\n" + - "/cSxWAQiAiAKHYbYwEy0XS9J0MeKQmqPswn0nCJKvH+esfMKkZvV3w==" + "MIICZQIBADCCAjoGByqGSM44BAEwggItAoIBAQCaVq+CgmsxyOpZFVwMTZ2ZYA9E\n" + + "SCfNC+d4QN5/rxymsPONLC86zlB4XLHvmJtp2jV7qM7+tbWmsIZYcMPWgD9Ofg+T\n" + + "e+7SdTHO+XWoBNz5olulRTk6nRu+notsKxBCyVX0V5GHsRvts/qClz+QcIHMSALV\n" + + "UIrumtxxdbL63pIw4ds0PeH5RtyR9JpS5pWxUk1MPUPoYSV9GPg2bPUEY99Ji8qi\n" + + "vA1kRTeBfAWC0OgBwbHu1zIKUukMd+pF/D/vKKovJ6I/sPiGXmUw2i4lYOUB0wWn\n" + + "QUyBLez1UMWKchyvpW0FYxMbv7j5zMcAH7d++vCWo3OEGfe4Uet6Q3/Dx83HAiEA\n" + + "mjtJp7XNrsgxbRDqETkHhv6zYH8Voo+gDgSO1m7XcH0CggEBAI5moswp++plZSBP\n" + + "Q9DdCNHt0AV13Toj1jf5M5DNmTX6P6/D/oRfi8Ui1fiCKFv+7lD0J2anCFUaLtu+\n" + + "j7v78gp5OChDp/n49K6DtKtZZRDmw/Bpm6LNdknmdN5kO5wVzbIXHTCCeNs/CJTF\n" + + "mSU5PvEaI4y3M5NraSgLPkq4gEv7/A8orGbKmj1Whj3F9t1Tosxdm/+WkPldMz2t\n" + + "gev+9RM2S6S9XoembRgwRaFVkpQmKoKpOoZcdqV47FLDq5BYH/5POeJ9wLuAHjxQ\n" + + "5CMKo4p/lW7BCd4kuGWFT+OFFXfG2v6EtlqFbXBiFWLxyMsOtkUqWARCqEHhyucl\n" + + "TSYlj60EIgIgLfA75+8KcKxdN8mr6gzGjQe7jPFGG42Ejhd7Q2F4wuw=" + }; + + // Private key algorithm of endEntityPrivateKeys. + private final static String[] endEntityPrivateKeyAlgs = { + "EC", + "RSA", + "EC", + "DSA", }; // Private key names of endEntityPrivateKeys. private final static String[] endEntityPrivateKeyNames = { - "EC", - "RSA", - "DSA", + "ecdsa", + "rsa", + "ec-rsa", + "dsa", }; /* @@ -599,6 +679,7 @@ String[] keyMaterialCerts, String[] keyMaterialKeys, String[] keyMaterialKeyAlgs, + String[] keyMaterialKeyNames, ContextParameters params) throws Exception { KeyStore ts = null; // trust store @@ -665,7 +746,7 @@ Certificate[] chain = new Certificate[] { keyCert }; // import the key entry. - ks.setKeyEntry("cert-" + keyMaterialKeyAlgs[i], + ks.setKeyEntry("cert-" + keyMaterialKeyNames[i], priKey, passphrase, chain); } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java --- a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/ReadTimeout.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, 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 @@ -175,9 +175,9 @@ throw new Exception( "system property timeout configuration does not work"); - } catch (SocketTimeoutException stex) { + } catch (SSLException | SocketTimeoutException ex) { System.out.println("Got expected timeout exception for " + - "system property timeout configuration: " + stex); + "system property timeout configuration: " + getCause(ex)); } finally { done(); http.disconnect(); @@ -196,9 +196,9 @@ throw new Exception( "HttpsURLConnection.setReadTimeout() does not work"); - } catch (SocketTimeoutException stex) { + } catch (SSLException | SocketTimeoutException ex) { System.out.println("Got expected timeout exception for " + - "HttpsURLConnection.setReadTimeout(): " + stex); + "HttpsURLConnection.setReadTimeout(): " + getCause(ex)); } finally { done(); http.disconnect(); @@ -208,6 +208,20 @@ } } + private Exception getCause(Exception ex) { + Exception cause = null; + if (ex instanceof SSLException) { + cause = (Exception) ex.getCause(); + if (!(cause instanceof SocketTimeoutException)) { + throw new RuntimeException("Unexpected cause", cause); + } + } else { + cause = ex; + } + + return cause; + } + static class NameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ec/TestEC.java --- a/test/jdk/sun/security/ec/TestEC.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ec/TestEC.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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,6 +34,7 @@ * @library ../pkcs11/ec * @library ../pkcs11/sslecc * @library ../../../java/security/testlibrary + * @library ../../../javax/net/ssl/TLSCommon * @modules jdk.crypto.cryptoki/sun.security.pkcs11.wrapper * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" TestEC * @run main/othervm/java.security.policy=TestEC.policy -Djdk.tls.namedGroups="secp256r1,sect193r1" TestEC diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/SSL.java --- a/test/jdk/sun/security/krb5/auto/SSL.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,329 +0,0 @@ -/* - * Copyright (c) 2009, 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. - * - * 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. - */ - -/* - * @test - * @bug 6894643 6913636 8005523 8025123 8194486 - * @summary Test JSSE Kerberos ciphersuite - * @library /test/lib - * @run main jdk.test.lib.FileInstaller TestHosts TestHosts - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_RC4_128_SHA - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_RC4_128_SHA unbound - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_RC4_128_SHA unbound sni - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_3DES_EDE_CBC_SHA - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_3DES_EDE_CBC_MD5 - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_DES_CBC_SHA - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_WITH_DES_CBC_MD5 - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_EXPORT_WITH_RC4_40_SHA - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_EXPORT_WITH_RC4_40_MD5 - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL - * TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 - */ -import java.io.*; -import java.security.Permission; -import javax.net.ssl.*; -import java.security.Principal; -import java.security.Security; -import java.util.Date; -import java.util.List; -import java.util.ArrayList; -import java.util.Locale; -import javax.security.auth.kerberos.ServicePermission; -import sun.security.jgss.GSSUtil; -import sun.security.krb5.PrincipalName; -import sun.security.krb5.internal.ktab.KeyTab; - -public class SSL extends SecurityManager { - - private static String krb5Cipher; - private static final int LOOP_LIMIT = 3; - private static int loopCount = 0; - private static volatile String server; - private static volatile int port; - private static String sniHostname = null; - private static String sniMatcherPattern = null; - - private static String permChecks = ""; - - // 0-Not started, 1-Start OK, 2-Failure - private static volatile int serverState = 0; - - @Override - public void checkPermission(Permission perm, Object context) { - checkPermission(perm); - } - - public void checkPermission(Permission perm) { - if (!(perm instanceof ServicePermission)) { - return; - } - ServicePermission p = (ServicePermission)perm; - // ServicePermissions required to create GSSName are ignored - if (!p.getActions().isEmpty()) { - permChecks = permChecks - + p.getActions().toUpperCase(Locale.US).charAt(0); - } - } - - public static void main(String[] args) throws Exception { - // reset the security property to make sure that the algorithms - // and keys used in this test are not disabled. - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - - krb5Cipher = args[0]; - - boolean unbound = args.length > 1; - - // Workaround for JDK-8161101, reference the class before - // SecurityManager is set. - System.out.println("Touching " + ServicePermission.class); - - System.setSecurityManager(new SSL()); - - KDC kdc = KDC.create(OneKDC.REALM); - server = "host." + OneKDC.REALM_LOWER_CASE; - - if (args.length > 2) { - sniHostname = "test." + server; - sniMatcherPattern = ".*"; - } - - kdc.addPrincipal(OneKDC.USER, OneKDC.PASS); - kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); - KDC.saveConfig(OneKDC.KRB5_CONF, kdc); - System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF); - - // Add 3 versions of keys into keytab - KeyTab ktab = KeyTab.create(OneKDC.KTAB); - String serviceName = null; - if (sniHostname != null) { - serviceName = "host/" + sniHostname; - } else { - serviceName = "host/" + server; - } - PrincipalName service = new PrincipalName( - serviceName, PrincipalName.KRB_NT_SRV_HST); - ktab.addEntry(service, "pass1".toCharArray(), 1, true); - ktab.addEntry(service, "pass2".toCharArray(), 2, true); - ktab.addEntry(service, "pass3".toCharArray(), 3, true); - ktab.save(); - - // and use the middle one as the real key - kdc.addPrincipal(serviceName, "pass2".toCharArray()); - - - // JAAS config entry name ssl - System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF); - File f = new File(OneKDC.JAAS_CONF); - FileOutputStream fos = new FileOutputStream(f); - fos.write(( - "ssl {\n" + - " com.sun.security.auth.module.Krb5LoginModule required\n" + - (unbound ? - " principal=*\n" : - " principal=\"" + serviceName + "\"\n") + - " useKeyTab=true\n" + - " keyTab=" + OneKDC.KTAB + "\n" + - " isInitiator=false\n" + - " storeKey=true;\n};\n" - ).getBytes()); - fos.close(); - - Context c; - final Context s = Context.fromJAAS("ssl"); - - s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); - - Thread server = new Thread(new Runnable() { - public void run() { - try { - s.doAs(new JsseServerAction(), null); - } catch (Exception e) { - e.printStackTrace(); - serverState = 2; - } - } - }); - server.setDaemon(true); - server.start(); - - while (serverState == 0) { - Thread.sleep(50); - } - - if (serverState == 2) { - throw new Exception("Server already failed"); - } - - c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); - c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); - c.doAs(new JsseClientAction(), null); - - // Add another version of key, make sure it can be loaded - Thread.sleep(2000); - ktab = KeyTab.getInstance(OneKDC.KTAB); - ktab.addEntry(service, "pass4".toCharArray(), 4, true); - ktab.save(); - kdc.addPrincipal(serviceName, "pass4".toCharArray()); - - c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); - c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); - c.doAs(new JsseClientAction(), null); - - // Permission checking check. Please note this is highly - // implementation related. - if (unbound) { - // For unbound, server does not know what name to check. - // Client checks "initiate", then server gets the name - // and checks "accept". Second connection resume. - if (!permChecks.equals("IA")) { - throw new Exception(permChecks); - } - } else { - // For bound, JAAS checks "accept" once. Server checks again, - // client then checks "initiate". Second connection resume. - if (!permChecks.equals("AAI")) { - throw new Exception(permChecks); - } - } - } - - // Following codes copied from - // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/lab/part2.html#JSSE - private static class JsseClientAction implements Action { - public byte[] run(Context s, byte[] input) throws Exception { - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); - System.out.println("Connecting " + server + ":" + port); - SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port); - - // Enable only a KRB5 cipher suite. - String enabledSuites[] = {krb5Cipher}; - sslSocket.setEnabledCipherSuites(enabledSuites); - // Should check for exception if enabledSuites is not supported - - if (sniHostname != null) { - List<SNIServerName> serverNames = new ArrayList<>(); - serverNames.add(new SNIHostName(sniHostname)); - SSLParameters params = sslSocket.getSSLParameters(); - params.setServerNames(serverNames); - sslSocket.setSSLParameters(params); - } - - BufferedReader in = new BufferedReader(new InputStreamReader( - sslSocket.getInputStream())); - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - sslSocket.getOutputStream())); - - String outStr = "Hello There!\n"; - out.write(outStr); - out.flush(); - System.out.print("Sending " + outStr); - - String inStr = in.readLine(); - System.out.println("Received " + inStr); - - String cipherSuiteChosen = sslSocket.getSession().getCipherSuite(); - System.out.println("Cipher suite in use: " + cipherSuiteChosen); - Principal self = sslSocket.getSession().getLocalPrincipal(); - System.out.println("I am: " + self.toString()); - Principal peer = sslSocket.getSession().getPeerPrincipal(); - System.out.println("Server is: " + peer.toString()); - - sslSocket.close(); - // This line should not be needed. It's the server's duty to - // forget the old key - //sslSocket.getSession().invalidate(); - return null; - } - } - - private static class JsseServerAction implements Action { - public byte[] run(Context s, byte[] input) throws Exception { - SSLServerSocketFactory sslssf = - (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); - SSLServerSocket sslServerSocket = - (SSLServerSocket) sslssf.createServerSocket(0); // any port - port = sslServerSocket.getLocalPort(); - System.out.println("Listening on " + port); - serverState = 1; - - // Enable only a KRB5 cipher suite. - String enabledSuites[] = {krb5Cipher}; - sslServerSocket.setEnabledCipherSuites(enabledSuites); - // Should check for exception if enabledSuites is not supported - - if (sniMatcherPattern != null) { - List<SNIMatcher> matchers = new ArrayList<>(); - matchers.add(SNIHostName.createSNIMatcher(sniMatcherPattern)); - SSLParameters params = sslServerSocket.getSSLParameters(); - params.setSNIMatchers(matchers); - sslServerSocket.setSSLParameters(params); - } - - while (loopCount++ < LOOP_LIMIT) { - System.out.println("Waiting for incoming connection..."); - - SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); - - System.out.println("Got connection from client " - + sslSocket.getInetAddress()); - - BufferedReader in = new BufferedReader(new InputStreamReader( - sslSocket.getInputStream())); - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - sslSocket.getOutputStream())); - - String inStr = in.readLine(); - System.out.println("Received " + inStr); - - String outStr = inStr + " " + new Date().toString() + "\n"; - out.write(outStr); - System.out.println("Sending " + outStr); - out.flush(); - - String cipherSuiteChosen = - sslSocket.getSession().getCipherSuite(); - System.out.println("Cipher suite in use: " + cipherSuiteChosen); - Principal self = sslSocket.getSession().getLocalPrincipal(); - System.out.println("I am: " + self.toString()); - Principal peer = sslSocket.getSession().getPeerPrincipal(); - System.out.println("Client is: " + peer.toString()); - - sslSocket.close(); - } - return null; - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/SSLwithPerms.java --- a/test/jdk/sun/security/krb5/auto/SSLwithPerms.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,235 +0,0 @@ -/* - * 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. - * - * 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. - */ - -/* - * @test - * @bug 8038089 8194486 - * @summary TLS optional support for Kerberos cipher suites needs to be re-examined - * @library ../../../../java/security/testlibrary/ /test/lib - * @run main jdk.test.lib.FileInstaller TestHosts TestHosts - * @run main/othervm -Djdk.net.hosts.file=TestHosts SSLwithPerms - */ -import java.io.*; -import javax.net.ssl.*; -import javax.security.auth.AuthPermission; -import javax.security.auth.kerberos.ServicePermission; -import java.net.SocketPermission; -import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.Principal; -import java.security.Security; -import java.security.SecurityPermission; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.ArrayList; -import java.util.Locale; -import java.util.PropertyPermission; - -import sun.security.jgss.GSSUtil; - -public class SSLwithPerms { - - static String KRB5_CONF = "krb5.conf"; - static String JAAS_CONF = "jaas.conf"; - static String REALM = "REALM"; - static String KTAB = "ktab"; - static String HOST = "host." + REALM.toLowerCase(Locale.US); - static String SERVER = "host/" + HOST; - static String USER = "user"; - static char[] PASS = "password".toCharArray(); - - public static void main(String[] args) throws Exception { - - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - if (args.length == 0) { - KDC kdc = KDC.create(REALM, HOST, 0, true); - - kdc.addPrincipal(USER, PASS); - kdc.addPrincipalRandKey("krbtgt/" + REALM); - kdc.addPrincipalRandKey(SERVER); - KDC.saveConfig(KRB5_CONF, kdc); - kdc.writeKtab(KTAB); - - File f = new File(JAAS_CONF); - FileOutputStream fos = new FileOutputStream(f); - fos.write(( - "ssl {\n" + - " com.sun.security.auth.module.Krb5LoginModule required\n" + - " principal=\"" + SERVER + "\"\n" + - " useKeyTab=true\n" + - " keyTab=" + KTAB + "\n" + - " isInitiator=false\n" + - " storeKey=true;\n};\n" - ).getBytes()); - fos.close(); - - String hostsFileName = System.getProperty("test.src", ".") + "/TestHosts"; - - Proc pc = Proc.create("SSLwithPerms") - .args("client") - .inheritIO() - .prop("java.security.manager", "") - .prop("java.security.krb5.conf", KRB5_CONF) - .prop("jdk.net.hosts.file", hostsFileName) - .prop("javax.net.ssl", "handshake") - .prop("sun.security.krb5.debug", "true") - .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms")) - .perm(new java.util.PropertyPermission("user.name", "read")) - .perm(new PropertyPermission("sun.security.krb5.principal", "read")) - .perm(new FilePermission("port", "read")) - .perm(new FilePermission(hostsFileName, "read")) - .perm(new FilePermission(KTAB, "read")) - .perm(new AuthPermission("modifyPrincipals")) - .perm(new AuthPermission("modifyPrivateCredentials")) - .perm(new AuthPermission("doAs")) - .perm(new SocketPermission("127.0.0.1", "connect")) - .perm(new ServicePermission("host/host.realm@REALM", "initiate")) - .start(); - - Proc ps = Proc.create("SSLwithPerms") - .args("server") - .inheritIO() - .prop("java.security.manager", "") - .prop("java.security.krb5.conf", KRB5_CONF) - .prop("java.security.auth.login.config", JAAS_CONF) - .prop("jdk.net.hosts.file", hostsFileName) - .prop("javax.net.ssl", "handshake") - .prop("sun.security.krb5.debug", "true") - .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms")) - .perm(new AuthPermission("createLoginContext.ssl")) - .perm(new AuthPermission("doAs")) - .perm(new FilePermission(hostsFileName, "read")) - .perm(new FilePermission("port", "write")) - .perm(new SocketPermission("127.0.0.1", "accept")) - .perm(new ServicePermission("host/host.realm@REALM", "accept")) - .start(); - - if (pc.waitFor() != 0) { - throw new Exception(); - } - if (ps.waitFor() != 0) { - throw new Exception(); - } - } else if (args[0].equals("client")) { - Context c; - c = Context.fromUserPass(USER, PASS, false); - c.doAs(new JsseClientAction(), null); - } else if (args[0].equals("server")) { - final Context s = Context.fromJAAS("ssl"); - s.doAs(new JsseServerAction(), null); - } - } - - private static class JsseClientAction implements Action { - public byte[] run(Context s, byte[] input) throws Exception { - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); - while (!Files.exists(Paths.get("port"))) { - Thread.sleep(100); - } - int port = ByteBuffer.allocate(4) - .put(Files.readAllBytes(Paths.get("port"))).getInt(0); - System.out.println("Connecting " + SERVER + ":" + port); - SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port); - - // Enable only a KRB5 cipher suite. - String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"}; - sslSocket.setEnabledCipherSuites(enabledSuites); - - SSLParameters params = sslSocket.getSSLParameters(); - params.setServerNames(Collections.singletonList(new SNIHostName(HOST))); - sslSocket.setSSLParameters(params); - - BufferedReader in = new BufferedReader(new InputStreamReader( - sslSocket.getInputStream())); - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - sslSocket.getOutputStream())); - - String outStr = "Hello There!\n"; - out.write(outStr); - out.flush(); - System.out.print("Sending " + outStr); - - String inStr = in.readLine(); - System.out.println("Received " + inStr); - - String cipherSuiteChosen = sslSocket.getSession().getCipherSuite(); - System.out.println("Cipher suite in use: " + cipherSuiteChosen); - Principal self = sslSocket.getSession().getLocalPrincipal(); - System.out.println("I am: " + self.toString()); - Principal peer = sslSocket.getSession().getPeerPrincipal(); - System.out.println("Server is: " + peer.toString()); - - sslSocket.close(); - return null; - } - } - - private static class JsseServerAction implements Action { - public byte[] run(Context s, byte[] input) throws Exception { - SSLServerSocketFactory sslssf = - (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); - SSLServerSocket sslServerSocket = - (SSLServerSocket) sslssf.createServerSocket(0); // any port - int port = sslServerSocket.getLocalPort(); - System.out.println("Listening on " + port); - - String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"}; - sslServerSocket.setEnabledCipherSuites(enabledSuites); - - Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array()); - System.out.println("Waiting for incoming connection..."); - - SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); - - System.out.println("Got connection from client " - + sslSocket.getInetAddress()); - - BufferedReader in = new BufferedReader(new InputStreamReader( - sslSocket.getInputStream())); - BufferedWriter out = new BufferedWriter(new OutputStreamWriter( - sslSocket.getOutputStream())); - - String inStr = in.readLine(); - System.out.println("Received " + inStr); - - String outStr = inStr + " " + new Date().toString() + "\n"; - out.write(outStr); - System.out.println("Sending " + outStr); - out.flush(); - - String cipherSuiteChosen = - sslSocket.getSession().getCipherSuite(); - System.out.println("Cipher suite in use: " + cipherSuiteChosen); - Principal self = sslSocket.getSession().getLocalPrincipal(); - System.out.println("I am: " + self.toString()); - Principal peer = sslSocket.getSession().getPeerPrincipal(); - System.out.println("Client is: " + peer.toString()); - - sslSocket.close(); - return null; - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/UnboundSSL.java --- a/test/jdk/sun/security/krb5/auto/UnboundSSL.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/* - * 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. - * - * 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. - */ - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedActionException; -import java.util.HashMap; -import java.util.Map; - -import javax.security.auth.login.LoginException; - -/* - * @test - * @bug 8025123 8194486 - * @summary Checks if an unbound server can handle connections - * only for allowed service principals - * @library /test/lib - * @run main jdk.test.lib.FileInstaller TestHosts TestHosts - * @run main/othervm/policy=unbound.ssl.policy -Djdk.net.hosts.file=TestHosts - * UnboundSSL unbound.ssl.jaas.conf server_star - * @run main/othervm/policy=unbound.ssl.policy -Djdk.net.hosts.file=TestHosts - * UnboundSSL unbound.ssl.jaas.conf server_multiple_principals - */ -public class UnboundSSL { - - public static void main(String[] args) throws IOException, - NoSuchAlgorithmException,LoginException, PrivilegedActionException, - InterruptedException { - UnboundSSL test = new UnboundSSL(); - test.start(args[0], args[1]); - } - - private void start(String jaacConfigFile, String serverJaasConfig) - throws IOException, NoSuchAlgorithmException,LoginException, - PrivilegedActionException, InterruptedException { - - // define principals - String service1host = "service1." + UnboundSSLUtils.HOST; - String service2host = "service2." + UnboundSSLUtils.HOST; - String service3host = "service3." + UnboundSSLUtils.HOST; - String service1Principal = "host/" + service1host + "@" - + UnboundSSLUtils.REALM; - String service2Principal = "host/" + service2host + "@" - + UnboundSSLUtils.REALM; - String service3Principal = "host/" + service3host + "@" - + UnboundSSLUtils.REALM; - - Map<String, String> principals = new HashMap<>(); - principals.put(UnboundSSLUtils.USER_PRINCIPAL, - UnboundSSLUtils.USER_PASSWORD); - principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null); - principals.put(service1Principal, null); - principals.put(service2Principal, null); - principals.put(service3Principal, null); - - System.setProperty("java.security.krb5.conf", - UnboundSSLUtils.KRB5_CONF_FILENAME); - - // start a local KDC instance - KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME, - UnboundSSLUtils.REALM, principals, - UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND); - - System.setProperty("java.security.auth.login.config", - UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile); - System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); - - try (final SSLEchoServer server = SSLEchoServer.init( - UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) { - - // start a server instance - UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig); - - // wait for the server is ready - while (!server.isReady()) { - Thread.sleep(UnboundSSLUtils.DELAY); - } - - int port = server.getPort(); - - // run clients - - // the server should have a permission to handle a request - // with this service principal (there should be an appropriate - // javax.security.auth.kerberos.ServicePermission in policy file) - System.out.println("Connect: SNI hostname = " + service1host - + ", successful connection is expected"); - SSLClient.init(UnboundSSLUtils.HOST, port, - UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect(); - - // the server should NOT have a permission to handle a request - // with this service principal (there should be an appropriate - // javax.security.auth.kerberos.ServicePermission in policy file) - // handshake failures is expected - System.out.println("Connect: SNI hostname = " + service2host - + ", connection failure is expected"); - try { - SSLClient.init(UnboundSSLUtils.HOST, port, - UnboundSSLUtils.TLS_KRB5_FILTER, service2host) - .connect(); - throw new RuntimeException("Test failed: " - + "expected IOException not thrown"); - } catch (IOException e) { - System.out.println("Expected exception: " + e); - } - - // the server should have a permission to handle a request - // with this service principal (there should be an appropriate - // javax.security.auth.kerberos.ServicePermission in policy file) - System.out.println("Connect: SNI hostname = " + service3host - + ", successful connection is expected"); - SSLClient.init(UnboundSSLUtils.HOST, port, - UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect(); - } - - System.out.println("Test passed"); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/UnboundSSLMultipleKeys.java --- a/test/jdk/sun/security/krb5/auto/UnboundSSLMultipleKeys.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * 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. - * - * 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. - */ - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedActionException; -import java.util.HashMap; -import java.util.Map; - -import javax.security.auth.login.LoginException; - -/* - * @test - * @bug 8025123 8194486 - * @summary Checks if an unbound server pick up a correct key from keytab - * @library /test/lib - * @run main jdk.test.lib.FileInstaller TestHosts TestHosts - * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLMultipleKeys - * unbound.ssl.jaas.conf server_star - * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLMultipleKeys - * unbound.ssl.jaas.conf server_multiple_principals - */ -public class UnboundSSLMultipleKeys { - - public static void main(String[] args) - throws IOException, NoSuchAlgorithmException, LoginException, - PrivilegedActionException, InterruptedException { - UnboundSSLMultipleKeys test = new UnboundSSLMultipleKeys(); - test.start(args[0], args[1]); - } - - private void start(String jaacConfigFile, String serverJaasConfig) - throws IOException, NoSuchAlgorithmException, LoginException, - PrivilegedActionException, InterruptedException { - - // define service principals - String service1host = "service1." + UnboundSSLUtils.HOST; - String service2host = "service2." + UnboundSSLUtils.HOST; - String service3host = "service3." + UnboundSSLUtils.HOST; - String service1Principal = "host/" + service1host + "@" - + UnboundSSLUtils.REALM; - String service2Principal = "host/" + service2host + "@" - + UnboundSSLUtils.REALM; - String service3Principal = "host/" + service3host + "@" - + UnboundSSLUtils.REALM; - - Map<String, String> principals = new HashMap<>(); - principals.put(UnboundSSLUtils.USER_PRINCIPAL, - UnboundSSLUtils.USER_PASSWORD); - principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, "pass"); - principals.put(service1Principal, "pass0"); - principals.put(service1Principal, "pass1"); - principals.put(service1Principal, "pass2"); - principals.put(service2Principal, "pass"); - principals.put(service3Principal, "pass"); - - System.setProperty("java.security.krb5.conf", - UnboundSSLUtils.KRB5_CONF_FILENAME); - - /* - * Start a local KDC instance - * - * Keytab file contains 3 keys (with different KVNO) for service1 - * principal, but password for only one key is the same with the record - * for service1 principal in KDC. - */ - KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME, - UnboundSSLUtils.REALM, principals, - UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND); - - System.setProperty("java.security.auth.login.config", - UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile); - System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); - - // start an SSL server instance - try (SSLEchoServer server = SSLEchoServer.init( - UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) { - - UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig); - - // wait for the server is ready - while (!server.isReady()) { - Thread.sleep(UnboundSSLUtils.DELAY); - } - - // run a client - System.out.println("Successful connection is expected"); - SSLClient.init(UnboundSSLUtils.HOST, server.getPort(), - UnboundSSLUtils.TLS_KRB5_FILTER, service1host).connect(); - } - - System.out.println("Test passed"); - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java --- a/test/jdk/sun/security/krb5/auto/UnboundSSLPrincipalProperty.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/* - * 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. - * - * 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. - */ - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedActionException; -import java.util.HashMap; -import java.util.Map; -import javax.security.auth.login.LoginException; - -/* - * @test - * @bug 8025123 8194486 - * @summary Checks if an unbound server uses a service principal - * from sun.security.krb5.principal system property if specified - * @library /test/lib - * @run main jdk.test.lib.FileInstaller TestHosts TestHosts - * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLPrincipalProperty - * unbound.ssl.jaas.conf server_star - * @run main/othervm -Djdk.net.hosts.file=TestHosts UnboundSSLPrincipalProperty - * unbound.ssl.jaas.conf server_multiple_principals - */ -public class UnboundSSLPrincipalProperty { - - public static void main(String[] args) throws IOException, - NoSuchAlgorithmException,LoginException, PrivilegedActionException, - InterruptedException { - UnboundSSLPrincipalProperty test = new UnboundSSLPrincipalProperty(); - test.start(args[0], args[1]); - } - - public void start(String jaacConfigFile, String serverJaasConfig) - throws IOException, NoSuchAlgorithmException,LoginException, - PrivilegedActionException, InterruptedException { - - // define principals - String service1host = "service1." + UnboundSSLUtils.HOST; - String service3host = "service3." + UnboundSSLUtils.HOST; - String service1Principal = "host/" + service1host + "@" - + UnboundSSLUtils.REALM; - String service3Principal = "host/" + service3host - + "@" + UnboundSSLUtils.REALM; - - Map<String, String> principals = new HashMap<>(); - principals.put(UnboundSSLUtils.USER_PRINCIPAL, - UnboundSSLUtils.USER_PASSWORD); - principals.put(UnboundSSLUtils.KRBTGT_PRINCIPAL, null); - principals.put(service1Principal, null); - principals.put(service3Principal, null); - - System.setProperty("java.security.krb5.conf", - UnboundSSLUtils.KRB5_CONF_FILENAME); - - // start a local KDC instance - KDC.startKDC(UnboundSSLUtils.HOST, UnboundSSLUtils.KRB5_CONF_FILENAME, - UnboundSSLUtils.REALM, principals, - UnboundSSLUtils.KTAB_FILENAME, KDC.KtabMode.APPEND); - - System.setProperty("java.security.auth.login.config", - UnboundSSLUtils.TEST_SRC + UnboundSSLUtils.FS + jaacConfigFile); - System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); - - // start an SSL server instance - try (final SSLEchoServer server = SSLEchoServer.init( - UnboundSSLUtils.TLS_KRB5_FILTER, UnboundSSLUtils.SNI_PATTERN)) { - - // specify a service principal for the server - System.setProperty("sun.security.krb5.principal", - service3Principal); - - UnboundSSLUtils.startServerWithJaas(server, serverJaasConfig); - - // wait for the server is ready - while (!server.isReady()) { - Thread.sleep(UnboundSSLUtils.DELAY); - } - - int port = server.getPort(); - - // connetion failure is expected - // since service3 principal was specified to use by the server - System.out.println("Connect: SNI hostname = " + service1host - + ", connection failure is expected"); - try { - SSLClient.init(UnboundSSLUtils.HOST, port, - UnboundSSLUtils.TLS_KRB5_FILTER, service1host) - .connect(); - throw new RuntimeException("Test failed: " - + "expected IOException not thrown"); - } catch (IOException e) { - System.out.println("Expected exception: " + e); - } - - System.out.println("Connect: SNI hostname = " + service3host - + ", successful connection is expected"); - SSLClient.init(UnboundSSLUtils.HOST, port, - UnboundSSLUtils.TLS_KRB5_FILTER, service3host).connect(); - } - - System.out.println("Test passed"); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/UnboundSSLUtils.java --- a/test/jdk/sun/security/krb5/auto/UnboundSSLUtils.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,292 +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. - * - * 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. - */ - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import javax.net.ssl.SNIHostName; -import javax.net.ssl.SNIMatcher; -import javax.net.ssl.SNIServerName; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -/* - * Helper class for unbound krb5 tests. - */ -class UnboundSSLUtils { - - static final String KTAB_FILENAME = "krb5.keytab.data"; - static final String HOST = "localhost"; - static final String REALM = "TEST.REALM"; - static final String KRBTGT_PRINCIPAL = "krbtgt/" + REALM; - static final String TEST_SRC = System.getProperty("test.src", "."); - static final String TLS_KRB5_FILTER = "TLS_KRB5"; - static final String USER = "USER"; - static final String USER_PASSWORD = "password"; - static final String FS = System.getProperty("file.separator"); - static final String SNI_PATTERN = ".*"; - static final String USER_PRINCIPAL = USER + "@" + REALM; - static final String KRB5_CONF_FILENAME = "krb5.conf"; - static final int DELAY = 1000; - - static String[] filterStringArray(String[] src, String filter) { - return Arrays.stream(src).filter((item) -> item.startsWith(filter)) - .toArray(size -> new String[size]); - } - - /* - * The method does JAAS login, - * and runs an SSL server in the JAAS context. - */ - static void startServerWithJaas(final SSLEchoServer server, - String config) throws LoginException, PrivilegedActionException { - LoginContext context = new LoginContext(config); - context.login(); - System.out.println("Server: successful authentication"); - Subject.doAs(context.getSubject(), - (PrivilegedExceptionAction<Object>) () -> { - SSLEchoServer.startServer(server); - return null; - }); - } - -} - -class SSLClient { - - private final static byte[][] arrays = { - new byte[] {-1, 0, 2}, - new byte[] {} - }; - - private final SSLSocket socket; - - private SSLClient(SSLSocket socket) { - this.socket = socket; - } - - void connect() throws IOException { - System.out.println("Client: connect to server"); - try (BufferedInputStream bis = new BufferedInputStream( - socket.getInputStream()); - BufferedOutputStream bos = new BufferedOutputStream( - socket.getOutputStream())) { - - for (byte[] bytes : arrays) { - System.out.println("Client: send byte array: " - + Arrays.toString(bytes)); - - bos.write(bytes); - bos.flush(); - - byte[] recieved = new byte[bytes.length]; - int read = bis.read(recieved, 0, bytes.length); - if (read < 0) { - throw new IOException("Client: couldn't read a response"); - } - - System.out.println("Client: recieved byte array: " - + Arrays.toString(recieved)); - - if (!Arrays.equals(bytes, recieved)) { - throw new IOException("Client: sent byte array " - + "is not equal with recieved byte array"); - } - } - socket.getSession().invalidate(); - } finally { - if (!socket.isClosed()) { - socket.close(); - } - } - } - - static SSLClient init(String host, int port, String cipherSuiteFilter, - String sniHostName) throws NoSuchAlgorithmException, IOException { - SSLContext sslContext = SSLContext.getDefault(); - SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); - SSLSocket socket = (SSLSocket) ssf.createSocket(host, port); - SSLParameters params = new SSLParameters(); - - if (cipherSuiteFilter != null) { - String[] cipherSuites = UnboundSSLUtils.filterStringArray( - ssf.getSupportedCipherSuites(), cipherSuiteFilter); - System.out.println("Client: enabled cipher suites: " - + Arrays.toString(cipherSuites)); - params.setCipherSuites(cipherSuites); - } - - if (sniHostName != null) { - System.out.println("Client: set SNI hostname: " + sniHostName); - SNIHostName serverName = new SNIHostName(sniHostName); - List<SNIServerName> serverNames = new ArrayList<>(); - serverNames.add(serverName); - params.setServerNames(serverNames); - } - - socket.setSSLParameters(params); - - return new SSLClient(socket); - } - -} - -class SSLEchoServer implements Runnable, AutoCloseable { - - private final SSLServerSocket ssocket; - private volatile boolean stopped = false; - private volatile boolean ready = false; - - /* - * Starts the server in a separate thread. - */ - static void startServer(SSLEchoServer server) { - Thread serverThread = new Thread(server, "SSL echo server thread"); - serverThread.setDaemon(true); - serverThread.start(); - } - - private SSLEchoServer(SSLServerSocket ssocket) { - this.ssocket = ssocket; - } - - /* - * Main server loop. - */ - @Override - public void run() { - System.out.println("Server: started"); - while (!stopped) { - ready = true; - try (SSLSocket socket = (SSLSocket) ssocket.accept()) { - System.out.println("Server: client connection accepted"); - try ( - BufferedInputStream bis = new BufferedInputStream( - socket.getInputStream()); - BufferedOutputStream bos = new BufferedOutputStream( - socket.getOutputStream()) - ) { - byte[] buffer = new byte[1024]; - int read; - while ((read = bis.read(buffer)) > 0) { - bos.write(buffer, 0, read); - System.out.println("Server: recieved " + read - + " bytes: " - + Arrays.toString(Arrays.copyOf(buffer, read))); - bos.flush(); - } - } - } catch (IOException e) { - if (stopped) { - // stopped == true means that stop() method was called, - // so just ignore the exception, and finish the loop - break; - } - System.out.println("Server: couldn't accept client connection: " - + e); - } - } - System.out.println("Server: finished"); - } - - boolean isReady() { - return ready; - } - - void stop() { - stopped = true; - ready = false; - - // close the server socket to interupt accept() method - try { - if (!ssocket.isClosed()) { - ssocket.close(); - } - } catch (IOException e) { - throw new RuntimeException("Unexpected exception: " + e); - } - } - - @Override - public void close() { - stop(); - } - - int getPort() { - return ssocket.getLocalPort(); - } - - /* - * Creates server instance. - * - * @param cipherSuiteFilter Filter for enabled cipher suites - * @param sniMatcherPattern Pattern for SNI server hame - */ - static SSLEchoServer init(String cipherSuiteFilter, - String sniPattern) throws NoSuchAlgorithmException, IOException { - SSLContext context = SSLContext.getDefault(); - SSLServerSocketFactory ssf = - (SSLServerSocketFactory) context.getServerSocketFactory(); - SSLServerSocket ssocket = - (SSLServerSocket) ssf.createServerSocket(0); - - // specify enabled cipher suites - if (cipherSuiteFilter != null) { - String[] ciphersuites = UnboundSSLUtils.filterStringArray( - ssf.getSupportedCipherSuites(), cipherSuiteFilter); - System.out.println("Server: enabled cipher suites: " - + Arrays.toString(ciphersuites)); - ssocket.setEnabledCipherSuites(ciphersuites); - } - - // specify SNI matcher pattern - if (sniPattern != null) { - System.out.println("Server: set SNI matcher: " + sniPattern); - SNIMatcher matcher = SNIHostName.createSNIMatcher(sniPattern); - List<SNIMatcher> matchers = new ArrayList<>(); - matchers.add(matcher); - SSLParameters params = ssocket.getSSLParameters(); - params.setSNIMatchers(matchers); - ssocket.setSSLParameters(params); - } - - return new SSLEchoServer(ssocket); - } - -} - diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/unbound.ssl.jaas.conf --- a/test/jdk/sun/security/krb5/auto/unbound.ssl.jaas.conf Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -com.sun.net.ssl.client { - com.sun.security.auth.module.Krb5LoginModule required - principal="USER@TEST.REALM" - doNotPrompt=true - useKeyTab=true - keyTab="krb5.keytab.data"; -}; - -server_star { - com.sun.security.auth.module.Krb5LoginModule required - principal="*" - isInitiator=false - useKeyTab=true - keyTab="krb5.keytab.data" - storeKey=true; -}; - -server_multiple_principals { - com.sun.security.auth.module.Krb5LoginModule required - principal="host/service1.localhost@TEST.REALM" - isInitiator=false - useKeyTab=true - keyTab="krb5.keytab.data" - storeKey=true; - - com.sun.security.auth.module.Krb5LoginModule required - principal="host/service2.localhost@TEST.REALM" - isInitiator=false - useKeyTab=true - keyTab="krb5.keytab.data" - storeKey=true; - - com.sun.security.auth.module.Krb5LoginModule required - principal="host/service3.localhost@TEST.REALM" - isInitiator=false - useKeyTab=true - keyTab="krb5.keytab.data" - storeKey=true; -}; - diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/krb5/auto/unbound.ssl.policy --- a/test/jdk/sun/security/krb5/auto/unbound.ssl.policy Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -grant { - permission java.util.PropertyPermission "*", "read,write"; - permission java.net.SocketPermission "*:*", "listen,resolve,accept,connect"; - permission java.io.FilePermission "/-", "read"; - permission java.io.FilePermission "*", "read,write,delete"; - permission java.lang.RuntimePermission "accessDeclaredMembers"; - permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; - permission java.lang.RuntimePermission "accessClassInPackage.*"; - permission javax.security.auth.AuthPermission "doAs"; - permission javax.security.auth.AuthPermission "getSubject"; - permission javax.security.auth.AuthPermission - "createLoginContext.server_star"; - permission javax.security.auth.AuthPermission - "createLoginContext.server_multiple_principals"; - permission javax.security.auth.AuthPermission "modifyPrincipals"; - permission javax.security.auth.PrivateCredentialPermission "javax.security.auth.kerberos.KeyTab java.security.Principal \"krb5.keytab.data\"", "read"; - - // clients have a permission to use all service principals - permission javax.security.auth.kerberos.ServicePermission "*", "initiate"; - - // server has a service permission - // to accept only service1 and service3 principals - permission javax.security.auth.kerberos.ServicePermission - "host/service1.localhost@TEST.REALM", "accept"; - permission javax.security.auth.kerberos.ServicePermission - "host/service3.localhost@TEST.REALM", "accept"; -}; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java --- a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.java Mon Jun 25 13:41:39 2018 -0700 @@ -197,6 +197,9 @@ } public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + Security.setProperty("jdk.certpath.disabledAlgorithms", ""); + // Get the customized arguments. parseArguments(args); main(new ClientAuth()); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh --- a/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/pkcs11/KeyStore/ClientAuth.sh Mon Jun 25 13:41:39 2018 -0700 @@ -55,46 +55,46 @@ ARCH=`isainfo` case "$ARCH" in sparc* ) - FS="/" - PS=":" - CP="${FS}bin${FS}cp" - CHMOD="${FS}bin${FS}chmod" - ;; + FS="/" + PS=":" + CP="${FS}bin${FS}cp" + CHMOD="${FS}bin${FS}chmod" + ;; i[3-6]86 ) - FS="/" - PS=":" - CP="${FS}bin${FS}cp" - CHMOD="${FS}bin${FS}chmod" - ;; + FS="/" + PS=":" + CP="${FS}bin${FS}cp" + CHMOD="${FS}bin${FS}chmod" + ;; amd64* ) - FS="/" - PS=":" - CP="${FS}bin${FS}cp" - CHMOD="${FS}bin${FS}chmod" - ;; + FS="/" + PS=":" + CP="${FS}bin${FS}cp" + CHMOD="${FS}bin${FS}chmod" + ;; * ) # ?itanium? ) # amd64* ) - echo "Unsupported System: Solaris ${ARCH}" - exit 0; - ;; + echo "Unsupported System: Solaris ${ARCH}" + exit 0; + ;; esac ;; Linux ) ARCH=`uname -m` case "$ARCH" in i[3-6]86 ) - FS="/" - PS=":" - CP="${FS}bin${FS}cp" - CHMOD="${FS}bin${FS}chmod" - ;; + FS="/" + PS=":" + CP="${FS}bin${FS}cp" + CHMOD="${FS}bin${FS}chmod" + ;; * ) # ia64 ) # x86_64 ) - echo "Unsupported System: Linux ${ARCH}" - exit 0; - ;; + echo "Unsupported System: Linux ${ARCH}" + exit 0; + ;; esac ;; Windows* ) @@ -126,23 +126,44 @@ # compile test ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ - -classpath ${TESTSRC} \ - -d ${TESTCLASSES} \ - ${TESTSRC}${FS}ClientAuth.java \ - ${TESTSRC}${FS}..${FS}PKCS11Test.java + -classpath ${TESTSRC} \ + -d ${TESTCLASSES} \ + ${TESTSRC}${FS}ClientAuth.java \ + ${TESTSRC}${FS}..${FS}PKCS11Test.java # run test -echo "Run ClientAuth ..." +echo "Run ClientAuth TLSv1 ..." ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \ - -classpath ${TESTCLASSES} \ - -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \ - -DCUSTOM_DB_DIR=${TESTCLASSES} \ - -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \ - -DNO_DEFAULT=true \ - -DNO_DEIMOS=true \ - -Dtest.src=${TESTSRC} \ - -Dtest.classes=${TESTCLASSES} \ - ClientAuth + -classpath ${TESTCLASSES} \ + -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \ + -DCUSTOM_DB_DIR=${TESTCLASSES} \ + -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \ + -DNO_DEFAULT=true \ + -DNO_DEIMOS=true \ + -Dtest.src=${TESTSRC} \ + -Dtest.classes=${TESTCLASSES} \ + ClientAuth TLSv1 + +# save error status +status=$? + +# return if failed +if [ "${status}" != "0" ] ; then + exit $status +fi + +# run test +echo "Run ClientAuth TLSv1.1 ..." +${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \ + -classpath ${TESTCLASSES} \ + -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \ + -DCUSTOM_DB_DIR=${TESTCLASSES} \ + -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \ + -DNO_DEFAULT=true \ + -DNO_DEIMOS=true \ + -Dtest.src=${TESTSRC} \ + -Dtest.classes=${TESTCLASSES} \ + ClientAuth TLSv1.1 # save error status status=$? @@ -155,15 +176,15 @@ # run test with specified TLS protocol and cipher suite echo "Run ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA" ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \ - -classpath ${TESTCLASSES} \ - -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \ - -DCUSTOM_DB_DIR=${TESTCLASSES} \ - -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \ - -DNO_DEFAULT=true \ - -DNO_DEIMOS=true \ - -Dtest.src=${TESTSRC} \ - -Dtest.classes=${TESTCLASSES} \ - ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA + -classpath ${TESTCLASSES} \ + -DDIR=${TESTSRC}${FS}ClientAuthData${FS} \ + -DCUSTOM_DB_DIR=${TESTCLASSES} \ + -DCUSTOM_P11_CONFIG=${TESTSRC}${FS}ClientAuthData${FS}p11-nss.txt \ + -DNO_DEFAULT=true \ + -DNO_DEIMOS=true \ + -Dtest.src=${TESTSRC} \ + -Dtest.classes=${TESTCLASSES} \ + ClientAuth TLSv1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA # save error status status=$? diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/pkcs11/sslecc/CipherTest.java --- a/test/jdk/sun/security/pkcs11/sslecc/CipherTest.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/pkcs11/sslecc/CipherTest.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -105,11 +105,11 @@ public static class TestParameters { - String cipherSuite; - String protocol; + CipherSuite cipherSuite; + Protocol protocol; String clientAuth; - TestParameters(String cipherSuite, String protocol, + TestParameters(CipherSuite cipherSuite, Protocol protocol, String clientAuth) { this.cipherSuite = cipherSuite; this.protocol = protocol; @@ -117,7 +117,7 @@ } boolean isEnabled() { - return TLSCipherStatus.isEnabled(cipherSuite, protocol); + return cipherSuite.supportedByProtocol(protocol); } @Override @@ -128,134 +128,6 @@ } return s; } - - static enum TLSCipherStatus { - // cipher suites supported since TLS 1.2 - CS_01("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_02("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_03("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_04("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_05("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0x0303, 0xFFFF), - CS_06("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_07("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - - CS_08("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_09("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_10("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_11("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_12("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_13("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_14("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - - CS_15("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x0303, 0xFFFF), - CS_16("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x0303, 0xFFFF), - CS_17("TLS_RSA_WITH_NULL_SHA256", 0x0303, 0xFFFF), - - CS_20("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_21("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_22("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_23("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_24("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_25("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_26("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_27("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - - CS_28("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_29("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_30("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_31("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_32("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - CS_33("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - - CS_34("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x0303, 0xFFFF), - CS_35("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x0303, 0xFFFF), - - // cipher suites obsoleted since TLS 1.2 - CS_50("SSL_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_51("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_52("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_53("SSL_DH_anon_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_54("TLS_KRB5_WITH_DES_CBC_SHA", 0x0000, 0x0303), - CS_55("TLS_KRB5_WITH_DES_CBC_MD5", 0x0000, 0x0303), - - // cipher suites obsoleted since TLS 1.1 - CS_60("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_61("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_62("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_63("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_64("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_65("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0000, 0x0302), - CS_66("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0000, 0x0302), - CS_67("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x0000, 0x0302), - CS_68("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0000, 0x0302), - CS_69("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0000, 0x0302), - - // ignore TLS_EMPTY_RENEGOTIATION_INFO_SCSV always - CS_99("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0xFFFF, 0x0000); - - // the cipher suite name - final String cipherSuite; - - // supported since protocol version - final int supportedSince; - - // obsoleted since protocol version - final int obsoletedSince; - - TLSCipherStatus(String cipherSuite, - int supportedSince, int obsoletedSince) { - this.cipherSuite = cipherSuite; - this.supportedSince = supportedSince; - this.obsoletedSince = obsoletedSince; - } - - static boolean isEnabled(String cipherSuite, String protocol) { - int versionNumber = toVersionNumber(protocol); - - if (versionNumber < 0) { - return true; // unlikely to happen - } - - for (TLSCipherStatus status : TLSCipherStatus.values()) { - if (cipherSuite.equals(status.cipherSuite)) { - if ((versionNumber < status.supportedSince) || - (versionNumber >= status.obsoletedSince)) { - return false; - } - - return true; - } - } - - return true; - } - - private static int toVersionNumber(String protocol) { - int versionNumber = -1; - - switch (protocol) { - case "SSLv2Hello": - versionNumber = 0x0002; - break; - case "SSLv3": - versionNumber = 0x0300; - break; - case "TLSv1": - versionNumber = 0x0301; - break; - case "TLSv1.1": - versionNumber = 0x0302; - break; - case "TLSv1.2": - versionNumber = 0x0303; - break; - default: - // unlikely to happen - } - - return versionNumber; - } - } } private List<TestParameters> tests; @@ -283,14 +155,21 @@ for (int k = 0; k < clientAuths.length; k++) { String clientAuth = clientAuths[k]; - if ((clientAuth != null) && - (cipherSuite.indexOf("DH_anon") != -1)) { - // no client with anonymous ciphersuites + // no client with anonymous cipher suites. + // TLS_EMPTY_RENEGOTIATION_INFO_SCSV always be skipped. + // TLS 1.3 is skipped due to the signature algorithm, + // exactly MD5withRSA, in the certificates is not allowed. + if ((clientAuth != null && cipherSuite.contains("DH_anon") + || cipherSuite.equals( + CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.name()) + || "TLSv1.3".equals(protocol))) { continue; } - tests.add(new TestParameters(cipherSuite, protocol, - clientAuth)); + tests.add(new TestParameters( + CipherSuite.cipherSuite(cipherSuite), + Protocol.protocol(protocol), + clientAuth)); } } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java --- a/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -31,7 +31,7 @@ * @bug 6405536 * @summary Verify that all ciphersuites work (incl. ECC using NSS crypto) * @author Andreas Sterbenz - * @library .. + * @library .. ../../../../javax/net/ssl/TLSCommon * @library ../../../../java/security/testlibrary * @modules jdk.crypto.cryptoki * @run main/othervm -Djdk.tls.namedGroups="secp256r1,sect193r1" diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java --- a/test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/pkcs11/sslecc/JSSEClient.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -67,8 +67,8 @@ } socket.setSoTimeout(CipherTest.TIMEOUT); - socket.setEnabledCipherSuites(new String[] {params.cipherSuite}); - socket.setEnabledProtocols(new String[] {params.protocol}); + socket.setEnabledCipherSuites(new String[] {params.cipherSuite.name()}); + socket.setEnabledProtocols(new String[] {params.protocol.name}); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); sendRequest(in, out); @@ -76,11 +76,11 @@ SSLSession session = socket.getSession(); session.invalidate(); String cipherSuite = session.getCipherSuite(); - if (params.cipherSuite.equals(cipherSuite) == false) { + if (!params.cipherSuite.name().equals(cipherSuite)) { throw new Exception("Negotiated ciphersuite mismatch: " + cipherSuite + " != " + params.cipherSuite); } String protocol = session.getProtocol(); - if (params.protocol.equals(protocol) == false) { + if (!params.protocol.name.equals(protocol)) { throw new Exception("Negotiated protocol mismatch: " + protocol + " != " + params.protocol); } if (cipherSuite.indexOf("DH_anon") == -1) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java --- a/test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/AppOutputStream/NoExceptionOnClose.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, 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 @@ -21,14 +21,16 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test 1.3 01/03/08 * @bug 4378397 * @summary JSSE socket output stream doesn't throw after socket is closed * @run main/othervm NoExceptionOnClose - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. * @author Jaya Hangal */ @@ -152,11 +154,11 @@ try { sslOS.write(22); sslOS.flush(); - } catch (SocketException socketClosed) { + } catch (SSLException | SocketException socketClosed) { System.out.println("Received \"" + socketClosed.getMessage() + "\" exception as expected"); isSocketClosedThrown = true; - } + } if (!isSocketClosedThrown) { throw new Exception("No Exception thrown on write() after" + " close()"); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java --- a/test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/CipherSuite/SSL_NULL.java Mon Jun 25 13:41:39 2018 -0700 @@ -24,7 +24,8 @@ /** * @test * @bug 4854838 - * @summary Verify that SSL_NULL_WITH_NULL_NULL is returned as ciphersuite if the handshake fails + * @summary Verify that SSL_NULL_WITH_NULL_NULL is returned as ciphersuite + * if the handshake fails * @author Andreas Sterbenz */ @@ -34,7 +35,6 @@ import javax.net.ssl.*; public class SSL_NULL { - private static volatile Boolean result; public static void main(String[] args) throws Exception { @@ -71,15 +71,18 @@ SSLSocketFactory.getDefault().createSocket( "localhost", serverSocket.getLocalPort()); socket.setEnabledCipherSuites( - new String[] { "SSL_RSA_WITH_RC4_128_MD5" }); + new String[] { "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" }); try { OutputStream out = socket.getOutputStream(); out.write(0); out.flush(); throw new RuntimeException("No exception received"); } catch (SSLHandshakeException e) { + System.out.println("Expected handshake exception: " + e); } + System.out.println("client: " + socket.getSession().getCipherSuite()); + // wait for other thread to set result while (result == null) { Thread.sleep(50); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java --- a/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java Mon Jun 25 13:41:39 2018 -0700 @@ -74,7 +74,6 @@ import java.nio.*; import java.util.List; import java.util.ArrayList; -import sun.security.ssl.ProtocolVersion; public class LengthCheckTest { @@ -155,6 +154,7 @@ private static final int TLS_ALERT_UNEXPECTED_MSG = 0x0A; private static final int TLS_ALERT_HANDSHAKE_FAILURE = 0x28; private static final int TLS_ALERT_INTERNAL_ERROR = 0x50; + private static final int TLS_ALERT_ILLEGAL_PARAMETER = 0x2F; public interface HandshakeTest { void execTest() throws Exception; @@ -268,24 +268,24 @@ runDelegatedTasks(serverResult, serverEngine); sTOc.flip(); dumpByteBuffer("SERVER-TO-CLIENT", sTOc); + + // We expect to see the server generate an alert here + serverResult = serverEngine.wrap(serverOut, sTOc); + log("server wrap: ", serverResult); + runDelegatedTasks(serverResult, serverEngine); + sTOc.flip(); + dumpByteBuffer("SERVER-TO-CLIENT", sTOc); } catch (SSLProtocolException ssle) { log("Received expected SSLProtocolException: " + ssle); gotException = true; } - // We expect to see the server generate an alert here - serverResult = serverEngine.wrap(serverOut, sTOc); - log("server wrap: ", serverResult); - runDelegatedTasks(serverResult, serverEngine); - sTOc.flip(); - dumpByteBuffer("SERVER-TO-CLIENT", sTOc); - // At this point we can verify that both an exception // was thrown and the proper action (a TLS alert) was // sent back to the client. if (gotException == false || !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, - TLS_ALERT_UNEXPECTED_MSG)) { + TLS_ALERT_ILLEGAL_PARAMETER)) { throw new SSLException( "Server failed to throw Alert:fatal:internal_error"); } @@ -783,10 +783,9 @@ int ver_major = Byte.toUnsignedInt(bBuf.get()); int ver_minor = Byte.toUnsignedInt(bBuf.get()); int recLen = Short.toUnsignedInt(bBuf.getShort()); - ProtocolVersion pv = ProtocolVersion.valueOf(ver_major, ver_minor); log("===== " + header + " (" + tlsRecType(type) + " / " + - pv + " / " + bufLen + " bytes) ====="); + ver_major + "." + ver_minor + " / " + bufLen + " bytes) ====="); bBuf.reset(); for (int i = 0; i < bufLen; i++) { if (i != 0 && i % 16 == 0) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh --- a/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.sh Mon Jun 25 13:41:39 2018 -0700 @@ -1,7 +1,7 @@ #! /bin/sh # -# Copyright (c) 2012, 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 @@ -54,7 +54,7 @@ ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ ${TESTSRC}${FS}DebugReportsOneExtraByte.java -STRING='main, WRITE: TLSv1 Application Data, length = 8' +STRING='WRITE: TLS10 application_data, length = 8' echo "Examining debug output for the string:" echo "${STRING}" diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java --- a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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. - */ - -/** - * @test - * @bug 8080535 - * @summary Expected size of Character.UnicodeBlock.map is not optimal - * @library /lib/testlibrary - * @modules java.base/java.util:open - * java.base/sun.security.ssl:open - * @build jdk.testlibrary.OptimalCapacity - * @run main OptimalListSize - */ - -import jdk.testlibrary.OptimalCapacity; - -public class OptimalListSize { - public static void main(String[] args) throws Throwable { - OptimalCapacity.ofArrayList( - Class.forName("sun.security.ssl.ExtensionType"), - "knownExtensions", 16); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSDefaultProtocols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSDefaultProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,263 @@ +/* + * 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. + * + * 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @summary Test jdk.tls.client.protocols with DTLS + * @run main/othervm -Djdk.tls.client.protocols="DTLSv1.0" + * CustomizedDTLSDefaultProtocols +*/ + + +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +public class CustomizedDTLSDefaultProtocols { + + enum ContextVersion { + TLS_CV_01("DTLS", + new String[] {"DTLSv1.0"}), + TLS_CV_02("DTLSv1.0", + new String[] {"DTLSv1.0"}), + TLS_CV_03("DTLSv1.2", + new String[] {"DTLSv1.0", "DTLSv1.2"}); + + final String contextVersion; + final String[] enabledProtocols; + final static String[] supportedProtocols = new String[] { + "DTLSv1.0", "DTLSv1.2"}; + + ContextVersion(String contextVersion, String[] enabledProtocols) { + this.contextVersion = contextVersion; + this.enabledProtocols = enabledProtocols; + } + } + + private static boolean checkProtocols(String[] target, String[] expected) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No protocols"); + success = false; + } + + if (!protocolEquals(target, expected)) { + System.out.println("\tError: Expected to get protocols " + + Arrays.toString(expected)); + success = false; + } + System.out.println("\t Protocols found " + Arrays.toString(target)); + + return success; + } + + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + + private static boolean checkCipherSuites(String[] target) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No cipher suites"); + success = false; + } + + return success; + } + + public static void main(String[] args) throws Exception { + // reset the security property to make sure that the algorithms + // and keys used in this test are not disabled. + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + boolean failed = false; + for (ContextVersion cv : ContextVersion.values()) { + System.out.println("Checking SSLContext of " + cv.contextVersion); + SSLContext context = SSLContext.getInstance(cv.contextVersion); + + // Default SSLContext is initialized automatically. + if (!cv.contextVersion.equals("Default")) { + // Use default TK, KM and random. + context.init((KeyManager[])null, (TrustManager[])null, null); + } + + // + // Check SSLContext + // + // Check default SSLParameters of SSLContext + System.out.println("\tChecking default SSLParameters"); + SSLParameters parameters = context.getDefaultSSLParameters(); + + String[] protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + String[] ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // Check supported SSLParameters of SSLContext + System.out.println("\tChecking supported SSLParameters"); + parameters = context.getSupportedSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLEngine + // + // Check SSLParameters of SSLEngine + System.out.println(); + System.out.println("\tChecking SSLEngine of this SSLContext"); + System.out.println("\tChecking SSLEngine.getSSLParameters()"); + SSLEngine engine = context.createSSLEngine(); + engine.setUseClientMode(true); + parameters = engine.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = engine.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = engine.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = engine.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = engine.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLSocket + // + // Check SSLParameters of SSLSocket + System.out.println(); + System.out.println("\tChecking SSLSocket of this SSLContext"); + System.out.println("\tChecking SSLSocket.getSSLParameters()"); + SocketFactory fac = context.getSocketFactory(); + SSLSocket socket = (SSLSocket)fac.createSocket(); + parameters = socket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = socket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = socket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = socket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = socket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLServerSocket + // + // Check SSLParameters of SSLServerSocket + System.out.println(); + System.out.println("\tChecking SSLServerSocket of this SSLContext"); + System.out.println("\tChecking SSLServerSocket.getSSLParameters()"); + SSLServerSocketFactory sf = context.getServerSocketFactory(); + SSLServerSocket ssocket = (SSLServerSocket)sf.createServerSocket(); + parameters = ssocket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = ssocket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = ssocket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = ssocket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = ssocket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + } + + if (failed) { + throw new Exception("Run into problems, see log for more details"); + } else { + System.out.println("\t... Success"); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSServerDefaultProtocols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDTLSServerDefaultProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,283 @@ +/* + * 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. + * + * 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @summary Test jdk.tls.server.protocols with DTLS + * @run main/othervm -Djdk.tls.server.protocols="DTLSv1.0" + * CustomizedDTLSServerDefaultProtocols + */ + +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; + +public class CustomizedDTLSServerDefaultProtocols { + + final static String[] supportedProtocols = new String[]{ + "DTLSv1.0", "DTLSv1.2"}; + + enum ContextVersion { + TLS_CV_01("DTLS", + new String[]{"DTLSv1.0"}, + supportedProtocols), + TLS_CV_02("DTLSv1.0", + supportedProtocols, + new String[]{"DTLSv1.0"}), + TLS_CV_03("DTLS1.2", + supportedProtocols, + supportedProtocols); + + final String contextVersion; + final String[] serverEnabledProtocols; + final String[] clientEnabledProtocols; + + ContextVersion(String contextVersion, String[] serverEnabledProtocols, + String[] clientEnabledProtocols) { + this.contextVersion = contextVersion; + this.serverEnabledProtocols = serverEnabledProtocols; + this.clientEnabledProtocols = clientEnabledProtocols; + } + } + + private static boolean checkProtocols(String[] target, String[] expected) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No protocols"); + success = false; + } + + if (!protocolEquals(target, expected)) { + System.out.println("\tError: Expected to get protocols " + + Arrays.toString(expected)); + success = false; + } + System.out.println("\t Protocols found " + Arrays.toString(target)); + return success; + } + + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + + private static boolean checkCipherSuites(String[] target) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No cipher suites"); + success = false; + } + + return success; + } + + public static void main(String[] args) throws Exception { + // reset the security property to make sure that the algorithms + // and keys used in this test are not disabled. + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + System.out.println("jdk.tls.client.protocols = " + + System.getProperty("jdk.tls.client.protocols")); + System.out.println("jdk.tls.server.protocols = "+ + System.getProperty("jdk.tls.server.protocols")); + Test(); + } + + static void Test() throws Exception { + boolean failed = false; + + SSLContext context; + for (ContextVersion cv : ContextVersion.values()) { + System.out.println("Checking SSLContext of " + cv.contextVersion); + try { + context = SSLContext.getInstance(cv.contextVersion); + } catch (NoSuchAlgorithmException e) { + if (cv.contextVersion.compareToIgnoreCase("DTLS1.2") == 0) { + System.out.println("Exception expected: " + e.getMessage()); + continue; + } + throw e; + } + // Default SSLContext is initialized automatically. + if (!cv.contextVersion.equals("Default")) { + // Use default TK, KM and random. + context.init(null, null, null); + } + + // + // Check SSLContext + // + // Check default SSLParameters of SSLContext + System.out.println("\tChecking default SSLParameters"); + SSLParameters parameters = context.getDefaultSSLParameters(); + + String[] protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + String[] ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // Check supported SSLParameters of SSLContext + System.out.println("\tChecking supported SSLParameters"); + parameters = context.getSupportedSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLEngine + // + // Check SSLParameters of SSLEngine + System.out.println(); + System.out.println("\tChecking SSLEngine of this SSLContext"); + System.out.println("\tChecking SSLEngine.getSSLParameters()"); + SSLEngine engine = context.createSSLEngine(); + engine.setUseClientMode(true); + parameters = engine.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = engine.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = engine.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = engine.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = engine.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLSocket + // + // Check SSLParameters of SSLSocket + System.out.println(); + System.out.println("\tChecking SSLSocket of this SSLContext"); + System.out.println("\tChecking SSLSocket.getSSLParameters()"); + SocketFactory fac = context.getSocketFactory(); + SSLSocket socket = (SSLSocket) fac.createSocket(); + parameters = socket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLSocket.getEnabledProtocols()"); + protocols = socket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + System.out.println("\tChecking SSLSocket.getEnabledCipherSuites()"); + ciphers = socket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLSocket.getSupportedProtocols()"); + protocols = socket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLSocket.getSupportedCipherSuites()"); + ciphers = socket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLServerSocket + // + // Check SSLParameters of SSLServerSocket + System.out.println(); + System.out.println("\tChecking SSLServerSocket of this SSLContext"); + System.out.println("\tChecking SSLServerSocket.getSSLParameters()"); + SSLServerSocketFactory sf = context.getServerSocketFactory(); + SSLServerSocket ssocket = (SSLServerSocket) sf.createServerSocket(); + parameters = ssocket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.serverEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = ssocket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.serverEnabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = ssocket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = ssocket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = ssocket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + if (failed) { + throw new Exception("Run into problems, see log for more details"); + } else { + System.out.println("\t... Success"); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java --- a/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedDefaultProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -32,13 +32,23 @@ * CustomizedDefaultProtocols */ -import javax.net.*; -import javax.net.ssl.*; +import java.security.Security; import java.util.Arrays; -import java.security.Security; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; public class CustomizedDefaultProtocols { - static enum ContextVersion { + enum ContextVersion { TLS_CV_01("SSL", new String[] {"SSLv3", "TLSv1", "TLSv1.1"}), TLS_CV_02("TLS", @@ -51,13 +61,15 @@ new String[] {"SSLv3", "TLSv1", "TLSv1.1"}), TLS_CV_06("TLSv1.2", new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), - TLS_CV_07("Default", + TLS_CV_07("TLSv1.3", + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_08("Default", new String[] {"SSLv3", "TLSv1", "TLSv1.1"}); final String contextVersion; final String[] enabledProtocols; final static String[] supportedProtocols = new String[] { - "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}; + "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; ContextVersion(String contextVersion, String[] enabledProtocols) { this.contextVersion = contextVersion; @@ -72,17 +84,33 @@ success = false; } - if (!Arrays.equals(target, expected)) { + if (!protocolEquals(target, expected)) { System.out.println("\tError: Expected to get protocols " + Arrays.toString(expected)); - System.out.println("\tError: The actual protocols " + - Arrays.toString(target)); success = false; } + System.out.println("\t Protocols found " + Arrays.toString(target)); return success; } + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + private static boolean checkCipherSuites(String[] target) { boolean success = true; if (target.length == 0) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/CustomizedServerDefaultProtocols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/CustomizedServerDefaultProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,289 @@ +/* + * 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. + * + * 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @summary Test jdk.tls.server.protocols with TLS + * @run main/othervm -Djdk.tls.server.protocols="SSLv3,TLSv1,TLSv1.1" + * CustomizedServerDefaultProtocols + */ + +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; + +public class CustomizedServerDefaultProtocols { + + final static String[] supportedProtocols = new String[]{ + "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; + + enum ContextVersion { + TLS_CV_01("SSL", + new String[]{"SSLv3", "TLSv1", "TLSv1.1"}, + new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_02("TLS", + new String[]{"SSLv3", "TLSv1", "TLSv1.1"}, + new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_03("SSLv3", + supportedProtocols, + new String[]{"SSLv3", "TLSv1"}), + TLS_CV_04("TLSv1", + supportedProtocols, + new String[]{"SSLv3", "TLSv1"}), + TLS_CV_05("TLSv1.1", + supportedProtocols, + new String[]{"SSLv3", "TLSv1", "TLSv1.1"}), + TLS_CV_06("TLSv1.2", + supportedProtocols, + new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), + TLS_CV_07("TLSv1.3", + supportedProtocols, + new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_08("Default", + new String[]{"SSLv3", "TLSv1", "TLSv1.1"}, + new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); + + final String contextVersion; + final String[] serverEnabledProtocols; + final String[] clientEnabledProtocols; + + ContextVersion(String contextVersion, String[] serverEnabledProtocols, + String[] clientEnabledProtocols) { + this.contextVersion = contextVersion; + this.serverEnabledProtocols = serverEnabledProtocols; + this.clientEnabledProtocols = clientEnabledProtocols; + } + } + + private static boolean checkProtocols(String[] target, String[] expected) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No protocols"); + success = false; + } + + if (!protocolEquals(target, expected)) { + System.out.println("\tError: Expected to get protocols " + + Arrays.toString(expected)); + success = false; + } + System.out.println("\t Protocols found " + Arrays.toString(target)); + return success; + } + + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + + private static boolean checkCipherSuites(String[] target) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No cipher suites"); + success = false; + } + + return success; + } + + public static void main(String[] args) throws Exception { + // reset the security property to make sure that the algorithms + // and keys used in this test are not disabled. + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + System.out.println("jdk.tls.client.protocols = " + + System.getProperty("jdk.tls.client.protocols")); + System.out.println("jdk.tls.server.protocols = "+ + System.getProperty("jdk.tls.server.protocols")); + Test(); + } + + static void Test() throws Exception { + boolean failed = false; + + for (ContextVersion cv : ContextVersion.values()) { + System.out.println("Checking SSLContext of " + cv.contextVersion); + SSLContext context = SSLContext.getInstance(cv.contextVersion); + + // Default SSLContext is initialized automatically. + if (!cv.contextVersion.equals("Default")) { + // Use default TK, KM and random. + context.init(null, null, null); + } + + // + // Check SSLContext + // + // Check default SSLParameters of SSLContext + System.out.println("\tChecking default SSLParameters"); + SSLParameters parameters = context.getDefaultSSLParameters(); + + String[] protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + String[] ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // Check supported SSLParameters of SSLContext + System.out.println("\tChecking supported SSLParameters"); + parameters = context.getSupportedSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLEngine + // + // Check SSLParameters of SSLEngine + System.out.println(); + System.out.println("\tChecking SSLEngine of this SSLContext"); + System.out.println("\tChecking SSLEngine.getSSLParameters()"); + SSLEngine engine = context.createSSLEngine(); + engine.setUseClientMode(true); + parameters = engine.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = engine.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = engine.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = engine.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = engine.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLSocket + // + // Check SSLParameters of SSLSocket + System.out.println(); + System.out.println("\tChecking SSLSocket of this SSLContext"); + System.out.println("\tChecking SSLSocket.getSSLParameters()"); + SocketFactory fac = context.getSocketFactory(); + SSLSocket socket = (SSLSocket) fac.createSocket(); + parameters = socket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLSocket.getEnabledProtocols()"); + protocols = socket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.clientEnabledProtocols); + + System.out.println("\tChecking SSLSocket.getEnabledCipherSuites()"); + ciphers = socket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLSocket.getSupportedProtocols()"); + protocols = socket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLSocket.getSupportedCipherSuites()"); + ciphers = socket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLServerSocket + // + // Check SSLParameters of SSLServerSocket + System.out.println(); + System.out.println("\tChecking SSLServerSocket of this SSLContext"); + System.out.println("\tChecking SSLServerSocket.getSSLParameters()"); + SSLServerSocketFactory sf = context.getServerSocketFactory(); + SSLServerSocket ssocket = (SSLServerSocket) sf.createServerSocket(); + parameters = ssocket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.serverEnabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = ssocket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.serverEnabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = ssocket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = ssocket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = ssocket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + if (failed) { + throw new Exception("Run into problems, see log for more details"); + } else { + System.out.println("\t... Success"); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/DefaultDTLSEnabledProtocols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/DefaultDTLSEnabledProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2013, 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. + * + * 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @summary Test jdk.tls.client.protocols with DTLS + * @run main/othervm DefaultDTLSEnabledProtocols + */ + +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; + +public class DefaultDTLSEnabledProtocols { + static enum ContextVersion { + TLS_CV_01("DTLS", + new String[] {"DTLSv1.0", "DTLSv1.2"}), + TLS_CV_02("DTLSv1.0", + new String[] {"DTLSv1.0"}), + TLS_CV_03("DTLSv1.2", + new String[] {"DTLSv1.0", "DTLSv1.2"}); + + final String contextVersion; + final String[] enabledProtocols; + final static String[] supportedProtocols = new String[] { + "DTLSv1.0", "DTLSv1.2"}; + + ContextVersion(String contextVersion, String[] enabledProtocols) { + this.contextVersion = contextVersion; + this.enabledProtocols = enabledProtocols; + } + } + + private static boolean checkProtocols(String[] target, String[] expected) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No protocols"); + success = false; + } + + if (!protocolEquals(target, expected)) { + System.out.println("\tError: Expected to get protocols " + + Arrays.toString(expected)); + success = false; + } + System.out.println("\t Protocols found " + Arrays.toString(target)); + + return success; + } + + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + + private static boolean checkCipherSuites(String[] target) { + boolean success = true; + if (target.length == 0) { + System.out.println("\tError: No cipher suites"); + success = false; + } + + return success; + } + + public static void main(String[] args) throws Exception { + // reset the security property to make sure that the algorithms + // and keys used in this test are not disabled. + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + boolean failed = false; + for (ContextVersion cv : ContextVersion.values()) { + System.out.println("Checking SSLContext of " + cv.contextVersion); + SSLContext context = SSLContext.getInstance(cv.contextVersion); + + // Default SSLContext is initialized automatically. + if (!cv.contextVersion.equals("Default")) { + // Use default TK, KM and random. + context.init((KeyManager[])null, (TrustManager[])null, null); + } + + // + // Check SSLContext + // + // Check default SSLParameters of SSLContext + System.out.println("\tChecking default SSLParameters"); + SSLParameters parameters = context.getDefaultSSLParameters(); + + String[] protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + String[] ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // Check supported SSLParameters of SSLContext + System.out.println("\tChecking supported SSLParameters"); + parameters = context.getSupportedSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLEngine + // + // Check SSLParameters of SSLEngine + System.out.println(); + System.out.println("\tChecking SSLEngine of this SSLContext"); + System.out.println("\tChecking SSLEngine.getSSLParameters()"); + SSLEngine engine = context.createSSLEngine(); + engine.setUseClientMode(true); + parameters = engine.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = engine.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = engine.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = engine.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = engine.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLSocket + // + // Check SSLParameters of SSLSocket + System.out.println(); + System.out.println("\tChecking SSLSocket of this SSLContext"); + System.out.println("\tChecking SSLSocket.getSSLParameters()"); + SocketFactory fac = context.getSocketFactory(); + SSLSocket socket = (SSLSocket)fac.createSocket(); + parameters = socket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = socket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.enabledProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = socket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = socket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = socket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + // + // Check SSLServerSocket + // + // Check SSLParameters of SSLServerSocket + System.out.println(); + System.out.println("\tChecking SSLServerSocket of this SSLContext"); + System.out.println("\tChecking SSLServerSocket.getSSLParameters()"); + SSLServerSocketFactory sf = context.getServerSocketFactory(); + SSLServerSocket ssocket = (SSLServerSocket)sf.createServerSocket(); + parameters = ssocket.getSSLParameters(); + + protocols = parameters.getProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + ciphers = parameters.getCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getEnabledProtocols()"); + protocols = ssocket.getEnabledProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println("\tChecking SSLEngine.getEnabledCipherSuites()"); + ciphers = ssocket.getEnabledCipherSuites(); + failed |= !checkCipherSuites(ciphers); + + System.out.println("\tChecking SSLEngine.getSupportedProtocols()"); + protocols = ssocket.getSupportedProtocols(); + failed |= !checkProtocols(protocols, cv.supportedProtocols); + + System.out.println( + "\tChecking SSLEngine.getSupportedCipherSuites()"); + ciphers = ssocket.getSupportedCipherSuites(); + failed |= !checkCipherSuites(ciphers); + } + + if (failed) { + throw new Exception("Run into problems, see log for more details"); + } else { + System.out.println("\t... Success"); + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java --- a/test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/DefaultEnabledProtocols.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -31,17 +31,27 @@ * @run main/othervm DefaultEnabledProtocols */ -import javax.net.*; -import javax.net.ssl.*; +import java.security.Security; import java.util.Arrays; -import java.security.Security; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; public class DefaultEnabledProtocols { - static enum ContextVersion { + enum ContextVersion { TLS_CV_01("SSL", - new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), TLS_CV_02("TLS", - new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), TLS_CV_03("SSLv3", new String[] {"SSLv3", "TLSv1"}), TLS_CV_04("TLSv1", @@ -50,13 +60,15 @@ new String[] {"SSLv3", "TLSv1", "TLSv1.1"}), TLS_CV_06("TLSv1.2", new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), - TLS_CV_07("Default", - new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}); + TLS_CV_07("TLSv1.3", + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_08("Default", + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}); final String contextVersion; final String[] enabledProtocols; final static String[] supportedProtocols = new String[] { - "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}; + "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; ContextVersion(String contextVersion, String[] enabledProtocols) { this.contextVersion = contextVersion; @@ -71,17 +83,33 @@ success = false; } - if (!Arrays.equals(target, expected)) { + if (!protocolEquals(target, expected)) { System.out.println("\tError: Expected to get protocols " + Arrays.toString(expected)); - System.out.println("\tError: The actual protocols " + - Arrays.toString(target)); success = false; } + System.out.println("\t Protocols found " + Arrays.toString(target)); return success; } + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + private static boolean checkCipherSuites(String[] target) { boolean success = true; if (target.length == 0) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java --- a/test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/NoOldVersionContext.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -32,10 +32,20 @@ * NoOldVersionContext */ -import javax.net.*; -import javax.net.ssl.*; +import java.security.Security; import java.util.Arrays; -import java.security.Security; +import java.util.HashSet; +import java.util.Set; + +import javax.net.SocketFactory; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; public class NoOldVersionContext { static enum ContextVersion { @@ -51,13 +61,15 @@ new String[] {"SSLv3", "TLSv1", "TLSv1.1"}), TLS_CV_06("TLSv1.2", new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}), - TLS_CV_07("Default", + TLS_CV_07("TLSv1.3", + new String[] {"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}), + TLS_CV_08("Default", new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}); final String contextVersion; final String[] enabledProtocols; final static String[] supportedProtocols = new String[] { - "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}; + "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; ContextVersion(String contextVersion, String[] enabledProtocols) { this.contextVersion = contextVersion; @@ -72,7 +84,7 @@ success = false; } - if (!Arrays.equals(target, expected)) { + if (!protocolEquals(target, expected)) { System.out.println("\tError: Expected to get protocols " + Arrays.toString(expected)); System.out.println("\tError: The actual protocols " + @@ -83,6 +95,23 @@ return success; } + private static boolean protocolEquals( + String[] actualProtocols, + String[] expectedProtocols) { + if (actualProtocols.length != expectedProtocols.length) { + return false; + } + + Set<String> set = new HashSet<>(Arrays.asList(expectedProtocols)); + for (String actual : actualProtocols) { + if (set.add(actual)) { + return false; + } + } + + return true; + } + private static boolean checkCipherSuites(String[] target) { boolean success = true; if (target.length == 0) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java --- a/test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -131,12 +131,10 @@ sslIS.read(); sslOS.write('A'); sslOS.flush(); - } catch (SSLHandshakeException e) { - if (expectFail && !e.toString().contains("certificate_unknown")) { - throw new RuntimeException( - "Expected to see certificate_unknown in exception output", - e); - } + } catch (SSLException ssle) { + if (!expectFail) { + throw ssle; + } // Otherwise, ignore. } } @@ -158,12 +156,15 @@ sslOS.flush(); sslIS.read(); } catch (SSLHandshakeException e) { + if (expectFail) { // focus on the CertPathValidatorException - Throwable t = e.getCause().getCause(); - if ((t == null) - || (expectFail && !t.toString().contains("MD5withRSA"))) { - throw new RuntimeException( + Throwable t = e.getCause().getCause(); + if (t == null || !t.toString().contains("MD5withRSA")) { + throw new RuntimeException( "Expected to see MD5withRSA in exception output", t); + } + } else { + throw e; } } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java --- a/test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/CloseEngineException.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -45,9 +45,10 @@ import java.security.*; import java.nio.*; +// Note that this test case depends on JSSE provider implementation details. public class CloseEngineException { - private static boolean debug = false; + private static boolean debug = true; private SSLContext sslc; private SSLEngine ssle1; // client @@ -94,43 +95,53 @@ SSLEngineResult result1; // ssle1's results from last operation SSLEngineResult result2; // ssle2's results from last operation - while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) { + while (!isEngineClosed(ssle1) && !isEngineClosed(ssle2)) { log("================"); - result1 = ssle1.wrap(appOut1, oneToTwo); - result2 = ssle2.wrap(appOut2, twoToOne); + if (!isEngineClosed(ssle1)) { + result1 = ssle1.wrap(appOut1, oneToTwo); + runDelegatedTasks(result1, ssle1); - log("wrap1: " + result1); - log("oneToTwo = " + oneToTwo); - log(""); + log("wrap1: " + result1); + log("oneToTwo = " + oneToTwo); + log(""); - log("wrap2: " + result2); - log("twoToOne = " + twoToOne); + oneToTwo.flip(); + } + if (!isEngineClosed(ssle2)) { + result2 = ssle2.wrap(appOut2, twoToOne); + runDelegatedTasks(result2, ssle2); - runDelegatedTasks(result1, ssle1); - runDelegatedTasks(result2, ssle2); + log("wrap2: " + result2); + log("twoToOne = " + twoToOne); - oneToTwo.flip(); - twoToOne.flip(); + twoToOne.flip(); + } log("----"); - result1 = ssle1.unwrap(twoToOne, appIn1); - result2 = ssle2.unwrap(oneToTwo, appIn2); + if (!isEngineClosed(ssle1) && !dataDone) { + log("--"); + result1 = ssle1.unwrap(twoToOne, appIn1); + runDelegatedTasks(result1, ssle1); - log("unwrap1: " + result1); - log("twoToOne = " + twoToOne); - log(""); + log("unwrap1: " + result1); + log("twoToOne = " + twoToOne); + log(""); - log("unwrap2: " + result2); - log("oneToTwo = " + oneToTwo); + twoToOne.compact(); + } + if (!isEngineClosed(ssle2)) { + log("---"); + result2 = ssle2.unwrap(oneToTwo, appIn2); + runDelegatedTasks(result2, ssle2); - runDelegatedTasks(result1, ssle1); - runDelegatedTasks(result2, ssle2); + log("unwrap2: " + result2); + log("oneToTwo = " + oneToTwo); - oneToTwo.compact(); - twoToOne.compact(); + oneToTwo.compact(); + } /* * If we've transfered all the data between app1 and app2, @@ -154,7 +165,7 @@ throw new Exception( "TEST FAILED: didn't throw Exception"); } catch (SSLException e) { - System.out.println("PARTIAL PASS"); + System.err.println("PARTIAL PASS"); } } } @@ -167,7 +178,7 @@ throw new Exception( "TEST FAILED: didn't throw Exception"); } catch (SSLException e) { - System.out.println("TEST PASSED"); + System.err.println("TEST PASSED"); } } @@ -181,7 +192,7 @@ test.runTest(); - System.out.println("Test Passed."); + System.err.println("Test Passed."); } /* @@ -277,7 +288,7 @@ private static void log(String str) { if (debug) { - System.out.println(str); + System.err.println(str); } } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/CloseInboundException.java --- a/test/jdk/sun/security/ssl/SSLEngineImpl/CloseInboundException.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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. - */ - -// -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. -// - -/* - * @test - * @bug 4931274 - * @summary closeInbound does not signal when a close_notify has not - * been received. - * @run main/othervm CloseInboundException - * @author Brad Wetmore - */ - -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; -import java.io.*; -import java.security.*; -import java.nio.*; - -public class CloseInboundException { - - private SSLEngine ssle1; // client - private SSLEngine ssle2; // server - - SSLEngineResult result1; // ssle1's results from last operation - SSLEngineResult result2; // ssle2's results from last operation - - private static String pathToStores = "../../../../javax/net/ssl/etc"; - private static String keyStoreFile = "keystore"; - private static String trustStoreFile = "truststore"; - private static String passwd = "passphrase"; - - private static String keyFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + keyStoreFile; - private static String trustFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + trustStoreFile; - - private ByteBuffer appOut1; // write side of ssle1 - private ByteBuffer appIn1; // read side of ssle1 - private ByteBuffer appOut2; // write side of ssle2 - private ByteBuffer appIn2; // read side of ssle2 - - private ByteBuffer oneToTwo; // "reliable" transport ssle1->ssle2 - private ByteBuffer twoToOne; // "reliable" transport ssle2->ssle1 - - /* - * Majority of the test case is here, setup is done below. - */ - private void runTest(boolean inboundClose) throws Exception { - - boolean done = false; - - while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) { - - System.out.println("================"); - - result1 = ssle1.wrap(appOut1, oneToTwo); - result2 = ssle2.wrap(appOut2, twoToOne); - - System.out.println("wrap1 = " + result1); - System.out.println("oneToTwo = " + oneToTwo); - - System.out.println("wrap2 = " + result2); - System.out.println("twoToOne = " + twoToOne); - - runDelegatedTasks(result1, ssle1); - runDelegatedTasks(result2, ssle2); - - oneToTwo.flip(); - twoToOne.flip(); - - System.out.println("----"); - result1 = ssle1.unwrap(twoToOne, appIn1); - - if (done && inboundClose) { - try { - result2 = ssle2.unwrap(oneToTwo, appIn2); - throw new Exception("Didn't throw Exception"); - } catch (SSLException e) { - System.out.println("Caught proper exception\n" + e); - return; - } - } else { - result2 = ssle2.unwrap(oneToTwo, appIn2); - } - - System.out.println("unwrap1 = " + result1); - System.out.println("twoToOne = " + twoToOne); - - System.out.println("unwrap2 = " + result2); - System.out.println("oneToTwo = " + oneToTwo); - - runDelegatedTasks(result1, ssle1); - runDelegatedTasks(result2, ssle2); - - oneToTwo.compact(); - twoToOne.compact(); - - /* - * If we've transfered all the data between app1 and app2, - * we try to close and see what that gets us. - */ - if (!done && (appOut1.limit() == appIn2.position()) && - (appOut2.limit() == appIn1.position())) { - - if (inboundClose) { - try { - System.out.println("Closing ssle1's *INBOUND*..."); - ssle1.closeInbound(); - throw new Exception("closeInbound didn't throw"); - } catch (SSLException e) { - System.out.println("Caught closeInbound exc properly"); - checkStatus(); - /* - * Let the message processing continue to - * handle the alert. - */ - } - done = true; - } else { - done = true; - System.out.println("Closing ssle1's *OUTBOUND*..."); - ssle1.closeOutbound(); - } - } - } - } - - /* - * Check to see if the close generated a close_notify message, - * that the result status is sane, and that close again doesn't - * generate a new exception. - * - * We'll consume the wrapped data when we loop back around. - */ - private void checkStatus() throws Exception { - System.out.println("\nCalling last wrap"); - int pos = oneToTwo.position(); - - result1 = ssle1.wrap(appOut1, oneToTwo); - System.out.println("result1 = " + result1); - - if ((pos >= oneToTwo.position()) || - !result1.getStatus().equals(Status.CLOSED) || - !result1.getHandshakeStatus().equals( - HandshakeStatus.NOT_HANDSHAKING) || - !ssle1.isOutboundDone() || - !ssle1.isInboundDone()) { - throw new Exception(result1.toString()); - } - System.out.println("Make sure we don't throw a second SSLException."); - ssle1.closeInbound(); - } - - public static void main(String args[]) throws Exception { - - CloseInboundException test; - - test = new CloseInboundException(); - test.runTest(false); - - test = new CloseInboundException(); - test.runTest(true); - System.out.println("Test PASSED!!!"); - } - - /* - * ********************************************************** - * Majority of the test case is above, below is just setup stuff - * ********************************************************** - */ - - public CloseInboundException() throws Exception { - - SSLContext sslc = getSSLContext(keyFilename, trustFilename); - - ssle1 = sslc.createSSLEngine("host1", 1); - ssle1.setUseClientMode(true); - - ssle2 = sslc.createSSLEngine("host2", 2); - ssle2.setUseClientMode(false); - - createBuffers(); - } - - /* - * Create an initialized SSLContext to use for this test. - */ - private SSLContext getSSLContext(String keyFile, String trustFile) - throws Exception { - - KeyStore ks = KeyStore.getInstance("JKS"); - KeyStore ts = KeyStore.getInstance("JKS"); - - char[] passphrase = "passphrase".toCharArray(); - - ks.load(new FileInputStream(keyFile), passphrase); - ts.load(new FileInputStream(trustFile), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); - - SSLContext sslCtx = SSLContext.getInstance("TLS"); - - sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - - return sslCtx; - } - - private void createBuffers() { - // Size the buffers as appropriate. - SSLSession session = ssle1.getSession(); - int appBufferMax = session.getApplicationBufferSize(); - int netBufferMax = session.getPacketBufferSize(); - - appIn1 = ByteBuffer.allocateDirect(appBufferMax + 50); - appIn2 = ByteBuffer.allocateDirect(appBufferMax + 50); - - oneToTwo = ByteBuffer.allocateDirect(netBufferMax); - twoToOne = ByteBuffer.allocateDirect(netBufferMax); - - appOut1 = ByteBuffer.wrap("Hi Engine2, I'm SSLEngine1".getBytes()); - appOut2 = ByteBuffer.wrap("Hello Engine1, I'm SSLEngine2".getBytes()); - - System.out.println("AppOut1 = " + appOut1); - System.out.println("AppOut2 = " + appOut2); - System.out.println(); - } - - private static void runDelegatedTasks(SSLEngineResult result, - SSLEngine engine) throws Exception { - - if (result.getHandshakeStatus().equals(HandshakeStatus.NEED_TASK)) { - Runnable runnable; - while ((runnable = engine.getDelegatedTask()) != null) { - System.out.println("running delegated task..."); - runnable.run(); - } - } - } - - private static boolean isEngineClosed(SSLEngine engine) { - return (engine.isOutboundDone() && engine.isInboundDone()); - } - -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java --- a/test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/EngineEnforceUseClientMode.java Mon Jun 25 13:41:39 2018 -0700 @@ -107,10 +107,9 @@ "wrap(): Didn't catch the exception properly"); } catch (IllegalStateException e) { System.out.println("Caught the correct exception."); - ssle3.wrap(appOut1, oneToTwo); oneToTwo.flip(); if (oneToTwo.hasRemaining()) { - throw new Exception("wrap1 generated data"); + throw new Exception("wrap generated data"); } oneToTwo.clear(); } @@ -122,12 +121,11 @@ "unwrap(): Didn't catch the exception properly"); } catch (IllegalStateException e) { System.out.println("Caught the correct exception."); - ssle4.wrap(appOut1, oneToTwo); - oneToTwo.flip(); - if (oneToTwo.hasRemaining()) { - throw new Exception("wrap2 generated data"); + appIn1.flip(); + if (appIn1.hasRemaining()) { + throw new Exception("unwrap generated data"); } - oneToTwo.clear(); + appIn1.clear(); } try { @@ -137,12 +135,6 @@ "unwrap(): Didn't catch the exception properly"); } catch (IllegalStateException e) { System.out.println("Caught the correct exception."); - ssle5.wrap(appOut1, oneToTwo); - oneToTwo.flip(); - if (oneToTwo.hasRemaining()) { - throw new Exception("wrap3 generated data"); - } - oneToTwo.clear(); } boolean dataDone = false; @@ -200,7 +192,7 @@ System.out.println("Try changing modes..."); try { - ssle2.setUseClientMode(false); + ssle2.setUseClientMode(true); throw new RuntimeException( "setUseClientMode(): " + "Didn't catch the exception properly"); @@ -311,8 +303,10 @@ log("Data transferred cleanly"); } - a.clear(); - b.clear(); + a.position(a.limit()); + b.position(b.limit()); + a.limit(a.capacity()); + b.limit(b.capacity()); } private static void log(String str) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java --- a/test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/RehandshakeFinished.java Mon Jun 25 13:41:39 2018 -0700 @@ -160,7 +160,7 @@ TrustManagerFactory.getInstance("SunX509"); tmf.init(ts); - SSLContext sslCtx = SSLContext.getInstance("TLS"); + SSLContext sslCtx = SSLContext.getInstance("TLSv1.2"); sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); sslc = sslCtx; } catch (Exception e) { diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/SSLEngineKeyLimit.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,482 @@ +/* + * 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. + * + * 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. + */ + +/* + * @test + * @bug 8164879 + * @library /lib/testlibrary ../../ + * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property + * start a new handshake sequence to renegotiate the symmetric key with an + * SSLSocket connection. This test verifies the handshake method was called + * via debugging info. It does not verify the renegotiation was successful + * as that is very hard. + * + * @run main SSLEngineKeyLimit 0 server AES/GCM/NoPadding keyupdate 1050000 + * @run main SSLEngineKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22 + */ + +/* + * This test runs in another process so we can monitor the debug + * results. The OutputAnalyzer must see correct debug output to return a + * success. + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.Arrays; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.Utils; +import jdk.testlibrary.OutputAnalyzer; + +public class SSLEngineKeyLimit { + + SSLEngine eng; + static ByteBuffer cTos; + static ByteBuffer sToc; + static ByteBuffer outdata; + ByteBuffer buf; + static boolean ready = false; + + static String pathToStores = "../../../../javax/net/ssl/etc/"; + static String keyStoreFile = "keystore"; + static String passwd = "passphrase"; + static String keyFilename; + static int dataLen = 10240; + static boolean serverwrite = true; + int totalDataLen = 0; + static boolean sc = true; + int delay = 1; + static boolean readdone = false; + + // Turn on debugging + static boolean debug = false; + + SSLEngineKeyLimit() { + buf = ByteBuffer.allocate(dataLen*4); + } + + /** + * args should have two values: server|client, <limit size> + * Prepending 'p' is for internal use only. + */ + public static void main(String args[]) throws Exception { + + for (int i = 0; i < args.length; i++) { + System.out.print(" " + args[i]); + } + System.out.println(); + if (args[0].compareTo("p") != 0) { + boolean expectedFail = (Integer.parseInt(args[0]) == 1); + if (expectedFail) { + System.out.println("Test expected to not find updated msg"); + } + + // Write security property file to overwrite default + File f = new File("keyusage."+ System.nanoTime()); + PrintWriter p = new PrintWriter(f); + p.write("jdk.tls.keyLimits="); + for (int i = 2; i < args.length; i++) { + p.write(" "+ args[i]); + } + p.close(); + + System.setProperty("test.java.opts", + "-Dtest.src=" + System.getProperty("test.src") + + " -Dtest.jdk=" + System.getProperty("test.jdk") + + " -Djavax.net.debug=ssl,handshake" + + " -Djava.security.properties=" + f.getName()); + + System.out.println("test.java.opts: " + + System.getProperty("test.java.opts")); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1])); + + OutputAnalyzer output = ProcessTools.executeProcess(pb); + try { + if (expectedFail) { + output.shouldNotContain("KeyUpdate: write key updated"); + output.shouldNotContain("KeyUpdate: read key updated"); + } else { + output.shouldContain("KeyUpdate: triggered, read side"); + output.shouldContain("KeyUpdate: triggered, write side"); + output.shouldContain("KeyUpdate: write key updated"); + output.shouldContain("KeyUpdate: read key updated"); + } + } catch (Exception e) { + throw e; + } finally { + System.out.println("-- BEGIN Stdout:"); + System.out.println(output.getStdout()); + System.out.println("-- END Stdout"); + System.out.println("-- BEGIN Stderr:"); + System.out.println(output.getStderr()); + System.out.println("-- END Stderr"); + } + return; + } + + if (args[0].compareTo("p") != 0) { + throw new Exception ("Tried to run outside of a spawned process"); + } + + if (args[1].compareTo("client") == 0) { + serverwrite = false; + } + + cTos = ByteBuffer.allocateDirect(dataLen*4); + keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + + sToc = ByteBuffer.allocateDirect(dataLen*4); + outdata = ByteBuffer.allocateDirect(dataLen); + + byte[] data = new byte[dataLen]; + Arrays.fill(data, (byte)0x0A); + outdata.put(data); + outdata.flip(); + cTos.clear(); + sToc.clear(); + + Thread ts = new Thread(serverwrite ? new Client() : new Server()); + ts.start(); + (serverwrite ? new Server() : new Client()).run(); + ts.interrupt(); + ts.join(); + } + + private static void doTask(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == + SSLEngineResult.HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + print("\trunning delegated task..."); + runnable.run(); + } + SSLEngineResult.HandshakeStatus hsStatus = + engine.getHandshakeStatus(); + if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) { + throw new Exception( + "handshake shouldn't need additional tasks"); + } + print("\tnew HandshakeStatus: " + hsStatus); + } + } + + static void print(String s) { + if (debug) { + System.out.println(s); + } + } + + static void log(String s, SSLEngineResult r) { + if (!debug) { + return; + } + System.out.println(s + ": " + + r.getStatus() + "/" + r.getHandshakeStatus()+ " " + + r.bytesConsumed() + "/" + r.bytesProduced() + " "); + + } + + void write() throws Exception { + int i = 0; + SSLEngineResult r; + boolean again = true; + + while (!ready) { + Thread.sleep(delay); + } + print("Write-side. "); + + while (i++ < 150) { + while (sc) { + if (readdone) { + return; + } + Thread.sleep(delay); + } + + outdata.rewind(); + print("write wrap"); + + while (true) { + r = eng.wrap(outdata, getWriteBuf()); + log("write wrap", r); + if (debug && r.getStatus() != SSLEngineResult.Status.OK) { + print("outdata pos: " + outdata.position() + + " rem: " + outdata.remaining() + + " lim: " + outdata.limit() + + " cap: " + outdata.capacity()); + print("writebuf pos: " + getWriteBuf().position() + + " rem: " + getWriteBuf().remaining() + + " lim: " + getWriteBuf().limit() + + " cap: " + getWriteBuf().capacity()); + } + if (again && r.getStatus() == SSLEngineResult.Status.OK && + r.getHandshakeStatus() == + SSLEngineResult.HandshakeStatus.NEED_WRAP) { + print("again"); + again = false; + continue; + } + break; + } + doTask(r, eng); + getWriteBuf().flip(); + sc = true; + while (sc) { + if (readdone) { + return; + } + Thread.sleep(delay); + } + + while (true) { + buf.clear(); + r = eng.unwrap(getReadBuf(), buf); + log("write unwrap", r); + if (debug && r.getStatus() != SSLEngineResult.Status.OK) { + print("buf pos: " + buf.position() + + " rem: " + buf.remaining() + + " lim: " + buf.limit() + + " cap: " + buf.capacity()); + print("readbuf pos: " + getReadBuf().position() + + " rem: " + getReadBuf().remaining() + + " lim: " + getReadBuf().limit() + + " cap:" + getReadBuf().capacity()); + } + break; + } + doTask(r, eng); + getReadBuf().compact(); + print("compacted readbuf pos: " + getReadBuf().position() + + " rem: " + getReadBuf().remaining() + + " lim: " + getReadBuf().limit() + + " cap: " + getReadBuf().capacity()); + sc = true; + } + } + + void read() throws Exception { + byte b = 0x0B; + ByteBuffer buf2 = ByteBuffer.allocateDirect(dataLen); + SSLEngineResult r = null; + boolean exit, again = true; + + while (eng == null) { + Thread.sleep(delay); + } + + try { + System.out.println("connected"); + print("entering read loop"); + ready = true; + while (true) { + + while (!sc) { + Thread.sleep(delay); + } + + print("read wrap"); + exit = false; + while (!exit) { + buf2.put(b); + buf2.flip(); + r = eng.wrap(buf2, getWriteBuf()); + log("read wrap", r); + if (debug) { + // && r.getStatus() != SSLEngineResult.Status.OK) { + print("buf2 pos: " + buf2.position() + + " rem: " + buf2.remaining() + + " cap: " + buf2.capacity()); + print("writebuf pos: " + getWriteBuf().position() + + " rem: " + getWriteBuf().remaining() + + " cap: " + getWriteBuf().capacity()); + } + if (again && r.getStatus() == SSLEngineResult.Status.OK && + r.getHandshakeStatus() == + SSLEngineResult.HandshakeStatus.NEED_WRAP) { + buf2.compact(); + again = false; + continue; + } + exit = true; + } + doTask(r, eng); + buf2.clear(); + getWriteBuf().flip(); + + sc = false; + + while (!sc) { + Thread.sleep(delay); + } + + while (true) { + buf.clear(); + r = eng.unwrap(getReadBuf(), buf); + log("read unwrap", r); + if (debug && + r.getStatus() != SSLEngineResult.Status.OK) { + print("buf pos " + buf.position() + + " rem: " + buf.remaining() + + " lim: " + buf.limit() + + " cap: " + buf.capacity()); + print("readbuf pos: " + getReadBuf().position() + + " rem: " + getReadBuf().remaining() + + " lim: " + getReadBuf().limit() + + " cap: " + getReadBuf().capacity()); + doTask(r, eng); + } + + if (again && r.getStatus() == SSLEngineResult.Status.OK && + r.getHandshakeStatus() == + SSLEngineResult.HandshakeStatus.NEED_UNWRAP) { + buf.clear(); + print("again"); + again = false; + continue; + + } + break; + } + buf.clear(); + getReadBuf().compact(); + + totalDataLen += r.bytesProduced(); + sc = false; + } + } catch (Exception e) { + sc = false; + readdone = true; + System.out.println(e.getMessage()); + e.printStackTrace(); + System.out.println("Total data read = " + totalDataLen); + } + } + + ByteBuffer getReadBuf() { + return null; + } + + ByteBuffer getWriteBuf() { + return null; + } + + + SSLContext initContext() throws Exception { + SSLContext sc = SSLContext.getInstance("TLSv1.3"); + KeyStore ks = KeyStore.getInstance( + new File(System.getProperty("javax.net.ssl.keyStore")), + passwd.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, passwd.toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + sc.init(kmf.getKeyManagers(), + tmf.getTrustManagers(), new SecureRandom()); + return sc; + } + + static class Server extends SSLEngineKeyLimit implements Runnable { + Server() throws Exception { + super(); + eng = initContext().createSSLEngine(); + eng.setUseClientMode(false); + eng.setNeedClientAuth(true); + } + + public void run() { + try { + if (serverwrite) { + write(); + } else { + read(); + } + + } catch (Exception e) { + System.out.println("server: " + e.getMessage()); + e.printStackTrace(); + } + System.out.println("Server closed"); + } + + @Override + ByteBuffer getWriteBuf() { + return sToc; + } + @Override + ByteBuffer getReadBuf() { + return cTos; + } + } + + + static class Client extends SSLEngineKeyLimit implements Runnable { + Client() throws Exception { + super(); + eng = initContext().createSSLEngine(); + eng.setUseClientMode(true); + } + + public void run() { + try { + if (!serverwrite) { + write(); + } else { + read(); + } + } catch (Exception e) { + System.out.println("client: " + e.getMessage()); + e.printStackTrace(); + } + System.out.println("Client closed"); + } + @Override + ByteBuffer getWriteBuf() { + return cTos; + } + @Override + ByteBuffer getReadBuf() { + return sToc; + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLEngineImpl/TLS13BeginHandshake.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLEngineImpl/TLS13BeginHandshake.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,191 @@ +/* + * 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. + * + * 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. + */ + +/* + * @test + * @summary Test SSLEngine.begineHandshake() triggers a KeyUpdate handshake + * in TLSv1.3 + * @run main/othervm TLS13BeginHandshake + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.nio.ByteBuffer; +import java.security.KeyStore; +import java.security.SecureRandom; + +public class TLS13BeginHandshake { + static String pathToStores = + System.getProperty("test.src") + "/../../../../javax/net/ssl/etc/"; + static String keyStoreFile = "keystore"; + static String passwd = "passphrase"; + + private SSLEngine serverEngine, clientEngine; + SSLEngineResult clientResult, serverResult; + private ByteBuffer clientOut, clientIn; + private ByteBuffer serverOut, serverIn; + private ByteBuffer cTOs,sTOc; + + public static void main(String args[]) throws Exception{ + new TLS13BeginHandshake().runDemo(); + } + + private void runDemo() throws Exception { + int done = 0; + + createSSLEngines(); + createBuffers(); + + while (!isEngineClosed(clientEngine) || !isEngineClosed(serverEngine)) { + + System.out.println("================"); + clientResult = clientEngine.wrap(clientOut, cTOs); + System.out.println("client wrap: " + clientResult); + runDelegatedTasks(clientResult, clientEngine); + serverResult = serverEngine.wrap(serverOut, sTOc); + System.out.println("server wrap: " + serverResult); + runDelegatedTasks(serverResult, serverEngine); + + cTOs.flip(); + sTOc.flip(); + + System.out.println("----"); + clientResult = clientEngine.unwrap(sTOc, clientIn); + System.out.println("client unwrap: " + clientResult); + if (clientResult.getStatus() == SSLEngineResult.Status.CLOSED) { + break; + } runDelegatedTasks(clientResult, clientEngine); + serverResult = serverEngine.unwrap(cTOs, serverIn); + System.out.println("server unwrap: " + serverResult); + runDelegatedTasks(serverResult, serverEngine); + + cTOs.compact(); + sTOc.compact(); + + //System.err.println("so limit="+serverOut.limit()+" so pos="+serverOut.position()); + //System.out.println("bf ctos limit="+cTOs.limit()+" pos="+cTOs.position()+" cap="+cTOs.capacity()); + //System.out.println("bf stoc limit="+sTOc.limit()+" pos="+sTOc.position()+" cap="+sTOc.capacity()); + if (done < 2 && (clientOut.limit() == serverIn.position()) && + (serverOut.limit() == clientIn.position())) { + + if (done == 0) { + checkTransfer(serverOut, clientIn); + checkTransfer(clientOut, serverIn); + clientEngine.beginHandshake(); + done++; + continue; + } + + checkTransfer(serverOut, clientIn); + checkTransfer(clientOut, serverIn); + System.out.println("\tClosing..."); + clientEngine.closeOutbound(); + done++; + continue; + } + } + + } + + private static boolean isEngineClosed(SSLEngine engine) { + if (engine.isInboundDone()) + System.out.println("inbound closed"); + if (engine.isOutboundDone()) + System.out.println("outbound closed"); + return (engine.isOutboundDone() && engine.isInboundDone()); + } + + private static void checkTransfer(ByteBuffer a, ByteBuffer b) + throws Exception { + a.flip(); + b.flip(); + + if (!a.equals(b)) { + throw new Exception("Data didn't transfer cleanly"); + } else { + System.out.println("\tData transferred cleanly"); + } + + a.compact(); + b.compact(); + + } + private void createBuffers() { + SSLSession session = clientEngine.getSession(); + int appBufferMax = session.getApplicationBufferSize(); + int netBufferMax = session.getPacketBufferSize(); + + clientIn = ByteBuffer.allocate(appBufferMax + 50); + serverIn = ByteBuffer.allocate(appBufferMax + 50); + + cTOs = ByteBuffer.allocateDirect(netBufferMax); + sTOc = ByteBuffer.allocateDirect(netBufferMax); + + clientOut = ByteBuffer.wrap("client".getBytes()); + serverOut = ByteBuffer.wrap("server".getBytes()); + } + + private void createSSLEngines() throws Exception { + serverEngine = initContext().createSSLEngine(); + serverEngine.setUseClientMode(false); + serverEngine.setNeedClientAuth(true); + + clientEngine = initContext().createSSLEngine("client", 80); + clientEngine.setUseClientMode(true); + } + + private SSLContext initContext() throws Exception { + SSLContext sc = SSLContext.getInstance("TLSv1.3"); + KeyStore ks = KeyStore.getInstance(new File(pathToStores + keyStoreFile), + passwd.toCharArray()); + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, passwd.toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + return sc; + } + + private static void runDelegatedTasks(SSLEngineResult result, + SSLEngine engine) throws Exception { + + if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + Runnable runnable; + while ((runnable = engine.getDelegatedTask()) != null) { + runnable.run(); + } + HandshakeStatus hsStatus = engine.getHandshakeStatus(); + if (hsStatus == HandshakeStatus.NEED_TASK) { + throw new Exception( + "handshake shouldn't need additional tasks"); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/AsyncSSLSocketClose.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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,10 +25,6 @@ // SunJSSE does not support dynamic system properties, no way to re-use // system properties in samevm/agentvm mode. // -// The test may timeout occasionally on heavy loaded system because -// there are lot of TLS transactions involved. Frequent timeout(s) should -// be analyzed further. -// /* * @test @@ -40,19 +36,23 @@ import javax.net.ssl.*; import java.io.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; -public class AsyncSSLSocketClose implements Runnable -{ +public class AsyncSSLSocketClose implements Runnable { SSLSocket socket; SSLServerSocket ss; + // Is the socket ready to close? + private final CountDownLatch closeCondition = new CountDownLatch(1); + // Where do we find the keystores? static String pathToStores = "../../../../javax/net/ssl/etc"; static String keyStoreFile = "keystore"; static String trustStoreFile = "truststore"; static String passwd = "passphrase"; - public static void main(String[] args) { + public static void main(String[] args) throws Exception { String keyFilename = System.getProperty("test.src", "./") + "/" + pathToStores + "/" + keyStoreFile; @@ -68,58 +68,68 @@ new AsyncSSLSocketClose(); } - public AsyncSSLSocketClose() { - try { - SSLServerSocketFactory sslssf = + public AsyncSSLSocketClose() throws Exception { + SSLServerSocketFactory sslssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); - ss = (SSLServerSocket) sslssf.createServerSocket(0); - - SSLSocketFactory sslsf = - (SSLSocketFactory)SSLSocketFactory.getDefault(); - socket = (SSLSocket)sslsf.createSocket("localhost", - ss.getLocalPort()); - SSLSocket serverSoc = (SSLSocket) ss.accept(); - ss.close(); + ss = (SSLServerSocket) sslssf.createServerSocket(0); - (new Thread(this)).start(); - serverSoc.startHandshake(); - - try { - Thread.sleep(5000); - } catch (Exception e) { - e.printStackTrace(); - } + SSLSocketFactory sslsf = + (SSLSocketFactory)SSLSocketFactory.getDefault(); + socket = (SSLSocket)sslsf.createSocket("localhost", ss.getLocalPort()); + SSLSocket serverSoc = (SSLSocket)ss.accept(); + ss.close(); - socket.setSoLinger(true, 10); - System.out.println("Calling Socket.close"); - socket.close(); - System.out.println("ssl socket get closed"); - System.out.flush(); + (new Thread(this)).start(); + serverSoc.startHandshake(); - } catch (IOException e) { - e.printStackTrace(); + boolean closeIsReady = closeCondition.await(90L, TimeUnit.SECONDS); + if (!closeIsReady) { + System.out.println( + "Ignore, the closure is not ready yet in 90 seconds."); + return; } + socket.setSoLinger(true, 10); + System.out.println("Calling Socket.close"); + socket.close(); + System.out.println("ssl socket get closed"); + System.out.flush(); } // block in write public void run() { + byte[] ba = new byte[1024]; + for (int i = 0; i < ba.length; i++) { + ba[i] = 0x7A; + } + try { - byte[] ba = new byte[1024]; - for (int i=0; i<ba.length; i++) - ba[i] = 0x7A; - OutputStream os = socket.getOutputStream(); int count = 0; + + // 1st round write + count += ba.length; + System.out.println(count + " bytes to be written"); + os.write(ba); + System.out.println(count + " bytes written"); + + // Signal, ready to close. + closeCondition.countDown(); + + // write more while (true) { count += ba.length; System.out.println(count + " bytes to be written"); os.write(ba); System.out.println(count + " bytes written"); } - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + if (socket.isClosed() || socket.isOutputShutdown()) { + System.out.println("interrupted, the socket is closed"); + } else { + throw new RuntimeException("interrupted?", e); + } } } +} -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/ClientTimeout.java Mon Jun 25 13:41:39 2018 -0700 @@ -21,14 +21,15 @@ * questions. */ +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + /* * @test * @bug 4836493 + * @ignore need further evaluation * @summary Socket timeouts for SSLSockets causes data corruption. * @run main/othervm ClientTimeout - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. */ import java.io.*; diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/InvalidateServerSessionRenegotiate.java Mon Jun 25 13:41:39 2018 -0700 @@ -21,19 +21,25 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 4403428 * @summary Invalidating JSSE session on server causes SSLProtocolException - * @run main/othervm InvalidateServerSessionRenegotiate - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. + * @run main/othervm InvalidateServerSessionRenegotiate SSLv3 + * @run main/othervm InvalidateServerSessionRenegotiate TLSv1 + * @run main/othervm InvalidateServerSessionRenegotiate TLSv1.1 + * @run main/othervm InvalidateServerSessionRenegotiate TLSv1.2 * @author Brad Wetmore */ import java.io.*; import java.net.*; +import java.security.Security; import javax.net.ssl.*; public class InvalidateServerSessionRenegotiate implements @@ -157,6 +163,7 @@ (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", serverPort); + sslSocket.setEnabledProtocols(new String[] { tlsProtocol }); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); @@ -187,6 +194,9 @@ volatile Exception serverException = null; volatile Exception clientException = null; + // the specified protocol + private static String tlsProtocol; + public static void main(String[] args) throws Exception { String keyFilename = System.getProperty("test.src", "./") + "/" + pathToStores + @@ -200,8 +210,13 @@ System.setProperty("javax.net.ssl.trustStore", trustFilename); System.setProperty("javax.net.ssl.trustStorePassword", passwd); - if (debug) + if (debug) { System.setProperty("javax.net.debug", "all"); + } + + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + tlsProtocol = args[0]; /* * Start the tests. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NoImpactServerRenego.java Mon Jun 25 13:41:39 2018 -0700 @@ -28,12 +28,19 @@ * @test * @bug 7188658 * @summary Add possibility to disable client initiated renegotiation - * @run main/othervm - * -Djdk.tls.rejectClientInitiatedRenegotiation=true NoImpactServerRenego + * @run main/othervm -Djdk.tls.rejectClientInitiatedRenegotiation=true + * NoImpactServerRenego SSLv3 + * @run main/othervm -Djdk.tls.rejectClientInitiatedRenegotiation=true + * NoImpactServerRenego TLSv1 + * @run main/othervm -Djdk.tls.rejectClientInitiatedRenegotiation=true + * NoImpactServerRenego TLSv1.1 + * @run main/othervm -Djdk.tls.rejectClientInitiatedRenegotiation=true + * NoImpactServerRenego TLSv1.2 */ import java.io.*; import java.net.*; +import java.security.Security; import javax.net.ssl.*; public class NoImpactServerRenego implements @@ -157,6 +164,7 @@ (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", serverPort); + sslSocket.setEnabledProtocols(new String[] { tlsProtocol }); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); @@ -187,6 +195,9 @@ volatile Exception serverException = null; volatile Exception clientException = null; + // the specified protocol + private static String tlsProtocol; + public static void main(String[] args) throws Exception { String keyFilename = System.getProperty("test.src", "./") + "/" + pathToStores + @@ -200,8 +211,13 @@ System.setProperty("javax.net.ssl.trustStore", trustFilename); System.setProperty("javax.net.ssl.trustStorePassword", passwd); - if (debug) + if (debug) { System.setProperty("javax.net.debug", "all"); + } + + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + tlsProtocol = args[0]; /* * Start the tests. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, 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 @@ -21,14 +21,18 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 4404399 + * @ignore this test does not work any more as the TLS spec changes the + * behaviors of close_notify. * @summary When a layered SSL socket is closed, it should wait for close_notify * @run main/othervm NonAutoClose - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. * @author Brad Wetmore */ @@ -72,7 +76,7 @@ * Turn on SSL debugging? */ private final static boolean DEBUG = false; - private final static boolean VERBOSE = false; + private final static boolean VERBOSE = true; private final static int NUM_ITERATIONS = 10; private final static int PLAIN_SERVER_VAL = 1; private final static int PLAIN_CLIENT_VAL = 2; @@ -90,7 +94,7 @@ void expectValue(int got, int expected, String msg) throws IOException { if (VERBOSE) { - System.out.println(msg + ": read (" + got + ")"); + System.err.println(msg + ": read (" + got + ")"); } if (got != expected) { throw new IOException(msg + ": read (" + got @@ -108,7 +112,7 @@ void doServerSide() throws Exception { if (VERBOSE) { - System.out.println("Starting server"); + System.err.println("Starting server"); } /* @@ -137,8 +141,8 @@ for (int i = 1; i <= NUM_ITERATIONS; i++) { if (VERBOSE) { - System.out.println("================================="); - System.out.println("Server Iteration #" + i); + System.err.println("================================="); + System.err.println("Server Iteration #" + i); } SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, @@ -158,7 +162,7 @@ ssls.close(); if (VERBOSE) { - System.out.println("TLS socket is closed"); + System.err.println("TLS socket is closed"); } } @@ -172,7 +176,7 @@ plainSocket.close(); if (VERBOSE) { - System.out.println("Server plain socket is closed"); + System.err.println("Server plain socket is closed"); } } @@ -191,7 +195,7 @@ } if (VERBOSE) { - System.out.println("Starting client"); + System.err.println("Starting client"); } /* @@ -211,8 +215,8 @@ for (int i = 1; i <= NUM_ITERATIONS; i++) { if (VERBOSE) { - System.out.println("==================================="); - System.out.println("Client Iteration #" + i); + System.err.println("==================================="); + System.err.println("Client Iteration #" + i); } SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, @@ -233,7 +237,7 @@ ssls.close(); if (VERBOSE) { - System.out.println("Client TLS socket is closed"); + System.err.println("Client TLS socket is closed"); } } @@ -247,7 +251,7 @@ plainSocket.close(); if (VERBOSE) { - System.out.println("Client plain socket is closed"); + System.err.println("Client plain socket is closed"); } } diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/RejectClientRenego.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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,12 +28,19 @@ * @test * @bug 7188658 * @summary Add possibility to disable client initiated renegotiation - * @run main/othervm RejectClientRenego true - * @run main/othervm RejectClientRenego false + * @run main/othervm RejectClientRenego true SSLv3 + * @run main/othervm RejectClientRenego false SSLv3 + * @run main/othervm RejectClientRenego true TLSv1 + * @run main/othervm RejectClientRenego false TLSv1 + * @run main/othervm RejectClientRenego true TLSv1.1 + * @run main/othervm RejectClientRenego false TLSv1.1 + * @run main/othervm RejectClientRenego true TLSv1.2 + * @run main/othervm RejectClientRenego false TLSv1.2 */ import java.io.*; import java.net.*; +import java.security.Security; import javax.net.ssl.*; public class RejectClientRenego implements @@ -113,6 +120,7 @@ serverReady = true; SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + sslSocket.setEnabledProtocols(new String[] { tlsProtocol }); sslSocket.addHandshakeCompletedListener(this); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); @@ -157,6 +165,7 @@ (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", serverPort); + sslSocket.setEnabledProtocols(new String[] { tlsProtocol }); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); @@ -202,6 +211,9 @@ // Is it abbreviated handshake? private static boolean isAbbreviated = false; + // the specified protocol + private static String tlsProtocol; + public static void main(String[] args) throws Exception { String keyFilename = System.getProperty("test.src", "./") + "/" + pathToStores + @@ -219,14 +231,19 @@ System.setProperty( "jdk.tls.rejectClientInitiatedRenegotiation", "true"); - if (debug) + if (debug) { System.setProperty("javax.net.debug", "all"); + } + + Security.setProperty("jdk.tls.disabledAlgorithms", ""); // Is it abbreviated handshake? if ("true".equals(args[0])) { isAbbreviated = true; } + tlsProtocol = args[1]; + /* * Start the tests. */ diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SSLSocketKeyLimit.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,302 @@ +/* + * 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. + * + * 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. + */ + +/* + * @test + * @bug 8164879 + * @library /lib/testlibrary ../../ + * @modules java.base/sun.security.util + * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property + * @run main SSLSocketKeyLimit 0 server AES/GCM/NoPadding keyupdate 1000000 + * @run main SSLSocketKeyLimit 0 client AES/GCM/NoPadding keyupdate 1000000 + * @run main SSLSocketKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22 + */ + + /** + * Verify AES/GCM's limits set in the jdk.tls.keyLimits property + * start a new handshake sequence to renegotiate the symmetric key with an + * SSLSocket connection. This test verifies the handshake method was called + * via debugging info. It does not verify the renegotiation was successful + * as that is very hard. + */ + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.Arrays; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.Utils; +import jdk.testlibrary.OutputAnalyzer; +import sun.security.util.HexDumpEncoder; + +public class SSLSocketKeyLimit { + SSLSocket socket; + private InputStream in; + private OutputStream out; + + static boolean serverReady = false; + static int serverPort = 0; + + static String pathToStores = "../../../../javax/net/ssl/etc/"; + static String keyStoreFile = "keystore"; + static String passwd = "passphrase"; + static int dataLen = 10240; + static byte[] data = new byte[dataLen]; + static boolean serverwrite = true; + int totalDataLen = 0; + static boolean done = false; + + SSLSocketKeyLimit() { + } + + SSLContext initContext() throws Exception { + SSLContext sc = SSLContext.getInstance("TLSv1.3"); + KeyStore ks = KeyStore.getInstance( + new File(System.getProperty("javax.net.ssl.keyStore")), + passwd.toCharArray()); + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, passwd.toCharArray()); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom()); + return sc; + } + + /** + * args should have two values: server|client, <limit size> + * Prepending 'p' is for internal use only. + */ + public static void main(String args[]) throws Exception { + if (args[0].compareTo("p") != 0) { + + boolean expectedFail = (Integer.parseInt(args[0]) == 1); + if (expectedFail) { + System.out.println("Test expected to not find updated msg"); + } + //Write security property file to overwrite default + File f = new File("keyusage."+ System.nanoTime()); + PrintWriter p = new PrintWriter(f); + p.write("jdk.tls.keyLimits="); + for (int i = 2; i < args.length; i++) { + p.write(" "+ args[i]); + } + p.close(); + System.out.println("Keyusage path = " + f.getAbsolutePath()); + System.setProperty("test.java.opts", + "-Dtest.src=" + System.getProperty("test.src") + + " -Dtest.jdk=" + System.getProperty("test.jdk") + + " -Djavax.net.debug=ssl,handshake " + + " -Djava.security.properties=" + f.getName()); + + System.out.println("test.java.opts: " + + System.getProperty("test.java.opts")); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, + Utils.addTestJavaOpts("SSLSocketKeyLimit", "p", args[1])); + + OutputAnalyzer output = ProcessTools.executeProcess(pb); + try { + if (expectedFail) { + output.shouldNotContain("KeyUpdate: write key updated"); + output.shouldNotContain("KeyUpdate: read key updated"); + } else { + output.shouldContain("KeyUpdate: triggered, read side"); + output.shouldContain("KeyUpdate: triggered, write side"); + output.shouldContain("KeyUpdate: write key updated"); + output.shouldContain("KeyUpdate: read key updated"); + } + } catch (Exception e) { + throw e; + } finally { + System.out.println("-- BEGIN Stdout:"); + System.out.println(output.getStdout()); + System.out.println("-- END Stdout"); + System.out.println("-- BEGIN Stderr:"); + System.out.println(output.getStderr()); + System.out.println("-- END Stderr"); + } + return; + } + + if (args.length > 0 && args[0].compareToIgnoreCase("client") == 0) { + serverwrite = false; + } + + String keyFilename = + System.getProperty("test.src", "./") + "/" + pathToStores + + "/" + keyStoreFile; + + System.setProperty("javax.net.ssl.keyStore", keyFilename); + System.setProperty("javax.net.ssl.keyStorePassword", passwd); + + Arrays.fill(data, (byte)0x0A); + Thread ts = new Thread(new Server()); + + ts.start(); + while (!serverReady) { + Thread.sleep(100); + } + new Client().run(); + ts.join(10000); // 10sec + System.exit(0); + } + + void write(SSLSocket s) throws Exception { + int i = 0; + in = s.getInputStream(); + out = s.getOutputStream(); + while (i++ < 150) { + out.write(data, 0, dataLen); + System.out.print("W"); + in.readNBytes(1); + System.out.print("R"); + } + out.write(0x0D); + out.flush(); + + // Let read side all the data + while (!done) { + Thread.sleep(100); + } + out.close(); + in.close(); + } + + + void read(SSLSocket s) throws Exception { + byte[] buf = new byte[dataLen]; + int len; + byte i = 0; + try { + System.out.println("Server: connected " + s.getSession().getCipherSuite()); + in = s.getInputStream(); + out = s.getOutputStream(); + while (true) { + len = in.read(buf, 0, dataLen); + System.out.print("r"); + out.write(i++); + System.out.print("w"); + for (byte b: buf) { + if (b == 0x0A || b == 0x0D) { + continue; + } + System.out.println("\nData invalid: " + new HexDumpEncoder().encode(buf)); + break; + } + + if (len > 0 && buf[len-1] == 0x0D) { + System.out.println("got end byte"); + break; + } + totalDataLen += len; + } + } catch (Exception e) { + System.out.println("\n" + e.getMessage()); + e.printStackTrace(); + } finally { + // Tell write side that we are done reading + out.close(); + in.close(); + done = true; + } + System.out.println("\nTotalDataLen = " + totalDataLen); + } + + static class Server extends SSLSocketKeyLimit implements Runnable { + private SSLServerSocketFactory ssf; + private SSLServerSocket ss; + Server() { + super(); + try { + ssf = initContext().getServerSocketFactory(); + ss = (SSLServerSocket) ssf.createServerSocket(serverPort); + serverPort = ss.getLocalPort(); + } catch (Exception e) { + System.out.println("server: " + e.getMessage()); + e.printStackTrace(); + } + } + + public void run() { + try { + serverReady = true; + System.out.println("Server waiting... port: " + serverPort); + socket = (SSLSocket) ss.accept(); + if (serverwrite) { + write(socket); + } else { + read(socket); + } + + socket.close(); + } catch (Exception e) { + System.out.println("server: " + e.getMessage()); + e.printStackTrace(); + } + System.out.println("Server closed"); + } + } + + + static class Client extends SSLSocketKeyLimit implements Runnable { + private SSLSocketFactory sf; + + Client() { + super(); + } + + public void run() { + try { + sf = initContext().getSocketFactory(); + System.out.println("Client: connecting... port: " + serverPort); + socket = (SSLSocket)sf.createSocket("localhost", serverPort); + System.out.println("Client: connected." + socket.getSession().getCipherSuite()); + + // Opposite of what the server does + if (!serverwrite) { + write(socket); + } else { + read(socket); + } + + } catch (Exception e) { + System.err.println("client: " + e.getMessage()); + e.printStackTrace(); + } + } + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java Mon Jun 25 13:41:39 2018 -0700 @@ -21,15 +21,17 @@ * questions. */ +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + /* * @test * @bug 6223624 + * @ignore this test does not grant to work. The handshake may have completed + * when getSession() return. Please update or remove this test case. * @summary SSLSocket.setUseClientMode() fails to throw expected * IllegalArgumentException * @run main/othervm SetClientMode - * - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. */ /* diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/Stapling/StatusResponseManager.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015, 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. + * + * 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. + */ + +/* + * @test + * @bug 8046321 + * @library ../../../../java/security/testlibrary + * @build CertificateBuilder SimpleOCSPServer + * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests + * @summary OCSP Stapling for TLS + */ + diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/Stapling/TEST.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/Stapling/TEST.properties Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,6 @@ +modules = \ + java.base/sun.security.provider.certpath \ + java.base/sun.security.util \ + java.base/sun.security.x509 \ + java.base/sun.security.ssl +bootclasspath.dirs=. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/Stapling/java.base/sun/security/ssl/StatusResponseManagerTests.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2015, 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. + * + * 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.cert.*; +import java.util.*; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PublicKey; +import java.util.concurrent.TimeUnit; + +import sun.security.testlibrary.SimpleOCSPServer; +import sun.security.testlibrary.CertificateBuilder; + +import static sun.security.ssl.CertStatusExtension.*; + +/* + * Checks that the hash value for a certificate's issuer name is generated + * correctly. Requires any certificate that is not self-signed. + * + * NOTE: this test uses Sun private classes which are subject to change. + */ +public class StatusResponseManagerTests { + + private static final boolean debug = true; + private static final boolean ocspDebug = false; + + // PKI components we will need for this test + static String passwd = "passphrase"; + static String ROOT_ALIAS = "root"; + static String INT_ALIAS = "intermediate"; + static String SSL_ALIAS = "ssl"; + static KeyStore rootKeystore; // Root CA Keystore + static KeyStore intKeystore; // Intermediate CA Keystore + static KeyStore serverKeystore; // SSL Server Keystore + static KeyStore trustStore; // SSL Client trust store + static X509Certificate rootCert; + static X509Certificate intCert; + static X509Certificate sslCert; + static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder + static int rootOcspPort; // Port number for root OCSP + static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder + static int intOcspPort; // Port number for intermed. OCSP + + static X509Certificate[] chain; + + public static void main(String[] args) throws Exception { + Map<String, TestCase> testList = + new LinkedHashMap<String, TestCase>() {{ + put("Basic OCSP fetch test", testOcspFetch); + put("Clear StatusResponseManager cache", testClearSRM); + put("Basic OCSP_MULTI fetch test", testOcspMultiFetch); + put("Test Cache Expiration", testCacheExpiry); + }}; + + // Create the CAs and OCSP responders + createPKI(); + + // Grab the certificates and make a chain we can reuse for tests + sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS); + intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS); + rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS); + chain = new X509Certificate[3]; + chain[0] = sslCert; + chain[1] = intCert; + chain[2] = rootCert; + + runTests(testList); + + intOcsp.stop(); + rootOcsp.stop(); + } + + // Test a simple RFC 6066 server-side fetch + public static final TestCase testOcspFetch = new TestCase() { + @Override + public Map.Entry<Boolean, String> runTest() { + StatusResponseManager srm = new StatusResponseManager(); + Boolean pass = Boolean.FALSE; + String message = null; + CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP; + + try { + // Get OCSP responses for non-root certs in the chain + Map<X509Certificate, byte[]> responseMap = srm.get( + CertStatusRequestType.OCSP, oReq, chain, 5000, + TimeUnit.MILLISECONDS); + + // There should be one entry in the returned map and + // one entry in the cache when the operation is complete. + if (responseMap.size() != 1) { + message = "Incorrect number of responses: expected 1, got " + + responseMap.size(); + } else if (!responseMap.containsKey(sslCert)) { + message = "Response map key is incorrect, expected " + + sslCert.getSubjectX500Principal().toString(); + } else if (srm.size() != 1) { + message = "Incorrect number of cache entries: " + + "expected 1, got " + srm.size(); + } else { + pass = Boolean.TRUE; + } + } catch (Exception e) { + e.printStackTrace(System.out); + message = e.getClass().getName(); + } + + return new AbstractMap.SimpleEntry<>(pass, message); + } + }; + + // Test clearing the StatusResponseManager cache. + public static final TestCase testClearSRM = new TestCase() { + @Override + public Map.Entry<Boolean, String> runTest() { + StatusResponseManager srm = new StatusResponseManager(); + Boolean pass = Boolean.FALSE; + String message = null; + CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI; + + try { + // Get OCSP responses for non-root certs in the chain + srm.get(CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000, + TimeUnit.MILLISECONDS); + + // There should be two entries in the returned map and + // two entries in the cache when the operation is complete. + if (srm.size() != 2) { + message = "Incorrect number of responses: expected 2, got " + + srm.size(); + } else { + // Next, clear the SRM, then check the size again + srm.clear(); + if (srm.size() != 0) { + message = "Incorrect number of responses: expected 0," + + " got " + srm.size(); + } else { + pass = Boolean.TRUE; + } + } + } catch (Exception e) { + e.printStackTrace(System.out); + message = e.getClass().getName(); + } + + return new AbstractMap.SimpleEntry<>(pass, message); + } + }; + + // Test a simple RFC 6961 server-side fetch + public static final TestCase testOcspMultiFetch = new TestCase() { + @Override + public Map.Entry<Boolean, String> runTest() { + StatusResponseManager srm = new StatusResponseManager(); + Boolean pass = Boolean.FALSE; + String message = null; + CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI; + + try { + // Get OCSP responses for non-root certs in the chain + Map<X509Certificate, byte[]> responseMap = srm.get( + CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000, + TimeUnit.MILLISECONDS); + + // There should be two entries in the returned map and + // two entries in the cache when the operation is complete. + if (responseMap.size() != 2) { + message = "Incorrect number of responses: expected 2, got " + + responseMap.size(); + } else if (!responseMap.containsKey(sslCert) || + !responseMap.containsKey(intCert)) { + message = "Response map keys are incorrect, expected " + + sslCert.getSubjectX500Principal().toString() + + " and " + + intCert.getSubjectX500Principal().toString(); + } else if (srm.size() != 2) { + message = "Incorrect number of cache entries: " + + "expected 2, got " + srm.size(); + } else { + pass = Boolean.TRUE; + } + } catch (Exception e) { + e.printStackTrace(System.out); + message = e.getClass().getName(); + } + + return new AbstractMap.SimpleEntry<>(pass, message); + } + }; + + // Test cache expiration + public static final TestCase testCacheExpiry = new TestCase() { + @Override + public Map.Entry<Boolean, String> runTest() { + // For this test, we will set the cache expiry to 5 seconds + System.setProperty("jdk.tls.stapling.cacheLifetime", "5"); + StatusResponseManager srm = new StatusResponseManager(); + Boolean pass = Boolean.FALSE; + String message = null; + CertStatusRequest oReq = OCSPStatusRequest.EMPTY_OCSP_MULTI; + + try { + // Get OCSP responses for non-root certs in the chain + srm.get(CertStatusRequestType.OCSP_MULTI, oReq, chain, 5000, + TimeUnit.MILLISECONDS); + + // There should be two entries in the returned map and + // two entries in the cache when the operation is complete. + if (srm.size() != 2) { + message = "Incorrect number of responses: expected 2, got " + + srm.size(); + } else { + // Next, wait for more than 5 seconds so the responses + // in the SRM will expire. + Thread.sleep(7000); + if (srm.size() != 0) { + message = "Incorrect number of responses: expected 0," + + " got " + srm.size(); + } else { + pass = Boolean.TRUE; + } + } + } catch (Exception e) { + e.printStackTrace(System.out); + message = e.getClass().getName(); + } + + // Set the cache lifetime back to the default + System.setProperty("jdk.tls.stapling.cacheLifetime", ""); + return new AbstractMap.SimpleEntry<>(pass, message); + } + }; + + /** + * Creates the PKI components necessary for this test, including + * Root CA, Intermediate CA and SSL server certificates, the keystores + * for each entity, a client trust store, and starts the OCSP responders. + */ + private static void createPKI() throws Exception { + CertificateBuilder cbld = new CertificateBuilder(); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyStore.Builder keyStoreBuilder = + KeyStore.Builder.newInstance("PKCS12", null, + new KeyStore.PasswordProtection(passwd.toCharArray())); + + // Generate Root, IntCA, EE keys + KeyPair rootCaKP = keyGen.genKeyPair(); + log("Generated Root CA KeyPair"); + KeyPair intCaKP = keyGen.genKeyPair(); + log("Generated Intermediate CA KeyPair"); + KeyPair sslKP = keyGen.genKeyPair(); + log("Generated SSL Cert KeyPair"); + + // Set up the Root CA Cert + cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"); + cbld.setPublicKey(rootCaKP.getPublic()); + cbld.setSerialNumber(new BigInteger("1")); + // Make a 3 year validity starting from 60 days ago + long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); + long end = start + TimeUnit.DAYS.toMillis(1085); + cbld.setValidity(new Date(start), new Date(end)); + addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic()); + addCommonCAExts(cbld); + // Make our Root CA Cert! + X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), + "SHA256withRSA"); + log("Root CA Created:\n" + certInfo(rootCert)); + + // Now build a keystore and add the keys and cert + rootKeystore = keyStoreBuilder.getKeyStore(); + Certificate[] rootChain = {rootCert}; + rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(), + passwd.toCharArray(), rootChain); + + // Now fire up the OCSP responder + rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); + rootOcsp.enableLog(ocspDebug); + rootOcsp.setNextUpdateInterval(3600); + rootOcsp.start(); + + // Wait 5 seconds for server ready + for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { + Thread.sleep(50); + } + if (!rootOcsp.isServerReady()) { + throw new RuntimeException("Server not ready yet"); + } + + rootOcspPort = rootOcsp.getPort(); + String rootRespURI = "http://localhost:" + rootOcspPort; + log("Root OCSP Responder URI is " + rootRespURI); + + // Now that we have the root keystore and OCSP responder we can + // create our intermediate CA. + cbld.reset(); + cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"); + cbld.setPublicKey(intCaKP.getPublic()); + cbld.setSerialNumber(new BigInteger("100")); + // Make a 2 year validity starting from 30 days ago + start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); + end = start + TimeUnit.DAYS.toMillis(730); + cbld.setValidity(new Date(start), new Date(end)); + addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic()); + addCommonCAExts(cbld); + cbld.addAIAExt(Collections.singletonList(rootRespURI)); + // Make our Intermediate CA Cert! + X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), + "SHA256withRSA"); + log("Intermediate CA Created:\n" + certInfo(intCaCert)); + + // Provide intermediate CA cert revocation info to the Root CA + // OCSP responder. + Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = + new HashMap<>(); + revInfo.put(intCaCert.getSerialNumber(), + new SimpleOCSPServer.CertStatusInfo( + SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); + rootOcsp.updateStatusDb(revInfo); + + // Now build a keystore and add the keys, chain and root cert as a TA + intKeystore = keyStoreBuilder.getKeyStore(); + Certificate[] intChain = {intCaCert, rootCert}; + intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(), + passwd.toCharArray(), intChain); + intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); + + // Now fire up the Intermediate CA OCSP responder + intOcsp = new SimpleOCSPServer(intKeystore, passwd, + INT_ALIAS, null); + intOcsp.enableLog(ocspDebug); + intOcsp.setNextUpdateInterval(3600); + intOcsp.start(); + + // Wait 5 seconds for server ready + for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) { + Thread.sleep(50); + } + if (!intOcsp.isServerReady()) { + throw new RuntimeException("Server not ready yet"); + } + + intOcspPort = intOcsp.getPort(); + String intCaRespURI = "http://localhost:" + intOcspPort; + log("Intermediate CA OCSP Responder URI is " + intCaRespURI); + + // Last but not least, let's make our SSLCert and add it to its own + // Keystore + cbld.reset(); + cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany"); + cbld.setPublicKey(sslKP.getPublic()); + cbld.setSerialNumber(new BigInteger("4096")); + // Make a 1 year validity starting from 7 days ago + start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); + end = start + TimeUnit.DAYS.toMillis(365); + cbld.setValidity(new Date(start), new Date(end)); + + // Add extensions + addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic()); + boolean[] kuBits = {true, false, true, false, false, false, + false, false, false}; + cbld.addKeyUsageExt(kuBits); + List<String> ekuOids = new ArrayList<>(); + ekuOids.add("1.3.6.1.5.5.7.3.1"); + ekuOids.add("1.3.6.1.5.5.7.3.2"); + cbld.addExtendedKeyUsageExt(ekuOids); + cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost")); + cbld.addAIAExt(Collections.singletonList(intCaRespURI)); + // Make our SSL Server Cert! + X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), + "SHA256withRSA"); + log("SSL Certificate Created:\n" + certInfo(sslCert)); + + // Provide SSL server cert revocation info to the Intermeidate CA + // OCSP responder. + revInfo = new HashMap<>(); + revInfo.put(sslCert.getSerialNumber(), + new SimpleOCSPServer.CertStatusInfo( + SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); + intOcsp.updateStatusDb(revInfo); + + // Now build a keystore and add the keys, chain and root cert as a TA + serverKeystore = keyStoreBuilder.getKeyStore(); + Certificate[] sslChain = {sslCert, intCaCert, rootCert}; + serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(), + passwd.toCharArray(), sslChain); + serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); + + // And finally a Trust Store for the client + trustStore = keyStoreBuilder.getKeyStore(); + trustStore.setCertificateEntry(ROOT_ALIAS, rootCert); + } + + private static void addCommonExts(CertificateBuilder cbld, + PublicKey subjKey, PublicKey authKey) throws IOException { + cbld.addSubjectKeyIdExt(subjKey); + cbld.addAuthorityKeyIdExt(authKey); + } + + private static void addCommonCAExts(CertificateBuilder cbld) + throws IOException { + cbld.addBasicConstraintsExt(true, true, -1); + // Set key usage bits for digitalSignature, keyCertSign and cRLSign + boolean[] kuBitSettings = {true, false, false, false, false, true, + true, false, false}; + cbld.addKeyUsageExt(kuBitSettings); + } + + /** + * Helper routine that dumps only a few cert fields rather than + * the whole toString() output. + * + * @param cert An X509Certificate to be displayed + * + * @return The {@link String} output of the issuer, subject and + * serial number + */ + private static String certInfo(X509Certificate cert) { + StringBuilder sb = new StringBuilder(); + sb.append("Issuer: ").append(cert.getIssuerX500Principal()). + append("\n"); + sb.append("Subject: ").append(cert.getSubjectX500Principal()). + append("\n"); + sb.append("Serial: ").append(cert.getSerialNumber()).append("\n"); + return sb.toString(); + } + + /** + * Log a message on stdout + * + * @param message The message to log + */ + private static void log(String message) { + if (debug) { + System.out.println(message); + } + } + + public static void runTests(Map<String, TestCase> testList) { + int testNo = 0; + int numberFailed = 0; + Map.Entry<Boolean, String> result; + + System.out.println("============ Tests ============"); + for (String testName : testList.keySet()) { + System.out.println("Test " + ++testNo + ": " + testName); + result = testList.get(testName).runTest(); + System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL")); + System.out.println(" " + + (result.getValue() != null ? result.getValue() : "")); + System.out.println("-------------------------------------------"); + if (!result.getKey()) { + numberFailed++; + } + } + + System.out.println("End Results: " + (testList.size() - numberFailed) + + " Passed" + ", " + numberFailed + " Failed."); + if (numberFailed > 0) { + throw new RuntimeException( + "One or more tests failed, see test output for details"); + } + } + + public interface TestCase { + Map.Entry<Boolean, String> runTest(); + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/RunStatReqSelect.java --- a/test/jdk/sun/security/ssl/StatusStapling/RunStatReqSelect.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * Copyright (c) 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. - * - * 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. - */ - -/* - * @test - * @bug 8132943 - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer - * @run main/othervm java.base/sun.security.ssl.StatusReqSelection - * @summary ServerHandshaker may select non-empty OCSPStatusRequest - * structures when Responder ID selection is not supported - */ - diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/TEST.properties --- a/test/jdk/sun/security/ssl/StatusStapling/TEST.properties Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -modules = \ - java.base/sun.security.provider.certpath \ - java.base/sun.security.util \ - java.base/sun.security.x509 \ - java.base/sun.security.ssl -bootclasspath.dirs=. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/TestRun.java --- a/test/jdk/sun/security/ssl/StatusStapling/TestRun.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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. - */ - -/* - * @test - * @bug 8046321 - * @library ../../../../java/security/testlibrary - * @build CertificateBuilder SimpleOCSPServer - * @run main/othervm java.base/sun.security.ssl.CertStatusReqExtensionTests - * @run main/othervm java.base/sun.security.ssl.CertStatusReqItemV2Tests - * @run main/othervm java.base/sun.security.ssl.CertStatusReqListV2ExtensionTests - * @run main/othervm java.base/sun.security.ssl.OCSPStatusRequestTests - * @run main/othervm -Djavax.net.debug=ssl:respmgr java.base/sun.security.ssl.StatusResponseManagerTests - * @summary OCSP Stapling for TLS - */ - diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/BogusStatusRequest.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/BogusStatusRequest.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +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. - * - * 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; - -final class BogusStatusRequest implements StatusRequest { - BogusStatusRequest() { } - - @Override - public int length() { return 0; } - - @Override - public void send(HandshakeOutStream s) throws IOException { } - - @Override - public String toString() { - return "BogusStatusRequest"; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqExtensionTests.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqExtensionTests.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,338 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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.*; -import java.nio.ByteBuffer; - -/* - * Checks that the hash value for a certificate's issuer name is generated - * correctly. Requires any certificate that is not self-signed. - * - * NOTE: this test uses Sun private classes which are subject to change. - */ -public class CertStatusReqExtensionTests { - - private static final boolean debug = false; - - // Default status_request extension (type = ocsp, OCSPStatusRequest - // with no responder IDs or extensions - private static final byte[] CSRE_DEF_OSR = {1, 0, 0, 0, 0}; - - // A status_request extension using a user-defined type (0xFF) and - // an underlying no-Responder ID/no-extension OCSPStatusRequest - private static final byte[] CSRE_TYPE_FF = {-1, 0, 0, 0, 0}; - - // A CertStatusReqExtension with 5 ResponderIds and 1 Extension - private static final byte[] CSRE_REQ_RID_EXTS = { - 1, 0, -13, 0, 59, -95, 57, 48, - 55, 49, 16, 48, 14, 6, 3, 85, - 4, 10, 19, 7, 83, 111, 109, 101, - 73, 110, 99, 49, 16, 48, 14, 6, - 3, 85, 4, 11, 19, 7, 83, 111, - 109, 101, 80, 75, 73, 49, 17, 48, - 15, 6, 3, 85, 4, 3, 19, 8, - 83, 111, 109, 101, 79, 67, 83, 80, - 0, 68, -95, 66, 48, 64, 49, 13, - 48, 11, 6, 3, 85, 4, 10, 19, - 4, 79, 104, 77, 121, 49, 14, 48, - 12, 6, 3, 85, 4, 11, 19, 5, - 66, 101, 97, 114, 115, 49, 15, 48, - 13, 6, 3, 85, 4, 11, 19, 6, - 84, 105, 103, 101, 114, 115, 49, 14, - 48, 12, 6, 3, 85, 4, 3, 19, - 5, 76, 105, 111, 110, 115, 0, 58, - -95, 56, 48, 54, 49, 16, 48, 14, - 6, 3, 85, 4, 10, 19, 7, 67, - 111, 109, 112, 97, 110, 121, 49, 13, - 48, 11, 6, 3, 85, 4, 11, 19, - 4, 87, 101, 115, 116, 49, 19, 48, - 17, 6, 3, 85, 4, 3, 19, 10, - 82, 101, 115, 112, 111, 110, 100, 101, - 114, 49, 0, 24, -94, 22, 4, 20, - -67, -36, 114, 121, 92, -79, 116, -1, - 102, -107, 7, -21, 18, -113, 64, 76, - 96, -7, -66, -63, 0, 24, -94, 22, - 4, 20, -51, -69, 107, -82, -39, -87, - 45, 25, 41, 28, -76, -68, -11, -110, - -94, -97, 62, 47, 58, -125, 0, 51, - 48, 49, 48, 47, 6, 9, 43, 6, - 1, 5, 5, 7, 48, 1, 2, 4, - 34, 4, 32, -26, -81, -120, -61, -127, - -79, 0, -39, -54, 49, 3, -51, -57, - -85, 19, -126, 94, -2, 21, 26, 98, - 6, 105, -35, -37, -29, -73, 101, 53, - 44, 15, -19 - }; - - public static void main(String[] args) throws Exception { - Map<String, TestCase> testList = - new LinkedHashMap<String, TestCase>() {{ - put("CTOR (default)", testCtorDefault); - put("CTOR (int, StatusRequest)", testCtorStatReqs); - put("CTOR (HandshakeInStream, length, getReqType, getRequest)", - testCtorInStream); - }}; - - TestUtils.runTests(testList); - } - - public static final TestCase testCtorDefault = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - CertStatusReqExtension csreDef = new CertStatusReqExtension(); - HandshakeOutStream hsout = - new HandshakeOutStream(null); - csreDef.send(hsout); - TestUtils.valueCheck(wrapExtData(null), hsout.toByteArray()); - - // The length should be 4 (2 bytes for the type, 2 for the - // encoding of zero-length - if (csreDef.length() != 4) { - throw new RuntimeException("Incorrect length from " + - "default object. Expected 4, got " + - csreDef.length()); - } - - // Since there's no data, there are no status_type or request - // data fields defined. Both should return null in this case - if (csreDef.getType() != null) { - throw new RuntimeException("Default CSRE returned " + - "non-null status_type"); - } else if (csreDef.getRequest() != null) { - throw new RuntimeException("Default CSRE returned " + - "non-null request object"); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - public static final TestCase testCtorStatReqs = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - HandshakeOutStream hsout = - new HandshakeOutStream(null); - StatusRequest basicStatReq = new OCSPStatusRequest(); - - // Create an extension using a default-style OCSPStatusRequest - // (no responder IDs, no extensions). - CertStatusReqExtension csre1 = new CertStatusReqExtension( - StatusRequestType.OCSP, basicStatReq); - csre1.send(hsout); - TestUtils.valueCheck(wrapExtData(CSRE_DEF_OSR), - hsout.toByteArray()); - hsout.reset(); - - // Create the extension using a StatusRequestType not already - // instantiated as a static StatusRequestType - // (e.g. OCSP/OCSP_MULTI) - CertStatusReqExtension csre2 = - new CertStatusReqExtension(StatusRequestType.get(-1), - basicStatReq); - csre2.send(hsout); - TestUtils.valueCheck(wrapExtData(CSRE_TYPE_FF), - hsout.toByteArray()); - - // Create the extension using a StatusRequest that - // does not match the status_type field - // This should throw an IllegalArgumentException - try { - CertStatusReqExtension csreBadRequest = - new CertStatusReqExtension(StatusRequestType.OCSP, - new BogusStatusRequest()); - throw new RuntimeException("Constructor accepted a " + - "StatusRequest that is inconsistent with " + - "the status_type"); - } catch (IllegalArgumentException iae) { } - - // We don't allow a null value for the StatusRequestType - // parameter in this constructor. - try { - CertStatusReqExtension csreBadRequest = - new CertStatusReqExtension(null, basicStatReq); - throw new RuntimeException("Constructor accepted a " + - "null StatusRequestType"); - } catch (NullPointerException npe) { } - - // We also don't allow a null value for the StatusRequest - // parameter in this constructor. - try { - CertStatusReqExtension csreBadRequest = - new CertStatusReqExtension(StatusRequestType.OCSP, - null); - throw new RuntimeException("Constructor accepted a " + - "null StatusRequest"); - } catch (NullPointerException npe) { } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor that builds the ob ject using data from - // a HandshakeInStream - // This also tests the length, getReqType and getRequest methods - public static final TestCase testCtorInStream = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - OCSPStatusRequest osr; - - try { - // To simulate the extension coming in a ServerHello, the - // type and length would already be read by HelloExtensions - // and there is no extension data - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(new byte[0])); - CertStatusReqExtension csre = - new CertStatusReqExtension(hsis, hsis.available()); - // Verify length/type/request - if (csre.length() != 4) { - throw new RuntimeException("Invalid length: received " + - csre.length() + ", expected 4"); - } else if (csre.getType() != null) { - throw new RuntimeException("Non-null type from default " + - "extension"); - } else if (csre.getRequest() != null) { - throw new RuntimeException("Non-null request from default " + - "extension"); - } - - // Try the an extension with a default OCSPStatusRequest - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRE_DEF_OSR)); - csre = new CertStatusReqExtension(hsis, hsis.available()); - if (csre.length() != (CSRE_DEF_OSR.length + 4)) { - throw new RuntimeException("Invalid length: received " + - csre.length() + ", expected " + - CSRE_DEF_OSR.length + 4); - } else if (!csre.getType().equals(StatusRequestType.OCSP)) { - throw new RuntimeException("Unknown status_type: " + - String.format("0x%02X", csre.getType().id)); - } else { - osr = (OCSPStatusRequest)csre.getRequest(); - if (!osr.getResponderIds().isEmpty() || - !osr.getExtensions().isEmpty()) { - throw new RuntimeException("Non-default " + - "OCSPStatusRequest found in extension"); - } - } - - // Try with a non-default extension - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRE_REQ_RID_EXTS)); - csre = new CertStatusReqExtension(hsis, hsis.available()); - if (csre.length() != (CSRE_REQ_RID_EXTS.length + 4)) { - throw new RuntimeException("Invalid length: received " + - csre.length() + ", expected " + - CSRE_REQ_RID_EXTS.length + 4); - } else if (!(csre.getType().equals(StatusRequestType.OCSP))) { - throw new RuntimeException("Unknown status_type: " + - String.format("0x%02X", csre.getType().id)); - } else { - osr = (OCSPStatusRequest)csre.getRequest(); - if (osr.getResponderIds().size() != 5 || - osr.getExtensions().size() != 1) { - throw new RuntimeException("Incorrect number of " + - "ResponderIds or Extensions found in " + - "OCSPStatusRequest"); - } - } - - // Create a CSRE that asserts status_request and has the - // proper length, but really is a bunch of random junk inside - // In this case, it will create an UnknownStatusRequest to - // handle the unparseable data. - byte[] junkData = new byte[48]; - Random r = new Random(System.currentTimeMillis()); - r.nextBytes(junkData); - junkData[0] = 7; // Ensure it isn't a valid status_type - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(junkData)); - csre = new CertStatusReqExtension(hsis, hsis.available()); - StatusRequest sr = csre.getRequest(); - if (!(sr instanceof UnknownStatusRequest)) { - throw new RuntimeException("Expected returned status " + - "request to be of type UnknownStatusRequest but " + - "received " + sr.getClass().getName()); - } else if (csre.length() != (junkData.length + 4)) { - throw new RuntimeException("Invalid length: received " + - csre.length() + ", expected " + - junkData.length + 4); - } - - // Set the leading byte to 1 (OCSP type) and run again - // It should pass the argument check and fail trying to parse - // the underlying StatusRequest. - junkData[0] = (byte)StatusRequestType.OCSP.id; - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(junkData)); - try { - csre = new CertStatusReqExtension(hsis, hsis.available()); - throw new RuntimeException("Expected CTOR exception did " + - "not occur"); - } catch (IOException ioe) { } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Take CSRE extension data and add extension type and length decorations - private static byte[] wrapExtData(byte[] extData) { - int bufferLen = (extData != null ? extData.length : 0) + 4; - ByteBuffer bb = ByteBuffer.allocate(bufferLen); - bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST.id); - bb.putShort((short)(extData != null ? extData.length: 0)); - if (extData != null) { - bb.put(extData); - } - return bb.array(); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqItemV2Tests.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqItemV2Tests.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,455 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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.cert.*; -import java.util.*; -import java.nio.ByteBuffer; -import javax.net.ssl.SSLException; -import javax.security.auth.x500.X500Principal; -import sun.security.provider.certpath.ResponderId; -import sun.security.provider.certpath.OCSPNonceExtension; - -/* - * Checks that the hash value for a certificate's issuer name is generated - * correctly. Requires any certificate that is not self-signed. - * - * NOTE: this test uses Sun private classes which are subject to change. - */ -public class CertStatusReqItemV2Tests { - - private static final boolean debug = false; - - private static final byte[] DEF_CSRIV2_OCSP_MULTI_BYTES = { - 2, 0, 4, 0, 0, 0, 0 - }; - - private static final byte[] DEF_CSRIV2_OCSP_BYTES = { - 1, 0, 4, 0, 0, 0, 0 - }; - - // This is a CSRIV2 (ocsp_multi) that has a single - // responder ID and no extensions. - private static final byte[] CSRIV2_1RID = { - 2, 0, 32, 0, 28, 0, 26, -95, - 24, 48, 22, 49, 20, 48, 18, 6, - 3, 85, 4, 3, 19, 11, 79, 67, - 83, 80, 32, 83, 105, 103, 110, 101, - 114, 0 , 0 - }; - - // This is a CSRIV2 (ocsp_multi) that has a single - // responder ID and no extensions. The request_length - // field is too short in this case. - private static final byte[] CSRIV2_LENGTH_TOO_SHORT = { - 2, 0, 27, 0, 28, 0, 26, -95, - 24, 48, 22, 49, 20, 48, 18, 6, - 3, 85, 4, 3, 19, 11, 79, 67, - 83, 80, 32, 83, 105, 103, 110, 101, - 114, 0 , 0 - }; - - // This is a CSRIV2 (ocsp_multi) that has a single - // responder ID and no extensions. The request_length - // field is too long in this case. - private static final byte[] CSRIV2_LENGTH_TOO_LONG = { - 2, 0, 54, 0, 28, 0, 26, -95, - 24, 48, 22, 49, 20, 48, 18, 6, - 3, 85, 4, 3, 19, 11, 79, 67, - 83, 80, 32, 83, 105, 103, 110, 101, - 114, 0 , 0 - }; - - // A CSRIV2 (ocsp) with one Responder ID (byName: CN=OCSP Signer) - // and a nonce extension (32 bytes). - private static final byte[] CSRIV2_OCSP_1RID_1EXT = { - 1, 0, 83, 0, 28, 0, 26, -95, - 24, 48, 22, 49, 20, 48, 18, 6, - 3, 85, 4, 3, 19, 11, 79, 67, - 83, 80, 32, 83, 105, 103, 110, 101, - 114, 0, 51, 48, 49, 48, 47, 6, - 9, 43, 6, 1, 5, 5, 7, 48, - 1, 2, 4, 34, 4, 32, -34, -83, - -66, -17, -34, -83, -66, -17, -34, -83, - -66, -17, -34, -83, -66, -17, -34, -83, - -66, -17, -34, -83, -66, -17, -34, -83, - -66, -17, -34, -83, -66, -17 - }; - - public static void main(String[] args) throws Exception { - Map<String, TestCase> testList = - new LinkedHashMap<String, TestCase>() {{ - put("CTOR (Default)", testCtorTypeStatReq); - put("CTOR (Byte array)", testCtorByteArray); - put("CTOR (invalid lengths)", testCtorInvalidLengths); - }}; - - TestUtils.runTests(testList); - } - - public static final TestCase testCtorTypeStatReq = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - // Attempt to create CSRIv2 objects using null pointers - // for either parameter. In either case NPE should be thrown - CertStatusReqItemV2 csriNull; - try { - csriNull = new CertStatusReqItemV2(null, - new OCSPStatusRequest()); - throw new RuntimeException("Did not catch expected NPE " + - "for null status_type parameter"); - } catch (NullPointerException npe) { } - - try { - csriNull = new CertStatusReqItemV2(StatusRequestType.OCSP, - null); - throw new RuntimeException("Did not catch expected NPE " + - "for null StatusRequest parameter"); - } catch (NullPointerException npe) { } - - // Create an "ocsp_multi" type request using a default - // (no Responder IDs, no Extensions) OCSPStatusRequest - CertStatusReqItemV2 csriMulti = - new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest()); - HandshakeOutStream hsout = new HandshakeOutStream(null); - csriMulti.send(hsout); - TestUtils.valueCheck(DEF_CSRIV2_OCSP_MULTI_BYTES, - hsout.toByteArray()); - hsout.reset(); - - // Create an "ocsp" type request using a default - // (no Responder IDs, no Extensions) OCSPStatusRequest - CertStatusReqItemV2 csriSingle = - new CertStatusReqItemV2(StatusRequestType.OCSP, - new OCSPStatusRequest(new LinkedList<>(), - new LinkedList<>())); - csriSingle.send(hsout); - TestUtils.valueCheck(DEF_CSRIV2_OCSP_BYTES, - hsout.toByteArray()); - - // Create the CertStatusRequestItemV2 with a user-defined - // StatusRequestType value - CertStatusReqItemV2 csriNine = - new CertStatusReqItemV2(StatusRequestType.get(9), - new OCSPStatusRequest(null, null)); - if (csriNine.getType().id != 9) { - throw new RuntimeException("Expected status_type = 9, " + - "got " + csriNine.getType().id); - } else { - StatusRequest sr = csriNine.getRequest(); - if (!(sr instanceof OCSPStatusRequest)) { - throw new RuntimeException("Expected " + - "OCSPStatusRequest, got " + - sr.getClass().getName()); - } - } - - // Create the CertStatusRequestItemV2 with a StatusRequest - // that does not match the status_type argument. - // We expect IllegalArgumentException in this case. - try { - CertStatusReqItemV2 csriBadSR = new CertStatusReqItemV2( - StatusRequestType.OCSP_MULTI, - new BogusStatusRequest()); - throw new RuntimeException("Constructor accepted a " + - "StatusRequest that is inconsistent with " + - "the status_type"); - } catch (IllegalArgumentException iae) { - // The expected result...nothing to do here - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor form that takes the data from a byte array - public static final TestCase testCtorByteArray = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - StatusRequestType sType; - StatusRequest sReq; - ResponderId checkRid = - new ResponderId(new X500Principal("CN=OCSP Signer")); - Extension checkExt = new OCSPNonceExtension(32); - - CertStatusReqItemV2 csriv = - new CertStatusReqItemV2(CSRIV2_OCSP_1RID_1EXT); - sType = csriv.getType(); - if (sType != StatusRequestType.OCSP) { - throw new RuntimeException("Unexpected StatusRequestType " + - sType.getClass().getName()); - } - - sReq = csriv.getRequest(); - if (sReq instanceof OCSPStatusRequest) { - OCSPStatusRequest osr = (OCSPStatusRequest)sReq; - List<ResponderId> ridList = osr.getResponderIds(); - List<Extension> extList = osr.getExtensions(); - - if (ridList.size() != 1 || !ridList.contains(checkRid)) { - throw new RuntimeException("Responder list mismatch"); - } else if (extList.size() != 1 || - !extList.get(0).getId().equals(checkExt.getId())) { - throw new RuntimeException("Extension list mismatch"); - } - } else { - throw new RuntimeException("Expected OCSPStatusRequest " + - "from decoded bytes, got " + - sReq.getClass().getName()); - } - - // Create a CSRIV2 out of random data. A non-OCSP/OCSP_MULTI - // type will be forcibly set and the outer length field will - // be correct. - // The constructor should create a StatusRequestType object - // and an UnknownStatusRequest object consisting of the - // data segment. - byte[] junkData = new byte[48]; - Random r = new Random(System.currentTimeMillis()); - r.nextBytes(junkData); - junkData[0] = 7; // status_type = 7 - junkData[1] = 0; - junkData[2] = 45; // request_length = 45 - csriv = new CertStatusReqItemV2(junkData); - - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType.id != junkData[0]) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected 7, got " + sType.id); - } - if (sReq instanceof UnknownStatusRequest) { - // Verify the underlying StatusRequest bytes have been - // preserved correctly. - HandshakeOutStream hsout = new HandshakeOutStream(null); - sReq.send(hsout); - byte[] srDataOut = hsout.toByteArray(); - TestUtils.valueCheck(srDataOut, junkData, 0, 3, - srDataOut.length); - } else { - throw new RuntimeException("StatusRequest mismatch: " + - "expected UnknownStatusRequest, got " + - sReq.getClass().getName()); - } - - // Test the parsing of the default OCSP/OCSP_MULTI extensions - // and make sure the underlying StatusRequestType and - // StatusRequest objects are correct. - csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_MULTI_BYTES); - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType != StatusRequestType.OCSP_MULTI) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected OCSP_MULTI (2), got " + sType.id); - } - if (!(sReq instanceof OCSPStatusRequest)) { - throw new RuntimeException("StatusRequest mismatch: " + - "expected OCSPStatusRequest, got " + - sReq.getClass().getName()); - } - - csriv = new CertStatusReqItemV2(DEF_CSRIV2_OCSP_BYTES); - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType != StatusRequestType.OCSP) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected OCSP (1), got " + sType.id); - } - if (!(sReq instanceof OCSPStatusRequest)) { - throw new RuntimeException("StatusRequest mismatch: " + - "expected OCSPStatusRequest, got " + - sReq.getClass().getName()); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - public static final TestCase testCtorInvalidLengths = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - try { - CertStatusReqItemV2 csriTooShort = - new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_SHORT); - throw new RuntimeException("Expected exception not thrown"); - } catch (SSLException ssle) { } - - try { - CertStatusReqItemV2 csriTooLong = - new CertStatusReqItemV2(CSRIV2_LENGTH_TOO_LONG); - throw new RuntimeException("Expected exception not thrown"); - } catch (SSLException ssle) { } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor form that takes the data from HandshakeInputStream - public static final TestCase testCtorInputStream = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - StatusRequestType sType; - StatusRequest sReq; - ResponderId checkRid = - new ResponderId(new X500Principal("CN=OCSP Signer")); - Extension checkExt = new OCSPNonceExtension(32); - - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRIV2_OCSP_1RID_1EXT)); - CertStatusReqItemV2 csriv = new CertStatusReqItemV2(hsis); - sType = csriv.getType(); - if (sType != StatusRequestType.OCSP) { - throw new RuntimeException("Unexpected StatusRequestType " + - sType.getClass().getName()); - } - - sReq = csriv.getRequest(); - if (sReq instanceof OCSPStatusRequest) { - OCSPStatusRequest osr = (OCSPStatusRequest)sReq; - List<ResponderId> ridList = osr.getResponderIds(); - List<Extension> extList = osr.getExtensions(); - - if (ridList.size() != 1 || !ridList.contains(checkRid)) { - throw new RuntimeException("Responder list mismatch"); - } else if (extList.size() != 1 || - !extList.get(0).getId().equals(checkExt.getId())) { - throw new RuntimeException("Extension list mismatch"); - } - } else { - throw new RuntimeException("Expected OCSPStatusRequest " + - "from decoded bytes, got " + - sReq.getClass().getName()); - } - - // Create a CSRIV2 out of random data. A non-OCSP/OCSP_MULTI - // type will be forcibly set and the outer length field will - // be correct. - // The constructor should create a StatusRequestType object - // and an UnknownStatusRequest object consisting of the - // data segment. - byte[] junkData = new byte[48]; - Random r = new Random(System.currentTimeMillis()); - r.nextBytes(junkData); - junkData[0] = 7; // status_type = 7 - junkData[1] = 0; - junkData[2] = 45; // request_length = 45 - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(junkData)); - csriv = new CertStatusReqItemV2(hsis); - - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType.id != junkData[0]) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected 7, got " + sType.id); - } - if (sReq instanceof UnknownStatusRequest) { - // Verify the underlying StatusRequest bytes have been - // preserved correctly. - HandshakeOutStream hsout = new HandshakeOutStream(null); - sReq.send(hsout); - byte[] srDataOut = hsout.toByteArray(); - TestUtils.valueCheck(srDataOut, junkData, 0, 3, - srDataOut.length); - } else { - throw new RuntimeException("StatusRequest mismatch: " + - "expected UnknownStatusRequest, got " + - sReq.getClass().getName()); - } - - // Test the parsing of the default OCSP/OCSP_MULTI extensions - // and make sure the underlying StatusRequestType and - // StatusRequest objects are correct. - hsis = new HandshakeInStream(); - hsis.incomingRecord( - ByteBuffer.wrap(DEF_CSRIV2_OCSP_MULTI_BYTES)); - csriv = new CertStatusReqItemV2(hsis); - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType != StatusRequestType.OCSP_MULTI) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected OCSP_MULTI (2), got " + sType.id); - } - if (!(sReq instanceof OCSPStatusRequest)) { - throw new RuntimeException("StatusRequest mismatch: " + - "expected OCSPStatusRequest, got " + - sReq.getClass().getName()); - } - - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(DEF_CSRIV2_OCSP_BYTES)); - csriv = new CertStatusReqItemV2(hsis); - sType = csriv.getType(); - sReq = csriv.getRequest(); - if (sType != StatusRequestType.OCSP) { - throw new RuntimeException("StatusRequestType mismatch: " + - "expected OCSP (1), got " + sType.id); - } - if (!(sReq instanceof OCSPStatusRequest)) { - throw new RuntimeException("StatusRequest mismatch: " + - "expected OCSPStatusRequest, got " + - sReq.getClass().getName()); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqListV2ExtensionTests.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/CertStatusReqListV2ExtensionTests.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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.*; -import java.nio.ByteBuffer; -import javax.net.ssl.*; - -/* - * Checks that the hash value for a certificate's issuer name is generated - * correctly. Requires any certificate that is not self-signed. - * - * NOTE: this test uses Sun private classes which are subject to change. - */ -public class CertStatusReqListV2ExtensionTests { - - private static final boolean debug = false; - - // Default status_request_v2 extension with two items - // 1. Type = ocsp_multi, OCSPStatusRequest is default - // 2. Type = ocsp, OCSPStatusRequest is default - private static final byte[] CSRLV2_DEF = { - 0, 14, 2, 0, 4, 0, 0, 0, - 0, 1, 0, 4, 0, 0, 0, 0 - }; - - // A status_request_v2 where the item list length is - // longer than the provided data - private static final byte[] CSRLV2_LEN_TOO_LONG = { - 0, 18, 2, 0, 4, 0, 0, 0, - 0, 1, 0, 4, 0, 0, 0, 0 - }; - - // A status_request_v2 where the item list length is - // shorter than the provided data - private static final byte[] CSRLV2_LEN_TOO_SHORT = { - 0, 11, 2, 0, 4, 0, 0, 0, - 0, 1, 0, 4, 0, 0, 0, 0 - }; - - // A status_request_v2 extension with a zero-length - // certificate_status_req_list (not allowed by the spec) - private static final byte[] CSRLV2_INVALID_ZEROLEN = {0, 0}; - - // A status_request_v2 extension with two items (ocsp_multi and ocsp) - // using OCSPStatusRequests with 5 ResponderIds and 1 Extension each. - private static final byte[] CSRLV2_TWO_NON_DEF_ITEMS = { - 2, 90, 2, 1, 42, 0, -13, 0, - 59, -95, 57, 48, 55, 49, 16, 48, - 14, 6, 3, 85, 4, 10, 19, 7, - 83, 111, 109, 101, 73, 110, 99, 49, - 16, 48, 14, 6, 3, 85, 4, 11, - 19, 7, 83, 111, 109, 101, 80, 75, - 73, 49, 17, 48, 15, 6, 3, 85, - 4, 3, 19, 8, 83, 111, 109, 101, - 79, 67, 83, 80, 0, 68, -95, 66, - 48, 64, 49, 13, 48, 11, 6, 3, - 85, 4, 10, 19, 4, 79, 104, 77, - 121, 49, 14, 48, 12, 6, 3, 85, - 4, 11, 19, 5, 66, 101, 97, 114, - 115, 49, 15, 48, 13, 6, 3, 85, - 4, 11, 19, 6, 84, 105, 103, 101, - 114, 115, 49, 14, 48, 12, 6, 3, - 85, 4, 3, 19, 5, 76, 105, 111, - 110, 115, 0, 58, -95, 56, 48, 54, - 49, 16, 48, 14, 6, 3, 85, 4, - 10, 19, 7, 67, 111, 109, 112, 97, - 110, 121, 49, 13, 48, 11, 6, 3, - 85, 4, 11, 19, 4, 87, 101, 115, - 116, 49, 19, 48, 17, 6, 3, 85, - 4, 3, 19, 10, 82, 101, 115, 112, - 111, 110, 100, 101, 114, 49, 0, 24, - -94, 22, 4, 20, -67, -36, 114, 121, - 92, -79, 116, -1, 102, -107, 7, -21, - 18, -113, 64, 76, 96, -7, -66, -63, - 0, 24, -94, 22, 4, 20, -51, -69, - 107, -82, -39, -87, 45, 25, 41, 28, - -76, -68, -11, -110, -94, -97, 62, 47, - 58, -125, 0, 51, 48, 49, 48, 47, - 6, 9, 43, 6, 1, 5, 5, 7, - 48, 1, 2, 4, 34, 4, 32, -26, - -81, -120, -61, -127, -79, 0, -39, -54, - 49, 3, -51, -57, -85, 19, -126, 94, - -2, 21, 26, 98, 6, 105, -35, -37, - -29, -73, 101, 53, 44, 15, -19, 1, - 1, 42, 0, -13, 0, 59, -95, 57, - 48, 55, 49, 16, 48, 14, 6, 3, - 85, 4, 10, 19, 7, 83, 111, 109, - 101, 73, 110, 99, 49, 16, 48, 14, - 6, 3, 85, 4, 11, 19, 7, 83, - 111, 109, 101, 80, 75, 73, 49, 17, - 48, 15, 6, 3, 85, 4, 3, 19, - 8, 83, 111, 109, 101, 79, 67, 83, - 80, 0, 68, -95, 66, 48, 64, 49, - 13, 48, 11, 6, 3, 85, 4, 10, - 19, 4, 79, 104, 77, 121, 49, 14, - 48, 12, 6, 3, 85, 4, 11, 19, - 5, 66, 101, 97, 114, 115, 49, 15, - 48, 13, 6, 3, 85, 4, 11, 19, - 6, 84, 105, 103, 101, 114, 115, 49, - 14, 48, 12, 6, 3, 85, 4, 3, - 19, 5, 76, 105, 111, 110, 115, 0, - 58, -95, 56, 48, 54, 49, 16, 48, - 14, 6, 3, 85, 4, 10, 19, 7, - 67, 111, 109, 112, 97, 110, 121, 49, - 13, 48, 11, 6, 3, 85, 4, 11, - 19, 4, 87, 101, 115, 116, 49, 19, - 48, 17, 6, 3, 85, 4, 3, 19, - 10, 82, 101, 115, 112, 111, 110, 100, - 101, 114, 49, 0, 24, -94, 22, 4, - 20, -67, -36, 114, 121, 92, -79, 116, - -1, 102, -107, 7, -21, 18, -113, 64, - 76, 96, -7, -66, -63, 0, 24, -94, - 22, 4, 20, -51, -69, 107, -82, -39, - -87, 45, 25, 41, 28, -76, -68, -11, - -110, -94, -97, 62, 47, 58, -125, 0, - 51, 48, 49, 48, 47, 6, 9, 43, - 6, 1, 5, 5, 7, 48, 1, 2, - 4, 34, 4, 32, -26, -81, -120, -61, - -127, -79, 0, -39, -54, 49, 3, -51, - -57, -85, 19, -126, 94, -2, 21, 26, - 98, 6, 105, -35, -37, -29, -73, 101, - 53, 44, 15, -19 - }; - - public static void main(String[] args) throws Exception { - Map<String, TestCase> testList = - new LinkedHashMap<String, TestCase>() {{ - put("CTOR (default)", testCtorDefault); - put("CTOR (List<CertStatusReqItemV2)", testCtorItemList); - put("CTOR (HandshakeInStream, getRequestList", - testCtorInStream); - }}; - - TestUtils.runTests(testList); - } - - public static final TestCase testCtorDefault = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - CertStatusReqListV2Extension csrlV2 = - new CertStatusReqListV2Extension(); - HandshakeOutStream hsout = new HandshakeOutStream(null); - csrlV2.send(hsout); - TestUtils.valueCheck(wrapExtData(new byte[0]), - hsout.toByteArray()); - - // The length should be 4 (2 bytes for the type, 2 for the - // encoding of zero-length - if (csrlV2.length() != 4) { - throw new RuntimeException("Incorrect length from " + - "default object. Expected 4, got " + - csrlV2.length()); - } - - // Since there's no data, there are no status_type or request - // data fields defined. An empty, unmodifiable list should be - // returned when obtained from the extension. - List<CertStatusReqItemV2> itemList = csrlV2.getRequestItems(); - if (!itemList.isEmpty()) { - throw new RuntimeException("Default CSRLV2 returned " + - "non-empty request list"); - } else { - try { - itemList.add(new CertStatusReqItemV2( - StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest())); - throw new RuntimeException("Returned itemList is " + - "modifiable!"); - } catch (UnsupportedOperationException uoe) { } - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - public static final TestCase testCtorItemList = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - OCSPStatusRequest osr = new OCSPStatusRequest(); - List<CertStatusReqItemV2> noItems = Collections.emptyList(); - List<CertStatusReqItemV2> defList = - new ArrayList<CertStatusReqItemV2>() {{ - add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, osr)); - add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); - }}; - List<CertStatusReqItemV2> unknownTypesList = - new ArrayList<CertStatusReqItemV2>() {{ - add(new CertStatusReqItemV2(StatusRequestType.get(8), - new UnknownStatusRequest(new byte[0]))); - add(new CertStatusReqItemV2(StatusRequestType.get(12), - new UnknownStatusRequest(new byte[5]))); - }}; - - try { - HandshakeOutStream hsout = new HandshakeOutStream(null); - StatusRequest basicStatReq = new OCSPStatusRequest(); - - // Create an extension using a default-style OCSPStatusRequest - // (no responder IDs, no extensions). - CertStatusReqListV2Extension csrlv2 = - new CertStatusReqListV2Extension(defList); - csrlv2.send(hsout); - TestUtils.valueCheck(wrapExtData(CSRLV2_DEF), - hsout.toByteArray()); - hsout.reset(); - - // Create the extension using a StatusRequestType not already - // instantiated as a static StatusRequestType - // (e.g. OCSP/OCSP_MULTI) - csrlv2 = new CertStatusReqListV2Extension(unknownTypesList); - List<CertStatusReqItemV2> itemList = csrlv2.getRequestItems(); - if (itemList.size() != unknownTypesList.size()) { - throw new RuntimeException("Custom CSRLV2 returned " + - "an incorrect number of items: expected " + - unknownTypesList.size() + ", got " + - itemList.size()); - } else { - // Verify that the list is unmodifiable - try { - itemList.add(new CertStatusReqItemV2( - StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest())); - throw new RuntimeException("Returned itemList is " + - "modifiable!"); - } catch (UnsupportedOperationException uoe) { } - } - - // Pass a null value for the item list. This should throw - // an exception - try { - CertStatusReqListV2Extension csrlv2Null = - new CertStatusReqListV2Extension(null); - throw new RuntimeException("Constructor accepted a " + - "null request list"); - } catch (NullPointerException npe) { } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor that builds the ob ject using data from - // a HandshakeInStream - // This also tests the length, getReqType and getRequest methods - public static final TestCase testCtorInStream = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - OCSPStatusRequest osr; - CertStatusReqListV2Extension csrlv2; - - try { - // To simulate the extension coming in a ServerHello, the - // type and length would already be read by HelloExtensions - // and there is no extension data - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(new byte[0])); - csrlv2 = new CertStatusReqListV2Extension(hsis, - hsis.available()); - - // Verify length/request list - if (csrlv2.length() != 4) { - throw new RuntimeException("Invalid length: received " + - csrlv2.length() + ", expected 4"); - } else { - List<CertStatusReqItemV2> itemList = - csrlv2.getRequestItems(); - if (!itemList.isEmpty()) { - throw new RuntimeException("Default CSRLV2 returned " + - "non-empty request list"); - } else { - try { - itemList.add(new CertStatusReqItemV2( - StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest())); - throw new RuntimeException("Returned itemList is " + - "modifiable!"); - } catch (UnsupportedOperationException uoe) { } - } - } - - // Try the an extension with our basic client-generated - // status_request_v2 (2 items, ocsp_multi and ocsp, each with - // a default OCSPStatusRequest - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_DEF)); - csrlv2 = new CertStatusReqListV2Extension(hsis, - hsis.available()); - if (csrlv2.length() != (CSRLV2_DEF.length + 4)) { - throw new RuntimeException("Invalid length: received " + - csrlv2.length() + ", expected " + - CSRLV2_DEF.length + 4); - } else { - List<CertStatusReqItemV2> itemList = - csrlv2.getRequestItems(); - if (itemList.size() != 2) { - throw new RuntimeException("Unexpected number of " + - "items request list, expected 2, got " + - itemList.size()); - } else { - try { - itemList.add(new CertStatusReqItemV2( - StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest())); - throw new RuntimeException("Returned itemList is " + - "modifiable!"); - } catch (UnsupportedOperationException uoe) { } - } - } - - // Try incoming data with an illegal zero-length - // certificate_status_req_list - try { - hsis = new HandshakeInStream(); - hsis.incomingRecord( - ByteBuffer.wrap(CSRLV2_INVALID_ZEROLEN)); - csrlv2 = new CertStatusReqListV2Extension(hsis, - hsis.available()); - throw new RuntimeException("Unxpected successful " + - "object construction"); - } catch (SSLException ssle) { } - - // Try extensions where the certificate_status_req_list length - // is either too long or too short - try { - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_LONG)); - csrlv2 = new CertStatusReqListV2Extension(hsis, - hsis.available()); - throw new RuntimeException("Unxpected successful " + - "object construction"); - } catch (SSLException ssle) { } - - try { - hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(CSRLV2_LEN_TOO_SHORT)); - csrlv2 = new CertStatusReqListV2Extension(hsis, - hsis.available()); - throw new RuntimeException("Unxpected successful " + - "object construction"); - } catch (SSLException ssle) { } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Take CSRE extension data and add extension type and length decorations - private static byte[] wrapExtData(byte[] extData) { - int bufferLen = extData.length + 4; - ByteBuffer bb = ByteBuffer.allocate(bufferLen); - - bb.putShort((short)ExtensionType.EXT_STATUS_REQUEST_V2.id); - bb.putShort((short)extData.length); - if (extData.length != 0) { - bb.put(extData); - } - return bb.array(); - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/OCSPStatusRequestTests.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/OCSPStatusRequestTests.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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.cert.*; -import java.util.*; -import java.nio.ByteBuffer; -import javax.security.auth.x500.X500Principal; -import sun.security.provider.certpath.ResponderId; -import sun.security.provider.certpath.OCSPNonceExtension; - -/* - * Checks that the hash value for a certificate's issuer name is generated - * correctly. Requires any certificate that is not self-signed. - * - * NOTE: this test uses Sun private classes which are subject to change. - */ -public class OCSPStatusRequestTests { - - private static final boolean debug = false; - - // The default (no Responder IDs or Extensions) - private static final byte[] DEF_OCSPREQ_BYTES = { 0, 0, 0, 0 }; - - // OCSP Extension with one Responder ID (byName: CN=OCSP Signer) and - // a nonce extension (32 bytes). - private static final byte[] OCSPREQ_1RID_1EXT = { - 0, 28, 0, 26, -95, 24, 48, 22, - 49, 20, 48, 18, 6, 3, 85, 4, - 3, 19, 11, 79, 67, 83, 80, 32, - 83, 105, 103, 110, 101, 114, 0, 51, - 48, 49, 48, 47, 6, 9, 43, 6, - 1, 5, 5, 7, 48, 1, 2, 4, - 34, 4, 32, -34, -83, -66, -17, -34, - -83, -66, -17, -34, -83, -66, -17, -34, - -83, -66, -17, -34, -83, -66, -17, -34, - -83, -66, -17, -34, -83, -66, -17, -34, - -83, -66, -17 - }; - - public static void main(String[] args) throws Exception { - Map<String, TestCase> testList = - new LinkedHashMap<String, TestCase>() {{ - put("CTOR (default)", testCtorDefault); - put("CTOR (Responder Id and Extension)", testCtorRidsExts); - put("CTOR (HandshakeInStream)", testCtorInStream); - put("CTOR (byte array)", testCtorByteArray); - put("Length tests", testLength); - put("Equals tests", testEquals); - }}; - - TestUtils.runTests(testList); - } - - // Test the default constructor and its encoding - public static final TestCase testCtorDefault = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - // Create a OCSPStatusRequest with a single ResponderId - // and Extension - OCSPStatusRequest osrDefault = new OCSPStatusRequest(); - HandshakeOutStream hsout = new HandshakeOutStream(null); - osrDefault.send(hsout); - System.out.println("Encoded Result:"); - TestUtils.dumpBytes(hsout.toByteArray()); - - TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray()); - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor form that allows the user to specify zero - // or more ResponderId objects and/or Extensions - public static final TestCase testCtorRidsExts = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - List<ResponderId> ridList = new LinkedList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=OCSP Signer"))); - }}; - List<Extension> extList = new LinkedList<Extension>() {{ - add(new OCSPNonceExtension(32)); - }}; - - // Default-style OCSPStatusRequest using both empty Lists and - // null inputs - OCSPStatusRequest osrDef1 = - new OCSPStatusRequest(new LinkedList<ResponderId>(), - null); - OCSPStatusRequest osrDef2 = - new OCSPStatusRequest(null, - new LinkedList<Extension>()); - HandshakeOutStream hsout = new HandshakeOutStream(null); - osrDef1.send(hsout); - System.out.println("Encoded Result:"); - TestUtils.dumpBytes(hsout.toByteArray()); - TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray()); - - hsout.reset(); - osrDef2.send(hsout); - System.out.println("Encoded Result:"); - TestUtils.dumpBytes(hsout.toByteArray()); - TestUtils.valueCheck(DEF_OCSPREQ_BYTES, hsout.toByteArray()); - - hsout.reset(); - OCSPStatusRequest osrWithItems = - new OCSPStatusRequest(ridList, extList); - osrWithItems.send(hsout); - System.out.println("Encoded Result:"); - byte[] encodedData = hsout.toByteArray(); - TestUtils.dumpBytes(encodedData); - // Check everything except the last 32 bytes (nonce data) - TestUtils.valueCheck(OCSPREQ_1RID_1EXT, encodedData, 0, 0, - encodedData.length - 32); - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor that builds the ob ject using data from - // a HandshakeInStream - public static final TestCase testCtorInStream = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - ResponderId checkRid = - new ResponderId(new X500Principal("CN=OCSP Signer")); - Extension checkExt = new OCSPNonceExtension(32); - - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT)); - OCSPStatusRequest osr = new OCSPStatusRequest(hsis); - - List<ResponderId> ridList = osr.getResponderIds(); - List<Extension> extList = osr.getExtensions(); - - if (ridList.size() != 1 || !ridList.contains(checkRid)) { - throw new RuntimeException("Responder list mismatch"); - } else if (extList.size() != 1 || - !extList.get(0).getId().equals(checkExt.getId())) { - throw new RuntimeException("Extension list mismatch"); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the constructor form that takes the data from a byte array - public static final TestCase testCtorByteArray = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - ResponderId checkRid = - new ResponderId(new X500Principal("CN=OCSP Signer")); - Extension checkExt = new OCSPNonceExtension(32); - - OCSPStatusRequest osr = - new OCSPStatusRequest(OCSPREQ_1RID_1EXT); - - List<ResponderId> ridList = osr.getResponderIds(); - List<Extension> extList = osr.getExtensions(); - - if (ridList.size() != 1 || !ridList.contains(checkRid)) { - throw new RuntimeException("Responder list mismatch"); - } else if (extList.size() != 1 || - !extList.get(0).getId().equals(checkExt.getId())) { - throw new RuntimeException("Extension list mismatch"); - } - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the length functions for both default and non-default - // OCSPStatusRequest objects - public static final TestCase testLength = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT)); - OCSPStatusRequest osr = new OCSPStatusRequest(hsis); - OCSPStatusRequest osrDefault = new OCSPStatusRequest(); - - if (osrDefault.length() != DEF_OCSPREQ_BYTES.length) { - throw new RuntimeException("Invalid length for default: " + - "Expected" + DEF_OCSPREQ_BYTES.length + - ", received " + osrDefault.length()); - } else if (osr.length() != OCSPREQ_1RID_1EXT.length) { - throw new RuntimeException("Invalid length for default: " + - "Expected" + OCSPREQ_1RID_1EXT.length + - ", received " + osr.length()); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test the equals method with default and non-default objects - public static final TestCase testEquals = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - Boolean pass = Boolean.FALSE; - String message = null; - try { - // Make two different lists with the same ResponderId values - // and also make a extension list - List<ResponderId> ridList1 = new LinkedList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=OCSP Signer"))); - }}; - List<ResponderId> ridList2 = new LinkedList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=OCSP Signer"))); - }}; - List<Extension> extList = new LinkedList<Extension>() {{ - add(new OCSPNonceExtension(32)); - }}; - - // We expect two default OCSP objects to be equal - OCSPStatusRequest osrDefault = new OCSPStatusRequest(); - if (!osrDefault.equals(new OCSPStatusRequest())) { - throw new RuntimeException("Default OCSPStatusRequest" + - " equality test failed"); - } - - // null test (expect false return) - if (osrDefault.equals(null)) { - throw new RuntimeException("OCSPStatusRequest matched" + - " unexpectedly"); - } - - // Self-reference test - OCSPStatusRequest osrSelfRef = osrDefault; - if (!osrDefault.equals(osrSelfRef)) { - throw new RuntimeException("Default OCSPStatusRequest" + - " equality test failed"); - } - - // Two OCSPStatusRequests with matching ResponderIds should - // be considered equal - OCSPStatusRequest osrByList1 = - new OCSPStatusRequest(ridList1, null); - OCSPStatusRequest osrByList2 = new OCSPStatusRequest(ridList2, - Collections.emptyList()); - if (!osrByList1.equals(osrByList2)) { - throw new RuntimeException("Single Responder ID " + - "OCSPStatusRequest equality test failed"); - } - - // We expect OCSPStatusRequests with different nonces to be - // considered unequal. - HandshakeInStream hsis = new HandshakeInStream(); - hsis.incomingRecord(ByteBuffer.wrap(OCSPREQ_1RID_1EXT)); - OCSPStatusRequest osrStream = new OCSPStatusRequest(hsis); - OCSPStatusRequest osrRidExt = new OCSPStatusRequest(ridList1, - extList); - if (osrStream.equals(osrRidExt)) { - throw new RuntimeException("OCSPStatusRequest matched" + - " unexpectedly"); - } - - pass = Boolean.TRUE; - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusReqSelection.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusReqSelection.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,899 +0,0 @@ -/* - * Copyright (c) 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. - * - * 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. - */ - -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. - -// See ../../../../RunStatReqSelect.java for the jtreg header - -package sun.security.ssl; - -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; -import javax.security.auth.x500.X500Principal; -import java.io.*; -import java.math.BigInteger; -import java.security.*; -import java.nio.*; -import java.security.cert.X509Certificate; -import java.security.cert.Extension; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -import sun.security.provider.certpath.OCSPNonceExtension; -import sun.security.provider.certpath.ResponderId; -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; - -public class StatusReqSelection { - - /* - * Enables logging of the SSLEngine operations. - */ - private static final boolean logging = true; - - /* - * Enables the JSSE system debugging system property: - * - * -Djavax.net.debug=all - * - * This gives a lot of low-level information about operations underway, - * including specific handshake messages, and might be best examined - * after gaining some familiarity with this application. - */ - private static final boolean debug = false; - - // The following items are used to set up the keystores. - private static final String passwd = "passphrase"; - private static final String ROOT_ALIAS = "root"; - private static final String INT_ALIAS = "intermediate"; - private static final String SSL_ALIAS = "ssl"; - - // PKI and server components we will need for this test - private static KeyManagerFactory kmf; - private static TrustManagerFactory tmf; - private static KeyStore rootKeystore; // Root CA Keystore - private static KeyStore intKeystore; // Intermediate CA Keystore - private static KeyStore serverKeystore; // SSL Server Keystore - private static KeyStore trustStore; // SSL Client trust store - private static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder - private static int rootOcspPort; // Port for root OCSP - private static SimpleOCSPServer intOcsp; // Intermediate CA OCSP server - private static int intOcspPort; // Port for intermediate OCSP - private static SSLContext ctxStaple; // SSLContext for all tests - - // Some useful objects we will need for test purposes - private static final SecureRandom RNG = new SecureRandom(); - - // We'll be using these objects repeatedly to make hello messages - private static final ProtocolVersion VER_1_0 = ProtocolVersion.TLS10; - private static final ProtocolVersion VER_1_2 = ProtocolVersion.TLS12; - private static final CipherSuiteList SUITES = new CipherSuiteList( - CipherSuite.valueOf("TLS_RSA_WITH_AES_128_GCM_SHA256")); - private static final SessionId SID = new SessionId(new byte[0]); - private static final HelloExtension RNIEXT = - new RenegotiationInfoExtension(new byte[0], new byte[0]); - private static final List<SignatureAndHashAlgorithm> algList = - new ArrayList<SignatureAndHashAlgorithm>() {{ - add(SignatureAndHashAlgorithm.valueOf(4, 1, 0)); - }}; // List with only SHA256withRSA - private static final SignatureAlgorithmsExtension SIGALGEXT = - new SignatureAlgorithmsExtension(algList); - - /* - * Main entry point for this test. - */ - public static void main(String args[]) throws Exception { - int testsPassed = 0; - - if (debug) { - System.setProperty("javax.net.debug", "ssl"); - } - - // All tests will have stapling enabled on the server side - System.setProperty("jdk.tls.server.enableStatusRequestExtension", - "true"); - - // Create a single SSLContext that we can use for all tests - ctxStaple = SSLContext.getInstance("TLS"); - - // Create the PKI we will use for the test and start the OCSP servers - createPKI(); - - // Set up the KeyManagerFactory and TrustManagerFactory - kmf = KeyManagerFactory.getInstance("PKIX"); - kmf.init(serverKeystore, passwd.toCharArray()); - tmf = TrustManagerFactory.getInstance("PKIX"); - tmf.init(trustStore); - - List<TestCase> testList = new ArrayList<TestCase>() {{ - add(new TestCase("ClientHello: No stapling extensions", - makeHelloNoStaplingExts(), false, false)); - add(new TestCase("ClientHello: Default status_request only", - makeDefaultStatReqOnly(), true, false)); - add(new TestCase("ClientHello: Default status_request_v2 only", - makeDefaultStatReqV2Only(), false, true)); - add(new TestCase("ClientHello: Both status_request exts, default", - makeDefaultStatReqBoth(), false, true)); - add(new TestCase( - "ClientHello: Hello with status_request and responder IDs", - makeStatReqWithRid(), false, false)); - add(new TestCase( - "ClientHello: Hello with status_request using no " + - "responder IDs but provides the OCSP nonce extension", - makeStatReqNoRidNonce(), true, false)); - add(new TestCase("ClientHello with default status_request and " + - "status_request_v2 with ResponderIds", - makeStatReqDefV2WithRid(), true, false)); - add(new TestCase("ClientHello with default status_request and " + - "status_request_v2 (OCSP_MULTI with ResponderId, " + - "OCSP as a default request)", - makeStatReqDefV2MultiWithRidSingleDef(), false, true)); - add(new TestCase("ClientHello with status_request and " + - "status_request_v2 and all OCSPStatusRequests use " + - "Responder IDs", - makeStatReqAllWithRid(), false, false)); - add(new TestCase("ClientHello with default status_request and " + - "status_request_v2 that has a default OCSP item and " + - "multiple OCSP_MULTI items, only one is default", - makeHelloMultiV2andSingle(), false, true)); - }}; - - // Run the client and server property tests - for (TestCase test : testList) { - try { - log("*** Test: " + test.testName); - if (runTest(test)) { - log("PASS: status_request: " + test.statReqEnabled + - ", status_request_v2: " + test.statReqV2Enabled); - testsPassed++; - } - } catch (Exception e) { - // If we get an exception, we'll count it as a failure - log("Test failure due to exception: " + e); - } - log(""); - } - - // Summary - if (testsPassed != testList.size()) { - throw new RuntimeException(testList.size() - testsPassed + - " tests failed out of " + testList.size() + " total."); - } else { - log("Total tests: " + testList.size() + ", all passed"); - } - } - - private static boolean runTest(TestCase test) throws Exception { - SSLEngineResult serverResult; - - // Create a Server SSLEngine to receive our customized ClientHello - ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - SSLEngine engine = ctxStaple.createSSLEngine(); - engine.setUseClientMode(false); - engine.setNeedClientAuth(false); - - SSLSession session = engine.getSession(); - ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes()); - ByteBuffer serverIn = - ByteBuffer.allocate(session.getApplicationBufferSize() + 50); - ByteBuffer sTOc = - ByteBuffer.allocateDirect(session.getPacketBufferSize()); - - // Send the ClientHello ByteBuffer in the test case - if (debug) { - System.out.println("Sending Client Hello:\n" + - dumpHexBytes(test.data)); - } - - // Consume the client hello - serverResult = engine.unwrap(test.data, serverIn); - if (debug) { - log("server unwrap: ", serverResult); - } - if (serverResult.getStatus() != SSLEngineResult.Status.OK) { - throw new SSLException("Server unwrap got status: " + - serverResult.getStatus()); - } else if (serverResult.getHandshakeStatus() != - SSLEngineResult.HandshakeStatus.NEED_TASK) { - throw new SSLException("Server unwrap expected NEED_TASK, got: " + - serverResult.getHandshakeStatus()); - } - runDelegatedTasks(serverResult, engine); - if (engine.getHandshakeStatus() != - SSLEngineResult.HandshakeStatus.NEED_WRAP) { - throw new SSLException("Expected NEED_WRAP, got: " + - engine.getHandshakeStatus()); - } - - // Generate a TLS record with the ServerHello - serverResult = engine.wrap(serverOut, sTOc); - if (debug) { - log("client wrap: ", serverResult); - } - if (serverResult.getStatus() != SSLEngineResult.Status.OK) { - throw new SSLException("Client wrap got status: " + - serverResult.getStatus()); - } - sTOc.flip(); - - if (debug) { - log("Server Response:\n" + dumpHexBytes(sTOc)); - } - - return checkServerHello(sTOc, test.statReqEnabled, - test.statReqV2Enabled); - } - - /** - * Make a TLSv1.2 ClientHello with only RNI and no stapling extensions - */ - private static ByteBuffer makeHelloNoStaplingExts() throws IOException { - // Craft the ClientHello byte buffer - HelloExtensions exts = new HelloExtensions(); - exts.add(RNIEXT); - exts.add(SIGALGEXT); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a TLSv1.2 ClientHello with the RNI and Status Request extensions - */ - private static ByteBuffer makeDefaultStatReqOnly() throws IOException { - // Craft the ClientHello byte buffer - HelloExtensions exts = new HelloExtensions(); - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a TLSv1.2 ClientHello with the RNI and Status Request V2 extension - */ - private static ByteBuffer makeDefaultStatReqV2Only() throws IOException { - // Craft the ClientHello byte buffer - HelloExtensions exts = new HelloExtensions(); - OCSPStatusRequest osr = new OCSPStatusRequest(); - List<CertStatusReqItemV2> itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - osr)); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - /** - * Make a TLSv1.2 ClientHello with Status Request and Status Request V2 - * extensions. - */ - private static ByteBuffer makeDefaultStatReqBoth() throws IOException { - // Craft the ClientHello byte buffer - HelloExtensions exts = new HelloExtensions(); - OCSPStatusRequest osr = new OCSPStatusRequest(); - List<CertStatusReqItemV2> itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - osr)); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a ClientHello using a status_request that has a single - * responder ID in it. - */ - private static ByteBuffer makeStatReqWithRid() throws IOException { - HelloExtensions exts = new HelloExtensions(); - exts.add(RNIEXT); - exts.add(SIGALGEXT); - List<ResponderId> rids = new ArrayList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=Foo"))); - }}; - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(rids, null))); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a ClientHello using a status_request that has no - * responder IDs but does provide the nonce extension. - */ - private static ByteBuffer makeStatReqNoRidNonce() throws IOException { - HelloExtensions exts = new HelloExtensions(); - exts.add(RNIEXT); - exts.add(SIGALGEXT); - List<Extension> ocspExts = new ArrayList<Extension>() {{ - add(new OCSPNonceExtension(16)); - }}; - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, ocspExts))); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a ClientHello using a default status_request and a - * status_request_v2 that has a single responder ID in it. - */ - private static ByteBuffer makeStatReqDefV2WithRid() throws IOException { - HelloExtensions exts = new HelloExtensions(); - List<ResponderId> rids = new ArrayList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=Foo"))); - }}; - List<CertStatusReqItemV2> itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(rids, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, - new OCSPStatusRequest(rids, null))); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a ClientHello using a default status_request and a - * status_request_v2 that has a single responder ID in it for the - * OCSP_MULTI request item and a default OCSP request item. - */ - private static ByteBuffer makeStatReqDefV2MultiWithRidSingleDef() - throws IOException { - HelloExtensions exts = new HelloExtensions(); - List<ResponderId> rids = new ArrayList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=Foo"))); - }}; - List<CertStatusReqItemV2> itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(rids, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a ClientHello using status_request and status_request_v2 where - * all underlying OCSPStatusRequests use responder IDs. - */ - private static ByteBuffer makeStatReqAllWithRid() throws IOException { - HelloExtensions exts = new HelloExtensions(); - List<ResponderId> rids = new ArrayList<ResponderId>() {{ - add(new ResponderId(new X500Principal("CN=Foo"))); - }}; - List<CertStatusReqItemV2> itemList = new ArrayList<>(2); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(rids, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, - new OCSPStatusRequest(rids, null))); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(rids, null))); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Make a TLSv1.2 ClientHello multiple CertStatusReqItemV2s of different - * types. One of the middle items should be acceptable while the others - * have responder IDs. The status_request (v1) should also be acceptable - * but should be overridden in favor of the status_request_v2. - */ - private static ByteBuffer makeHelloMultiV2andSingle() throws IOException { - // Craft the ClientHello byte buffer - HelloExtensions exts = new HelloExtensions(); - List<ResponderId> fooRid = Collections.singletonList( - new ResponderId(new X500Principal("CN=Foo"))); - List<ResponderId> barRid = Collections.singletonList( - new ResponderId(new X500Principal("CN=Bar"))); - List<CertStatusReqItemV2> itemList = new ArrayList<>(); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(fooRid, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(null, null))); - itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, - new OCSPStatusRequest(barRid, null))); - - exts.add(RNIEXT); - exts.add(SIGALGEXT); - exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, - new OCSPStatusRequest(null, null))); - exts.add(new CertStatusReqListV2Extension(itemList)); - return createTlsRecord(Record.ct_handshake, VER_1_2, - createClientHelloMsg(VER_1_2, SID, SUITES, exts)); - } - - /** - * Wrap a TLS content message into a TLS record header - * - * @param contentType a byte containing the content type value - * @param pv the protocol version for this record - * @param data a byte buffer containing the message data - * @return - */ - private static ByteBuffer createTlsRecord(byte contentType, - ProtocolVersion pv, ByteBuffer data) { - int msgLen = (data != null) ? data.limit() : 0; - - // Allocate enough space to hold the TLS record header + the message - ByteBuffer recordBuf = ByteBuffer.allocate(msgLen + 5); - recordBuf.put(contentType); - recordBuf.putShort((short)pv.v); - recordBuf.putShort((short)msgLen); - if (msgLen > 0) { - recordBuf.put(data); - } - - recordBuf.flip(); - return recordBuf; - } - - /** - * Craft and encode a ClientHello message as a byte array. - * - * @param pv the protocol version asserted in the hello message. - * @param sessId the session ID for this hello message. - * @param suites a list consisting of one or more cipher suite objects - * @param extensions a list of HelloExtension objects - * - * @return a byte array containing the encoded ClientHello message. - */ - private static ByteBuffer createClientHelloMsg(ProtocolVersion pv, - SessionId sessId, CipherSuiteList suites, - HelloExtensions extensions) throws IOException { - ByteBuffer msgBuf; - - HandshakeOutStream hsos = - new HandshakeOutStream(new SSLEngineOutputRecord()); - - // Construct the client hello object from the first 3 parameters - HandshakeMessage.ClientHello cHello = - new HandshakeMessage.ClientHello(RNG, pv, sessId, suites, - false); - - // Use the HelloExtensions provided by the caller - if (extensions != null) { - cHello.extensions = extensions; - } - - cHello.send(hsos); - msgBuf = ByteBuffer.allocate(hsos.size() + 4); - - // Combine the handshake type with the length - msgBuf.putInt((HandshakeMessage.ht_client_hello << 24) | - (hsos.size() & 0x00FFFFFF)); - msgBuf.put(hsos.toByteArray()); - msgBuf.flip(); - return msgBuf; - } - - /* - * If the result indicates that we have outstanding tasks to do, - * go ahead and run them in this thread. - */ - private static void runDelegatedTasks(SSLEngineResult result, - SSLEngine engine) throws Exception { - - if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { - Runnable runnable; - while ((runnable = engine.getDelegatedTask()) != null) { - if (debug) { - log("\trunning delegated task..."); - } - runnable.run(); - } - HandshakeStatus hsStatus = engine.getHandshakeStatus(); - if (hsStatus == HandshakeStatus.NEED_TASK) { - throw new Exception( - "handshake shouldn't need additional tasks"); - } - if (debug) { - log("\tnew HandshakeStatus: " + hsStatus); - } - } - } - - private static void log(String str, SSLEngineResult result) { - if (!logging) { - return; - } - HandshakeStatus hsStatus = result.getHandshakeStatus(); - log(str + - result.getStatus() + "/" + hsStatus + ", " + - result.bytesConsumed() + "/" + result.bytesProduced() + - " bytes"); - if (hsStatus == HandshakeStatus.FINISHED) { - log("\t...ready for application data"); - } - } - - private static void log(String str) { - if (logging) { - System.out.println(str); - } - } - - /** - * Dump a ByteBuffer as a hexdump to stdout. The dumping routine will - * start at the current position of the buffer and run to its limit. - * After completing the dump, the position will be returned to its - * starting point. - * - * @param data the ByteBuffer to dump to stdout. - * - * @return the hexdump of the byte array. - */ - private static String dumpHexBytes(ByteBuffer data) { - StringBuilder sb = new StringBuilder(); - if (data != null) { - int i = 0; - data.mark(); - while (data.hasRemaining()) { - if (i % 16 == 0 && i != 0) { - sb.append("\n"); - } - sb.append(String.format("%02X ", data.get())); - i++; - } - data.reset(); - } - - return sb.toString(); - } - - /** - * Tests the ServerHello for the presence (or not) of the status_request - * or status_request_v2 hello extension. It is assumed that the provided - * ByteBuffer has its position set at the first byte of the TLS record - * containing the ServerHello and contains the entire hello message. Upon - * successful completion of this method the ByteBuffer will have its - * position reset to the initial offset in the buffer. If an exception is - * thrown the position at the time of the exception will be preserved. - * - * @param statReqPresent true if the status_request hello extension should - * be present. - * @param statReqV2Present true if the status_request_v2 hello extension - * should be present. - * - * @return true if the ServerHello's extension set matches the presence - * booleans for status_request and status_request_v2. False if - * not, or if the TLS record or message is of the wrong type. - */ - private static boolean checkServerHello(ByteBuffer data, - boolean statReqPresent, boolean statReqV2Present) { - boolean hasV1 = false; - boolean hasV2 = false; - Objects.requireNonNull(data); - int startPos = data.position(); - data.mark(); - - // Process the TLS record header - int type = Byte.toUnsignedInt(data.get()); - int ver_major = Byte.toUnsignedInt(data.get()); - int ver_minor = Byte.toUnsignedInt(data.get()); - int recLen = Short.toUnsignedInt(data.getShort()); - - // Simple sanity checks - if (type != 22) { - log("Not a handshake: Type = " + type); - return false; - } else if (recLen > data.remaining()) { - log("Incomplete record in buffer: Record length = " + recLen + - ", Remaining = " + data.remaining()); - return false; - } - - // Grab the handshake message header. - int msgHdr = data.getInt(); - int msgType = (msgHdr >> 24) & 0x000000FF; - int msgLen = msgHdr & 0x00FFFFFF; - - // More simple sanity checks - if (msgType != 2) { - log("Not a ServerHello: Type = " + msgType); - return false; - } - - // Skip over the protocol version and server random - data.position(data.position() + 34); - - // Jump past the session ID - int sessLen = Byte.toUnsignedInt(data.get()); - if (sessLen != 0) { - data.position(data.position() + sessLen); - } - - // Skip the cipher suite and compression method - data.position(data.position() + 3); - - // Go through the extensions and look for the request extension - // expected by the caller. - int extsLen = Short.toUnsignedInt(data.getShort()); - while (data.position() < recLen + startPos + 5) { - int extType = Short.toUnsignedInt(data.getShort()); - int extLen = Short.toUnsignedInt(data.getShort()); - hasV1 |= (extType == ExtensionType.EXT_STATUS_REQUEST.id); - hasV2 |= (extType == ExtensionType.EXT_STATUS_REQUEST_V2.id); - data.position(data.position() + extLen); - } - - if (hasV1 != statReqPresent) { - log("The status_request extension is " + - "inconsistent with the expected result: expected = " + - statReqPresent + ", actual = " + hasV1); - } - if (hasV2 != statReqV2Present) { - log("The status_request_v2 extension is " + - "inconsistent with the expected result: expected = " + - statReqV2Present + ", actual = " + hasV2); - } - - // Reset the position to the initial spot at the start of this method. - data.reset(); - - return ((hasV1 == statReqPresent) && (hasV2 == statReqV2Present)); - } - - /** - * Creates the PKI components necessary for this test, including - * Root CA, Intermediate CA and SSL server certificates, the keystores - * for each entity, a client trust store, and starts the OCSP responders. - */ - private static void createPKI() throws Exception { - CertificateBuilder cbld = new CertificateBuilder(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyStore.Builder keyStoreBuilder = - KeyStore.Builder.newInstance("PKCS12", null, - new KeyStore.PasswordProtection(passwd.toCharArray())); - - // Generate Root, IntCA, EE keys - KeyPair rootCaKP = keyGen.genKeyPair(); - log("Generated Root CA KeyPair"); - KeyPair intCaKP = keyGen.genKeyPair(); - log("Generated Intermediate CA KeyPair"); - KeyPair sslKP = keyGen.genKeyPair(); - log("Generated SSL Cert KeyPair"); - - // Set up the Root CA Cert - cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"); - cbld.setPublicKey(rootCaKP.getPublic()); - cbld.setSerialNumber(new BigInteger("1")); - // Make a 3 year validity starting from 60 days ago - long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); - long end = start + TimeUnit.DAYS.toMillis(1085); - cbld.setValidity(new Date(start), new Date(end)); - addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic()); - addCommonCAExts(cbld); - // Make our Root CA Cert! - X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), - "SHA256withRSA"); - log("Root CA Created:\n" + certInfo(rootCert)); - - // Now build a keystore and add the keys and cert - rootKeystore = keyStoreBuilder.getKeyStore(); - java.security.cert.Certificate[] rootChain = {rootCert}; - rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(), - passwd.toCharArray(), rootChain); - - // Now fire up the OCSP responder - rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); - rootOcsp.enableLog(debug); - rootOcsp.setNextUpdateInterval(3600); - rootOcsp.start(); - - // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { - Thread.sleep(50); - } - if (!rootOcsp.isServerReady()) { - throw new RuntimeException("Server not ready yet"); - } - - rootOcspPort = rootOcsp.getPort(); - String rootRespURI = "http://localhost:" + rootOcspPort; - log("Root OCSP Responder URI is " + rootRespURI); - - // Now that we have the root keystore and OCSP responder we can - // create our intermediate CA. - cbld.reset(); - cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"); - cbld.setPublicKey(intCaKP.getPublic()); - cbld.setSerialNumber(new BigInteger("100")); - // Make a 2 year validity starting from 30 days ago - start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); - end = start + TimeUnit.DAYS.toMillis(730); - cbld.setValidity(new Date(start), new Date(end)); - addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic()); - addCommonCAExts(cbld); - cbld.addAIAExt(Collections.singletonList(rootRespURI)); - // Make our Intermediate CA Cert! - X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), - "SHA256withRSA"); - log("Intermediate CA Created:\n" + certInfo(intCaCert)); - - // Provide intermediate CA cert revocation info to the Root CA - // OCSP responder. - Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = - new HashMap<>(); - revInfo.put(intCaCert.getSerialNumber(), - new SimpleOCSPServer.CertStatusInfo( - SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); - rootOcsp.updateStatusDb(revInfo); - - // Now build a keystore and add the keys, chain and root cert as a TA - intKeystore = keyStoreBuilder.getKeyStore(); - java.security.cert.Certificate[] intChain = {intCaCert, rootCert}; - intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(), - passwd.toCharArray(), intChain); - intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); - - // Now fire up the Intermediate CA OCSP responder - intOcsp = new SimpleOCSPServer(intKeystore, passwd, - INT_ALIAS, null); - intOcsp.enableLog(debug); - intOcsp.setNextUpdateInterval(3600); - intOcsp.start(); - - // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) { - Thread.sleep(50); - } - if (!intOcsp.isServerReady()) { - throw new RuntimeException("Server not ready yet"); - } - - intOcspPort = intOcsp.getPort(); - String intCaRespURI = "http://localhost:" + intOcspPort; - log("Intermediate CA OCSP Responder URI is " + intCaRespURI); - - // Last but not least, let's make our SSLCert and add it to its own - // Keystore - cbld.reset(); - cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany"); - cbld.setPublicKey(sslKP.getPublic()); - cbld.setSerialNumber(new BigInteger("4096")); - // Make a 1 year validity starting from 7 days ago - start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); - end = start + TimeUnit.DAYS.toMillis(365); - cbld.setValidity(new Date(start), new Date(end)); - - // Add extensions - addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic()); - boolean[] kuBits = {true, false, true, false, false, false, - false, false, false}; - cbld.addKeyUsageExt(kuBits); - List<String> ekuOids = new ArrayList<>(); - ekuOids.add("1.3.6.1.5.5.7.3.1"); - ekuOids.add("1.3.6.1.5.5.7.3.2"); - cbld.addExtendedKeyUsageExt(ekuOids); - cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost")); - cbld.addAIAExt(Collections.singletonList(intCaRespURI)); - // Make our SSL Server Cert! - X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), - "SHA256withRSA"); - log("SSL Certificate Created:\n" + certInfo(sslCert)); - - // Provide SSL server cert revocation info to the Intermeidate CA - // OCSP responder. - revInfo = new HashMap<>(); - revInfo.put(sslCert.getSerialNumber(), - new SimpleOCSPServer.CertStatusInfo( - SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); - intOcsp.updateStatusDb(revInfo); - - // Now build a keystore and add the keys, chain and root cert as a TA - serverKeystore = keyStoreBuilder.getKeyStore(); - java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert}; - serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(), - passwd.toCharArray(), sslChain); - serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); - - // And finally a Trust Store for the client - trustStore = keyStoreBuilder.getKeyStore(); - trustStore.setCertificateEntry(ROOT_ALIAS, rootCert); - } - - private static void addCommonExts(CertificateBuilder cbld, - PublicKey subjKey, PublicKey authKey) throws IOException { - cbld.addSubjectKeyIdExt(subjKey); - cbld.addAuthorityKeyIdExt(authKey); - } - - private static void addCommonCAExts(CertificateBuilder cbld) - throws IOException { - cbld.addBasicConstraintsExt(true, true, -1); - // Set key usage bits for digitalSignature, keyCertSign and cRLSign - boolean[] kuBitSettings = {true, false, false, false, false, true, - true, false, false}; - cbld.addKeyUsageExt(kuBitSettings); - } - - /** - * Helper routine that dumps only a few cert fields rather than - * the whole toString() output. - * - * @param cert an X509Certificate to be displayed - * - * @return the String output of the issuer, subject and - * serial number - */ - private static String certInfo(X509Certificate cert) { - StringBuilder sb = new StringBuilder(); - sb.append("Issuer: ").append(cert.getIssuerX500Principal()). - append("\n"); - sb.append("Subject: ").append(cert.getSubjectX500Principal()). - append("\n"); - sb.append("Serial: ").append(cert.getSerialNumber()).append("\n"); - return sb.toString(); - } - - private static class TestCase { - public final String testName; - public final ByteBuffer data; - public final boolean statReqEnabled; - public final boolean statReqV2Enabled; - - TestCase(String name, ByteBuffer buffer, boolean srEn, boolean srv2En) { - testName = (name != null) ? name : ""; - data = Objects.requireNonNull(buffer, - "TestCase requires a non-null ByteBuffer"); - statReqEnabled = srEn; - statReqV2Enabled = srv2En; - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusResponseManagerTests.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/StatusResponseManagerTests.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,460 +0,0 @@ -/* - * Copyright (c) 2015, 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. - * - * 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.cert.*; -import java.util.*; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.PublicKey; -import java.util.concurrent.TimeUnit; - -import sun.security.testlibrary.SimpleOCSPServer; -import sun.security.testlibrary.CertificateBuilder; - -/* - * Checks that the hash value for a certificate's issuer name is generated - * correctly. Requires any certificate that is not self-signed. - * - * NOTE: this test uses Sun private classes which are subject to change. - */ -public class StatusResponseManagerTests { - - private static final boolean debug = true; - private static final boolean ocspDebug = false; - - // PKI components we will need for this test - static String passwd = "passphrase"; - static String ROOT_ALIAS = "root"; - static String INT_ALIAS = "intermediate"; - static String SSL_ALIAS = "ssl"; - static KeyStore rootKeystore; // Root CA Keystore - static KeyStore intKeystore; // Intermediate CA Keystore - static KeyStore serverKeystore; // SSL Server Keystore - static KeyStore trustStore; // SSL Client trust store - static X509Certificate rootCert; - static X509Certificate intCert; - static X509Certificate sslCert; - static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder - static int rootOcspPort; // Port number for root OCSP - static SimpleOCSPServer intOcsp; // Intermediate CA OCSP Responder - static int intOcspPort; // Port number for intermed. OCSP - - static X509Certificate[] chain; - - public static void main(String[] args) throws Exception { - Map<String, TestCase> testList = - new LinkedHashMap<String, TestCase>() {{ - put("Basic OCSP fetch test", testOcspFetch); - put("Clear StatusResponseManager cache", testClearSRM); - put("Basic OCSP_MULTI fetch test", testOcspMultiFetch); - put("Test Cache Expiration", testCacheExpiry); - }}; - - // Create the CAs and OCSP responders - createPKI(); - - // Grab the certificates and make a chain we can reuse for tests - sslCert = (X509Certificate)serverKeystore.getCertificate(SSL_ALIAS); - intCert = (X509Certificate)intKeystore.getCertificate(INT_ALIAS); - rootCert = (X509Certificate)rootKeystore.getCertificate(ROOT_ALIAS); - chain = new X509Certificate[3]; - chain[0] = sslCert; - chain[1] = intCert; - chain[2] = rootCert; - - TestUtils.runTests(testList); - - intOcsp.stop(); - rootOcsp.stop(); - } - - // Test a simple RFC 6066 server-side fetch - public static final TestCase testOcspFetch = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - StatusResponseManager srm = new StatusResponseManager(); - Boolean pass = Boolean.FALSE; - String message = null; - StatusRequest oReq = new OCSPStatusRequest(); - - try { - // Get OCSP responses for non-root certs in the chain - Map<X509Certificate, byte[]> responseMap = srm.get( - StatusRequestType.OCSP, oReq, chain, 5000, - TimeUnit.MILLISECONDS); - - // There should be one entry in the returned map and - // one entry in the cache when the operation is complete. - if (responseMap.size() != 1) { - message = "Incorrect number of responses: expected 1, got " - + responseMap.size(); - } else if (!responseMap.containsKey(sslCert)) { - message = "Response map key is incorrect, expected " + - sslCert.getSubjectX500Principal().toString(); - } else if (srm.size() != 1) { - message = "Incorrect number of cache entries: " + - "expected 1, got " + srm.size(); - } else { - pass = Boolean.TRUE; - } - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test clearing the StatusResponseManager cache. - public static final TestCase testClearSRM = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - StatusResponseManager srm = new StatusResponseManager(); - Boolean pass = Boolean.FALSE; - String message = null; - StatusRequest oReq = new OCSPStatusRequest(); - - try { - // Get OCSP responses for non-root certs in the chain - srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000, - TimeUnit.MILLISECONDS); - - // There should be two entries in the returned map and - // two entries in the cache when the operation is complete. - if (srm.size() != 2) { - message = "Incorrect number of responses: expected 2, got " - + srm.size(); - } else { - // Next, clear the SRM, then check the size again - srm.clear(); - if (srm.size() != 0) { - message = "Incorrect number of responses: expected 0," + - " got " + srm.size(); - } else { - pass = Boolean.TRUE; - } - } - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test a simple RFC 6961 server-side fetch - public static final TestCase testOcspMultiFetch = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - StatusResponseManager srm = new StatusResponseManager(); - Boolean pass = Boolean.FALSE; - String message = null; - StatusRequest oReq = new OCSPStatusRequest(); - - try { - // Get OCSP responses for non-root certs in the chain - Map<X509Certificate, byte[]> responseMap = srm.get( - StatusRequestType.OCSP_MULTI, oReq, chain, 5000, - TimeUnit.MILLISECONDS); - - // There should be two entries in the returned map and - // two entries in the cache when the operation is complete. - if (responseMap.size() != 2) { - message = "Incorrect number of responses: expected 2, got " - + responseMap.size(); - } else if (!responseMap.containsKey(sslCert) || - !responseMap.containsKey(intCert)) { - message = "Response map keys are incorrect, expected " + - sslCert.getSubjectX500Principal().toString() + - " and " + - intCert.getSubjectX500Principal().toString(); - } else if (srm.size() != 2) { - message = "Incorrect number of cache entries: " + - "expected 2, got " + srm.size(); - } else { - pass = Boolean.TRUE; - } - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - // Test cache expiration - public static final TestCase testCacheExpiry = new TestCase() { - @Override - public Map.Entry<Boolean, String> runTest() { - // For this test, we will set the cache expiry to 5 seconds - System.setProperty("jdk.tls.stapling.cacheLifetime", "5"); - StatusResponseManager srm = new StatusResponseManager(); - Boolean pass = Boolean.FALSE; - String message = null; - StatusRequest oReq = new OCSPStatusRequest(); - - try { - // Get OCSP responses for non-root certs in the chain - srm.get(StatusRequestType.OCSP_MULTI, oReq, chain, 5000, - TimeUnit.MILLISECONDS); - - // There should be two entries in the returned map and - // two entries in the cache when the operation is complete. - if (srm.size() != 2) { - message = "Incorrect number of responses: expected 2, got " - + srm.size(); - } else { - // Next, wait for more than 5 seconds so the responses - // in the SRM will expire. - Thread.sleep(7000); - if (srm.size() != 0) { - message = "Incorrect number of responses: expected 0," + - " got " + srm.size(); - } else { - pass = Boolean.TRUE; - } - } - } catch (Exception e) { - e.printStackTrace(System.out); - message = e.getClass().getName(); - } - - // Set the cache lifetime back to the default - System.setProperty("jdk.tls.stapling.cacheLifetime", ""); - return new AbstractMap.SimpleEntry<>(pass, message); - } - }; - - /** - * Creates the PKI components necessary for this test, including - * Root CA, Intermediate CA and SSL server certificates, the keystores - * for each entity, a client trust store, and starts the OCSP responders. - */ - private static void createPKI() throws Exception { - CertificateBuilder cbld = new CertificateBuilder(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyStore.Builder keyStoreBuilder = - KeyStore.Builder.newInstance("PKCS12", null, - new KeyStore.PasswordProtection(passwd.toCharArray())); - - // Generate Root, IntCA, EE keys - KeyPair rootCaKP = keyGen.genKeyPair(); - log("Generated Root CA KeyPair"); - KeyPair intCaKP = keyGen.genKeyPair(); - log("Generated Intermediate CA KeyPair"); - KeyPair sslKP = keyGen.genKeyPair(); - log("Generated SSL Cert KeyPair"); - - // Set up the Root CA Cert - cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"); - cbld.setPublicKey(rootCaKP.getPublic()); - cbld.setSerialNumber(new BigInteger("1")); - // Make a 3 year validity starting from 60 days ago - long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); - long end = start + TimeUnit.DAYS.toMillis(1085); - cbld.setValidity(new Date(start), new Date(end)); - addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic()); - addCommonCAExts(cbld); - // Make our Root CA Cert! - X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), - "SHA256withRSA"); - log("Root CA Created:\n" + certInfo(rootCert)); - - // Now build a keystore and add the keys and cert - rootKeystore = keyStoreBuilder.getKeyStore(); - Certificate[] rootChain = {rootCert}; - rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(), - passwd.toCharArray(), rootChain); - - // Now fire up the OCSP responder - rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); - rootOcsp.enableLog(ocspDebug); - rootOcsp.setNextUpdateInterval(3600); - rootOcsp.start(); - - // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { - Thread.sleep(50); - } - if (!rootOcsp.isServerReady()) { - throw new RuntimeException("Server not ready yet"); - } - - rootOcspPort = rootOcsp.getPort(); - String rootRespURI = "http://localhost:" + rootOcspPort; - log("Root OCSP Responder URI is " + rootRespURI); - - // Now that we have the root keystore and OCSP responder we can - // create our intermediate CA. - cbld.reset(); - cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"); - cbld.setPublicKey(intCaKP.getPublic()); - cbld.setSerialNumber(new BigInteger("100")); - // Make a 2 year validity starting from 30 days ago - start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); - end = start + TimeUnit.DAYS.toMillis(730); - cbld.setValidity(new Date(start), new Date(end)); - addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic()); - addCommonCAExts(cbld); - cbld.addAIAExt(Collections.singletonList(rootRespURI)); - // Make our Intermediate CA Cert! - X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), - "SHA256withRSA"); - log("Intermediate CA Created:\n" + certInfo(intCaCert)); - - // Provide intermediate CA cert revocation info to the Root CA - // OCSP responder. - Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = - new HashMap<>(); - revInfo.put(intCaCert.getSerialNumber(), - new SimpleOCSPServer.CertStatusInfo( - SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); - rootOcsp.updateStatusDb(revInfo); - - // Now build a keystore and add the keys, chain and root cert as a TA - intKeystore = keyStoreBuilder.getKeyStore(); - Certificate[] intChain = {intCaCert, rootCert}; - intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(), - passwd.toCharArray(), intChain); - intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); - - // Now fire up the Intermediate CA OCSP responder - intOcsp = new SimpleOCSPServer(intKeystore, passwd, - INT_ALIAS, null); - intOcsp.enableLog(ocspDebug); - intOcsp.setNextUpdateInterval(3600); - intOcsp.start(); - - // Wait 5 seconds for server ready - for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) { - Thread.sleep(50); - } - if (!intOcsp.isServerReady()) { - throw new RuntimeException("Server not ready yet"); - } - - intOcspPort = intOcsp.getPort(); - String intCaRespURI = "http://localhost:" + intOcspPort; - log("Intermediate CA OCSP Responder URI is " + intCaRespURI); - - // Last but not least, let's make our SSLCert and add it to its own - // Keystore - cbld.reset(); - cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany"); - cbld.setPublicKey(sslKP.getPublic()); - cbld.setSerialNumber(new BigInteger("4096")); - // Make a 1 year validity starting from 7 days ago - start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); - end = start + TimeUnit.DAYS.toMillis(365); - cbld.setValidity(new Date(start), new Date(end)); - - // Add extensions - addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic()); - boolean[] kuBits = {true, false, true, false, false, false, - false, false, false}; - cbld.addKeyUsageExt(kuBits); - List<String> ekuOids = new ArrayList<>(); - ekuOids.add("1.3.6.1.5.5.7.3.1"); - ekuOids.add("1.3.6.1.5.5.7.3.2"); - cbld.addExtendedKeyUsageExt(ekuOids); - cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost")); - cbld.addAIAExt(Collections.singletonList(intCaRespURI)); - // Make our SSL Server Cert! - X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), - "SHA256withRSA"); - log("SSL Certificate Created:\n" + certInfo(sslCert)); - - // Provide SSL server cert revocation info to the Intermeidate CA - // OCSP responder. - revInfo = new HashMap<>(); - revInfo.put(sslCert.getSerialNumber(), - new SimpleOCSPServer.CertStatusInfo( - SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); - intOcsp.updateStatusDb(revInfo); - - // Now build a keystore and add the keys, chain and root cert as a TA - serverKeystore = keyStoreBuilder.getKeyStore(); - Certificate[] sslChain = {sslCert, intCaCert, rootCert}; - serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(), - passwd.toCharArray(), sslChain); - serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); - - // And finally a Trust Store for the client - trustStore = keyStoreBuilder.getKeyStore(); - trustStore.setCertificateEntry(ROOT_ALIAS, rootCert); - } - - private static void addCommonExts(CertificateBuilder cbld, - PublicKey subjKey, PublicKey authKey) throws IOException { - cbld.addSubjectKeyIdExt(subjKey); - cbld.addAuthorityKeyIdExt(authKey); - } - - private static void addCommonCAExts(CertificateBuilder cbld) - throws IOException { - cbld.addBasicConstraintsExt(true, true, -1); - // Set key usage bits for digitalSignature, keyCertSign and cRLSign - boolean[] kuBitSettings = {true, false, false, false, false, true, - true, false, false}; - cbld.addKeyUsageExt(kuBitSettings); - } - - /** - * Helper routine that dumps only a few cert fields rather than - * the whole toString() output. - * - * @param cert An X509Certificate to be displayed - * - * @return The {@link String} output of the issuer, subject and - * serial number - */ - private static String certInfo(X509Certificate cert) { - StringBuilder sb = new StringBuilder(); - sb.append("Issuer: ").append(cert.getIssuerX500Principal()). - append("\n"); - sb.append("Subject: ").append(cert.getSubjectX500Principal()). - append("\n"); - sb.append("Serial: ").append(cert.getSerialNumber()).append("\n"); - return sb.toString(); - } - - /** - * Log a message on stdout - * - * @param message The message to log - */ - private static void log(String message) { - if (debug) { - System.out.println(message); - } - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestCase.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestCase.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +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. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.security.ssl; - -import java.util.Map; - -public interface TestCase { - Map.Entry<Boolean, String> runTest(); -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestUtils.java --- a/test/jdk/sun/security/ssl/StatusStapling/java.base/sun/security/ssl/TestUtils.java Mon Jun 25 21:22:16 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +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. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.security.ssl; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Map; - -public class TestUtils { - - // private constructor to prevent instantiation for this utility class - private TestUtils() { - throw new AssertionError(); - } - - public static void runTests(Map<String, TestCase> testList) { - int testNo = 0; - int numberFailed = 0; - Map.Entry<Boolean, String> result; - - System.out.println("============ Tests ============"); - for (String testName : testList.keySet()) { - System.out.println("Test " + ++testNo + ": " + testName); - result = testList.get(testName).runTest(); - System.out.print("Result: " + (result.getKey() ? "PASS" : "FAIL")); - System.out.println(" " + - (result.getValue() != null ? result.getValue() : "")); - System.out.println("-------------------------------------------"); - if (!result.getKey()) { - numberFailed++; - } - } - - System.out.println("End Results: " + (testList.size() - numberFailed) + - " Passed" + ", " + numberFailed + " Failed."); - if (numberFailed > 0) { - throw new RuntimeException( - "One or more tests failed, see test output for details"); - } - } - - public static void dumpBytes(byte[] data) { - dumpBytes(ByteBuffer.wrap(data)); - } - - public static void dumpBytes(ByteBuffer data) { - int i = 0; - - data.mark(); - while (data.hasRemaining()) { - if (i % 16 == 0 && i != 0) { - System.out.print("\n"); - } - System.out.print(String.format("%02X ", data.get())); - i++; - } - System.out.print("\n"); - data.reset(); - } - - public static void valueCheck(byte[] array1, byte[] array2) { - if (!Arrays.equals(array1, array2)) { - throw new RuntimeException("Array mismatch"); - } - } - - // Compares a range of bytes at specific offsets in each array - public static void valueCheck(byte[] array1, byte[] array2, int skip1, - int skip2, int length) { - ByteBuffer buf1 = ByteBuffer.wrap(array1); - ByteBuffer buf2 = ByteBuffer.wrap(array2); - - // Skip past however many bytes are requested in both buffers - buf1.position(skip1); - buf2.position(skip2); - - // Reset the limits on each buffer to account for the length - buf1.limit(buf1.position() + length); - buf2.limit(buf2.position() + length); - - if (!buf1.equals(buf2)) { - throw new RuntimeException("Array range mismatch"); - } - } - - // Concatenate 1 or more arrays - public static byte[] gatherBuffers(byte[]... arrays) { - int totalLength = 0; - for (byte[] ar : arrays) { - totalLength += ar != null ? ar.length : 0; - } - - byte[] resultBuf = new byte[totalLength]; - int offset = 0; - for (byte[] ar : arrays) { - if (ar != null) { - System.arraycopy(ar, 0, resultBuf, offset, ar.length); - offset += ar.length; - } - } - return resultBuf; - } -} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/BasicConstraints.java Mon Jun 25 13:41:39 2018 -0700 @@ -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 @@ -357,6 +357,7 @@ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost", serverPort); + sslSocket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }); try { InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/CertRequestOverflow.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 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 @@ -96,6 +96,9 @@ SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(serverPort); serverPort = sslServerSocket.getLocalPort(); + if (debug) { + System.out.println("Server port is " + serverPort); + } // enable endpoint identification // ignore, we may test the feature when known how to parse client @@ -153,6 +156,10 @@ SSLSocketFactory sslsf = getContext(false).getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket("localhost", serverPort); + if (debug) { + System.out.println("Connected to: " + + sslSocket.getRemoteSocketAddress()); + } // enable endpoint identification SSLParameters params = sslSocket.getSSLParameters(); @@ -214,7 +221,7 @@ tms = new TrustManager[] {clientTM}; } - SSLContext ctx = SSLContext.getInstance("TLS"); + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); ctx.init(kmf.getKeyManagers(), tms, null); return ctx; @@ -243,17 +250,19 @@ return serverChecked; } - + @Override public void checkClientTrusted(X509Certificate chain[], String authType) throws CertificateException { tm.checkClientTrusted(chain, authType); } + @Override public void checkServerTrusted(X509Certificate chain[], String authType) throws CertificateException { tm.checkServerTrusted(chain, authType); } + @Override public X509Certificate[] getAcceptedIssuers() { // (hack code) increase the size of the returned array to make a // overflow CertificateRequest. @@ -268,24 +277,28 @@ return issuersList.toArray(issuers); } + @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { clientChecked = true; tm.checkClientTrusted(chain, authType); } + @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { serverChecked = true; tm.checkServerTrusted(chain, authType); } + @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { clientChecked = true; tm.checkClientTrusted(chain, authType); } + @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { serverChecked = true; @@ -357,6 +370,7 @@ void startServer(boolean newThread) throws Exception { if (newThread) { serverThread = new Thread() { + @Override public void run() { try { doServerSide(); @@ -381,6 +395,7 @@ void startClient(boolean newThread) throws Exception { if (newThread) { clientThread = new Thread() { + @Override public void run() { try { doClientSide(); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java --- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/SelfIssuedCert.java Mon Jun 25 13:41:39 2018 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -209,6 +209,7 @@ SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost", serverPort); + sslSocket.setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" }); InputStream sslIS = sslSocket.getInputStream(); OutputStream sslOS = sslSocket.getOutputStream(); diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/internal/TEST.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/internal/TEST.properties Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,3 @@ +modules = \ + java.base/sun.security.ssl +bootclasspath.dirs=. diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/internal/TestRun.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/internal/TestRun.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + */ + +/* + * @test + * @bug 8145255 + * @run main/othervm java.base/sun.security.ssl.TestHkdf + * @summary HKDF for Sun JSSE + */ + diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/internal/java.base/sun/security/ssl/TestHkdf.java Mon Jun 25 13:41:39 2018 -0700 @@ -0,0 +1,260 @@ +/* + * 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. + * + * 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. + */ + +/* + * Actual test code for the package private HKDF implementation + */ + +package sun.security.ssl; + +import java.util.Arrays; +import java.util.List; +import java.util.LinkedList; +import java.util.Objects; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +public class TestHkdf { + public static class TestData { + public TestData(String name, String algStr, String ikmStr, + String saltStr, String infoStr, int oLen, String expPrkStr, + String expOkmStr) { + testName = Objects.requireNonNull(name); + algName = Objects.requireNonNull(algStr); + IKM = hex2bin(Objects.requireNonNull(ikmStr)); + if ((outLen = oLen) <= 0) { + throw new IllegalArgumentException( + "Output length must be greater than 0"); + } + expectedPRK = hex2bin(Objects.requireNonNull(expPrkStr)); + expectedOKM = hex2bin(Objects.requireNonNull(expOkmStr)); + + // Non-mandatory fields - may be null + salt = (saltStr != null) ? hex2bin(saltStr) : null; + info = (infoStr != null) ? hex2bin(infoStr) : null; + } + + public final String testName; + public final String algName; + public final byte[] IKM; + public final byte[] salt; + public final byte[] info; + public final int outLen; + public final byte[] expectedPRK; + public final byte[] expectedOKM; + } + + public static final List<TestData> testList = new LinkedList<TestData>() {{ + add(new TestData("RFC 5689 Test Case 1", "SHA-256", + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + 42, + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + + "34007208d5b887185865")); + add(new TestData("RFC 5689 Test Case 2", "SHA-256", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" + + "404142434445464748494a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" + + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" + + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" + + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + 82, + "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c" + + "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71" + + "cc30c58179ec3e87c14c01d5c1f3434f1d87")); + add(new TestData("RFC 5689 Test Case 3", "SHA-256", + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + null, null, 42, + "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d" + + "9d201395faa4b61a96c8")); + add(new TestData("RFC 5689 Test Case 4", "SHA-1", + "0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + 42, + "9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243", + "085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2" + + "c22e422478d305f3f896")); + add(new TestData("RFC 5689 Test Case 5", "SHA-1", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + + "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" + + "404142434445464748494a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" + + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" + + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" + + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + 82, + "8adae09a2a307059478d309b26c4115a224cfaf6", + "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe" + + "8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e" + + "927336d0441f4c4300e2cff0d0900b52d3b4")); + add(new TestData("RFC 5689 Test Case 6", "SHA-1", + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + null, null, 42, + "da8c8a73c7fa77288ec6f5e7c297786aa0d32d01", + "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0" + + "ea00033de03984d34918")); + add(new TestData("RFC 5689 Test Case 7", "SHA-1", + "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + null, null, 42, + "2adccada18779e7c2077ad2eb19d3f3e731385dd", + "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5" + + "673a081d70cce7acfc48")); + }}; + + public static void main(String args[]) throws Exception { + int testsPassed = 0; + + int testNo = 0; + for (TestData test : testList) { + System.out.println("*** Test " + ++testNo + ": " + + test.testName); + if (runVector(test)) { + testsPassed++; + } + } + + System.out.println("Total tests: " + testList.size() + + ", Passed: " + testsPassed + ", Failed: " + + (testList.size() - testsPassed)); + if (testsPassed != testList.size()) { + throw new RuntimeException("One or more tests failed. " + + "Check output for details"); + } + } + + private static boolean runVector(TestData testData) + throws NoSuchAlgorithmException, InvalidKeyException { + String kdfName, prfName; + HKDF kdfHkdf; + boolean result = true; + SecretKey actualPRK; + SecretKey actualOKM; + byte[] deriveData; + + // Get an instance of the HKDF derivation engine + kdfHkdf = new HKDF(testData.algName); + + // Set up the input keying material and the salt as a secret + SecretKey ikmKey = new SecretKeySpec(testData.IKM, "HKDF-IKM"); + SecretKey saltKey = (testData.salt != null) ? + new SecretKeySpec(testData.salt, "HKDF-Salt") : null; + + // *** HKDF-Extract-only testing + System.out.println("* HKDF-Extract-Only:"); + actualPRK = kdfHkdf.extract(saltKey, ikmKey, "HKDF-PRK"); + result &= compareKeyAndData(actualPRK, testData.expectedPRK); + + // *** HKDF Expand-Only testing + // For these tests, we'll use the actualPRK as the input key + System.out.println("* HKDF-Expand-Only:"); + actualOKM = kdfHkdf.expand(actualPRK, testData.info, testData.outLen, + "HKDF-OKM"); + result &= compareKeyAndData(actualOKM, testData.expectedOKM); + + // *** HKDF Extract-then-Expand testing + // System.out.println("* HKDF-Extract-then-Expand:"); + // actualOKM = kdfHkdf.extractExpand(ikmKey, saltKey, testData.info, + // testData.outLen, "HKDF-OKM2"); + // result &= compareKeyAndData(actualOKM, testData.expectedOKM); + + return result; + } + + /** + * Compare actual key output from HKDF against an expected output value. + * + * @param outKey the KDF output in key form + * @param expectedOut the expected value + * + * @return true if the underlying data for outKey, outData and + * expectedOut are the same. + */ + private static boolean compareKeyAndData(SecretKey outKey, + byte[] expectedOut) { + boolean result = false; + + if (Arrays.equals(outKey.getEncoded(), expectedOut)) { + System.out.println("\t* Key output: Pass"); + result = true; + } else { + System.out.println("\t* Key output: FAIL"); + System.out.println("Expected:\n" + + dumpHexBytes(expectedOut, 16, "\n", " ")); + System.out.println("Actual:\n" + + dumpHexBytes(outKey.getEncoded(), 16, "\n", " ")); + System.out.println(); + } + + return result; + } + + /** + * Dump the hex bytes of a buffer into string form. + * + * @param data The array of bytes to dump to stdout. + * @param itemsPerLine The number of bytes to display per line + * if the {@code lineDelim} character is blank then all bytes + * will be printed on a single line. + * @param lineDelim The delimiter between lines + * @param itemDelim The delimiter between bytes + * + * @return The hexdump of the byte array + */ + private static String dumpHexBytes(byte[] data, int itemsPerLine, + String lineDelim, String itemDelim) { + StringBuilder sb = new StringBuilder(); + if (data != null) { + for (int i = 0; i < data.length; i++) { + if (i % itemsPerLine == 0 && i != 0) { + sb.append(lineDelim); + } + sb.append(String.format("%02X", data[i])).append(itemDelim); + } + } + + return sb.toString(); + } + + private static byte[] hex2bin(String hex) { + int i; + int len = hex.length(); + byte[] data = new byte [len / 2]; + for (i = 0; i < len; i += 2) { + data[i / 2] = (byte)((Character.digit(hex.charAt(i), 16) << 4) + + Character.digit(hex.charAt(i + 1), 16)); + } + return data; + } +} diff -r 356eaea05bf0 -r 68fa3d4026ea test/jdk/sun/security/tools/keytool/PrintSSL.java --- a/test/jdk/sun/security/tools/keytool/PrintSSL.java Mon Jun 25 21:22:16 2018 +0300 +++ b/test/jdk/sun/security/tools/keytool/PrintSSL.java Mon Jun 25 13:41:39 2018 -0700 @@ -53,7 +53,7 @@ // make sure that "-printcert" works with weak algorithms OutputAnalyzer out = SecurityTools.keytool("-genkeypair " + "-keystore keystore -storepass passphrase " - + "-keypass passphrase -keyalg rsa -keysize 512 " + + "-keypass passphrase -keyalg rsa -keysize 1024 " + "-sigalg MD5withRSA -alias rsa_alias -dname CN=Server"); System.out.println(out.getOutput()); out.shouldHaveExitValue(0); @@ -92,7 +92,7 @@ System.setProperty("javax.net.ssl.keyStorePassword", "passphrase"); System.setProperty("javax.net.ssl.keyStore", "keystore"); SSLServerSocketFactory sslssf = - (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); try (ServerSocket server = sslssf.createServerSocket(0)) { this.serverPort = server.getLocalPort(); System.out.printf("%nServer started on: %s%n", getServerPort()); @@ -110,5 +110,5 @@ } } +} -}