feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
297
jdkSrc/jdk8/sun/security/ssl/Alert.java
Normal file
297
jdkSrc/jdk8/sun/security/ssl/Alert.java
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
|
||||
/**
|
||||
* SSL/TLS Alter description
|
||||
*/
|
||||
enum Alert {
|
||||
// Please refer to TLS Alert Registry for the latest TLS Alert values:
|
||||
// https://www.iana.org/assignments/tls-parameters/
|
||||
CLOSE_NOTIFY ((byte)0, "close_notify", false),
|
||||
UNEXPECTED_MESSAGE ((byte)10, "unexpected_message", false),
|
||||
BAD_RECORD_MAC ((byte)20, "bad_record_mac", false),
|
||||
DECRYPTION_FAILED ((byte)21, "decryption_failed", false),
|
||||
RECORD_OVERFLOW ((byte)22, "record_overflow", false),
|
||||
DECOMPRESSION_FAILURE ((byte)30, "decompression_failure", false),
|
||||
HANDSHAKE_FAILURE ((byte)40, "handshake_failure", true),
|
||||
NO_CERTIFICATE ((byte)41, "no_certificate", true),
|
||||
BAD_CERTIFICATE ((byte)42, "bad_certificate", true),
|
||||
UNSUPPORTED_CERTIFICATE ((byte)43, "unsupported_certificate", true),
|
||||
CERTIFICATE_REVOKED ((byte)44, "certificate_revoked", true),
|
||||
CERTIFICATE_EXPIRED ((byte)45, "certificate_expired", true),
|
||||
CERTIFICATE_UNKNOWN ((byte)46, "certificate_unknown", true),
|
||||
ILLEGAL_PARAMETER ((byte)47, "illegal_parameter", true),
|
||||
UNKNOWN_CA ((byte)48, "unknown_ca", true),
|
||||
ACCESS_DENIED ((byte)49, "access_denied", true),
|
||||
DECODE_ERROR ((byte)50, "decode_error", true),
|
||||
DECRYPT_ERROR ((byte)51, "decrypt_error", true),
|
||||
EXPORT_RESTRICTION ((byte)60, "export_restriction", true),
|
||||
PROTOCOL_VERSION ((byte)70, "protocol_version", true),
|
||||
INSUFFICIENT_SECURITY ((byte)71, "insufficient_security", true),
|
||||
INTERNAL_ERROR ((byte)80, "internal_error", false),
|
||||
INAPPROPRIATE_FALLBACK ((byte)86, "inappropriate_fallback", false),
|
||||
USER_CANCELED ((byte)90, "user_canceled", false),
|
||||
NO_RENEGOTIATION ((byte)100, "no_renegotiation", true),
|
||||
MISSING_EXTENSION ((byte)109, "missing_extension", true),
|
||||
UNSUPPORTED_EXTENSION ((byte)110, "unsupported_extension", true),
|
||||
CERT_UNOBTAINABLE ((byte)111, "certificate_unobtainable", true),
|
||||
UNRECOGNIZED_NAME ((byte)112, "unrecognized_name", true),
|
||||
BAD_CERT_STATUS_RESPONSE((byte)113,
|
||||
"bad_certificate_status_response", true),
|
||||
BAD_CERT_HASH_VALUE ((byte)114, "bad_certificate_hash_value", true),
|
||||
UNKNOWN_PSK_IDENTITY ((byte)115, "unknown_psk_identity", true),
|
||||
CERTIFICATE_REQUIRED ((byte)116, "certificate_required", true),
|
||||
NO_APPLICATION_PROTOCOL ((byte)120, "no_application_protocol", true);
|
||||
|
||||
// ordinal value of the Alert
|
||||
final byte id;
|
||||
|
||||
// description of the Alert
|
||||
final String description;
|
||||
|
||||
// Does tha alert happen during handshake only?
|
||||
final boolean handshakeOnly;
|
||||
|
||||
// Alert message consumer
|
||||
static final SSLConsumer alertConsumer = new AlertConsumer();
|
||||
|
||||
private Alert(byte id, String description, boolean handshakeOnly) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.handshakeOnly = handshakeOnly;
|
||||
}
|
||||
|
||||
static Alert valueOf(byte id) {
|
||||
for (Alert al : Alert.values()) {
|
||||
if (al.id == id) {
|
||||
return al;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(byte id) {
|
||||
for (Alert al : Alert.values()) {
|
||||
if (al.id == id) {
|
||||
return al.description;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN ALERT (" + (id & 0x0FF) + ")";
|
||||
}
|
||||
|
||||
SSLException createSSLException(String reason) {
|
||||
return createSSLException(reason, null);
|
||||
}
|
||||
|
||||
SSLException createSSLException(String reason, Throwable cause) {
|
||||
if (reason == null) {
|
||||
reason = (cause != null) ? cause.getMessage() : "";
|
||||
}
|
||||
|
||||
SSLException ssle;
|
||||
if ((cause != null) && (cause instanceof IOException)) {
|
||||
ssle = new SSLException(reason);
|
||||
} else if ((this == UNEXPECTED_MESSAGE)) {
|
||||
ssle = new SSLProtocolException(reason);
|
||||
} else if (handshakeOnly) {
|
||||
ssle = new SSLHandshakeException(reason);
|
||||
} else {
|
||||
ssle = new SSLException(reason);
|
||||
}
|
||||
|
||||
if (cause != null) {
|
||||
ssle.initCause(cause);
|
||||
}
|
||||
|
||||
return ssle;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL/TLS Alert level.
|
||||
*/
|
||||
enum Level {
|
||||
WARNING ((byte)1, "warning"),
|
||||
FATAL ((byte)2, "fatal");
|
||||
|
||||
// ordinal value of the Alert level
|
||||
final byte level;
|
||||
|
||||
// description of the Alert level
|
||||
final String description;
|
||||
|
||||
private Level(byte level, String description) {
|
||||
this.level = level;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
static Level valueOf(byte level) {
|
||||
for (Level lv : Level.values()) {
|
||||
if (lv.level == level) {
|
||||
return lv;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(byte level) {
|
||||
for (Level lv : Level.values()) {
|
||||
if (lv.level == level) {
|
||||
return lv.description;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN ALERT LEVEL (" + (level & 0x0FF) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Alert message.
|
||||
*/
|
||||
private static final class AlertMessage {
|
||||
private final byte level; // level
|
||||
private final byte id; // description
|
||||
|
||||
AlertMessage(TransportContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
// struct {
|
||||
// AlertLevel level;
|
||||
// AlertDescription description;
|
||||
// } Alert;
|
||||
if (m.remaining() != 2) {
|
||||
throw context.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid Alert message: no sufficient data");
|
||||
}
|
||||
|
||||
this.level = m.get(); // level
|
||||
this.id = m.get(); // description
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"Alert\": '{'\n" +
|
||||
" \"level\" : \"{0}\",\n" +
|
||||
" \"description\": \"{1}\"\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
Level.nameOf(level),
|
||||
Alert.nameOf(id)
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumer of alert messages
|
||||
*/
|
||||
private static final class AlertConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private AlertConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
TransportContext tc = (TransportContext)context;
|
||||
|
||||
AlertMessage am = new AlertMessage(tc, m);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine("Received alert message", am);
|
||||
}
|
||||
|
||||
Level level = Level.valueOf(am.level);
|
||||
Alert alert = Alert.valueOf(am.id);
|
||||
if (alert == Alert.CLOSE_NOTIFY) {
|
||||
tc.isInputCloseNotified = true;
|
||||
tc.closeInbound();
|
||||
|
||||
if (tc.peerUserCanceled) {
|
||||
tc.closeOutbound();
|
||||
} else if (tc.handshakeContext != null) {
|
||||
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Received close_notify during handshake");
|
||||
}
|
||||
} else if (alert == Alert.USER_CANCELED) {
|
||||
if (level == Level.WARNING) {
|
||||
tc.peerUserCanceled = true;
|
||||
} else {
|
||||
throw tc.fatal(alert,
|
||||
"Received fatal close_notify alert", true, null);
|
||||
}
|
||||
} else if ((level == Level.WARNING) && (alert != null)) {
|
||||
// Terminate the connection if an alert with a level of warning
|
||||
// is received during handshaking, except the no_certificate
|
||||
// warning.
|
||||
if (alert.handshakeOnly && (tc.handshakeContext != null)) {
|
||||
// It's OK to get a no_certificate alert from a client of
|
||||
// which we requested client authentication. However,
|
||||
// if we required it, then this is not acceptable.
|
||||
if (tc.sslConfig.isClientMode ||
|
||||
alert != Alert.NO_CERTIFICATE ||
|
||||
(tc.sslConfig.clientAuthType !=
|
||||
ClientAuthType.CLIENT_AUTH_REQUESTED)) {
|
||||
throw tc.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"received handshake warning: " + alert.description);
|
||||
} else {
|
||||
// Otherwise ignore the warning but remove the
|
||||
// Certificate and CertificateVerify handshake
|
||||
// consumer so the state machine doesn't expect it.
|
||||
tc.handshakeContext.handshakeConsumers.remove(
|
||||
SSLHandshake.CERTIFICATE.id);
|
||||
tc.handshakeContext.handshakeConsumers.remove(
|
||||
SSLHandshake.CERTIFICATE_VERIFY.id);
|
||||
}
|
||||
} // Otherwise, ignore the warning
|
||||
} else { // fatal or unknown
|
||||
String diagnostic;
|
||||
if (alert == null) {
|
||||
alert = Alert.UNEXPECTED_MESSAGE;
|
||||
diagnostic = "Unknown alert description (" + am.id + ")";
|
||||
} else {
|
||||
diagnostic = "Received fatal alert: " + alert.description;
|
||||
}
|
||||
|
||||
throw tc.fatal(alert, diagnostic, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
536
jdkSrc/jdk8/sun/security/ssl/AlpnExtension.java
Normal file
536
jdkSrc/jdk8/sun/security/ssl/AlpnExtension.java
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "application_layer_protocol_negotiation" extensions [RFC 7301].
|
||||
*/
|
||||
final class AlpnExtension {
|
||||
static final HandshakeProducer chNetworkProducer = new CHAlpnProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer = new CHAlpnConsumer();
|
||||
static final HandshakeAbsence chOnLoadAbsence = new CHAlpnAbsence();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer = new SHAlpnProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer = new SHAlpnConsumer();
|
||||
static final HandshakeAbsence shOnLoadAbsence = new SHAlpnAbsence();
|
||||
|
||||
// Note: we reuse ServerHello operations for EncryptedExtensions for now.
|
||||
// Please be careful about any code or specification changes in the future.
|
||||
static final HandshakeProducer eeNetworkProducer = new SHAlpnProducer();
|
||||
static final ExtensionConsumer eeOnLoadConsumer = new SHAlpnConsumer();
|
||||
static final HandshakeAbsence eeOnLoadAbsence = new SHAlpnAbsence();
|
||||
|
||||
static final SSLStringizer alpnStringizer = new AlpnStringizer();
|
||||
|
||||
// Encoding Charset to convert between String and byte[]
|
||||
static final Charset alpnCharset;
|
||||
|
||||
static {
|
||||
String alpnCharsetString = AccessController.doPrivileged(
|
||||
(PrivilegedAction<String>) ()
|
||||
-> Security.getProperty("jdk.tls.alpnCharset"));
|
||||
if ((alpnCharsetString == null)
|
||||
|| (alpnCharsetString.length() == 0)) {
|
||||
alpnCharsetString = "ISO_8859_1";
|
||||
}
|
||||
alpnCharset = Charset.forName(alpnCharsetString);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "application_layer_protocol_negotiation" extension.
|
||||
*
|
||||
* See RFC 7301 for the specification of this extension.
|
||||
*/
|
||||
static final class AlpnSpec implements SSLExtensionSpec {
|
||||
final List<String> applicationProtocols;
|
||||
|
||||
private AlpnSpec(String[] applicationProtocols) {
|
||||
this.applicationProtocols = Collections.unmodifiableList(
|
||||
Arrays.asList(applicationProtocols));
|
||||
}
|
||||
|
||||
private AlpnSpec(ByteBuffer buffer) throws IOException {
|
||||
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
|
||||
if (buffer.remaining() < 2) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid application_layer_protocol_negotiation: " +
|
||||
"insufficient data (length=" + buffer.remaining() + ")");
|
||||
}
|
||||
|
||||
int listLen = Record.getInt16(buffer);
|
||||
if (listLen < 2 || listLen != buffer.remaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid application_layer_protocol_negotiation: " +
|
||||
"incorrect list length (length=" + listLen + ")");
|
||||
}
|
||||
|
||||
List<String> protocolNames = new LinkedList<>();
|
||||
while (buffer.hasRemaining()) {
|
||||
// opaque ProtocolName<1..2^8-1>, RFC 7301.
|
||||
byte[] bytes = Record.getBytes8(buffer);
|
||||
if (bytes.length == 0) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid application_layer_protocol_negotiation " +
|
||||
"extension: empty application protocol name");
|
||||
}
|
||||
|
||||
String appProtocol = new String(bytes, alpnCharset);
|
||||
protocolNames.add(appProtocol);
|
||||
}
|
||||
|
||||
this.applicationProtocols =
|
||||
Collections.unmodifiableList(protocolNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return applicationProtocols.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AlpnStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new AlpnSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension in a ClientHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class CHAlpnProducer implements HandshakeProducer {
|
||||
static final int MAX_AP_LENGTH = 255;
|
||||
static final int MAX_AP_LIST_LENGTH = 65535;
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private CHAlpnProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.info(
|
||||
"Ignore client unavailable extension: " +
|
||||
SSLExtension.CH_ALPN.name);
|
||||
}
|
||||
|
||||
chc.applicationProtocol = "";
|
||||
chc.conContext.applicationProtocol = "";
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] laps = chc.sslConfig.applicationProtocols;
|
||||
if ((laps == null) || (laps.length == 0)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.info(
|
||||
"No available application protocols");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Produce the extension: first find the overall length
|
||||
int listLength = 0; // ProtocolNameList length
|
||||
for (String ap : laps) {
|
||||
int length = ap.getBytes(alpnCharset).length;
|
||||
if (length == 0) {
|
||||
// log the configuration problem
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.severe(
|
||||
"Application protocol name cannot be empty");
|
||||
}
|
||||
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Application protocol name cannot be empty");
|
||||
}
|
||||
|
||||
if (length <= MAX_AP_LENGTH) {
|
||||
// opaque ProtocolName<1..2^8-1>, RFC 7301.
|
||||
listLength += (length + 1);
|
||||
} else {
|
||||
// log the configuration problem
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.severe(
|
||||
"Application protocol name (" + ap +
|
||||
") exceeds the size limit (" +
|
||||
MAX_AP_LENGTH + " bytes)");
|
||||
}
|
||||
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Application protocol name (" + ap +
|
||||
") exceeds the size limit (" +
|
||||
MAX_AP_LENGTH + " bytes)");
|
||||
}
|
||||
|
||||
if (listLength > MAX_AP_LIST_LENGTH) {
|
||||
// log the configuration problem
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.severe(
|
||||
"The configured application protocols (" +
|
||||
Arrays.toString(laps) +
|
||||
") exceed the size limit (" +
|
||||
MAX_AP_LIST_LENGTH + " bytes)");
|
||||
}
|
||||
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"The configured application protocols (" +
|
||||
Arrays.toString(laps) +
|
||||
") exceed the size limit (" +
|
||||
MAX_AP_LIST_LENGTH + " bytes)");
|
||||
}
|
||||
}
|
||||
|
||||
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
|
||||
byte[] extData = new byte[listLength + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, listLength);
|
||||
|
||||
// opaque ProtocolName<1..2^8-1>;
|
||||
for (String ap : laps) {
|
||||
Record.putBytes8(m, ap.getBytes(alpnCharset));
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(SSLExtension.CH_ALPN,
|
||||
new AlpnSpec(chc.sslConfig.applicationProtocols));
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of the extension in a ClientHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class CHAlpnConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHAlpnConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.CH_ALPN)) {
|
||||
shc.applicationProtocol = "";
|
||||
shc.conContext.applicationProtocol = "";
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.info(
|
||||
"Ignore server unavailable extension: " +
|
||||
SSLExtension.CH_ALPN.name);
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Is the extension enabled?
|
||||
boolean noAPSelector;
|
||||
if (shc.conContext.transport instanceof SSLEngine) {
|
||||
noAPSelector = (shc.sslConfig.engineAPSelector == null);
|
||||
} else {
|
||||
noAPSelector = (shc.sslConfig.socketAPSelector == null);
|
||||
}
|
||||
|
||||
boolean noAlpnProtocols =
|
||||
shc.sslConfig.applicationProtocols == null ||
|
||||
shc.sslConfig.applicationProtocols.length == 0;
|
||||
if (noAPSelector && noAlpnProtocols) {
|
||||
shc.applicationProtocol = "";
|
||||
shc.conContext.applicationProtocol = "";
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore server unenabled extension: " +
|
||||
SSLExtension.CH_ALPN.name);
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
AlpnSpec spec;
|
||||
try {
|
||||
spec = new AlpnSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
if (noAPSelector) { // noAlpnProtocols is false
|
||||
List<String> protocolNames = spec.applicationProtocols;
|
||||
boolean matched = false;
|
||||
// Use server application protocol preference order.
|
||||
for (String ap : shc.sslConfig.applicationProtocols) {
|
||||
if (protocolNames.contains(ap)) {
|
||||
shc.applicationProtocol = ap;
|
||||
shc.conContext.applicationProtocol = ap;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
throw shc.conContext.fatal(Alert.NO_APPLICATION_PROTOCOL,
|
||||
"No matching application layer protocol values");
|
||||
}
|
||||
} // Otherwise, applicationProtocol will be set by the
|
||||
// application selector callback later.
|
||||
|
||||
shc.handshakeExtensions.put(SSLExtension.CH_ALPN, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
//
|
||||
// [RFC 7301] Unlike many other TLS extensions, this extension
|
||||
// does not establish properties of the session, only of the
|
||||
// connection. When session resumption or session tickets are
|
||||
// used, the previous contents of this extension are irrelevant,
|
||||
// and only the values in the new handshake messages are
|
||||
// considered.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if the extension is not present in
|
||||
* a ClientHello handshake message.
|
||||
*/
|
||||
private static final class CHAlpnAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Please don't use the previous negotiated application protocol.
|
||||
shc.applicationProtocol = "";
|
||||
shc.conContext.applicationProtocol = "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension in the ServerHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class SHAlpnProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHAlpnProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// In response to ALPN request only
|
||||
AlpnSpec requestedAlps =
|
||||
(AlpnSpec)shc.handshakeExtensions.get(SSLExtension.CH_ALPN);
|
||||
if (requestedAlps == null) {
|
||||
// Ignore, this extension was not requested and accepted.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable extension: " +
|
||||
SSLExtension.SH_ALPN.name);
|
||||
}
|
||||
|
||||
shc.applicationProtocol = "";
|
||||
shc.conContext.applicationProtocol = "";
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> alps = requestedAlps.applicationProtocols;
|
||||
if (shc.conContext.transport instanceof SSLEngine) {
|
||||
if (shc.sslConfig.engineAPSelector != null) {
|
||||
SSLEngine engine = (SSLEngine)shc.conContext.transport;
|
||||
shc.applicationProtocol =
|
||||
shc.sslConfig.engineAPSelector.apply(engine, alps);
|
||||
if ((shc.applicationProtocol == null) ||
|
||||
(!shc.applicationProtocol.isEmpty() &&
|
||||
!alps.contains(shc.applicationProtocol))) {
|
||||
throw shc.conContext.fatal(
|
||||
Alert.NO_APPLICATION_PROTOCOL,
|
||||
"No matching application layer protocol values");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (shc.sslConfig.socketAPSelector != null) {
|
||||
SSLSocket socket = (SSLSocket)shc.conContext.transport;
|
||||
shc.applicationProtocol =
|
||||
shc.sslConfig.socketAPSelector.apply(socket, alps);
|
||||
if ((shc.applicationProtocol == null) ||
|
||||
(!shc.applicationProtocol.isEmpty() &&
|
||||
!alps.contains(shc.applicationProtocol))) {
|
||||
throw shc.conContext.fatal(
|
||||
Alert.NO_APPLICATION_PROTOCOL,
|
||||
"No matching application layer protocol values");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((shc.applicationProtocol == null) ||
|
||||
(shc.applicationProtocol.isEmpty())) {
|
||||
// Ignore, no negotiated application layer protocol.
|
||||
shc.applicationProtocol = "";
|
||||
shc.conContext.applicationProtocol = "";
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore, no negotiated application layer protocol");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// opaque ProtocolName<1..2^8-1>, RFC 7301.
|
||||
byte[] bytes = shc.applicationProtocol.getBytes(alpnCharset);
|
||||
int listLen = bytes.length + 1; // 1: length byte
|
||||
|
||||
// ProtocolName protocol_name_list<2..2^16-1>, RFC 7301.
|
||||
byte[] extData = new byte[listLen + 2]; // 2: list length
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, listLen);
|
||||
Record.putBytes8(m, bytes);
|
||||
|
||||
// Update the context.
|
||||
shc.conContext.applicationProtocol = shc.applicationProtocol;
|
||||
|
||||
// Clean or register the extension
|
||||
//
|
||||
// No further use of the request and respond extension any more.
|
||||
shc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of the extension in the ServerHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class SHAlpnConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHAlpnConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to ALPN request only
|
||||
AlpnSpec requestedAlps =
|
||||
(AlpnSpec)chc.handshakeExtensions.get(SSLExtension.CH_ALPN);
|
||||
if (requestedAlps == null ||
|
||||
requestedAlps.applicationProtocols == null ||
|
||||
requestedAlps.applicationProtocols.isEmpty()) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected " + SSLExtension.CH_ALPN.name + " extension");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
AlpnSpec spec;
|
||||
try {
|
||||
spec = new AlpnSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// Only one application protocol is allowed.
|
||||
if (spec.applicationProtocols.size() != 1) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
|
||||
"Only one application protocol name " +
|
||||
"is allowed in ServerHello message");
|
||||
}
|
||||
|
||||
// The respond application protocol must be one of the requested.
|
||||
if (!requestedAlps.applicationProtocols.containsAll(
|
||||
spec.applicationProtocols)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid " + SSLExtension.CH_ALPN.name + " extension: " +
|
||||
"Only client specified application protocol " +
|
||||
"is allowed in ServerHello message");
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.applicationProtocol = spec.applicationProtocols.get(0);
|
||||
chc.conContext.applicationProtocol = chc.applicationProtocol;
|
||||
|
||||
// Clean or register the extension
|
||||
//
|
||||
// No further use of the request and respond extension any more.
|
||||
chc.handshakeExtensions.remove(SSLExtension.CH_ALPN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if the extension is not present in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final class SHAlpnAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Please don't use the previous negotiated application protocol.
|
||||
chc.applicationProtocol = "";
|
||||
chc.conContext.applicationProtocol = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
460
jdkSrc/jdk8/sun/security/ssl/Authenticator.java
Normal file
460
jdkSrc/jdk8/sun/security/ssl/Authenticator.java
Normal file
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import sun.security.ssl.CipherSuite.MacAlg;
|
||||
|
||||
/**
|
||||
* This class represents an SSL/TLS message authentication token,
|
||||
* which encapsulates a sequence number and ensures that attempts to
|
||||
* delete or reorder messages can be detected.
|
||||
*/
|
||||
abstract class Authenticator {
|
||||
// byte array containing the additional authentication information for
|
||||
// each record
|
||||
protected final byte[] block; // at least 8 bytes for sequence number
|
||||
|
||||
private Authenticator(byte[] block) {
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the message authentication token for the specified
|
||||
* SSL/TLS protocol.
|
||||
*/
|
||||
static Authenticator valueOf(ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
return new TLS13Authenticator(protocolVersion);
|
||||
} else if (protocolVersion.useTLS10PlusSpec()) {
|
||||
return new TLS10Authenticator(protocolVersion);
|
||||
} else {
|
||||
return new SSL30Authenticator();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
static <T extends Authenticator & MAC> T
|
||||
valueOf(ProtocolVersion protocolVersion, MacAlg macAlg,
|
||||
SecretKey key) throws NoSuchAlgorithmException,
|
||||
InvalidKeyException {
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
throw new RuntimeException("No MacAlg used in TLS 1.3");
|
||||
} else if (protocolVersion.useTLS10PlusSpec()) {
|
||||
return (T)(new TLS10Mac(protocolVersion, macAlg, key));
|
||||
} else {
|
||||
return (T)(new SSL30Mac(protocolVersion, macAlg, key));
|
||||
}
|
||||
}
|
||||
|
||||
static Authenticator nullTlsMac() {
|
||||
return new SSLNullMac();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the sequence number is close to wrap.
|
||||
*
|
||||
* Sequence numbers are of type uint64 and may not exceed 2^64-1.
|
||||
* Sequence numbers do not wrap. When the sequence number is near
|
||||
* to wrap, we need to close the connection immediately.
|
||||
*
|
||||
* @return true if the sequence number is close to wrap
|
||||
*/
|
||||
abstract boolean seqNumOverflow();
|
||||
|
||||
/**
|
||||
* Checks whether the sequence number close to renew.
|
||||
*
|
||||
* Sequence numbers are of type uint64 and may not exceed 2^64-1.
|
||||
* Sequence numbers do not wrap. If a TLS
|
||||
* implementation would need to wrap a sequence number, it must
|
||||
* renegotiate instead.
|
||||
*
|
||||
* @return true if the sequence number is huge enough to renew
|
||||
*/
|
||||
abstract boolean seqNumIsHuge();
|
||||
|
||||
/**
|
||||
* Gets the current sequence number.
|
||||
*
|
||||
* @return the byte array of the current sequence number
|
||||
*/
|
||||
final byte[] sequenceNumber() {
|
||||
return Arrays.copyOf(block, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the sequence number.
|
||||
*/
|
||||
final void increaseSequenceNumber() {
|
||||
/*
|
||||
* The sequence number in the block array is a 64-bit
|
||||
* number stored in big-endian format.
|
||||
*/
|
||||
int k = 7;
|
||||
while ((k >= 0) && (++block[k] == 0)) {
|
||||
k--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the current message authentication information with the
|
||||
* specified record type and fragment length, and then increases the
|
||||
* sequence number if using implicit sequence number.
|
||||
*
|
||||
* @param type the record type
|
||||
* @param length the fragment of the record
|
||||
* @param sequence the explicit sequence number of the record
|
||||
*
|
||||
* @return the byte array of the current message authentication information
|
||||
*/
|
||||
byte[] acquireAuthenticationBytes(
|
||||
byte type, int length, byte[] sequence) {
|
||||
throw new UnsupportedOperationException("Used by AEAD algorithms only");
|
||||
}
|
||||
|
||||
private static class SSLAuthenticator extends Authenticator {
|
||||
private SSLAuthenticator(byte[] block) {
|
||||
super(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean seqNumOverflow() {
|
||||
/*
|
||||
* Conservatively, we don't allow more records to be generated
|
||||
* when there are only 2^8 sequence numbers left.
|
||||
*/
|
||||
return (block.length != 0 &&
|
||||
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
|
||||
block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
|
||||
block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
|
||||
block[6] == (byte)0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean seqNumIsHuge() {
|
||||
return (block.length != 0 &&
|
||||
block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
|
||||
block[2] == (byte)0xFF && block[3] == (byte)0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// For null MAC only.
|
||||
private static class SSLNullAuthenticator extends SSLAuthenticator {
|
||||
private SSLNullAuthenticator() {
|
||||
super(new byte[8]);
|
||||
}
|
||||
}
|
||||
|
||||
// For SSL 3.0
|
||||
private static class SSL30Authenticator extends SSLAuthenticator {
|
||||
// Block size of SSL v3.0:
|
||||
// sequence number + record type + + record length
|
||||
private static final int BLOCK_SIZE = 11; // 8 + 1 + 2
|
||||
|
||||
private SSL30Authenticator() {
|
||||
super(new byte[BLOCK_SIZE]);
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] acquireAuthenticationBytes(
|
||||
byte type, int length, byte[] sequence) {
|
||||
byte[] ad = block.clone();
|
||||
|
||||
// Increase the implicit sequence number in the block array.
|
||||
increaseSequenceNumber();
|
||||
|
||||
ad[8] = type;
|
||||
ad[9] = (byte)(length >> 8);
|
||||
ad[10] = (byte)(length);
|
||||
|
||||
return ad;
|
||||
}
|
||||
}
|
||||
|
||||
// For TLS 1.0 - 1.2
|
||||
private static class TLS10Authenticator extends SSLAuthenticator {
|
||||
// Block size of TLS v1.0/1.1/1.2.
|
||||
// sequence number + record type + protocol version + record length
|
||||
private static final int BLOCK_SIZE = 13; // 8 + 1 + 2 + 2
|
||||
|
||||
private TLS10Authenticator(ProtocolVersion protocolVersion) {
|
||||
super(new byte[BLOCK_SIZE]);
|
||||
block[9] = protocolVersion.major;
|
||||
block[10] = protocolVersion.minor;
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] acquireAuthenticationBytes(
|
||||
byte type, int length, byte[] sequence) {
|
||||
byte[] ad = block.clone();
|
||||
if (sequence != null) {
|
||||
if (sequence.length != 8) {
|
||||
throw new RuntimeException(
|
||||
"Insufficient explicit sequence number bytes");
|
||||
}
|
||||
|
||||
System.arraycopy(sequence, 0, ad, 0, sequence.length);
|
||||
} else { // Otherwise, use the implicit sequence number.
|
||||
// Increase the implicit sequence number in the block array.
|
||||
increaseSequenceNumber();
|
||||
}
|
||||
|
||||
ad[8] = type;
|
||||
ad[11] = (byte)(length >> 8);
|
||||
ad[12] = (byte)(length);
|
||||
|
||||
return ad;
|
||||
}
|
||||
}
|
||||
|
||||
// For TLS 1.3
|
||||
private static final class TLS13Authenticator extends SSLAuthenticator {
|
||||
// Block size of TLS v1.3:
|
||||
// record type + protocol version + record length + sequence number
|
||||
private static final int BLOCK_SIZE = 13; // 1 + 2 + 2 + 8
|
||||
|
||||
private TLS13Authenticator(ProtocolVersion protocolVersion) {
|
||||
super(new byte[BLOCK_SIZE]);
|
||||
block[9] = ProtocolVersion.TLS12.major;
|
||||
block[10] = ProtocolVersion.TLS12.minor;
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] acquireAuthenticationBytes(
|
||||
byte type, int length, byte[] sequence) {
|
||||
byte[] ad = Arrays.copyOfRange(block, 8, 13);
|
||||
|
||||
// Increase the implicit sequence number in the block array.
|
||||
increaseSequenceNumber();
|
||||
|
||||
ad[0] = type;
|
||||
ad[3] = (byte)(length >> 8);
|
||||
ad[4] = (byte)(length & 0xFF);
|
||||
|
||||
return ad;
|
||||
}
|
||||
}
|
||||
|
||||
interface MAC {
|
||||
MacAlg macAlg();
|
||||
|
||||
/**
|
||||
* Compute and returns the MAC for the remaining data
|
||||
* in this ByteBuffer.
|
||||
*
|
||||
* On return, the bb position == limit, and limit will
|
||||
* have not changed.
|
||||
*
|
||||
* @param type record type
|
||||
* @param bb a ByteBuffer in which the position and limit
|
||||
* demarcate the data to be MAC'd.
|
||||
* @param isSimulated if true, simulate the MAC computation
|
||||
* @param sequence the explicit sequence number, or null if using
|
||||
* the implicit sequence number for the computation
|
||||
*
|
||||
* @return the MAC result
|
||||
*/
|
||||
byte[] compute(byte type, ByteBuffer bb,
|
||||
byte[] sequence, boolean isSimulated);
|
||||
|
||||
|
||||
/**
|
||||
* Compute and returns the MAC for the remaining data
|
||||
* in this ByteBuffer.
|
||||
*
|
||||
* On return, the bb position == limit, and limit will
|
||||
* have not changed.
|
||||
*
|
||||
* @param type record type
|
||||
* @param bb a ByteBuffer in which the position and limit
|
||||
* demarcate the data to be MAC'd.
|
||||
* @param isSimulated if true, simulate the MAC computation
|
||||
*
|
||||
* @return the MAC result
|
||||
*/
|
||||
default byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
|
||||
return compute(type, bb, null, isSimulated);
|
||||
}
|
||||
}
|
||||
|
||||
private class MacImpl implements MAC {
|
||||
// internal identifier for the MAC algorithm
|
||||
private final MacAlg macAlg;
|
||||
|
||||
// JCE Mac object
|
||||
private final Mac mac;
|
||||
|
||||
private MacImpl() {
|
||||
macAlg = MacAlg.M_NULL;
|
||||
mac = null;
|
||||
}
|
||||
|
||||
private MacImpl(ProtocolVersion protocolVersion, MacAlg macAlg,
|
||||
SecretKey key) throws NoSuchAlgorithmException,
|
||||
InvalidKeyException {
|
||||
if (macAlg == null) {
|
||||
throw new RuntimeException("Null MacAlg");
|
||||
}
|
||||
|
||||
// using SSL MAC computation?
|
||||
boolean useSSLMac = (protocolVersion.id < ProtocolVersion.TLS10.id);
|
||||
String algorithm;
|
||||
switch (macAlg) {
|
||||
case M_MD5:
|
||||
algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
|
||||
break;
|
||||
case M_SHA:
|
||||
algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
|
||||
break;
|
||||
case M_SHA256:
|
||||
algorithm = "HmacSHA256"; // TLS 1.2+
|
||||
break;
|
||||
case M_SHA384:
|
||||
algorithm = "HmacSHA384"; // TLS 1.2+
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown MacAlg " + macAlg);
|
||||
}
|
||||
|
||||
Mac m = JsseJce.getMac(algorithm);
|
||||
m.init(key);
|
||||
this.macAlg = macAlg;
|
||||
this.mac = m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MacAlg macAlg() {
|
||||
return macAlg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compute(byte type, ByteBuffer bb,
|
||||
byte[] sequence, boolean isSimulated) {
|
||||
|
||||
if (macAlg.size == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
if (!isSimulated) {
|
||||
// Uses the explicit sequence number for the computation.
|
||||
byte[] additional =
|
||||
acquireAuthenticationBytes(type, bb.remaining(), sequence);
|
||||
mac.update(additional);
|
||||
}
|
||||
mac.update(bb);
|
||||
|
||||
return mac.doFinal();
|
||||
}
|
||||
}
|
||||
|
||||
// NULL SSL MAC
|
||||
private static final
|
||||
class SSLNullMac extends SSLNullAuthenticator implements MAC {
|
||||
private final MacImpl macImpl;
|
||||
public SSLNullMac() {
|
||||
super();
|
||||
this.macImpl = new MacImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MacAlg macAlg() {
|
||||
return macImpl.macAlg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compute(byte type, ByteBuffer bb,
|
||||
byte[] sequence, boolean isSimulated) {
|
||||
return macImpl.compute(type, bb, sequence, isSimulated);
|
||||
}
|
||||
}
|
||||
|
||||
// For SSL 3.0
|
||||
private static final
|
||||
class SSL30Mac extends SSL30Authenticator implements MAC {
|
||||
private final MacImpl macImpl;
|
||||
public SSL30Mac(ProtocolVersion protocolVersion,
|
||||
MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
|
||||
InvalidKeyException {
|
||||
super();
|
||||
this.macImpl = new MacImpl(protocolVersion, macAlg, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MacAlg macAlg() {
|
||||
return macImpl.macAlg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compute(byte type, ByteBuffer bb,
|
||||
byte[] sequence, boolean isSimulated) {
|
||||
return macImpl.compute(type, bb, sequence, isSimulated);
|
||||
}
|
||||
}
|
||||
|
||||
// For TLS 1.0 - 1.2
|
||||
private static final
|
||||
class TLS10Mac extends TLS10Authenticator implements MAC {
|
||||
private final MacImpl macImpl;
|
||||
public TLS10Mac(ProtocolVersion protocolVersion,
|
||||
MacAlg macAlg, SecretKey key) throws NoSuchAlgorithmException,
|
||||
InvalidKeyException {
|
||||
super(protocolVersion);
|
||||
this.macImpl = new MacImpl(protocolVersion, macAlg, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MacAlg macAlg() {
|
||||
return macImpl.macAlg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] compute(byte type, ByteBuffer bb,
|
||||
byte[] sequence, boolean isSimulated) {
|
||||
return macImpl.compute(type, bb, sequence, isSimulated);
|
||||
}
|
||||
}
|
||||
|
||||
static final long toLong(byte[] recordEnS) {
|
||||
if (recordEnS != null && recordEnS.length == 8) {
|
||||
return ((recordEnS[0] & 0xFFL) << 56) |
|
||||
((recordEnS[1] & 0xFFL) << 48) |
|
||||
((recordEnS[2] & 0xFFL) << 40) |
|
||||
((recordEnS[3] & 0xFFL) << 32) |
|
||||
((recordEnS[4] & 0xFFL) << 24) |
|
||||
((recordEnS[5] & 0xFFL) << 16) |
|
||||
((recordEnS[6] & 0xFFL) << 8) |
|
||||
(recordEnS[7] & 0xFFL);
|
||||
}
|
||||
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
646
jdkSrc/jdk8/sun/security/ssl/BaseSSLSocketImpl.java
Normal file
646
jdkSrc/jdk8/sun/security/ssl/BaseSSLSocketImpl.java
Normal file
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Set;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
/**
|
||||
* Abstract base class for SSLSocketImpl.
|
||||
*
|
||||
* Its purpose is to house code with no SSL related logic (or no logic at all).
|
||||
* This makes SSLSocketImpl shorter and easier to read. It contains a few
|
||||
* constants and static methods plus overridden java.net.Socket methods.
|
||||
*
|
||||
* Methods are defined final to ensure that they are not accidentally
|
||||
* overridden in SSLSocketImpl.
|
||||
*
|
||||
* @see javax.net.ssl.SSLSocket
|
||||
* @see SSLSocketImpl
|
||||
*/
|
||||
abstract class BaseSSLSocketImpl extends SSLSocket {
|
||||
|
||||
/*
|
||||
* Normally "self" is "this" ... but not when this connection is
|
||||
* layered over a preexisting socket. If we're using an existing
|
||||
* socket, we delegate some actions to it. Else, we delegate
|
||||
* instead to "super". This is important to ensure that we don't
|
||||
* recurse infinitely ... e.g. close() calling itself, or doing
|
||||
* I/O in terms of our own streams.
|
||||
*/
|
||||
private final Socket self;
|
||||
private final InputStream consumedInput;
|
||||
|
||||
BaseSSLSocketImpl() {
|
||||
super();
|
||||
this.self = this;
|
||||
this.consumedInput = null;
|
||||
}
|
||||
|
||||
BaseSSLSocketImpl(Socket socket) {
|
||||
super();
|
||||
this.self = socket;
|
||||
this.consumedInput = null;
|
||||
}
|
||||
|
||||
BaseSSLSocketImpl(Socket socket, InputStream consumed) {
|
||||
super();
|
||||
this.self = socket;
|
||||
this.consumedInput = consumed;
|
||||
}
|
||||
|
||||
//
|
||||
// CONSTANTS AND STATIC METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* TLS requires that a close_notify warning alert is sent before the
|
||||
* connection is closed in order to avoid truncation attacks. Some
|
||||
* implementations (MS IIS and others) don't do that. The property
|
||||
* below controls whether we accept that or treat it as an error.
|
||||
*
|
||||
* The default is "false", i.e. tolerate the broken behavior.
|
||||
*/
|
||||
private static final String PROP_NAME =
|
||||
"com.sun.net.ssl.requireCloseNotify";
|
||||
|
||||
static final boolean requireCloseNotify =
|
||||
Utilities.getBooleanProperty(PROP_NAME, false);
|
||||
|
||||
//
|
||||
// MISC SOCKET METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns the unique {@link java.nio.SocketChannel SocketChannel} object
|
||||
* associated with this socket, if any.
|
||||
* @see java.net.Socket#getChannel
|
||||
*/
|
||||
@Override
|
||||
public final SocketChannel getChannel() {
|
||||
if (self == this) {
|
||||
return super.getChannel();
|
||||
} else {
|
||||
return self.getChannel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the address to the socket.
|
||||
* @see java.net.Socket#bind
|
||||
*/
|
||||
@Override
|
||||
public void bind(SocketAddress bindpoint) throws IOException {
|
||||
/*
|
||||
* Bind to this socket
|
||||
*/
|
||||
if (self == this) {
|
||||
super.bind(bindpoint);
|
||||
} else {
|
||||
// If we're binding on a layered socket...
|
||||
throw new IOException(
|
||||
"Underlying socket should already be connected");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the endpoint this socket is connected to
|
||||
* @see java.net.Socket#getLocalSocketAddress
|
||||
*/
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
if (self == this) {
|
||||
return super.getLocalSocketAddress();
|
||||
} else {
|
||||
return self.getLocalSocketAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the endpoint this socket is connected to
|
||||
* @see java.net.Socket#getRemoteSocketAddress
|
||||
*/
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
if (self == this) {
|
||||
return super.getRemoteSocketAddress();
|
||||
} else {
|
||||
return self.getRemoteSocketAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects this socket to the server.
|
||||
*
|
||||
* This method is either called on an unconnected SSLSocketImpl by the
|
||||
* application, or it is called in the constructor of a regular
|
||||
* SSLSocketImpl. If we are layering on top on another socket, then
|
||||
* this method should not be called, because we assume that the
|
||||
* underlying socket is already connected by the time it is passed to
|
||||
* us.
|
||||
*
|
||||
* @param endpoint the <code>SocketAddress</code>
|
||||
* @throws IOException if an error occurs during the connection
|
||||
*/
|
||||
@Override
|
||||
public final void connect(SocketAddress endpoint) throws IOException {
|
||||
connect(endpoint, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection state of the socket.
|
||||
* @see java.net.Socket#isConnected
|
||||
*/
|
||||
@Override
|
||||
public final boolean isConnected() {
|
||||
if (self == this) {
|
||||
return super.isConnected();
|
||||
} else {
|
||||
return self.isConnected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binding state of the socket.
|
||||
* @see java.net.Socket#isBound
|
||||
*/
|
||||
@Override
|
||||
public final boolean isBound() {
|
||||
if (self == this) {
|
||||
return super.isBound();
|
||||
} else {
|
||||
return self.isBound();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CLOSE RELATED METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* Places the input stream for this socket at "end of stream". Any data
|
||||
* sent to the input stream side of the socket is acknowledged and then
|
||||
* silently discarded.
|
||||
*
|
||||
* @see java.net.Socket#shutdownInput
|
||||
*/
|
||||
@Override
|
||||
public void shutdownInput() throws IOException {
|
||||
if (self == this) {
|
||||
super.shutdownInput();
|
||||
} else {
|
||||
self.shutdownInput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the output stream for this socket. For a TCP socket, any
|
||||
* previously written data will be sent followed by TCP's normal
|
||||
* connection termination sequence.
|
||||
*
|
||||
* @see java.net.Socket#shutdownOutput
|
||||
*/
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException {
|
||||
if (self == this) {
|
||||
super.shutdownOutput();
|
||||
} else {
|
||||
self.shutdownOutput();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input state of the socket
|
||||
* @see java.net.Socket#isInputShutdown
|
||||
*/
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
if (self == this) {
|
||||
return super.isInputShutdown();
|
||||
} else {
|
||||
return self.isInputShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output state of the socket
|
||||
* @see java.net.Socket#isOutputShutdown
|
||||
*/
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
if (self == this) {
|
||||
return super.isOutputShutdown();
|
||||
} else {
|
||||
return self.isOutputShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the SSL connection is closed down as cleanly
|
||||
* as possible, in case the application forgets to do so.
|
||||
* This allows SSL connections to be implicitly reclaimed,
|
||||
* rather than forcing them to be explicitly reclaimed at
|
||||
* the penalty of prematurly killing SSL sessions.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected final void finalize() throws Throwable {
|
||||
try {
|
||||
close();
|
||||
} catch (IOException e1) {
|
||||
try {
|
||||
if (self == this) {
|
||||
super.close();
|
||||
}
|
||||
} catch (IOException e2) {
|
||||
// ignore
|
||||
}
|
||||
} finally {
|
||||
// We called close on the underlying socket above to
|
||||
// make doubly sure all resources got released. We
|
||||
// don't finalize self in the case of overlain sockets,
|
||||
// that's a different object which the GC will finalize
|
||||
// separately.
|
||||
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET ADDRESS METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns the address of the remote peer for this connection.
|
||||
*/
|
||||
@Override
|
||||
public final InetAddress getInetAddress() {
|
||||
if (self == this) {
|
||||
return super.getInetAddress();
|
||||
} else {
|
||||
return self.getInetAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local address to which the socket is bound.
|
||||
*
|
||||
* @return the local address to which the socket is bound.
|
||||
* @since 1.1
|
||||
*/
|
||||
@Override
|
||||
public final InetAddress getLocalAddress() {
|
||||
if (self == this) {
|
||||
return super.getLocalAddress();
|
||||
} else {
|
||||
return self.getLocalAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of the remote port that this connection uses.
|
||||
*/
|
||||
@Override
|
||||
public final int getPort() {
|
||||
if (self == this) {
|
||||
return super.getPort();
|
||||
} else {
|
||||
return self.getPort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of the local port that this connection uses.
|
||||
*/
|
||||
@Override
|
||||
public final int getLocalPort() {
|
||||
if (self == this) {
|
||||
return super.getLocalPort();
|
||||
} else {
|
||||
return self.getLocalPort();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SOCKET OPTION METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* Enables or disables the Nagle optimization.
|
||||
* @see java.net.Socket#setTcpNoDelay
|
||||
*/
|
||||
@Override
|
||||
public final void setTcpNoDelay(boolean value) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setTcpNoDelay(value);
|
||||
} else {
|
||||
self.setTcpNoDelay(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Nagle optimization is disabled. This
|
||||
* relates to low-level buffering of TCP traffic, delaying the
|
||||
* traffic to promote better throughput.
|
||||
*
|
||||
* @see java.net.Socket#getTcpNoDelay
|
||||
*/
|
||||
@Override
|
||||
public final boolean getTcpNoDelay() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getTcpNoDelay();
|
||||
} else {
|
||||
return self.getTcpNoDelay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the socket's linger timeout.
|
||||
* @see java.net.Socket#setSoLinger
|
||||
*/
|
||||
@Override
|
||||
public final void setSoLinger(boolean flag, int linger)
|
||||
throws SocketException {
|
||||
if (self == this) {
|
||||
super.setSoLinger(flag, linger);
|
||||
} else {
|
||||
self.setSoLinger(flag, linger);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the socket's linger timeout.
|
||||
* @see java.net.Socket#getSoLinger
|
||||
*/
|
||||
@Override
|
||||
public final int getSoLinger() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getSoLinger();
|
||||
} else {
|
||||
return self.getSoLinger();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send one byte of urgent data on the socket.
|
||||
* @see java.net.Socket#sendUrgentData
|
||||
* At this point, there seems to be no specific requirement to support
|
||||
* this for an SSLSocket. An implementation can be provided if a need
|
||||
* arises in future.
|
||||
*/
|
||||
@Override
|
||||
public final void sendUrgentData(int data) throws SocketException {
|
||||
throw new SocketException("This method is not supported "
|
||||
+ "by SSLSockets");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable OOBINLINE (receipt of TCP urgent data) By default, this
|
||||
* option is disabled and TCP urgent data received on a socket is silently
|
||||
* discarded.
|
||||
* @see java.net.Socket#setOOBInline
|
||||
* Setting OOBInline does not have any effect on SSLSocket,
|
||||
* since currently we don't support sending urgent data.
|
||||
*/
|
||||
@Override
|
||||
public final void setOOBInline(boolean on) throws SocketException {
|
||||
throw new SocketException("This method is ineffective, since"
|
||||
+ " sending urgent data is not supported by SSLSockets");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if OOBINLINE is enabled.
|
||||
* @see java.net.Socket#getOOBInline
|
||||
*/
|
||||
@Override
|
||||
public final boolean getOOBInline() throws SocketException {
|
||||
throw new SocketException("This method is ineffective, since"
|
||||
+ " sending urgent data is not supported by SSLSockets");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the socket timeout.
|
||||
* @see java.net.Socket#getSoTimeout
|
||||
*/
|
||||
@Override
|
||||
public final int getSoTimeout() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getSoTimeout();
|
||||
} else {
|
||||
return self.getSoTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setSendBufferSize(int size) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setSendBufferSize(size);
|
||||
} else {
|
||||
self.setSendBufferSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getSendBufferSize() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getSendBufferSize();
|
||||
} else {
|
||||
return self.getSendBufferSize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setReceiveBufferSize(int size) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setReceiveBufferSize(size);
|
||||
} else {
|
||||
self.setReceiveBufferSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getReceiveBufferSize() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getReceiveBufferSize();
|
||||
} else {
|
||||
return self.getReceiveBufferSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable SO_KEEPALIVE.
|
||||
* @see java.net.Socket#setKeepAlive
|
||||
*/
|
||||
@Override
|
||||
public final void setKeepAlive(boolean on) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setKeepAlive(on);
|
||||
} else {
|
||||
self.setKeepAlive(on);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if SO_KEEPALIVE is enabled.
|
||||
* @see java.net.Socket#getKeepAlive
|
||||
*/
|
||||
@Override
|
||||
public final boolean getKeepAlive() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getKeepAlive();
|
||||
} else {
|
||||
return self.getKeepAlive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets traffic class or type-of-service octet in the IP header for
|
||||
* packets sent from this Socket.
|
||||
* @see java.net.Socket#setTrafficClass
|
||||
*/
|
||||
@Override
|
||||
public final void setTrafficClass(int tc) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setTrafficClass(tc);
|
||||
} else {
|
||||
self.setTrafficClass(tc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets traffic class or type-of-service in the IP header for packets
|
||||
* sent from this Socket.
|
||||
* @see java.net.Socket#getTrafficClass
|
||||
*/
|
||||
@Override
|
||||
public final int getTrafficClass() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getTrafficClass();
|
||||
} else {
|
||||
return self.getTrafficClass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable SO_REUSEADDR.
|
||||
* @see java.net.Socket#setReuseAddress
|
||||
*/
|
||||
@Override
|
||||
public final void setReuseAddress(boolean on) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setReuseAddress(on);
|
||||
} else {
|
||||
self.setReuseAddress(on);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if SO_REUSEADDR is enabled.
|
||||
* @see java.net.Socket#getReuseAddress
|
||||
*/
|
||||
@Override
|
||||
public final boolean getReuseAddress() throws SocketException {
|
||||
if (self == this) {
|
||||
return super.getReuseAddress();
|
||||
} else {
|
||||
return self.getReuseAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets performance preferences for this socket.
|
||||
*
|
||||
* @see java.net.Socket#setPerformancePreferences(int, int, int)
|
||||
*/
|
||||
@Override
|
||||
public void setPerformancePreferences(int connectionTime,
|
||||
int latency, int bandwidth) {
|
||||
if (self == this) {
|
||||
super.setPerformancePreferences(
|
||||
connectionTime, latency, bandwidth);
|
||||
} else {
|
||||
self.setPerformancePreferences(
|
||||
connectionTime, latency, bandwidth);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (self == this) {
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
return self.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (self == this) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
if (consumedInput != null) {
|
||||
return new SequenceInputStream(consumedInput,
|
||||
self.getInputStream());
|
||||
}
|
||||
|
||||
return self.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
if (self == this) {
|
||||
return super.getOutputStream();
|
||||
}
|
||||
|
||||
return self.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (self == this) {
|
||||
super.close();
|
||||
} else {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSoTimeout(int timeout) throws SocketException {
|
||||
if (self == this) {
|
||||
super.setSoTimeout(timeout);
|
||||
} else {
|
||||
self.setSoTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isLayered() {
|
||||
return (self != this);
|
||||
}
|
||||
}
|
||||
352
jdkSrc/jdk8/sun/security/ssl/CertSignAlgsExtension.java
Normal file
352
jdkSrc/jdk8/sun/security/ssl/CertSignAlgsExtension.java
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SignatureAlgorithmsExtension.SignatureSchemesSpec;
|
||||
|
||||
/**
|
||||
* Pack of the "signature_algorithms_cert" extensions.
|
||||
*/
|
||||
final class CertSignAlgsExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHCertSignatureSchemesProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHCertSignatureSchemesConsumer();
|
||||
static final HandshakeConsumer chOnTradeConsumer =
|
||||
new CHCertSignatureSchemesUpdate();
|
||||
|
||||
static final HandshakeProducer crNetworkProducer =
|
||||
new CRCertSignatureSchemesProducer();
|
||||
static final ExtensionConsumer crOnLoadConsumer =
|
||||
new CRCertSignatureSchemesConsumer();
|
||||
static final HandshakeConsumer crOnTradeConsumer =
|
||||
new CRCertSignatureSchemesUpdate();
|
||||
|
||||
static final SSLStringizer ssStringizer =
|
||||
new CertSignatureSchemesStringizer();
|
||||
|
||||
private static final
|
||||
class CertSignatureSchemesStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new SignatureSchemesSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "signature_algorithms_cert" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHCertSignatureSchemesProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCertSignatureSchemesProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"signature_algorithms_cert extension");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
// Produce the extension.
|
||||
if (chc.localSupportedSignAlgs == null) {
|
||||
chc.localSupportedSignAlgs =
|
||||
SignatureScheme.getSupportedAlgorithms(
|
||||
chc.sslConfig,
|
||||
chc.algorithmConstraints, chc.activeProtocols);
|
||||
}
|
||||
|
||||
int vectorLen = SignatureScheme.sizeInRecord() *
|
||||
chc.localSupportedSignAlgs.size();
|
||||
byte[] extData = new byte[vectorLen + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, vectorLen);
|
||||
for (SignatureScheme ss : chc.localSupportedSignAlgs) {
|
||||
Record.putInt16(m, ss.id);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT,
|
||||
new SignatureSchemesSpec(chc.localSupportedSignAlgs));
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "signature_algorithms_cert" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHCertSignatureSchemesConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCertSignatureSchemesConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"signature_algorithms_cert extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
SignatureSchemesSpec spec;
|
||||
try {
|
||||
spec = new SignatureSchemesSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After session creation consuming of a "signature_algorithms_cert"
|
||||
* extension in the ClientHello handshake message.
|
||||
*/
|
||||
private static final class CHCertSignatureSchemesUpdate
|
||||
implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCertSignatureSchemesUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
SignatureSchemesSpec spec = (SignatureSchemesSpec)
|
||||
shc.handshakeExtensions.get(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
|
||||
if (spec == null) {
|
||||
// Ignore, no signature_algorithms_cert extension requested.
|
||||
return;
|
||||
}
|
||||
|
||||
// update the context
|
||||
List<SignatureScheme> schemes =
|
||||
SignatureScheme.getSupportedAlgorithms(
|
||||
shc.sslConfig,
|
||||
shc.algorithmConstraints, shc.negotiatedProtocol,
|
||||
spec.signatureSchemes);
|
||||
shc.peerRequestedCertSignSchemes = schemes;
|
||||
shc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
|
||||
|
||||
if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
if (shc.sslConfig.clientAuthType !=
|
||||
ClientAuthType.CLIENT_AUTH_NONE) {
|
||||
shc.handshakeProducers.putIfAbsent(
|
||||
SSLHandshake.CERTIFICATE_REQUEST.id,
|
||||
SSLHandshake.CERTIFICATE_REQUEST);
|
||||
}
|
||||
shc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id,
|
||||
SSLHandshake.CERTIFICATE);
|
||||
shc.handshakeProducers.putIfAbsent(
|
||||
SSLHandshake.CERTIFICATE_VERIFY.id,
|
||||
SSLHandshake.CERTIFICATE_VERIFY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "signature_algorithms_cert" extension in
|
||||
* the CertificateRequest handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CRCertSignatureSchemesProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CRCertSignatureSchemesProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"signature_algorithms_cert extension");
|
||||
}
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
// Produce the extension.
|
||||
List<ProtocolVersion> protocols = Arrays.asList(shc.negotiatedProtocol);
|
||||
protocols = Collections.unmodifiableList(protocols);
|
||||
List<SignatureScheme> sigAlgs =
|
||||
SignatureScheme.getSupportedAlgorithms(
|
||||
shc.sslConfig,
|
||||
shc.algorithmConstraints,
|
||||
protocols);
|
||||
|
||||
int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size();
|
||||
byte[] extData = new byte[vectorLen + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, vectorLen);
|
||||
for (SignatureScheme ss : sigAlgs) {
|
||||
Record.putInt16(m, ss.id);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT,
|
||||
new SignatureSchemesSpec(shc.localSupportedSignAlgs));
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "signature_algorithms_cert" extension in
|
||||
* the CertificateRequest handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CRCertSignatureSchemesConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CRCertSignatureSchemesConsumer() {
|
||||
// blank
|
||||
}
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"signature_algorithms_cert extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
SignatureSchemesSpec spec;
|
||||
try {
|
||||
spec = new SignatureSchemesSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After session creation consuming of a "signature_algorithms_cert"
|
||||
* extension in the CertificateRequest handshake message.
|
||||
*/
|
||||
private static final class CRCertSignatureSchemesUpdate
|
||||
implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CRCertSignatureSchemesUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
SignatureSchemesSpec spec = (SignatureSchemesSpec)
|
||||
chc.handshakeExtensions.get(
|
||||
SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT);
|
||||
if (spec == null) {
|
||||
// Ignore, no "signature_algorithms_cert" extension requested.
|
||||
return;
|
||||
}
|
||||
|
||||
// update the context
|
||||
List<SignatureScheme> schemes =
|
||||
SignatureScheme.getSupportedAlgorithms(
|
||||
chc.sslConfig,
|
||||
chc.algorithmConstraints, chc.negotiatedProtocol,
|
||||
spec.signatureSchemes);
|
||||
chc.peerRequestedCertSignSchemes = schemes;
|
||||
chc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes);
|
||||
}
|
||||
}
|
||||
}
|
||||
1219
jdkSrc/jdk8/sun/security/ssl/CertStatusExtension.java
Normal file
1219
jdkSrc/jdk8/sun/security/ssl/CertStatusExtension.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "certificate_authorities" extensions.
|
||||
*/
|
||||
final class CertificateAuthoritiesExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHCertificateAuthoritiesProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHCertificateAuthoritiesConsumer();
|
||||
|
||||
static final HandshakeProducer crNetworkProducer =
|
||||
new CRCertificateAuthoritiesProducer();
|
||||
static final ExtensionConsumer crOnLoadConsumer =
|
||||
new CRCertificateAuthoritiesConsumer();
|
||||
|
||||
static final SSLStringizer ssStringizer =
|
||||
new CertificateAuthoritiesStringizer();
|
||||
|
||||
/**
|
||||
* The "certificate_authorities" extension.
|
||||
*/
|
||||
static final class CertificateAuthoritiesSpec implements SSLExtensionSpec {
|
||||
final List<byte[]> authorities; // certificate authorities
|
||||
|
||||
private CertificateAuthoritiesSpec(List<byte[]> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
private CertificateAuthoritiesSpec(ByteBuffer m) throws IOException {
|
||||
if (m.remaining() < 3) { // 2: the length of the list
|
||||
// 1: at least one byte authorities
|
||||
throw new SSLProtocolException(
|
||||
"Invalid certificate_authorities extension: " +
|
||||
"insufficient data");
|
||||
}
|
||||
|
||||
int listLen = Record.getInt16(m);
|
||||
if (listLen == 0) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid certificate_authorities extension: " +
|
||||
"no certificate authorities");
|
||||
}
|
||||
|
||||
if (listLen > m.remaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid certificate_authorities extension: " +
|
||||
"insufficient data");
|
||||
}
|
||||
|
||||
this.authorities = new LinkedList<>();
|
||||
while (listLen > 0) {
|
||||
// opaque DistinguishedName<1..2^16-1>;
|
||||
byte[] encoded = Record.getBytes16(m);
|
||||
listLen -= (2 + encoded.length);
|
||||
authorities.add(encoded);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<byte[]> getEncodedAuthorities(
|
||||
X509Certificate[] trustedCerts) {
|
||||
List<byte[]> authorities = new ArrayList<>(trustedCerts.length);
|
||||
int sizeAccount = 0;
|
||||
for (X509Certificate cert : trustedCerts) {
|
||||
X500Principal x500Principal = cert.getSubjectX500Principal();
|
||||
byte[] encodedPrincipal = x500Principal.getEncoded();
|
||||
sizeAccount += encodedPrincipal.length;
|
||||
if (sizeAccount > 0xFFFF) { // the size limit of this extension
|
||||
// If there too many trusts CAs such that they exceed the
|
||||
// size limit of the extension, enabling this extension
|
||||
// does not really make sense as there is no way to
|
||||
// indicate the peer certificate selection accurately.
|
||||
// In such cases, the extension is just ignored, rather
|
||||
// than fatal close, for better compatibility and
|
||||
// interoperability.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (encodedPrincipal.length != 0) {
|
||||
authorities.add(encodedPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
return authorities;
|
||||
}
|
||||
|
||||
X500Principal[] getAuthorities() {
|
||||
X500Principal[] principals = new X500Principal[authorities.size()];
|
||||
int i = 0;
|
||||
for (byte[] encoded : authorities) {
|
||||
principals[i++] = new X500Principal(encoded);
|
||||
}
|
||||
|
||||
return principals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"certificate authorities\": '['\n{0}']'", Locale.ENGLISH);
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
for (byte[] encoded : authorities) {
|
||||
X500Principal principal = new X500Principal(encoded);
|
||||
builder.append(principal.toString());
|
||||
builder.append("\n");
|
||||
}
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(builder.toString())
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CertificateAuthoritiesStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new CertificateAuthoritiesSpec(buffer))
|
||||
.toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "certificate_authorities" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHCertificateAuthoritiesProducer implements HandshakeProducer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private CHCertificateAuthoritiesProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_CERTIFICATE_AUTHORITIES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"certificate_authorities extension");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
// Produce the extension.
|
||||
X509Certificate[] caCerts =
|
||||
chc.sslContext.getX509TrustManager().getAcceptedIssuers();
|
||||
if (caCerts.length == 0) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No available certificate authorities");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
List<byte[]> encodedCAs =
|
||||
CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts);
|
||||
if (encodedCAs.isEmpty()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"The number of CAs exceeds the maximum size" +
|
||||
"of the certificate_authorities extension");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
CertificateAuthoritiesSpec spec =
|
||||
new CertificateAuthoritiesSpec(encodedCAs);
|
||||
|
||||
int vectorLen = 0;
|
||||
for (byte[] encoded : spec.authorities) {
|
||||
vectorLen += encoded.length + 2;
|
||||
}
|
||||
|
||||
byte[] extData = new byte[vectorLen + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, vectorLen);
|
||||
for (byte[] encoded : spec.authorities) {
|
||||
Record.putBytes16(m, encoded);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SSLExtension.CH_CERTIFICATE_AUTHORITIES, spec);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "certificate_authorities" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHCertificateAuthoritiesConsumer implements ExtensionConsumer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private CHCertificateAuthoritiesConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(
|
||||
SSLExtension.CH_CERTIFICATE_AUTHORITIES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"certificate_authorities extension");
|
||||
}
|
||||
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
CertificateAuthoritiesSpec spec =
|
||||
new CertificateAuthoritiesSpec(buffer);
|
||||
|
||||
// Update the context.
|
||||
shc.peerSupportedAuthorities = spec.getAuthorities();
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.CH_CERTIFICATE_AUTHORITIES, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "certificate_authorities" extension in
|
||||
* the CertificateRequest handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CRCertificateAuthoritiesProducer implements HandshakeProducer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private CRCertificateAuthoritiesProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(
|
||||
SSLExtension.CR_CERTIFICATE_AUTHORITIES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"certificate_authorities extension");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
// Produce the extension.
|
||||
X509Certificate[] caCerts =
|
||||
shc.sslContext.getX509TrustManager().getAcceptedIssuers();
|
||||
if (caCerts.length == 0) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No available certificate authorities");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
List<byte[]> encodedCAs =
|
||||
CertificateAuthoritiesSpec.getEncodedAuthorities(caCerts);
|
||||
if (encodedCAs.isEmpty()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Too many certificate authorities to use " +
|
||||
"the certificate_authorities extension");
|
||||
}
|
||||
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
CertificateAuthoritiesSpec spec =
|
||||
new CertificateAuthoritiesSpec(encodedCAs);
|
||||
|
||||
int vectorLen = 0;
|
||||
for (byte[] encoded : spec.authorities) {
|
||||
vectorLen += encoded.length + 2;
|
||||
}
|
||||
|
||||
byte[] extData = new byte[vectorLen + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, vectorLen);
|
||||
for (byte[] encoded : spec.authorities) {
|
||||
Record.putBytes16(m, encoded);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.CR_CERTIFICATE_AUTHORITIES, spec);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "certificate_authorities" extension in
|
||||
* the CertificateRequest handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CRCertificateAuthoritiesConsumer implements ExtensionConsumer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private CRCertificateAuthoritiesConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(
|
||||
SSLExtension.CR_CERTIFICATE_AUTHORITIES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable " +
|
||||
"certificate_authorities extension");
|
||||
}
|
||||
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
CertificateAuthoritiesSpec spec =
|
||||
new CertificateAuthoritiesSpec(buffer);
|
||||
|
||||
// Update the context.
|
||||
chc.peerSupportedAuthorities = spec.getAuthorities();
|
||||
chc.handshakeExtensions.put(
|
||||
SSLExtension.CR_CERTIFICATE_AUTHORITIES, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
}
|
||||
1406
jdkSrc/jdk8/sun/security/ssl/CertificateMessage.java
Normal file
1406
jdkSrc/jdk8/sun/security/ssl/CertificateMessage.java
Normal file
File diff suppressed because it is too large
Load Diff
1004
jdkSrc/jdk8/sun/security/ssl/CertificateRequest.java
Normal file
1004
jdkSrc/jdk8/sun/security/ssl/CertificateRequest.java
Normal file
File diff suppressed because it is too large
Load Diff
369
jdkSrc/jdk8/sun/security/ssl/CertificateStatus.java
Normal file
369
jdkSrc/jdk8/sun/security/ssl/CertificateStatus.java
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import sun.security.provider.certpath.OCSPResponse;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import static sun.security.ssl.CertStatusExtension.*;
|
||||
import static sun.security.ssl.CertificateMessage.*;
|
||||
|
||||
/**
|
||||
* Consumers and producers for the CertificateStatus handshake message.
|
||||
* This message takes one of two related but slightly different forms,
|
||||
* depending on the type of stapling selected by the server. The message
|
||||
* data will be of the form(s):
|
||||
*
|
||||
* [status_request, RFC 6066]
|
||||
*
|
||||
* struct {
|
||||
* CertificateStatusType status_type;
|
||||
* select (status_type) {
|
||||
* case ocsp: OCSPResponse;
|
||||
* } response;
|
||||
* } CertificateStatus;
|
||||
*
|
||||
* opaque OCSPResponse<1..2^24-1>;
|
||||
*
|
||||
* [status_request_v2, RFC 6961]
|
||||
*
|
||||
* struct {
|
||||
* CertificateStatusType status_type;
|
||||
* select (status_type) {
|
||||
* case ocsp: OCSPResponse;
|
||||
* case ocsp_multi: OCSPResponseList;
|
||||
* } response;
|
||||
* } CertificateStatus;
|
||||
*
|
||||
* opaque OCSPResponse<0..2^24-1>;
|
||||
*
|
||||
* struct {
|
||||
* OCSPResponse ocsp_response_list<1..2^24-1>;
|
||||
* } OCSPResponseList;
|
||||
*/
|
||||
final class CertificateStatus {
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new CertificateStatusConsumer();
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new CertificateStatusProducer();
|
||||
static final HandshakeAbsence handshakeAbsence =
|
||||
new CertificateStatusAbsence();
|
||||
|
||||
/**
|
||||
* The CertificateStatus handshake message.
|
||||
*/
|
||||
static final class CertificateStatusMessage extends HandshakeMessage {
|
||||
|
||||
final CertStatusRequestType statusType;
|
||||
int encodedResponsesLen = 0;
|
||||
int messageLength = -1;
|
||||
final List<byte[]> encodedResponses = new ArrayList<>();
|
||||
|
||||
CertificateStatusMessage(HandshakeContext handshakeContext) {
|
||||
super(handshakeContext);
|
||||
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)handshakeContext;
|
||||
|
||||
// Get the Certificates from the SSLContextImpl amd the Stapling
|
||||
// parameters
|
||||
StatusResponseManager.StaplingParameters stapleParams =
|
||||
shc.stapleParams;
|
||||
if (stapleParams == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected null stapling parameters");
|
||||
}
|
||||
|
||||
X509Certificate[] certChain =
|
||||
(X509Certificate[])shc.handshakeSession.getLocalCertificates();
|
||||
if (certChain == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected null certificate chain");
|
||||
}
|
||||
|
||||
// Walk the certificate list and add the correct encoded responses
|
||||
// to the encoded responses list
|
||||
statusType = stapleParams.statReqType;
|
||||
if (statusType == CertStatusRequestType.OCSP) {
|
||||
// Just worry about the first cert in the chain
|
||||
byte[] resp = stapleParams.responseMap.get(certChain[0]);
|
||||
if (resp == null) {
|
||||
// A not-found return status means we should include
|
||||
// a zero-length response in CertificateStatus.
|
||||
// This is highly unlikely to happen in practice.
|
||||
resp = new byte[0];
|
||||
}
|
||||
encodedResponses.add(resp);
|
||||
encodedResponsesLen += resp.length + 3;
|
||||
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
|
||||
for (X509Certificate cert : certChain) {
|
||||
byte[] resp = stapleParams.responseMap.get(cert);
|
||||
if (resp == null) {
|
||||
resp = new byte[0];
|
||||
}
|
||||
encodedResponses.add(resp);
|
||||
encodedResponsesLen += resp.length + 3;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported StatusResponseType: " + statusType);
|
||||
}
|
||||
|
||||
messageLength = messageLength();
|
||||
}
|
||||
|
||||
CertificateStatusMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
statusType = CertStatusRequestType.valueOf((byte)Record.getInt8(m));
|
||||
if (statusType == CertStatusRequestType.OCSP) {
|
||||
byte[] respDER = Record.getBytes24(m);
|
||||
// Convert the incoming bytes to a OCSPResponse strucutre
|
||||
if (respDER.length > 0) {
|
||||
encodedResponses.add(respDER);
|
||||
encodedResponsesLen = 3 + respDER.length;
|
||||
} else {
|
||||
throw handshakeContext.conContext.fatal(
|
||||
Alert.HANDSHAKE_FAILURE,
|
||||
"Zero-length OCSP Response");
|
||||
}
|
||||
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
|
||||
int respListLen = Record.getInt24(m);
|
||||
encodedResponsesLen = respListLen;
|
||||
|
||||
// Add each OCSP reponse into the array list in the order
|
||||
// we receive them off the wire. A zero-length array is
|
||||
// allowed for ocsp_multi, and means that a response for
|
||||
// a given certificate is not available.
|
||||
while (respListLen > 0) {
|
||||
byte[] respDER = Record.getBytes24(m);
|
||||
encodedResponses.add(respDER);
|
||||
respListLen -= (respDER.length + 3);
|
||||
}
|
||||
|
||||
if (respListLen != 0) {
|
||||
throw handshakeContext.conContext.fatal(
|
||||
Alert.INTERNAL_ERROR,
|
||||
"Bad OCSP response list length");
|
||||
}
|
||||
} else {
|
||||
throw handshakeContext.conContext.fatal(
|
||||
Alert.HANDSHAKE_FAILURE,
|
||||
"Unsupported StatusResponseType: " + statusType);
|
||||
}
|
||||
messageLength = messageLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.CERTIFICATE_STATUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
int len = 1;
|
||||
|
||||
if (messageLength == -1) {
|
||||
if (statusType == CertStatusRequestType.OCSP) {
|
||||
len += encodedResponsesLen;
|
||||
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
|
||||
len += 3 + encodedResponsesLen;
|
||||
}
|
||||
messageLength = len;
|
||||
}
|
||||
|
||||
return messageLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream s) throws IOException {
|
||||
s.putInt8(statusType.id);
|
||||
if (statusType == CertStatusRequestType.OCSP) {
|
||||
s.putBytes24(encodedResponses.get(0));
|
||||
} else if (statusType == CertStatusRequestType.OCSP_MULTI) {
|
||||
s.putInt24(encodedResponsesLen);
|
||||
for (byte[] respBytes : encodedResponses) {
|
||||
if (respBytes != null) {
|
||||
s.putBytes24(respBytes);
|
||||
} else {
|
||||
s.putBytes24(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// It is highly unlikely that we will fall into this section
|
||||
// of the code.
|
||||
throw new SSLHandshakeException("Unsupported status_type: " +
|
||||
statusType.id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Stringify the encoded OCSP response list
|
||||
for (byte[] respDER : encodedResponses) {
|
||||
if (respDER.length > 0) {
|
||||
try {
|
||||
OCSPResponse oResp = new OCSPResponse(respDER);
|
||||
sb.append(oResp.toString()).append("\n");
|
||||
} catch (IOException ioe) {
|
||||
sb.append("OCSP Response Exception: ").append(ioe)
|
||||
.append("\n");
|
||||
}
|
||||
} else {
|
||||
sb.append("<Zero-length entry>\n");
|
||||
}
|
||||
}
|
||||
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"CertificateStatus\": '{'\n" +
|
||||
" \"type\" : \"{0}\",\n" +
|
||||
" \"responses \" : [\n" + "{1}\n" + " ]\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
Object[] messageFields = {
|
||||
statusType.name,
|
||||
Utilities.indent(Utilities.indent(sb.toString()))
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The CertificateStatus handshake message consumer.
|
||||
*/
|
||||
private static final class CertificateStatusConsumer
|
||||
implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CertificateStatusConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
CertificateStatusMessage cst =
|
||||
new CertificateStatusMessage(chc, message);
|
||||
|
||||
// Log the message
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming server CertificateStatus handshake message",
|
||||
cst);
|
||||
}
|
||||
|
||||
// Pin the received responses to the SSLSessionImpl. It will
|
||||
// be retrieved by the X509TrustManagerImpl during the certificate
|
||||
// checking phase.
|
||||
chc.handshakeSession.setStatusResponses(cst.encodedResponses);
|
||||
|
||||
// Now perform the check
|
||||
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
|
||||
|
||||
// Update the handshake consumers to remove this message, indicating
|
||||
// that it has been processed.
|
||||
chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_STATUS.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The CertificateStatus handshake message consumer.
|
||||
*/
|
||||
private static final class CertificateStatusProducer
|
||||
implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CertificateStatusProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// Only the server-side should be a producer of this message
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// If stapling is not active, immediately return without producing
|
||||
// a message or any further processing.
|
||||
if (!shc.staplingActive) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create the CertificateStatus message from info in the
|
||||
CertificateStatusMessage csm = new CertificateStatusMessage(shc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced server CertificateStatus handshake message", csm);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
csm.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CertificateStatusAbsence
|
||||
implements HandshakeAbsence {
|
||||
// Prevent instantiation of this class
|
||||
private CertificateStatusAbsence() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Processing should only continue if stapling is active
|
||||
if (chc.staplingActive) {
|
||||
// Because OCSP stapling is active, it means two things
|
||||
// if we're here: 1) The server hello asserted the
|
||||
// status_request[_v2] extension. 2) The CertificateStatus
|
||||
// message was not sent. This means that cert path checking
|
||||
// was deferred, but must happen immediately.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Server did not send CertificateStatus, " +
|
||||
"checking cert chain without status info.");
|
||||
}
|
||||
T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1184
jdkSrc/jdk8/sun/security/ssl/CertificateVerify.java
Normal file
1184
jdkSrc/jdk8/sun/security/ssl/CertificateVerify.java
Normal file
File diff suppressed because it is too large
Load Diff
252
jdkSrc/jdk8/sun/security/ssl/ChangeCipherSpec.java
Normal file
252
jdkSrc/jdk8/sun/security/ssl/ChangeCipherSpec.java
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.net.ssl.SSLException;
|
||||
import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SSLTrafficKeyDerivation.LegacyTrafficKeyDerivation;
|
||||
|
||||
/**
|
||||
* Pack of the ChangeCipherSpec message.
|
||||
*/
|
||||
final class ChangeCipherSpec {
|
||||
static final SSLConsumer t10Consumer =
|
||||
new T10ChangeCipherSpecConsumer();
|
||||
static final HandshakeProducer t10Producer =
|
||||
new T10ChangeCipherSpecProducer();
|
||||
static final SSLConsumer t13Consumer =
|
||||
new T13ChangeCipherSpecConsumer();
|
||||
|
||||
/**
|
||||
* The "ChangeCipherSpec" message producer.
|
||||
*/
|
||||
private static final
|
||||
class T10ChangeCipherSpecProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private T10ChangeCipherSpecProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
HandshakeContext hc = (HandshakeContext)context;
|
||||
SSLKeyDerivation kd = hc.handshakeKeyDerivation;
|
||||
|
||||
if (!(kd instanceof LegacyTrafficKeyDerivation)) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
|
||||
CipherSuite ncs = hc.negotiatedCipherSuite;
|
||||
Authenticator writeAuthenticator;
|
||||
if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
|
||||
writeAuthenticator =
|
||||
Authenticator.valueOf(hc.negotiatedProtocol);
|
||||
} else {
|
||||
try {
|
||||
writeAuthenticator = Authenticator.valueOf(
|
||||
hc.negotiatedProtocol, ncs.macAlg,
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"clientMacKey" : "serverMacKey"));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
// unlikely
|
||||
throw new SSLException("Algorithm missing: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey writeKey =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"clientWriteKey" : "serverWriteKey");
|
||||
SecretKey writeIv =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"clientWriteIv" : "serverWriteIv");
|
||||
IvParameterSpec iv = (writeIv == null) ? null :
|
||||
new IvParameterSpec(writeIv.getEncoded());
|
||||
SSLWriteCipher writeCipher;
|
||||
try {
|
||||
writeCipher = ncs.bulkCipher.createWriteCipher(
|
||||
writeAuthenticator,
|
||||
hc.negotiatedProtocol, writeKey, iv,
|
||||
hc.sslContext.getSecureRandom());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
// unlikely
|
||||
throw new SSLException("Algorithm missing: ", gse);
|
||||
}
|
||||
|
||||
if (writeCipher == null) {
|
||||
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Illegal cipher suite (" + ncs +
|
||||
") and protocol version (" + hc.negotiatedProtocol + ")");
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Produced ChangeCipherSpec message");
|
||||
}
|
||||
|
||||
hc.conContext.outputRecord.changeWriteCiphers(writeCipher, true);
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "ChangeCipherSpec" message producer.
|
||||
*/
|
||||
private static final
|
||||
class T10ChangeCipherSpecConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private T10ChangeCipherSpecConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
TransportContext tc = (TransportContext)context;
|
||||
|
||||
// This consumer can be used only once.
|
||||
tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
|
||||
|
||||
// parse
|
||||
if (message.remaining() != 1 || message.get() != 1) {
|
||||
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Malformed or unexpected ChangeCipherSpec message");
|
||||
}
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Consuming ChangeCipherSpec message");
|
||||
}
|
||||
|
||||
// validate
|
||||
if (tc.handshakeContext == null) {
|
||||
throw tc.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unexpected ChangeCipherSpec message");
|
||||
}
|
||||
|
||||
|
||||
HandshakeContext hc = tc.handshakeContext;
|
||||
|
||||
if (hc.handshakeKeyDerivation == null) {
|
||||
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ChangeCipherSpec message");
|
||||
}
|
||||
|
||||
SSLKeyDerivation kd = hc.handshakeKeyDerivation;
|
||||
if (kd instanceof LegacyTrafficKeyDerivation) {
|
||||
LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd;
|
||||
CipherSuite ncs = hc.negotiatedCipherSuite;
|
||||
Authenticator readAuthenticator;
|
||||
if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) {
|
||||
readAuthenticator =
|
||||
Authenticator.valueOf(hc.negotiatedProtocol);
|
||||
} else {
|
||||
try {
|
||||
readAuthenticator = Authenticator.valueOf(
|
||||
hc.negotiatedProtocol, ncs.macAlg,
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"serverMacKey" : "clientMacKey"));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
// unlikely
|
||||
throw new SSLException("Algorithm missing: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey readKey =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"serverWriteKey" : "clientWriteKey");
|
||||
SecretKey readIv =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"serverWriteIv" : "clientWriteIv");
|
||||
IvParameterSpec iv = (readIv == null) ? null :
|
||||
new IvParameterSpec(readIv.getEncoded());
|
||||
SSLReadCipher readCipher;
|
||||
try {
|
||||
readCipher = ncs.bulkCipher.createReadCipher(
|
||||
readAuthenticator,
|
||||
hc.negotiatedProtocol, readKey, iv,
|
||||
hc.sslContext.getSecureRandom());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
// unlikely
|
||||
throw new SSLException("Algorithm missing: ", gse);
|
||||
}
|
||||
|
||||
if (readCipher == null) {
|
||||
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
|
||||
") and protocol version (" + hc.negotiatedProtocol +
|
||||
")");
|
||||
}
|
||||
|
||||
tc.inputRecord.changeReadCiphers(readCipher);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class T13ChangeCipherSpecConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private T13ChangeCipherSpecConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
// An implementation may receive an unencrypted record of type
|
||||
// change_cipher_spec consisting of the single byte value 0x01
|
||||
// at any time after the first ClientHello message has been
|
||||
// sent or received and before the peer's Finished message has
|
||||
// been received and MUST simply drop it without further
|
||||
// processing.
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
TransportContext tc = (TransportContext)context;
|
||||
|
||||
// This consumer can be used only once.
|
||||
tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id);
|
||||
|
||||
// parse
|
||||
if (message.remaining() != 1 || message.get() != 1) {
|
||||
throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Malformed or unexpected ChangeCipherSpec message");
|
||||
}
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Consuming ChangeCipherSpec message");
|
||||
}
|
||||
|
||||
// no further processing
|
||||
}
|
||||
}
|
||||
}
|
||||
1229
jdkSrc/jdk8/sun/security/ssl/CipherSuite.java
Normal file
1229
jdkSrc/jdk8/sun/security/ssl/CipherSuite.java
Normal file
File diff suppressed because it is too large
Load Diff
37
jdkSrc/jdk8/sun/security/ssl/CipherType.java
Normal file
37
jdkSrc/jdk8/sun/security/ssl/CipherType.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
/**
|
||||
* Enum for SSL/TLS cipher types.
|
||||
*/
|
||||
enum CipherType {
|
||||
NULL_CIPHER, // null cipher
|
||||
STREAM_CIPHER, // stream cipher
|
||||
BLOCK_CIPHER, // block cipher in CBC mode
|
||||
AEAD_CIPHER // AEAD cipher
|
||||
}
|
||||
|
||||
53
jdkSrc/jdk8/sun/security/ssl/Ciphertext.java
Normal file
53
jdkSrc/jdk8/sun/security/ssl/Ciphertext.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
|
||||
/*
|
||||
* Ciphertext
|
||||
*/
|
||||
final class Ciphertext {
|
||||
final byte contentType;
|
||||
final byte handshakeType;
|
||||
final long recordSN;
|
||||
|
||||
HandshakeStatus handshakeStatus; // null if not used or not handshaking
|
||||
|
||||
private Ciphertext() {
|
||||
this.contentType = 0;
|
||||
this.handshakeType = -1;
|
||||
this.recordSN = -1L;
|
||||
this.handshakeStatus = null;
|
||||
}
|
||||
|
||||
Ciphertext(byte contentType, byte handshakeType, long recordSN) {
|
||||
this.contentType = contentType;
|
||||
this.handshakeType = handshakeType;
|
||||
this.recordSN = recordSN;
|
||||
this.handshakeStatus = null;
|
||||
}
|
||||
}
|
||||
36
jdkSrc/jdk8/sun/security/ssl/ClientAuthType.java
Normal file
36
jdkSrc/jdk8/sun/security/ssl/ClientAuthType.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
/**
|
||||
* Client authentication type.
|
||||
*/
|
||||
enum ClientAuthType {
|
||||
CLIENT_AUTH_NONE, // turn off client authentication
|
||||
CLIENT_AUTH_REQUESTED, // need to request client authentication
|
||||
CLIENT_AUTH_REQUIRED // require client authentication
|
||||
}
|
||||
|
||||
115
jdkSrc/jdk8/sun/security/ssl/ClientHandshakeContext.java
Normal file
115
jdkSrc/jdk8/sun/security/ssl/ClientHandshakeContext.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import sun.security.ssl.ClientHello.ClientHelloMessage;
|
||||
|
||||
class ClientHandshakeContext extends HandshakeContext {
|
||||
/*
|
||||
* Allow unsafe server certificate change?
|
||||
*
|
||||
* Server certificate change during SSL/TLS renegotiation may be considered
|
||||
* unsafe, as described in the Triple Handshake attacks:
|
||||
*
|
||||
* https://secure-resumption.com/tlsauth.pdf
|
||||
*
|
||||
* Endpoint identification (See
|
||||
* SSLParameters.getEndpointIdentificationAlgorithm()) is a pretty nice
|
||||
* guarantee that the server certificate change in renegotiation is legal.
|
||||
* However, endpoint identification is only enabled for HTTPS and LDAP
|
||||
* over SSL/TLS by default. It is not enough to protect SSL/TLS
|
||||
* connections other than HTTPS and LDAP.
|
||||
*
|
||||
* The renegotiation indication extension (See RFC 5746) is a pretty
|
||||
* strong guarantee that the endpoints on both client and server sides
|
||||
* are identical on the same connection. However, the Triple Handshake
|
||||
* attacks can bypass this guarantee if there is a session-resumption
|
||||
* handshake between the initial full handshake and the renegotiation
|
||||
* full handshake.
|
||||
*
|
||||
* Server certificate change may be unsafe and should be restricted if
|
||||
* endpoint identification is not enabled and the previous handshake is
|
||||
* a session-resumption abbreviated initial handshake, unless the
|
||||
* identities represented by both certificates can be regraded as the
|
||||
* same (See isIdentityEquivalent()).
|
||||
*
|
||||
* Considering the compatibility impact and the actual requirements to
|
||||
* support server certificate change in practice, the system property,
|
||||
* jdk.tls.allowUnsafeServerCertChange, is used to define whether unsafe
|
||||
* server certificate change in renegotiation is allowed or not. The
|
||||
* default value of the system property is "false". To mitigate the
|
||||
* compatibility impact, applications may want to set the system
|
||||
* property to "true" at their own risk.
|
||||
*
|
||||
* If the value of the system property is "false", server certificate
|
||||
* change in renegotiation after a session-resumption abbreviated initial
|
||||
* handshake is restricted (See isIdentityEquivalent()).
|
||||
*
|
||||
* If the system property is set to "true" explicitly, the restriction on
|
||||
* server certificate change in renegotiation is disabled.
|
||||
*/
|
||||
static final boolean allowUnsafeServerCertChange =
|
||||
Utilities.getBooleanProperty(
|
||||
"jdk.tls.allowUnsafeServerCertChange", false);
|
||||
|
||||
/*
|
||||
* the reserved server certificate chain in previous handshaking
|
||||
*
|
||||
* The server certificate chain is only reserved if the previous
|
||||
* handshake is a session-resumption abbreviated initial handshake.
|
||||
*/
|
||||
X509Certificate[] reservedServerCerts = null;
|
||||
|
||||
X509Certificate[] deferredCerts;
|
||||
|
||||
ClientHelloMessage initialClientHelloMsg = null;
|
||||
|
||||
// Flag to indicate receipt of a CertificateRequest message from
|
||||
// the server. Because this is optional, we cannot guarantee
|
||||
// the handshakeConsumers Map will always have it present there.
|
||||
boolean receivedCertReq = false;
|
||||
|
||||
// PSK identity is selected in first Hello and used again after HRR
|
||||
byte[] pskIdentity;
|
||||
|
||||
ClientHandshakeContext(SSLContextImpl sslContext,
|
||||
TransportContext conContext) throws IOException {
|
||||
super(sslContext, conContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
void kickstart() throws IOException {
|
||||
if (kickstartMessageDelivered) {
|
||||
return;
|
||||
}
|
||||
|
||||
SSLHandshake.kickstart(this);
|
||||
kickstartMessageDelivered = true;
|
||||
}
|
||||
}
|
||||
1146
jdkSrc/jdk8/sun/security/ssl/ClientHello.java
Normal file
1146
jdkSrc/jdk8/sun/security/ssl/ClientHello.java
Normal file
File diff suppressed because it is too large
Load Diff
122
jdkSrc/jdk8/sun/security/ssl/ClientKeyExchange.java
Normal file
122
jdkSrc/jdk8/sun/security/ssl/ClientKeyExchange.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "ClientKeyExchange" handshake message.
|
||||
*/
|
||||
final class ClientKeyExchange {
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new ClientKeyExchangeConsumer();
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new ClientKeyExchangeProducer();
|
||||
|
||||
|
||||
/**
|
||||
* The "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class ClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private ClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke != null) {
|
||||
for (Map.Entry<Byte, HandshakeProducer> hp :
|
||||
ke.getHandshakeProducers(chc)) {
|
||||
if (hp.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
|
||||
return hp.getValue().produce(context, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not consumer defined.
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ClientKeyExchange handshake message.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class ClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private ClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
// clean up this consumer
|
||||
shc.handshakeConsumers.remove(SSLHandshake.CLIENT_KEY_EXCHANGE.id);
|
||||
|
||||
// Check for an unprocessed client Certificate message. If that
|
||||
// handshake consumer is still present then that expected message
|
||||
// was not sent.
|
||||
if (shc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE.id)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ClientKeyExchange handshake message.");
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke != null) {
|
||||
for (Map.Entry<Byte, SSLConsumer> hc :
|
||||
ke.getHandshakeConsumers(shc)) {
|
||||
if (hc.getKey() == SSLHandshake.CLIENT_KEY_EXCHANGE.id) {
|
||||
hc.getValue().consume(context, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not consumer defined.
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ClientKeyExchange handshake message.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
jdkSrc/jdk8/sun/security/ssl/ConnectionContext.java
Normal file
34
jdkSrc/jdk8/sun/security/ssl/ConnectionContext.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
/**
|
||||
* SSL/TLS connection context.
|
||||
*/
|
||||
interface ConnectionContext {
|
||||
// blank
|
||||
}
|
||||
|
||||
73
jdkSrc/jdk8/sun/security/ssl/ContentType.java
Normal file
73
jdkSrc/jdk8/sun/security/ssl/ContentType.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
/**
|
||||
* Enum for SSL/TLS content types.
|
||||
*/
|
||||
enum ContentType {
|
||||
INVALID ((byte)0, "invalid",
|
||||
ProtocolVersion.PROTOCOLS_OF_13),
|
||||
CHANGE_CIPHER_SPEC ((byte)20, "change_cipher_spec",
|
||||
ProtocolVersion.PROTOCOLS_TO_12),
|
||||
ALERT ((byte)21, "alert",
|
||||
ProtocolVersion.PROTOCOLS_TO_13),
|
||||
HANDSHAKE ((byte)22, "handshake",
|
||||
ProtocolVersion.PROTOCOLS_TO_13),
|
||||
APPLICATION_DATA ((byte)23, "application_data",
|
||||
ProtocolVersion.PROTOCOLS_TO_13);
|
||||
|
||||
final byte id;
|
||||
final String name;
|
||||
final ProtocolVersion[] supportedProtocols;
|
||||
|
||||
private ContentType(byte id, String name,
|
||||
ProtocolVersion[] supportedProtocols) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
}
|
||||
|
||||
static ContentType valueOf(byte id) {
|
||||
for (ContentType ct : ContentType.values()) {
|
||||
if (ct.id == id) {
|
||||
return ct;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(byte id) {
|
||||
for (ContentType ct : ContentType.values()) {
|
||||
if (ct.id == id) {
|
||||
return ct.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "<UNKNOWN CONTENT TYPE: " + (id & 0x0FF) + ">";
|
||||
}
|
||||
}
|
||||
315
jdkSrc/jdk8/sun/security/ssl/CookieExtension.java
Normal file
315
jdkSrc/jdk8/sun/security/ssl/CookieExtension.java
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
|
||||
import sun.security.ssl.ClientHello.ClientHelloMessage;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.ServerHello.ServerHelloMessage;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
public class CookieExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHCookieProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHCookieConsumer();
|
||||
static final HandshakeConsumer chOnTradeConsumer =
|
||||
new CHCookieUpdate();
|
||||
|
||||
static final HandshakeProducer hrrNetworkProducer =
|
||||
new HRRCookieProducer();
|
||||
static final ExtensionConsumer hrrOnLoadConsumer =
|
||||
new HRRCookieConsumer();
|
||||
|
||||
static final HandshakeProducer hrrNetworkReproducer =
|
||||
new HRRCookieReproducer();
|
||||
|
||||
static final CookieStringizer cookieStringizer =
|
||||
new CookieStringizer();
|
||||
|
||||
/**
|
||||
* The "cookie" extension.
|
||||
*/
|
||||
static class CookieSpec implements SSLExtensionSpec {
|
||||
final byte[] cookie;
|
||||
|
||||
private CookieSpec(ByteBuffer m) throws IOException {
|
||||
// opaque cookie<1..2^16-1>;
|
||||
if (m.remaining() < 3) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid cookie extension: insufficient data");
|
||||
}
|
||||
|
||||
this.cookie = Record.getBytes16(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"cookie\": '{'\n" +
|
||||
"{0}\n" +
|
||||
"'}',", Locale.ENGLISH);
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(hexEncoder.encode(cookie))
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CookieStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new CookieSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHCookieProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCookieProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext) context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable cookie extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// response to an HelloRetryRequest cookie
|
||||
CookieSpec spec = (CookieSpec)chc.handshakeExtensions.get(
|
||||
SSLExtension.HRR_COOKIE);
|
||||
|
||||
if (spec != null &&
|
||||
spec.cookie != null && spec.cookie.length != 0) {
|
||||
byte[] extData = new byte[spec.cookie.length + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putBytes16(m, spec.cookie);
|
||||
return extData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHCookieConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCookieConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable cookie extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
CookieSpec spec;
|
||||
try {
|
||||
spec = new CookieSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
shc.handshakeExtensions.put(SSLExtension.CH_COOKIE, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
//
|
||||
// Note that the protocol version negotiation happens before the
|
||||
// session resumption negotiation. And the session resumption
|
||||
// negotiation depends on the negotiated protocol version.
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHCookieUpdate implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHCookieUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||
|
||||
CookieSpec spec = (CookieSpec)
|
||||
shc.handshakeExtensions.get(SSLExtension.CH_COOKIE);
|
||||
if (spec == null) {
|
||||
// Ignore, no "cookie" extension requested.
|
||||
return;
|
||||
}
|
||||
|
||||
HelloCookieManager hcm =
|
||||
shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
|
||||
if (!hcm.isCookieValid(shc, clientHello, spec.cookie)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"unrecognized cookie");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class HRRCookieProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRCookieProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
ServerHelloMessage hrrm = (ServerHelloMessage)message;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable cookie extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
HelloCookieManager hcm =
|
||||
shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
|
||||
|
||||
byte[] cookie = hcm.createCookie(shc, hrrm.clientHello);
|
||||
|
||||
byte[] extData = new byte[cookie.length + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putBytes16(m, cookie);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class HRRCookieConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRCookieConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable cookie extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
CookieSpec spec;
|
||||
try {
|
||||
spec = new CookieSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
chc.handshakeExtensions.put(SSLExtension.HRR_COOKIE, spec);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class HRRCookieReproducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRCookieReproducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext) context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable cookie extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// copy of the ClientHello cookie
|
||||
CookieSpec spec = (CookieSpec)shc.handshakeExtensions.get(
|
||||
SSLExtension.CH_COOKIE);
|
||||
|
||||
if (spec != null &&
|
||||
spec.cookie != null && spec.cookie.length != 0) {
|
||||
byte[] extData = new byte[spec.cookie.length + 2];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putBytes16(m, spec.cookie);
|
||||
return extData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
321
jdkSrc/jdk8/sun/security/ssl/DHClientKeyExchange.java
Normal file
321
jdkSrc/jdk8/sun/security/ssl/DHClientKeyExchange.java
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import javax.crypto.spec.DHPublicKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the "ClientKeyExchange" handshake message.
|
||||
*/
|
||||
final class DHClientKeyExchange {
|
||||
static final DHClientKeyExchangeConsumer dhHandshakeConsumer =
|
||||
new DHClientKeyExchangeConsumer();
|
||||
static final DHClientKeyExchangeProducer dhHandshakeProducer =
|
||||
new DHClientKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The DiffieHellman ClientKeyExchange handshake message.
|
||||
*
|
||||
* If the client has sent a certificate which contains a suitable
|
||||
* DiffieHellman key (for fixed_dh client authentication), then the
|
||||
* client public value is implicit and does not need to be sent again.
|
||||
* In this case, the client key exchange message will be sent, but it
|
||||
* MUST be empty.
|
||||
*
|
||||
* Currently, we don't support cipher suite that requires implicit public
|
||||
* key of client.
|
||||
*/
|
||||
private static final
|
||||
class DHClientKeyExchangeMessage extends HandshakeMessage {
|
||||
private byte[] y; // 1 to 2^16 - 1 bytes
|
||||
|
||||
DHClientKeyExchangeMessage(
|
||||
HandshakeContext handshakeContext) throws IOException {
|
||||
super(handshakeContext);
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc =
|
||||
(ClientHandshakeContext)handshakeContext;
|
||||
|
||||
DHEPossession dhePossession = null;
|
||||
for (SSLPossession possession : chc.handshakePossessions) {
|
||||
if (possession instanceof DHEPossession) {
|
||||
dhePossession = (DHEPossession)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dhePossession == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No DHE credentials negotiated for client key exchange");
|
||||
}
|
||||
|
||||
DHPublicKey publicKey = dhePossession.publicKey;
|
||||
DHParameterSpec params = publicKey.getParams();
|
||||
this.y = Utilities.toByteArray(publicKey.getY());
|
||||
}
|
||||
|
||||
DHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
// This happens in server side only.
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)handshakeContext;
|
||||
|
||||
if (m.remaining() < 3) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid DH ClientKeyExchange message: insufficient data");
|
||||
}
|
||||
|
||||
this.y = Record.getBytes16(m);
|
||||
|
||||
if (m.hasRemaining()) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid DH ClientKeyExchange message: unknown extra data");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
return y.length + 2; // 2: length filed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putBytes16(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"DH ClientKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"dh_Yc\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(y), " "),
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DiffieHellman "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class DHClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private DHClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
DHECredentials dheCredentials = null;
|
||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||
if (cd instanceof DHECredentials) {
|
||||
dheCredentials = (DHECredentials)cd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dheCredentials == null) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No DHE credentials negotiated for client key exchange");
|
||||
}
|
||||
|
||||
|
||||
DHEPossession dhePossession = new DHEPossession(
|
||||
dheCredentials, chc.sslContext.getSecureRandom());
|
||||
chc.handshakePossessions.add(dhePossession);
|
||||
DHClientKeyExchangeMessage ckem =
|
||||
new DHClientKeyExchangeMessage(chc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced DH ClientKeyExchange handshake message", ckem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
ckem.write(chc.handshakeOutput);
|
||||
chc.handshakeOutput.flush();
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
chc.negotiatedProtocol);
|
||||
} else {
|
||||
chc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(chc, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DiffieHellman "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class DHClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private DHClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
DHEPossession dhePossession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof DHEPossession) {
|
||||
dhePossession = (DHEPossession)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dhePossession == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No expected DHE possessions for client key exchange");
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
}
|
||||
|
||||
DHClientKeyExchangeMessage ckem =
|
||||
new DHClientKeyExchangeMessage(shc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming DH ClientKeyExchange handshake message", ckem);
|
||||
}
|
||||
|
||||
// create the credentials
|
||||
try {
|
||||
DHParameterSpec params = dhePossession.publicKey.getParams();
|
||||
DHPublicKeySpec spec = new DHPublicKeySpec(
|
||||
new BigInteger(1, ckem.y),
|
||||
params.getP(), params.getG());
|
||||
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
|
||||
DHPublicKey peerPublicKey =
|
||||
(DHPublicKey)kf.generatePublic(spec);
|
||||
|
||||
// check constraints of peer DHPublicKey
|
||||
if (!shc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
peerPublicKey)) {
|
||||
throw new SSLHandshakeException(
|
||||
"DHPublicKey does not comply to algorithm constraints");
|
||||
}
|
||||
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
||||
shc.handshakeCredentials.add(
|
||||
new DHECredentials(peerPublicKey, namedGroup));
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException)(new SSLHandshakeException(
|
||||
"Could not generate DHPublicKey").initCause(e));
|
||||
}
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " + shc.negotiatedProtocol);
|
||||
} else {
|
||||
shc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(shc, masterSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
534
jdkSrc/jdk8/sun/security/ssl/DHKeyExchange.java
Normal file
534
jdkSrc/jdk8/sun/security/ssl/DHKeyExchange.java
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import javax.crypto.spec.DHPublicKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
final class DHKeyExchange {
|
||||
static final SSLPossessionGenerator poGenerator =
|
||||
new DHEPossessionGenerator(false);
|
||||
static final SSLPossessionGenerator poExportableGenerator =
|
||||
new DHEPossessionGenerator(true);
|
||||
static final SSLKeyAgreementGenerator kaGenerator =
|
||||
new DHEKAGenerator();
|
||||
|
||||
static final class DHECredentials implements SSLCredentials {
|
||||
final DHPublicKey popPublicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
||||
DHECredentials(DHPublicKey popPublicKey, NamedGroup namedGroup) {
|
||||
this.popPublicKey = popPublicKey;
|
||||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
static DHECredentials valueOf(NamedGroup ng,
|
||||
byte[] encodedPublic) throws IOException, GeneralSecurityException {
|
||||
|
||||
if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||
throw new RuntimeException(
|
||||
"Credentials decoding: Not FFDHE named group");
|
||||
}
|
||||
|
||||
if (encodedPublic == null || encodedPublic.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DHParameterSpec params = (DHParameterSpec)ng.getParameterSpec();
|
||||
if (params == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
|
||||
DHPublicKeySpec spec = new DHPublicKeySpec(
|
||||
new BigInteger(1, encodedPublic),
|
||||
params.getP(), params.getG());
|
||||
DHPublicKey publicKey =
|
||||
(DHPublicKey)kf.generatePublic(spec);
|
||||
|
||||
return new DHECredentials(publicKey, ng);
|
||||
}
|
||||
}
|
||||
|
||||
static final class DHEPossession implements SSLPossession {
|
||||
final PrivateKey privateKey;
|
||||
final DHPublicKey publicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
||||
DHEPossession(NamedGroup namedGroup, SecureRandom random) {
|
||||
try {
|
||||
KeyPairGenerator kpg =
|
||||
JsseJce.getKeyPairGenerator("DiffieHellman");
|
||||
DHParameterSpec params =
|
||||
(DHParameterSpec)namedGroup.getParameterSpec();
|
||||
kpg.initialize(params, random);
|
||||
KeyPair kp = generateDHKeyPair(kpg);
|
||||
if (kp == null) {
|
||||
throw new RuntimeException("Could not generate DH keypair");
|
||||
}
|
||||
privateKey = kp.getPrivate();
|
||||
publicKey = (DHPublicKey)kp.getPublic();
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate DH keypair", gse);
|
||||
}
|
||||
|
||||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
DHEPossession(int keyLength, SecureRandom random) {
|
||||
DHParameterSpec params =
|
||||
PredefinedDHParameterSpecs.definedParams.get(keyLength);
|
||||
try {
|
||||
KeyPairGenerator kpg =
|
||||
JsseJce.getKeyPairGenerator("DiffieHellman");
|
||||
if (params != null) {
|
||||
kpg.initialize(params, random);
|
||||
} else {
|
||||
kpg.initialize(keyLength, random);
|
||||
}
|
||||
|
||||
KeyPair kp = generateDHKeyPair(kpg);
|
||||
if (kp == null) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate DH keypair of " +
|
||||
keyLength + " bits");
|
||||
}
|
||||
privateKey = kp.getPrivate();
|
||||
publicKey = (DHPublicKey)kp.getPublic();
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate DH keypair", gse);
|
||||
}
|
||||
|
||||
this.namedGroup = NamedGroup.valueOf(publicKey.getParams());
|
||||
}
|
||||
|
||||
DHEPossession(DHECredentials credentials, SecureRandom random) {
|
||||
try {
|
||||
KeyPairGenerator kpg =
|
||||
JsseJce.getKeyPairGenerator("DiffieHellman");
|
||||
kpg.initialize(credentials.popPublicKey.getParams(), random);
|
||||
KeyPair kp = generateDHKeyPair(kpg);
|
||||
if (kp == null) {
|
||||
throw new RuntimeException("Could not generate DH keypair");
|
||||
}
|
||||
privateKey = kp.getPrivate();
|
||||
publicKey = (DHPublicKey)kp.getPublic();
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate DH keypair", gse);
|
||||
}
|
||||
|
||||
this.namedGroup = credentials.namedGroup;
|
||||
}
|
||||
|
||||
// Generate and validate DHPublicKeySpec
|
||||
private KeyPair generateDHKeyPair(
|
||||
KeyPairGenerator kpg) throws GeneralSecurityException {
|
||||
boolean doExtraValiadtion =
|
||||
(!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName()));
|
||||
boolean isRecovering = false;
|
||||
for (int i = 0; i <= 2; i++) { // Try to recover from failure.
|
||||
KeyPair kp = kpg.generateKeyPair();
|
||||
// validate the Diffie-Hellman public key
|
||||
if (doExtraValiadtion) {
|
||||
DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic());
|
||||
try {
|
||||
KeyUtil.validate(spec);
|
||||
} catch (InvalidKeyException ivke) {
|
||||
if (isRecovering) {
|
||||
throw ivke;
|
||||
}
|
||||
// otherwise, ignore the exception and try again
|
||||
isRecovering = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) {
|
||||
if (key instanceof DHPublicKey) {
|
||||
DHPublicKey dhKey = (DHPublicKey)key;
|
||||
DHParameterSpec params = dhKey.getParams();
|
||||
return new DHPublicKeySpec(dhKey.getY(),
|
||||
params.getP(), params.getG());
|
||||
}
|
||||
try {
|
||||
KeyFactory factory = JsseJce.getKeyFactory("DiffieHellman");
|
||||
return factory.getKeySpec(key, DHPublicKeySpec.class);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
// unlikely
|
||||
throw new RuntimeException("Unable to get DHPublicKeySpec", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encode() {
|
||||
// Note: the DH public value is encoded as a big-endian integer
|
||||
// and padded to the left with zeros to the size of p in bytes.
|
||||
byte[] encoded = Utilities.toByteArray(publicKey.getY());
|
||||
int pSize = (KeyUtil.getKeySize(publicKey) + 7) >>> 3;
|
||||
if (pSize > 0 && encoded.length < pSize) {
|
||||
byte[] buffer = new byte[pSize];
|
||||
System.arraycopy(encoded, 0,
|
||||
buffer, pSize - encoded.length, encoded.length);
|
||||
encoded = buffer;
|
||||
}
|
||||
|
||||
return encoded;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class
|
||||
DHEPossessionGenerator implements SSLPossessionGenerator {
|
||||
// Flag to use smart ephemeral DH key which size matches the
|
||||
// corresponding authentication key
|
||||
private static final boolean useSmartEphemeralDHKeys;
|
||||
|
||||
// Flag to use legacy ephemeral DH key which size is 512 bits for
|
||||
// exportable cipher suites, and 768 bits for others
|
||||
private static final boolean useLegacyEphemeralDHKeys;
|
||||
|
||||
// The customized ephemeral DH key size for non-exportable
|
||||
// cipher suites.
|
||||
private static final int customizedDHKeySize;
|
||||
|
||||
// Is it for exportable cipher suite?
|
||||
private final boolean exportable;
|
||||
|
||||
static {
|
||||
String property = GetPropertyAction.privilegedGetProperty(
|
||||
"jdk.tls.ephemeralDHKeySize");
|
||||
if (property == null || property.isEmpty()) {
|
||||
useLegacyEphemeralDHKeys = false;
|
||||
useSmartEphemeralDHKeys = false;
|
||||
customizedDHKeySize = -1;
|
||||
} else if ("matched".equals(property)) {
|
||||
useLegacyEphemeralDHKeys = false;
|
||||
useSmartEphemeralDHKeys = true;
|
||||
customizedDHKeySize = -1;
|
||||
} else if ("legacy".equals(property)) {
|
||||
useLegacyEphemeralDHKeys = true;
|
||||
useSmartEphemeralDHKeys = false;
|
||||
customizedDHKeySize = -1;
|
||||
} else {
|
||||
useLegacyEphemeralDHKeys = false;
|
||||
useSmartEphemeralDHKeys = false;
|
||||
|
||||
try {
|
||||
// DH parameter generation can be extremely slow, best to
|
||||
// use one of the supported pre-computed DH parameters
|
||||
// (see DHCrypt class).
|
||||
customizedDHKeySize = Integer.parseUnsignedInt(property);
|
||||
if (customizedDHKeySize < 1024 ||
|
||||
customizedDHKeySize > 8192 ||
|
||||
(customizedDHKeySize & 0x3f) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported customized DH key size: " +
|
||||
customizedDHKeySize + ". " +
|
||||
"The key size must be multiple of 64, " +
|
||||
"and range from 1024 to 8192 (inclusive)");
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid system property jdk.tls.ephemeralDHKeySize");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private DHEPossessionGenerator(boolean exportable) {
|
||||
this.exportable = exportable;
|
||||
}
|
||||
|
||||
// Used for ServerKeyExchange, TLS 1.2 and prior versions.
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext context) {
|
||||
NamedGroup preferableNamedGroup = null;
|
||||
if (!useLegacyEphemeralDHKeys &&
|
||||
(context.clientRequestedNamedGroups != null) &&
|
||||
(!context.clientRequestedNamedGroups.isEmpty())) {
|
||||
preferableNamedGroup =
|
||||
SupportedGroups.getPreferredGroup(
|
||||
context.negotiatedProtocol,
|
||||
context.algorithmConstraints,
|
||||
NamedGroupType.NAMED_GROUP_FFDHE,
|
||||
context.clientRequestedNamedGroups);
|
||||
if (preferableNamedGroup != null) {
|
||||
return new DHEPossession(preferableNamedGroup,
|
||||
context.sslContext.getSecureRandom());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 768 bits ephemeral DH private keys were used to be used in
|
||||
* ServerKeyExchange except that exportable ciphers max out at 512
|
||||
* bits modulus values. We still adhere to this behavior in legacy
|
||||
* mode (system property "jdk.tls.ephemeralDHKeySize" is defined
|
||||
* as "legacy").
|
||||
*
|
||||
* Old JDK (JDK 7 and previous) releases don't support DH keys
|
||||
* bigger than 1024 bits. We have to consider the compatibility
|
||||
* requirement. 1024 bits DH key is always used for non-exportable
|
||||
* cipher suites in default mode (system property
|
||||
* "jdk.tls.ephemeralDHKeySize" is not defined).
|
||||
*
|
||||
* However, if applications want more stronger strength, setting
|
||||
* system property "jdk.tls.ephemeralDHKeySize" to "matched"
|
||||
* is a workaround to use ephemeral DH key which size matches the
|
||||
* corresponding authentication key. For example, if the public key
|
||||
* size of an authentication certificate is 2048 bits, then the
|
||||
* ephemeral DH key size should be 2048 bits accordingly unless
|
||||
* the cipher suite is exportable. This key sizing scheme keeps
|
||||
* the cryptographic strength consistent between authentication
|
||||
* keys and key-exchange keys.
|
||||
*
|
||||
* Applications may also want to customize the ephemeral DH key
|
||||
* size to a fixed length for non-exportable cipher suites. This
|
||||
* can be approached by setting system property
|
||||
* "jdk.tls.ephemeralDHKeySize" to a valid positive integer between
|
||||
* 1024 and 8192 bits, inclusive.
|
||||
*
|
||||
* Note that the minimum acceptable key size is 1024 bits except
|
||||
* exportable cipher suites or legacy mode.
|
||||
*
|
||||
* Note that per RFC 2246, the key size limit of DH is 512 bits for
|
||||
* exportable cipher suites. Because of the weakness, exportable
|
||||
* cipher suites are deprecated since TLS v1.1 and they are not
|
||||
* enabled by default in Oracle provider. The legacy behavior is
|
||||
* reserved and 512 bits DH key is always used for exportable
|
||||
* cipher suites.
|
||||
*/
|
||||
int keySize = exportable ? 512 : 1024; // default mode
|
||||
if (!exportable) {
|
||||
if (useLegacyEphemeralDHKeys) { // legacy mode
|
||||
keySize = 768;
|
||||
} else if (useSmartEphemeralDHKeys) { // matched mode
|
||||
PrivateKey key = null;
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)context;
|
||||
if (shc.interimAuthn instanceof X509Possession) {
|
||||
key = ((X509Possession)shc.interimAuthn).popPrivateKey;
|
||||
}
|
||||
|
||||
if (key != null) {
|
||||
int ks = KeyUtil.getKeySize(key);
|
||||
|
||||
// DH parameter generation can be extremely slow, make
|
||||
// sure to use one of the supported pre-computed DH
|
||||
// parameters.
|
||||
//
|
||||
// Old deployed applications may not be ready to
|
||||
// support DH key sizes bigger than 2048 bits. Please
|
||||
// DON'T use value other than 1024 and 2048 at present.
|
||||
// May improve the underlying providers and key size
|
||||
// limit in the future when the compatibility and
|
||||
// interoperability impact is limited.
|
||||
keySize = ks <= 1024 ? 1024 : 2048;
|
||||
} // Otherwise, anonymous cipher suites, 1024-bit is used.
|
||||
} else if (customizedDHKeySize > 0) { // customized mode
|
||||
keySize = customizedDHKeySize;
|
||||
}
|
||||
}
|
||||
|
||||
return new DHEPossession(
|
||||
keySize, context.sslContext.getSecureRandom());
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class DHEKAGenerator implements SSLKeyAgreementGenerator {
|
||||
static private DHEKAGenerator instance = new DHEKAGenerator();
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private DHEKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
DHEPossession dhePossession = null;
|
||||
DHECredentials dheCredentials = null;
|
||||
for (SSLPossession poss : context.handshakePossessions) {
|
||||
if (!(poss instanceof DHEPossession)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DHEPossession dhep = (DHEPossession)poss;
|
||||
for (SSLCredentials cred : context.handshakeCredentials) {
|
||||
if (!(cred instanceof DHECredentials)) {
|
||||
continue;
|
||||
}
|
||||
DHECredentials dhec = (DHECredentials)cred;
|
||||
if (dhep.namedGroup != null && dhec.namedGroup != null) {
|
||||
if (dhep.namedGroup.equals(dhec.namedGroup)) {
|
||||
dheCredentials = (DHECredentials)cred;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
DHParameterSpec pps = dhep.publicKey.getParams();
|
||||
DHParameterSpec cps = dhec.popPublicKey.getParams();
|
||||
if (pps.getP().equals(cps.getP()) &&
|
||||
pps.getG().equals(cps.getG())) {
|
||||
dheCredentials = (DHECredentials)cred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dheCredentials != null) {
|
||||
dhePossession = (DHEPossession)poss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dhePossession == null || dheCredentials == null) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient DHE key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new DHEKAKeyDerivation(context,
|
||||
dhePossession.privateKey, dheCredentials.popPublicKey);
|
||||
}
|
||||
|
||||
private static final
|
||||
class DHEKAKeyDerivation implements SSLKeyDerivation {
|
||||
private final HandshakeContext context;
|
||||
private final PrivateKey localPrivateKey;
|
||||
private final PublicKey peerPublicKey;
|
||||
|
||||
DHEKAKeyDerivation(HandshakeContext context,
|
||||
PrivateKey localPrivateKey,
|
||||
PublicKey peerPublicKey) {
|
||||
this.context = context;
|
||||
this.localPrivateKey = localPrivateKey;
|
||||
this.peerPublicKey = peerPublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return t12DeriveKey(algorithm, params);
|
||||
} else {
|
||||
return t13DeriveKey(algorithm, params);
|
||||
}
|
||||
}
|
||||
|
||||
private SecretKey t12DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey preMasterSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
"No expected master key derivation for protocol: " +
|
||||
context.negotiatedProtocol.name);
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
private SecretKey t13DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey sharedSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
|
||||
HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
|
||||
SSLKeyDerivation kd = context.handshakeKeyDerivation;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
if (kd == null) { // No PSK is in use.
|
||||
// If PSK is not in use Early Secret will still be
|
||||
// HKDF-Extract(0, 0).
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec ikm =
|
||||
new SecretKeySpec(zeros, "TlsPreSharedSecret");
|
||||
SecretKey earlySecret =
|
||||
hkdf.extract(zeros, ikm, "TlsEarlySecret");
|
||||
kd = new SSLSecretDerivation(context, earlySecret);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
|
||||
// derive handshake secret
|
||||
return hkdf.extract(saltSecret, sharedSecret, algorithm);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
557
jdkSrc/jdk8/sun/security/ssl/DHServerKeyExchange.java
Normal file
557
jdkSrc/jdk8/sun/security/ssl/DHServerKeyExchange.java
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import javax.crypto.spec.DHPublicKeySpec;
|
||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
/**
|
||||
* Pack of the ServerKeyExchange handshake message.
|
||||
*/
|
||||
final class DHServerKeyExchange {
|
||||
static final SSLConsumer dhHandshakeConsumer =
|
||||
new DHServerKeyExchangeConsumer();
|
||||
static final HandshakeProducer dhHandshakeProducer =
|
||||
new DHServerKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The DiffieHellman ServerKeyExchange handshake message.
|
||||
*/
|
||||
private static final
|
||||
class DHServerKeyExchangeMessage extends HandshakeMessage {
|
||||
// public key encapsulated in this message
|
||||
private final byte[] p; // 1 to 2^16 - 1 bytes
|
||||
private final byte[] g; // 1 to 2^16 - 1 bytes
|
||||
private final byte[] y; // 1 to 2^16 - 1 bytes
|
||||
|
||||
// the signature algorithm used by this ServerKeyExchange message
|
||||
private final boolean useExplicitSigAlgorithm;
|
||||
private final SignatureScheme signatureScheme;
|
||||
|
||||
// signature bytes, or null if anonymous
|
||||
private final byte[] paramsSignature;
|
||||
|
||||
DHServerKeyExchangeMessage(
|
||||
HandshakeContext handshakeContext) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in server side only.
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)handshakeContext;
|
||||
|
||||
DHEPossession dhePossession = null;
|
||||
X509Possession x509Possession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof DHEPossession) {
|
||||
dhePossession = (DHEPossession)possession;
|
||||
if (x509Possession != null) {
|
||||
break;
|
||||
}
|
||||
} else if (possession instanceof X509Possession) {
|
||||
x509Possession = (X509Possession)possession;
|
||||
if (dhePossession != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dhePossession == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No DHE credentials negotiated for server key exchange");
|
||||
}
|
||||
DHPublicKey publicKey = dhePossession.publicKey;
|
||||
DHParameterSpec params = publicKey.getParams();
|
||||
this.p = Utilities.toByteArray(params.getP());
|
||||
this.g = Utilities.toByteArray(params.getG());
|
||||
this.y = Utilities.toByteArray(publicKey.getY());
|
||||
|
||||
if (x509Possession == null) {
|
||||
// anonymous, no authentication, no signature
|
||||
paramsSignature = null;
|
||||
signatureScheme = null;
|
||||
useExplicitSigAlgorithm = false;
|
||||
} else {
|
||||
useExplicitSigAlgorithm =
|
||||
shc.negotiatedProtocol.useTLS12PlusSpec();
|
||||
Signature signer = null;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
|
||||
SignatureScheme.getSignerOfPreferableAlgorithm(
|
||||
shc.peerRequestedSignatureSchemes,
|
||||
x509Possession,
|
||||
shc.negotiatedProtocol);
|
||||
if (schemeAndSigner == null) {
|
||||
// Unlikely, the credentials generator should have
|
||||
// selected the preferable signature algorithm properly.
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No supported signature algorithm for " +
|
||||
x509Possession.popPrivateKey.getAlgorithm() +
|
||||
" key");
|
||||
} else {
|
||||
signatureScheme = schemeAndSigner.getKey();
|
||||
signer = schemeAndSigner.getValue();
|
||||
}
|
||||
} else {
|
||||
signatureScheme = null;
|
||||
try {
|
||||
signer = getSignature(
|
||||
x509Possession.popPrivateKey.getAlgorithm(),
|
||||
x509Possession.popPrivateKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
x509Possession.popPrivateKey.getAlgorithm(), e);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] signature = null;
|
||||
try {
|
||||
updateSignature(signer, shc.clientHelloRandom.randomBytes,
|
||||
shc.serverHelloRandom.randomBytes);
|
||||
signature = signer.sign();
|
||||
} catch (SignatureException ex) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failed to sign dhe parameters: " +
|
||||
x509Possession.popPrivateKey.getAlgorithm(), ex);
|
||||
}
|
||||
paramsSignature = signature;
|
||||
}
|
||||
}
|
||||
|
||||
DHServerKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc =
|
||||
(ClientHandshakeContext)handshakeContext;
|
||||
|
||||
this.p = Record.getBytes16(m);
|
||||
this.g = Record.getBytes16(m);
|
||||
this.y = Record.getBytes16(m);
|
||||
|
||||
try {
|
||||
KeyUtil.validate(new DHPublicKeySpec(
|
||||
new BigInteger(1, y),
|
||||
new BigInteger(1, p),
|
||||
new BigInteger(1, p)));
|
||||
} catch (InvalidKeyException ike) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid DH ServerKeyExchange: invalid parameters", ike);
|
||||
}
|
||||
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||
if (cd instanceof X509Credentials) {
|
||||
x509Credentials = (X509Credentials)cd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Credentials == null) {
|
||||
// anonymous, no authentication, no signature
|
||||
if (m.hasRemaining()) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid DH ServerKeyExchange: unknown extra data");
|
||||
}
|
||||
|
||||
this.signatureScheme = null;
|
||||
this.paramsSignature = null;
|
||||
this.useExplicitSigAlgorithm = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.useExplicitSigAlgorithm =
|
||||
chc.negotiatedProtocol.useTLS12PlusSpec();
|
||||
if (useExplicitSigAlgorithm) {
|
||||
int ssid = Record.getInt16(m);
|
||||
signatureScheme = SignatureScheme.valueOf(ssid);
|
||||
if (signatureScheme == null) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid signature algorithm (" + ssid +
|
||||
") used in DH ServerKeyExchange handshake message");
|
||||
}
|
||||
|
||||
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unsupported signature algorithm (" +
|
||||
signatureScheme.name +
|
||||
") used in DH ServerKeyExchange handshake message");
|
||||
}
|
||||
} else {
|
||||
this.signatureScheme = null;
|
||||
}
|
||||
|
||||
// read and verify the signature
|
||||
this.paramsSignature = Record.getBytes16(m);
|
||||
Signature signer;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
try {
|
||||
signer = signatureScheme.getVerifier(
|
||||
x509Credentials.popPublicKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException |
|
||||
InvalidAlgorithmParameterException nsae) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
signatureScheme.name, nsae);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
signer = getSignature(
|
||||
x509Credentials.popPublicKey.getAlgorithm(),
|
||||
x509Credentials.popPublicKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
x509Credentials.popPublicKey.getAlgorithm(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
updateSignature(signer,
|
||||
chc.clientHelloRandom.randomBytes,
|
||||
chc.serverHelloRandom.randomBytes);
|
||||
|
||||
if (!signer.verify(paramsSignature)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid signature on DH ServerKeyExchange message");
|
||||
}
|
||||
} catch (SignatureException ex) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Cannot verify DH ServerKeyExchange signature", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.SERVER_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
int sigLen = 0;
|
||||
if (paramsSignature != null) {
|
||||
sigLen = 2 + paramsSignature.length;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
sigLen += SignatureScheme.sizeInRecord();
|
||||
}
|
||||
}
|
||||
|
||||
return 6 + p.length + g.length + y.length + sigLen;
|
||||
// 6: overhead for p, g, y values
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putBytes16(p);
|
||||
hos.putBytes16(g);
|
||||
hos.putBytes16(y);
|
||||
|
||||
if (paramsSignature != null) {
|
||||
if (useExplicitSigAlgorithm) {
|
||||
hos.putInt16(signatureScheme.id);
|
||||
}
|
||||
|
||||
hos.putBytes16(paramsSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (paramsSignature == null) { // anonymous
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"DH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"dh_p\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_g\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_Ys\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(p), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(g), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(y), " "),
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
|
||||
if (useExplicitSigAlgorithm) {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"DH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"dh_p\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_g\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_Ys\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}',\n" +
|
||||
" '}',\n" +
|
||||
" \"digital signature\": '{'\n" +
|
||||
" \"signature algorithm\": \"{3}\"\n" +
|
||||
" \"signature\": '{'\n" +
|
||||
"{4}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(p), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(g), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(y), " "),
|
||||
signatureScheme.name,
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(paramsSignature), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
} else {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"DH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"dh_p\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_g\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" \"dh_Ys\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}',\n" +
|
||||
" '}',\n" +
|
||||
" \"signature\": '{'\n" +
|
||||
"{3}\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(p), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(g), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(y), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(paramsSignature), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static Signature getSignature(String keyAlgorithm,
|
||||
Key key) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
Signature signer = null;
|
||||
switch (keyAlgorithm) {
|
||||
case "DSA":
|
||||
signer = JsseJce.getSignature(JsseJce.SIGNATURE_DSA);
|
||||
break;
|
||||
case "RSA":
|
||||
signer = RSASignature.getInstance();
|
||||
break;
|
||||
default:
|
||||
throw new NoSuchAlgorithmException(
|
||||
"neither an RSA or a DSA key : " + keyAlgorithm);
|
||||
}
|
||||
|
||||
if (signer != null) {
|
||||
if (key instanceof PublicKey) {
|
||||
signer.initVerify((PublicKey)(key));
|
||||
} else {
|
||||
signer.initSign((PrivateKey)key);
|
||||
}
|
||||
}
|
||||
|
||||
return signer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update sig with nonces and Diffie-Hellman public key.
|
||||
*/
|
||||
private void updateSignature(Signature sig, byte[] clntNonce,
|
||||
byte[] svrNonce) throws SignatureException {
|
||||
int tmp;
|
||||
|
||||
sig.update(clntNonce);
|
||||
sig.update(svrNonce);
|
||||
|
||||
sig.update((byte)(p.length >> 8));
|
||||
sig.update((byte)(p.length & 0x0ff));
|
||||
sig.update(p);
|
||||
|
||||
sig.update((byte)(g.length >> 8));
|
||||
sig.update((byte)(g.length & 0x0ff));
|
||||
sig.update(g);
|
||||
|
||||
sig.update((byte)(y.length >> 8));
|
||||
sig.update((byte)(y.length & 0x0ff));
|
||||
sig.update(y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DiffieHellman "ServerKeyExchange" handshake message producer.
|
||||
*/
|
||||
static final class DHServerKeyExchangeProducer
|
||||
implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private DHServerKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
DHServerKeyExchangeMessage skem =
|
||||
new DHServerKeyExchangeMessage(shc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced DH ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
skem.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The DiffieHellman "ServerKeyExchange" handshake message consumer.
|
||||
*/
|
||||
static final class DHServerKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private DHServerKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
DHServerKeyExchangeMessage skem =
|
||||
new DHServerKeyExchangeMessage(chc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming DH ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
//
|
||||
// validate
|
||||
//
|
||||
// check constraints of EC PublicKey
|
||||
DHPublicKey publicKey;
|
||||
try {
|
||||
KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman");
|
||||
DHPublicKeySpec spec = new DHPublicKeySpec(
|
||||
new BigInteger(1, skem.y),
|
||||
new BigInteger(1, skem.p),
|
||||
new BigInteger(1, skem.g));
|
||||
publicKey = (DHPublicKey)kf.generatePublic(spec);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"Could not generate DHPublicKey", gse);
|
||||
}
|
||||
|
||||
if (!chc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"DH ServerKeyExchange does not comply to " +
|
||||
"algorithm constraints");
|
||||
}
|
||||
|
||||
//
|
||||
// update
|
||||
//
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams());
|
||||
chc.handshakeCredentials.add(
|
||||
new DHECredentials(publicKey, namedGroup));
|
||||
|
||||
//
|
||||
// produce
|
||||
//
|
||||
// Need no new handshake message producers here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
536
jdkSrc/jdk8/sun/security/ssl/ECDHClientKeyExchange.java
Normal file
536
jdkSrc/jdk8/sun/security/ssl/ECDHClientKeyExchange.java
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.AlgorithmConstraints;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the "ClientKeyExchange" handshake message.
|
||||
*/
|
||||
final class ECDHClientKeyExchange {
|
||||
static final SSLConsumer ecdhHandshakeConsumer =
|
||||
new ECDHClientKeyExchangeConsumer();
|
||||
static final HandshakeProducer ecdhHandshakeProducer =
|
||||
new ECDHClientKeyExchangeProducer();
|
||||
|
||||
static final SSLConsumer ecdheHandshakeConsumer =
|
||||
new ECDHEClientKeyExchangeConsumer();
|
||||
static final HandshakeProducer ecdheHandshakeProducer =
|
||||
new ECDHEClientKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The ECDH/ECDHE ClientKeyExchange handshake message.
|
||||
*/
|
||||
private static final
|
||||
class ECDHClientKeyExchangeMessage extends HandshakeMessage {
|
||||
private final byte[] encodedPoint;
|
||||
|
||||
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ECPublicKey publicKey) {
|
||||
super(handshakeContext);
|
||||
|
||||
ECPoint point = publicKey.getW();
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
encodedPoint = JsseJce.encodePoint(point, params.getCurve());
|
||||
}
|
||||
|
||||
ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
if (m.remaining() != 0) { // explicit PublicValueEncoding
|
||||
this.encodedPoint = Record.getBytes8(m);
|
||||
} else {
|
||||
this.encodedPoint = new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Check constraints of the specified EC public key.
|
||||
static void checkConstraints(AlgorithmConstraints constraints,
|
||||
ECPublicKey publicKey,
|
||||
byte[] encodedPoint) throws SSLHandshakeException {
|
||||
|
||||
try {
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(encodedPoint, params.getCurve());
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKey peerPublicKey =
|
||||
(ECPublicKey)kf.generatePublic(spec);
|
||||
|
||||
// check constraints of ECPublicKey
|
||||
if (constraints != null &&
|
||||
!constraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
peerPublicKey)) {
|
||||
throw new SSLHandshakeException(
|
||||
"ECPublicKey does not comply to algorithm constraints");
|
||||
}
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate ECPublicKey").initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
if (encodedPoint == null || encodedPoint.length == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1 + encodedPoint.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
if (encodedPoint != null && encodedPoint.length != 0) {
|
||||
hos.putBytes8(encodedPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"ECDH ClientKeyExchange\": '{'\n" +
|
||||
" \"ecdh public\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
if (encodedPoint == null || encodedPoint.length == 0) {
|
||||
Object[] messageFields = {
|
||||
" <implicit>"
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
} else {
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(encodedPoint), " "),
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDH "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLCredentials credential : chc.handshakeCredentials) {
|
||||
if (credential instanceof X509Credentials) {
|
||||
x509Credentials = (X509Credentials)credential;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Credentials == null) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No server certificate for ECDH client key exchange");
|
||||
}
|
||||
|
||||
PublicKey publicKey = x509Credentials.popPublicKey;
|
||||
if (!publicKey.getAlgorithm().equals("EC")) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Not EC server certificate for ECDH client key exchange");
|
||||
}
|
||||
|
||||
ECParameterSpec params = ((ECPublicKey)publicKey).getParams();
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
||||
if (namedGroup == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported EC server cert for ECDH client key exchange");
|
||||
}
|
||||
|
||||
ECDHEPossession ecdhePossession = new ECDHEPossession(
|
||||
namedGroup, chc.sslContext.getSecureRandom());
|
||||
chc.handshakePossessions.add(ecdhePossession);
|
||||
ECDHClientKeyExchangeMessage cke =
|
||||
new ECDHClientKeyExchangeMessage(
|
||||
chc, ecdhePossession.publicKey);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced ECDH ClientKeyExchange handshake message", cke);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
cke.write(chc.handshakeOutput);
|
||||
chc.handshakeOutput.flush();
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
chc.negotiatedProtocol);
|
||||
} else {
|
||||
chc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(chc, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDH "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
X509Possession x509Possession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof X509Possession) {
|
||||
x509Possession = (X509Possession)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Possession == null) {
|
||||
// unlikely, have been checked during cipher suite negotiation.
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No expected EC server cert for ECDH client key exchange");
|
||||
}
|
||||
|
||||
ECParameterSpec params = x509Possession.getECParameterSpec();
|
||||
if (params == null) {
|
||||
// unlikely, have been checked during cipher suite negotiation.
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Not EC server cert for ECDH client key exchange");
|
||||
}
|
||||
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
||||
if (namedGroup == null) {
|
||||
// unlikely, have been checked during cipher suite negotiation.
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported EC server cert for ECDH client key exchange");
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
}
|
||||
|
||||
// parse the handshake message
|
||||
ECDHClientKeyExchangeMessage cke =
|
||||
new ECDHClientKeyExchangeMessage(shc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming ECDH ClientKeyExchange handshake message", cke);
|
||||
}
|
||||
|
||||
// create the credentials
|
||||
try {
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKey peerPublicKey =
|
||||
(ECPublicKey)kf.generatePublic(spec);
|
||||
|
||||
// check constraints of peer ECPublicKey
|
||||
if (shc.algorithmConstraints != null &&
|
||||
!shc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
peerPublicKey)) {
|
||||
throw new SSLHandshakeException(
|
||||
"ECPublicKey does not comply to algorithm constraints");
|
||||
}
|
||||
|
||||
shc.handshakeCredentials.add(new ECDHECredentials(
|
||||
peerPublicKey, namedGroup));
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException)(new SSLHandshakeException(
|
||||
"Could not generate ECPublicKey").initCause(e));
|
||||
}
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " + shc.negotiatedProtocol);
|
||||
} else {
|
||||
shc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(shc, masterSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDHE "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHEClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHEClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
ECDHECredentials ecdheCredentials = null;
|
||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||
if (cd instanceof ECDHECredentials) {
|
||||
ecdheCredentials = (ECDHECredentials)cd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdheCredentials == null) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No ECDHE credentials negotiated for client key exchange");
|
||||
}
|
||||
|
||||
ECDHEPossession ecdhePossession = new ECDHEPossession(
|
||||
ecdheCredentials, chc.sslContext.getSecureRandom());
|
||||
chc.handshakePossessions.add(ecdhePossession);
|
||||
ECDHClientKeyExchangeMessage cke =
|
||||
new ECDHClientKeyExchangeMessage(
|
||||
chc, ecdhePossession.publicKey);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced ECDHE ClientKeyExchange handshake message", cke);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
cke.write(chc.handshakeOutput);
|
||||
chc.handshakeOutput.flush();
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
chc.negotiatedProtocol);
|
||||
} else {
|
||||
chc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(chc, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDHE "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHEClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHEClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
ECDHEPossession ecdhePossession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof ECDHEPossession) {
|
||||
ecdhePossession = (ECDHEPossession)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ecdhePossession == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No expected ECDHE possessions for client key exchange");
|
||||
}
|
||||
|
||||
ECParameterSpec params = ecdhePossession.publicKey.getParams();
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
||||
if (namedGroup == null) {
|
||||
// unlikely, have been checked during cipher suite negotiation.
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported EC server cert for ECDHE client key exchange");
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
}
|
||||
|
||||
// parse the handshake message
|
||||
ECDHClientKeyExchangeMessage cke =
|
||||
new ECDHClientKeyExchangeMessage(shc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming ECDHE ClientKeyExchange handshake message", cke);
|
||||
}
|
||||
|
||||
// create the credentials
|
||||
try {
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(cke.encodedPoint, params.getCurve());
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKey peerPublicKey =
|
||||
(ECPublicKey)kf.generatePublic(spec);
|
||||
|
||||
// check constraints of peer ECPublicKey
|
||||
if (shc.algorithmConstraints != null &&
|
||||
!shc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
peerPublicKey)) {
|
||||
throw new SSLHandshakeException(
|
||||
"ECPublicKey does not comply to algorithm constraints");
|
||||
}
|
||||
|
||||
shc.handshakeCredentials.add(new ECDHECredentials(
|
||||
peerPublicKey, namedGroup));
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException)(new SSLHandshakeException(
|
||||
"Could not generate ECPublicKey").initCause(e));
|
||||
}
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " + shc.negotiatedProtocol);
|
||||
} else {
|
||||
shc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(shc, masterSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
484
jdkSrc/jdk8/sun/security/ssl/ECDHKeyExchange.java
Normal file
484
jdkSrc/jdk8/sun/security/ssl/ECDHKeyExchange.java
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AlgorithmConstraints;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.util.EnumSet;
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.security.util.ECUtil;
|
||||
|
||||
final class ECDHKeyExchange {
|
||||
static final SSLPossessionGenerator poGenerator =
|
||||
new ECDHEPossessionGenerator();
|
||||
static final SSLKeyAgreementGenerator ecdheKAGenerator =
|
||||
new ECDHEKAGenerator();
|
||||
static final SSLKeyAgreementGenerator ecdhKAGenerator =
|
||||
new ECDHKAGenerator();
|
||||
|
||||
static final class ECDHECredentials implements SSLCredentials {
|
||||
final ECPublicKey popPublicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
||||
ECDHECredentials(ECPublicKey popPublicKey, NamedGroup namedGroup) {
|
||||
this.popPublicKey = popPublicKey;
|
||||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
static ECDHECredentials valueOf(NamedGroup namedGroup,
|
||||
byte[] encodedPoint) throws IOException, GeneralSecurityException {
|
||||
|
||||
if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||
throw new RuntimeException(
|
||||
"Credentials decoding: Not ECDHE named group");
|
||||
}
|
||||
|
||||
if (encodedPoint == null || encodedPoint.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ECParameterSpec parameters =
|
||||
JsseJce.getECParameterSpec(namedGroup.oid);
|
||||
if (parameters == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ECPoint point = JsseJce.decodePoint(
|
||||
encodedPoint, parameters.getCurve());
|
||||
KeyFactory factory = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKey publicKey = (ECPublicKey)factory.generatePublic(
|
||||
new ECPublicKeySpec(point, parameters));
|
||||
return new ECDHECredentials(publicKey, namedGroup);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ECDHEPossession implements SSLPossession {
|
||||
final PrivateKey privateKey;
|
||||
final ECPublicKey publicKey;
|
||||
final NamedGroup namedGroup;
|
||||
|
||||
ECDHEPossession(NamedGroup namedGroup, SecureRandom random) {
|
||||
try {
|
||||
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
|
||||
ECGenParameterSpec params =
|
||||
(ECGenParameterSpec)namedGroup.getParameterSpec();
|
||||
kpg.initialize(params, random);
|
||||
KeyPair kp = kpg.generateKeyPair();
|
||||
privateKey = kp.getPrivate();
|
||||
publicKey = (ECPublicKey)kp.getPublic();
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate ECDH keypair", e);
|
||||
}
|
||||
|
||||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
ECDHEPossession(ECDHECredentials credentials, SecureRandom random) {
|
||||
ECParameterSpec params = credentials.popPublicKey.getParams();
|
||||
try {
|
||||
KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC");
|
||||
kpg.initialize(params, random);
|
||||
KeyPair kp = kpg.generateKeyPair();
|
||||
privateKey = kp.getPrivate();
|
||||
publicKey = (ECPublicKey)kp.getPublic();
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new RuntimeException(
|
||||
"Could not generate ECDH keypair", e);
|
||||
}
|
||||
|
||||
this.namedGroup = credentials.namedGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encode() {
|
||||
return ECUtil.encodePoint(
|
||||
publicKey.getW(), publicKey.getParams().getCurve());
|
||||
}
|
||||
|
||||
// called by ClientHandshaker with either the server's static or
|
||||
// ephemeral public key
|
||||
SecretKey getAgreedSecret(
|
||||
PublicKey peerPublicKey) throws SSLHandshakeException {
|
||||
|
||||
try {
|
||||
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
|
||||
ka.init(privateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
return ka.generateSecret("TlsPremasterSecret");
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
// called by ServerHandshaker
|
||||
SecretKey getAgreedSecret(
|
||||
byte[] encodedPoint) throws SSLHandshakeException {
|
||||
try {
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(encodedPoint, params.getCurve());
|
||||
KeyFactory kf = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
||||
PublicKey peerPublicKey = kf.generatePublic(spec);
|
||||
return getAgreedSecret(peerPublicKey);
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Check constraints of the specified EC public key.
|
||||
void checkConstraints(AlgorithmConstraints constraints,
|
||||
byte[] encodedPoint) throws SSLHandshakeException {
|
||||
try {
|
||||
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(encodedPoint, params.getCurve());
|
||||
ECPublicKeySpec spec = new ECPublicKeySpec(point, params);
|
||||
|
||||
KeyFactory kf = JsseJce.getKeyFactory("EC");
|
||||
ECPublicKey pubKey = (ECPublicKey)kf.generatePublic(spec);
|
||||
|
||||
// check constraints of ECPublicKey
|
||||
if (!constraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), pubKey)) {
|
||||
throw new SSLHandshakeException(
|
||||
"ECPublicKey does not comply to algorithm constraints");
|
||||
}
|
||||
} catch (GeneralSecurityException | java.io.IOException e) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate ECPublicKey").initCause(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class ECDHEPossessionGenerator implements SSLPossessionGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHEPossessionGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext context) {
|
||||
NamedGroup preferableNamedGroup = null;
|
||||
if ((context.clientRequestedNamedGroups != null) &&
|
||||
(!context.clientRequestedNamedGroups.isEmpty())) {
|
||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||
context.negotiatedProtocol,
|
||||
context.algorithmConstraints,
|
||||
NamedGroupType.NAMED_GROUP_ECDHE,
|
||||
context.clientRequestedNamedGroups);
|
||||
} else {
|
||||
preferableNamedGroup = SupportedGroups.getPreferredGroup(
|
||||
context.negotiatedProtocol,
|
||||
context.algorithmConstraints,
|
||||
NamedGroupType.NAMED_GROUP_ECDHE);
|
||||
}
|
||||
|
||||
if (preferableNamedGroup != null) {
|
||||
return new ECDHEPossession(preferableNamedGroup,
|
||||
context.sslContext.getSecureRandom());
|
||||
}
|
||||
|
||||
// no match found, cannot use this cipher suite.
|
||||
//
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class ECDHKAGenerator implements SSLKeyAgreementGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
if (context instanceof ServerHandshakeContext) {
|
||||
return createServerKeyDerivation(
|
||||
(ServerHandshakeContext)context);
|
||||
} else {
|
||||
return createClientKeyDerivation(
|
||||
(ClientHandshakeContext)context);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLKeyDerivation createServerKeyDerivation(
|
||||
ServerHandshakeContext shc) throws IOException {
|
||||
X509Possession x509Possession = null;
|
||||
ECDHECredentials ecdheCredentials = null;
|
||||
for (SSLPossession poss : shc.handshakePossessions) {
|
||||
if (!(poss instanceof X509Possession)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ECParameterSpec params =
|
||||
((X509Possession)poss).getECParameterSpec();
|
||||
if (params == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NamedGroup ng = NamedGroup.valueOf(params);
|
||||
if (ng == null) {
|
||||
// unlikely, have been checked during cipher suite negotiation.
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported EC server cert for ECDH key exchange");
|
||||
}
|
||||
|
||||
for (SSLCredentials cred : shc.handshakeCredentials) {
|
||||
if (!(cred instanceof ECDHECredentials)) {
|
||||
continue;
|
||||
}
|
||||
if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
|
||||
ecdheCredentials = (ECDHECredentials)cred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdheCredentials != null) {
|
||||
x509Possession = (X509Possession)poss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Possession == null || ecdheCredentials == null) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient ECDHE key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(shc,
|
||||
x509Possession.popPrivateKey, ecdheCredentials.popPublicKey);
|
||||
}
|
||||
|
||||
private SSLKeyDerivation createClientKeyDerivation(
|
||||
ClientHandshakeContext chc) throws IOException {
|
||||
ECDHEPossession ecdhePossession = null;
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLPossession poss : chc.handshakePossessions) {
|
||||
if (!(poss instanceof ECDHEPossession)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
|
||||
for (SSLCredentials cred : chc.handshakeCredentials) {
|
||||
if (!(cred instanceof X509Credentials)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PublicKey publicKey = ((X509Credentials)cred).popPublicKey;
|
||||
if (!publicKey.getAlgorithm().equals("EC")) {
|
||||
continue;
|
||||
}
|
||||
ECParameterSpec params =
|
||||
((ECPublicKey)publicKey).getParams();
|
||||
NamedGroup namedGroup = NamedGroup.valueOf(params);
|
||||
if (namedGroup == null) {
|
||||
// unlikely, should have been checked previously
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported EC server cert for ECDH key exchange");
|
||||
}
|
||||
|
||||
if (ng.equals(namedGroup)) {
|
||||
x509Credentials = (X509Credentials)cred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Credentials != null) {
|
||||
ecdhePossession = (ECDHEPossession)poss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdhePossession == null || x509Credentials == null) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient ECDH key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(chc,
|
||||
ecdhePossession.privateKey, x509Credentials.popPublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class ECDHEKAGenerator implements SSLKeyAgreementGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHEKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
ECDHEPossession ecdhePossession = null;
|
||||
ECDHECredentials ecdheCredentials = null;
|
||||
for (SSLPossession poss : context.handshakePossessions) {
|
||||
if (!(poss instanceof ECDHEPossession)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NamedGroup ng = ((ECDHEPossession)poss).namedGroup;
|
||||
for (SSLCredentials cred : context.handshakeCredentials) {
|
||||
if (!(cred instanceof ECDHECredentials)) {
|
||||
continue;
|
||||
}
|
||||
if (ng.equals(((ECDHECredentials)cred).namedGroup)) {
|
||||
ecdheCredentials = (ECDHECredentials)cred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdheCredentials != null) {
|
||||
ecdhePossession = (ECDHEPossession)poss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdhePossession == null || ecdheCredentials == null) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient ECDHE key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new ECDHEKAKeyDerivation(context,
|
||||
ecdhePossession.privateKey, ecdheCredentials.popPublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class ECDHEKAKeyDerivation implements SSLKeyDerivation {
|
||||
private final HandshakeContext context;
|
||||
private final PrivateKey localPrivateKey;
|
||||
private final PublicKey peerPublicKey;
|
||||
|
||||
ECDHEKAKeyDerivation(HandshakeContext context,
|
||||
PrivateKey localPrivateKey,
|
||||
PublicKey peerPublicKey) {
|
||||
this.context = context;
|
||||
this.localPrivateKey = localPrivateKey;
|
||||
this.peerPublicKey = peerPublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return t12DeriveKey(algorithm, params);
|
||||
} else {
|
||||
return t13DeriveKey(algorithm, params);
|
||||
}
|
||||
}
|
||||
|
||||
private SecretKey t12DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey preMasterSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
"No expected master key derivation for protocol: " +
|
||||
context.negotiatedProtocol.name);
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
private SecretKey t13DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
KeyAgreement ka = JsseJce.getKeyAgreement("ECDH");
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey sharedSecret =
|
||||
ka.generateSecret("TlsPremasterSecret");
|
||||
|
||||
HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
|
||||
SSLKeyDerivation kd = context.handshakeKeyDerivation;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
if (kd == null) { // No PSK is in use.
|
||||
// If PSK is not in use Early Secret will still be
|
||||
// HKDF-Extract(0, 0).
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec ikm =
|
||||
new SecretKeySpec(zeros, "TlsPreSharedSecret");
|
||||
SecretKey earlySecret =
|
||||
hkdf.extract(zeros, ikm, "TlsEarlySecret");
|
||||
kd = new SSLSecretDerivation(context, earlySecret);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
|
||||
// derive handshake secret
|
||||
return hkdf.extract(saltSecret, sharedSecret, algorithm);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
560
jdkSrc/jdk8/sun/security/ssl/ECDHServerKeyExchange.java
Normal file
560
jdkSrc/jdk8/sun/security/ssl/ECDHServerKeyExchange.java
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the ServerKeyExchange handshake message.
|
||||
*/
|
||||
final class ECDHServerKeyExchange {
|
||||
static final SSLConsumer ecdheHandshakeConsumer =
|
||||
new ECDHServerKeyExchangeConsumer();
|
||||
static final HandshakeProducer ecdheHandshakeProducer =
|
||||
new ECDHServerKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The ECDH ServerKeyExchange handshake message.
|
||||
*/
|
||||
private static final
|
||||
class ECDHServerKeyExchangeMessage extends HandshakeMessage {
|
||||
private static final byte CURVE_NAMED_CURVE = (byte)0x03;
|
||||
|
||||
// id of the named curve
|
||||
private final NamedGroup namedGroup;
|
||||
|
||||
// encoded public point
|
||||
private final byte[] publicPoint;
|
||||
|
||||
// signature bytes, or null if anonymous
|
||||
private final byte[] paramsSignature;
|
||||
|
||||
// public key object encapsulated in this message
|
||||
private final ECPublicKey publicKey;
|
||||
|
||||
private final boolean useExplicitSigAlgorithm;
|
||||
|
||||
// the signature algorithm used by this ServerKeyExchange message
|
||||
private final SignatureScheme signatureScheme;
|
||||
|
||||
ECDHServerKeyExchangeMessage(
|
||||
HandshakeContext handshakeContext) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in server side only.
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)handshakeContext;
|
||||
|
||||
ECDHEPossession ecdhePossession = null;
|
||||
X509Possession x509Possession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof ECDHEPossession) {
|
||||
ecdhePossession = (ECDHEPossession)possession;
|
||||
if (x509Possession != null) {
|
||||
break;
|
||||
}
|
||||
} else if (possession instanceof X509Possession) {
|
||||
x509Possession = (X509Possession)possession;
|
||||
if (ecdhePossession != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ecdhePossession == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No ECDHE credentials negotiated for server key exchange");
|
||||
}
|
||||
|
||||
publicKey = ecdhePossession.publicKey;
|
||||
ECParameterSpec params = publicKey.getParams();
|
||||
ECPoint point = publicKey.getW();
|
||||
publicPoint = JsseJce.encodePoint(point, params.getCurve());
|
||||
|
||||
this.namedGroup = NamedGroup.valueOf(params);
|
||||
if ((namedGroup == null) || (namedGroup.oid == null) ) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unnamed EC parameter spec: " + params);
|
||||
}
|
||||
|
||||
if (x509Possession == null) {
|
||||
// anonymous, no authentication, no signature
|
||||
paramsSignature = null;
|
||||
signatureScheme = null;
|
||||
useExplicitSigAlgorithm = false;
|
||||
} else {
|
||||
useExplicitSigAlgorithm =
|
||||
shc.negotiatedProtocol.useTLS12PlusSpec();
|
||||
Signature signer = null;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
Map.Entry<SignatureScheme, Signature> schemeAndSigner =
|
||||
SignatureScheme.getSignerOfPreferableAlgorithm(
|
||||
shc.peerRequestedSignatureSchemes,
|
||||
x509Possession,
|
||||
shc.negotiatedProtocol);
|
||||
if (schemeAndSigner == null) {
|
||||
// Unlikely, the credentials generator should have
|
||||
// selected the preferable signature algorithm properly.
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"No supported signature algorithm for " +
|
||||
x509Possession.popPrivateKey.getAlgorithm() +
|
||||
" key");
|
||||
} else {
|
||||
signatureScheme = schemeAndSigner.getKey();
|
||||
signer = schemeAndSigner.getValue();
|
||||
}
|
||||
} else {
|
||||
signatureScheme = null;
|
||||
try {
|
||||
signer = getSignature(
|
||||
x509Possession.popPrivateKey.getAlgorithm(),
|
||||
x509Possession.popPrivateKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
x509Possession.popPrivateKey.getAlgorithm(), e);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] signature = null;
|
||||
try {
|
||||
updateSignature(signer, shc.clientHelloRandom.randomBytes,
|
||||
shc.serverHelloRandom.randomBytes,
|
||||
namedGroup.id, publicPoint);
|
||||
signature = signer.sign();
|
||||
} catch (SignatureException ex) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failed to sign ecdhe parameters: " +
|
||||
x509Possession.popPrivateKey.getAlgorithm(), ex);
|
||||
}
|
||||
paramsSignature = signature;
|
||||
}
|
||||
}
|
||||
|
||||
ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc =
|
||||
(ClientHandshakeContext)handshakeContext;
|
||||
|
||||
byte curveType = (byte)Record.getInt8(m);
|
||||
if (curveType != CURVE_NAMED_CURVE) {
|
||||
// Unlikely as only the named curves should be negotiated.
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported ECCurveType: " + curveType);
|
||||
}
|
||||
|
||||
int namedGroupId = Record.getInt16(m);
|
||||
this.namedGroup = NamedGroup.valueOf(namedGroupId);
|
||||
if (namedGroup == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unknown named group ID: " + namedGroupId);
|
||||
}
|
||||
|
||||
if (!SupportedGroups.isSupported(namedGroup)) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unsupported named group: " + namedGroup);
|
||||
}
|
||||
|
||||
if (namedGroup.oid == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Unknown named EC curve: " + namedGroup);
|
||||
}
|
||||
|
||||
ECParameterSpec parameters =
|
||||
JsseJce.getECParameterSpec(namedGroup.oid);
|
||||
if (parameters == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No supported EC parameter: " + namedGroup);
|
||||
}
|
||||
|
||||
publicPoint = Record.getBytes8(m);
|
||||
if (publicPoint.length == 0) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Insufficient ECPoint data: " + namedGroup);
|
||||
}
|
||||
|
||||
ECPublicKey ecPublicKey = null;
|
||||
try {
|
||||
ECPoint point =
|
||||
JsseJce.decodePoint(publicPoint, parameters.getCurve());
|
||||
KeyFactory factory = JsseJce.getKeyFactory("EC");
|
||||
ecPublicKey = (ECPublicKey)factory.generatePublic(
|
||||
new ECPublicKeySpec(point, parameters));
|
||||
} catch (NoSuchAlgorithmException |
|
||||
InvalidKeySpecException | IOException ex) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid ECPoint: " + namedGroup, ex);
|
||||
}
|
||||
|
||||
publicKey = ecPublicKey;
|
||||
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||
if (cd instanceof X509Credentials) {
|
||||
x509Credentials = (X509Credentials)cd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Credentials == null) {
|
||||
// anonymous, no authentication, no signature
|
||||
if (m.hasRemaining()) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid DH ServerKeyExchange: unknown extra data");
|
||||
}
|
||||
this.signatureScheme = null;
|
||||
this.paramsSignature = null;
|
||||
this.useExplicitSigAlgorithm = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.useExplicitSigAlgorithm =
|
||||
chc.negotiatedProtocol.useTLS12PlusSpec();
|
||||
if (useExplicitSigAlgorithm) {
|
||||
int ssid = Record.getInt16(m);
|
||||
signatureScheme = SignatureScheme.valueOf(ssid);
|
||||
if (signatureScheme == null) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid signature algorithm (" + ssid +
|
||||
") used in ECDH ServerKeyExchange handshake message");
|
||||
}
|
||||
|
||||
if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unsupported signature algorithm (" +
|
||||
signatureScheme.name +
|
||||
") used in ECDH ServerKeyExchange handshake message");
|
||||
}
|
||||
} else {
|
||||
signatureScheme = null;
|
||||
}
|
||||
|
||||
// read and verify the signature
|
||||
paramsSignature = Record.getBytes16(m);
|
||||
Signature signer;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
try {
|
||||
signer = signatureScheme.getVerifier(
|
||||
x509Credentials.popPublicKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException |
|
||||
InvalidAlgorithmParameterException nsae) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
signatureScheme.name, nsae);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
signer = getSignature(
|
||||
x509Credentials.popPublicKey.getAlgorithm(),
|
||||
x509Credentials.popPublicKey);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unsupported signature algorithm: " +
|
||||
x509Credentials.popPublicKey.getAlgorithm(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
updateSignature(signer,
|
||||
chc.clientHelloRandom.randomBytes,
|
||||
chc.serverHelloRandom.randomBytes,
|
||||
namedGroup.id, publicPoint);
|
||||
|
||||
if (!signer.verify(paramsSignature)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid ECDH ServerKeyExchange signature");
|
||||
}
|
||||
} catch (SignatureException ex) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Cannot verify ECDH ServerKeyExchange signature", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.SERVER_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
int sigLen = 0;
|
||||
if (paramsSignature != null) {
|
||||
sigLen = 2 + paramsSignature.length;
|
||||
if (useExplicitSigAlgorithm) {
|
||||
sigLen += SignatureScheme.sizeInRecord();
|
||||
}
|
||||
}
|
||||
|
||||
return 4 + publicPoint.length + sigLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putInt8(CURVE_NAMED_CURVE);
|
||||
hos.putInt16(namedGroup.id);
|
||||
hos.putBytes8(publicPoint);
|
||||
if (paramsSignature != null) {
|
||||
if (useExplicitSigAlgorithm) {
|
||||
hos.putInt16(signatureScheme.id);
|
||||
}
|
||||
|
||||
hos.putBytes16(paramsSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (useExplicitSigAlgorithm) {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"ECDH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"named group\": \"{0}\"\n" +
|
||||
" \"ecdh public\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" '}',\n" +
|
||||
" \"digital signature\": '{'\n" +
|
||||
" \"signature algorithm\": \"{2}\"\n" +
|
||||
" \"signature\": '{'\n" +
|
||||
"{3}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
namedGroup.name,
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(publicPoint), " "),
|
||||
signatureScheme.name,
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(paramsSignature), " ")
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
} else if (paramsSignature != null) {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"ECDH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"named group\": \"{0}\"\n" +
|
||||
" \"ecdh public\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" '}',\n" +
|
||||
" \"signature\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
namedGroup.name,
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(publicPoint), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(paramsSignature), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
} else { // anonymous
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"ECDH ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"named group\": \"{0}\"\n" +
|
||||
" \"ecdh public\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
namedGroup.name,
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(publicPoint), " "),
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static Signature getSignature(String keyAlgorithm,
|
||||
Key key) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
Signature signer = null;
|
||||
switch (keyAlgorithm) {
|
||||
case "EC":
|
||||
signer = JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA);
|
||||
break;
|
||||
case "RSA":
|
||||
signer = RSASignature.getInstance();
|
||||
break;
|
||||
default:
|
||||
throw new NoSuchAlgorithmException(
|
||||
"neither an RSA or a EC key : " + keyAlgorithm);
|
||||
}
|
||||
|
||||
if (signer != null) {
|
||||
if (key instanceof PublicKey) {
|
||||
signer.initVerify((PublicKey)(key));
|
||||
} else {
|
||||
signer.initSign((PrivateKey)key);
|
||||
}
|
||||
}
|
||||
|
||||
return signer;
|
||||
}
|
||||
|
||||
private static void updateSignature(Signature sig,
|
||||
byte[] clntNonce, byte[] svrNonce, int namedGroupId,
|
||||
byte[] publicPoint) throws SignatureException {
|
||||
sig.update(clntNonce);
|
||||
sig.update(svrNonce);
|
||||
|
||||
sig.update(CURVE_NAMED_CURVE);
|
||||
sig.update((byte)((namedGroupId >> 8) & 0xFF));
|
||||
sig.update((byte)(namedGroupId & 0xFF));
|
||||
sig.update((byte)publicPoint.length);
|
||||
sig.update(publicPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDH "ServerKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHServerKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHServerKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
ECDHServerKeyExchangeMessage skem =
|
||||
new ECDHServerKeyExchangeMessage(shc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced ECDH ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
skem.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The ECDH "ServerKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class ECDHServerKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private ECDHServerKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
ECDHServerKeyExchangeMessage skem =
|
||||
new ECDHServerKeyExchangeMessage(chc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming ECDH ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
//
|
||||
// validate
|
||||
//
|
||||
// check constraints of EC PublicKey
|
||||
if (chc.algorithmConstraints != null &&
|
||||
!chc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
skem.publicKey)) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"ECDH ServerKeyExchange does not comply " +
|
||||
"to algorithm constraints");
|
||||
}
|
||||
|
||||
//
|
||||
// update
|
||||
//
|
||||
chc.handshakeCredentials.add(
|
||||
new ECDHECredentials(skem.publicKey, skem.namedGroup));
|
||||
|
||||
//
|
||||
// produce
|
||||
//
|
||||
// Need no new handshake message producers here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
300
jdkSrc/jdk8/sun/security/ssl/ECPointFormatsExtension.java
Normal file
300
jdkSrc/jdk8/sun/security/ssl/ECPointFormatsExtension.java
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
|
||||
/**
|
||||
* Pack of the "ec_point_formats" extensions [RFC 4492].
|
||||
*/
|
||||
final class ECPointFormatsExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHECPointFormatsProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHECPointFormatsConsumer();
|
||||
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHECPointFormatsConsumer();
|
||||
|
||||
static final SSLStringizer epfStringizer =
|
||||
new ECPointFormatsStringizer();
|
||||
|
||||
/**
|
||||
* The "ec_point_formats" extension.
|
||||
*/
|
||||
static class ECPointFormatsSpec implements SSLExtensionSpec {
|
||||
static final ECPointFormatsSpec DEFAULT =
|
||||
new ECPointFormatsSpec(new byte[] {ECPointFormat.UNCOMPRESSED.id});
|
||||
|
||||
final byte[] formats;
|
||||
|
||||
ECPointFormatsSpec(byte[] formats) {
|
||||
this.formats = formats;
|
||||
}
|
||||
|
||||
private ECPointFormatsSpec(ByteBuffer m) throws IOException {
|
||||
if (!m.hasRemaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid ec_point_formats extension: " +
|
||||
"insufficient data");
|
||||
}
|
||||
|
||||
this.formats = Record.getBytes8(m);
|
||||
}
|
||||
|
||||
private boolean hasUncompressedFormat() {
|
||||
for (byte format : formats) {
|
||||
if (format == ECPointFormat.UNCOMPRESSED.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"formats\": '['{0}']'", Locale.ENGLISH);
|
||||
if (formats == null || formats.length == 0) {
|
||||
Object[] messageFields = {
|
||||
"<no EC point format specified>"
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
boolean isFirst = true;
|
||||
for (byte pf : formats) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(ECPointFormat.nameOf(pf));
|
||||
}
|
||||
|
||||
Object[] messageFields = {
|
||||
builder.toString()
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ECPointFormatsStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new ECPointFormatsSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static enum ECPointFormat {
|
||||
UNCOMPRESSED ((byte)0, "uncompressed"),
|
||||
ANSIX962_COMPRESSED_PRIME ((byte)1, "ansiX962_compressed_prime"),
|
||||
FMT_ANSIX962_COMPRESSED_CHAR2 ((byte)2, "ansiX962_compressed_char2");
|
||||
|
||||
final byte id;
|
||||
final String name;
|
||||
|
||||
private ECPointFormat(byte id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static String nameOf(int id) {
|
||||
for (ECPointFormat pf: ECPointFormat.values()) {
|
||||
if (pf.id == id) {
|
||||
return pf.name;
|
||||
}
|
||||
}
|
||||
return "UNDEFINED-EC-POINT-FORMAT(" + id + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "ec_point_formats" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHECPointFormatsProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHECPointFormatsProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable ec_point_formats extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Produce the extension.
|
||||
//
|
||||
// produce the extension only if EC cipher suite is activated.
|
||||
if (NamedGroupType.NAMED_GROUP_ECDHE.isSupported(
|
||||
chc.activeCipherSuites)) {
|
||||
// We are using uncompressed ECPointFormat only at present.
|
||||
byte[] extData = new byte[] {0x01, 0x00};
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
CH_EC_POINT_FORMATS, ECPointFormatsSpec.DEFAULT);
|
||||
|
||||
return extData;
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Need no ec_point_formats extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "ec_point_formats" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHECPointFormatsConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHECPointFormatsConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable ec_point_formats extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
ECPointFormatsSpec spec;
|
||||
try {
|
||||
spec = new ECPointFormatsSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// per RFC 4492, uncompressed points must always be supported.
|
||||
if (!spec.hasUncompressedFormat()) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid ec_point_formats extension data: " +
|
||||
"peer does not support uncompressed points");
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
|
||||
|
||||
// No impact on session resumption, as only uncompressed points
|
||||
// are supported at present.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "ec_point_formats" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHECPointFormatsConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHECPointFormatsConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to "ec_point_formats" extension request only
|
||||
ECPointFormatsSpec requestedSpec = (ECPointFormatsSpec)
|
||||
chc.handshakeExtensions.get(CH_EC_POINT_FORMATS);
|
||||
if (requestedSpec == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ec_point_formats extension in ServerHello");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
ECPointFormatsSpec spec;
|
||||
try {
|
||||
spec = new ECPointFormatsSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// per RFC 4492, uncompressed points must always be supported.
|
||||
if (!spec.hasUncompressedFormat()) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid ec_point_formats extension data: " +
|
||||
"peer does not support uncompressed points");
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
|
||||
|
||||
// No impact on session resumption, as only uncompressed points
|
||||
// are supported at present.
|
||||
}
|
||||
}
|
||||
}
|
||||
193
jdkSrc/jdk8/sun/security/ssl/EncryptedExtensions.java
Normal file
193
jdkSrc/jdk8/sun/security/ssl/EncryptedExtensions.java
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the EncryptedExtensions handshake message.
|
||||
*/
|
||||
final class EncryptedExtensions {
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new EncryptedExtensionsProducer();
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new EncryptedExtensionsConsumer();
|
||||
|
||||
/**
|
||||
* The EncryptedExtensions handshake message.
|
||||
*/
|
||||
static final class EncryptedExtensionsMessage extends HandshakeMessage {
|
||||
private final SSLExtensions extensions;
|
||||
|
||||
EncryptedExtensionsMessage(
|
||||
HandshakeContext handshakeContext) throws IOException {
|
||||
super(handshakeContext);
|
||||
this.extensions = new SSLExtensions(this);
|
||||
}
|
||||
|
||||
EncryptedExtensionsMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// struct {
|
||||
// Extension extensions<0..2^16-1>;
|
||||
// } EncryptedExtensions;
|
||||
if (m.remaining() < 2) {
|
||||
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid EncryptedExtensions handshake message: " +
|
||||
"no sufficient data");
|
||||
}
|
||||
|
||||
SSLExtension[] encryptedExtensions =
|
||||
handshakeContext.sslConfig.getEnabledExtensions(
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS);
|
||||
this.extensions = new SSLExtensions(this, m, encryptedExtensions);
|
||||
}
|
||||
|
||||
@Override
|
||||
SSLHandshake handshakeType() {
|
||||
return SSLHandshake.ENCRYPTED_EXTENSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
int messageLength() {
|
||||
int extLen = extensions.length();
|
||||
if (extLen == 0) {
|
||||
extLen = 2; // empty extensions
|
||||
}
|
||||
return extLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
void send(HandshakeOutStream hos) throws IOException {
|
||||
// Is it an empty extensions?
|
||||
if (extensions.length() == 0) {
|
||||
hos.putInt16(0);
|
||||
} else {
|
||||
extensions.send(hos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"EncryptedExtensions\": [\n" +
|
||||
"{0}\n" +
|
||||
"]",
|
||||
Locale.ENGLISH);
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(extensions.toString())
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The EncryptedExtensions handshake message consumer.
|
||||
*/
|
||||
private static final class EncryptedExtensionsProducer
|
||||
implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private EncryptedExtensionsProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
EncryptedExtensionsMessage eem =
|
||||
new EncryptedExtensionsMessage(shc);
|
||||
SSLExtension[] extTypes =
|
||||
shc.sslConfig.getEnabledExtensions(
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS,
|
||||
shc.negotiatedProtocol);
|
||||
eem.extensions.produce(shc, extTypes);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Produced EncryptedExtensions message", eem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
eem.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The EncryptedExtensions handshake message consumer.
|
||||
*/
|
||||
private static final class EncryptedExtensionsConsumer
|
||||
implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private EncryptedExtensionsConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// clean up this consumer
|
||||
chc.handshakeConsumers.remove(SSLHandshake.ENCRYPTED_EXTENSIONS.id);
|
||||
|
||||
EncryptedExtensionsMessage eem =
|
||||
new EncryptedExtensionsMessage(chc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming EncryptedExtensions handshake message", eem);
|
||||
}
|
||||
|
||||
//
|
||||
// validate
|
||||
//
|
||||
SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS);
|
||||
eem.extensions.consumeOnLoad(chc, extTypes);
|
||||
|
||||
//
|
||||
// update
|
||||
//
|
||||
eem.extensions.consumeOnTrade(chc, extTypes);
|
||||
|
||||
//
|
||||
// produce
|
||||
//
|
||||
// Need no new handshake message producers here.
|
||||
}
|
||||
}
|
||||
}
|
||||
124
jdkSrc/jdk8/sun/security/ssl/EphemeralKeyManager.java
Normal file
124
jdkSrc/jdk8/sun/security/ssl/EphemeralKeyManager.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.*;
|
||||
|
||||
/**
|
||||
* The "KeyManager" for ephemeral RSA keys. Ephemeral DH and ECDH keys
|
||||
* are handled by the DHCrypt and ECDHCrypt classes, respectively.
|
||||
*
|
||||
* @author Andreas Sterbenz
|
||||
*/
|
||||
final class EphemeralKeyManager {
|
||||
|
||||
// indices for the keys array below
|
||||
private static final int INDEX_RSA512 = 0;
|
||||
private static final int INDEX_RSA1024 = 1;
|
||||
|
||||
/*
|
||||
* Current cached RSA KeyPairs. Elements are never null.
|
||||
* Indexed via the constants above.
|
||||
*/
|
||||
private final EphemeralKeyPair[] keys = new EphemeralKeyPair[] {
|
||||
new EphemeralKeyPair(null),
|
||||
new EphemeralKeyPair(null),
|
||||
};
|
||||
|
||||
EphemeralKeyManager() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a temporary RSA KeyPair.
|
||||
*/
|
||||
KeyPair getRSAKeyPair(boolean export, SecureRandom random) {
|
||||
int length, index;
|
||||
if (export) {
|
||||
length = 512;
|
||||
index = INDEX_RSA512;
|
||||
} else {
|
||||
length = 1024;
|
||||
index = INDEX_RSA1024;
|
||||
}
|
||||
|
||||
synchronized (keys) {
|
||||
KeyPair kp = keys[index].getKeyPair();
|
||||
if (kp == null) {
|
||||
try {
|
||||
KeyPairGenerator kgen = JsseJce.getKeyPairGenerator("RSA");
|
||||
kgen.initialize(length, random);
|
||||
keys[index] = new EphemeralKeyPair(kgen.genKeyPair());
|
||||
kp = keys[index].getKeyPair();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return kp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to handle storage of ephemeral KeyPairs.
|
||||
*/
|
||||
private static class EphemeralKeyPair {
|
||||
|
||||
// maximum number of times a KeyPair is used
|
||||
private static final int MAX_USE = 200;
|
||||
|
||||
// maximum time interval in which the keypair is used (1 hour in ms)
|
||||
private static final long USE_INTERVAL = 3600*1000;
|
||||
|
||||
private KeyPair keyPair;
|
||||
private int uses;
|
||||
private long expirationTime;
|
||||
|
||||
private EphemeralKeyPair(KeyPair keyPair) {
|
||||
this.keyPair = keyPair;
|
||||
expirationTime = System.currentTimeMillis() + USE_INTERVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the KeyPair can still be used.
|
||||
*/
|
||||
private boolean isValid() {
|
||||
return (keyPair != null) && (uses < MAX_USE)
|
||||
&& (System.currentTimeMillis() < expirationTime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the KeyPair or null if it is invalid.
|
||||
*/
|
||||
private KeyPair getKeyPair() {
|
||||
if (isValid() == false) {
|
||||
keyPair = null;
|
||||
return null;
|
||||
}
|
||||
uses++;
|
||||
return keyPair;
|
||||
}
|
||||
}
|
||||
}
|
||||
389
jdkSrc/jdk8/sun/security/ssl/ExtendedMasterSecretExtension.java
Normal file
389
jdkSrc/jdk8/sun/security/ssl/ExtendedMasterSecretExtension.java
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import static sun.security.ssl.SSLExtension.CH_EXTENDED_MASTER_SECRET;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import static sun.security.ssl.SSLExtension.SH_EXTENDED_MASTER_SECRET;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "extended_master_secret" extensions [RFC 7627].
|
||||
*/
|
||||
final class ExtendedMasterSecretExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHExtendedMasterSecretProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHExtendedMasterSecretConsumer();
|
||||
static final HandshakeAbsence chOnLoadAbsence =
|
||||
new CHExtendedMasterSecretAbsence();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer =
|
||||
new SHExtendedMasterSecretProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHExtendedMasterSecretConsumer();
|
||||
static final HandshakeAbsence shOnLoadAbsence =
|
||||
new SHExtendedMasterSecretAbsence();
|
||||
|
||||
static final SSLStringizer emsStringizer =
|
||||
new ExtendedMasterSecretStringizer();
|
||||
|
||||
/**
|
||||
* The "extended_master_secret" extension.
|
||||
*/
|
||||
static final class ExtendedMasterSecretSpec implements SSLExtensionSpec {
|
||||
// A nominal object that does not holding any real renegotiation info.
|
||||
static final ExtendedMasterSecretSpec NOMINAL =
|
||||
new ExtendedMasterSecretSpec();
|
||||
|
||||
private ExtendedMasterSecretSpec() {
|
||||
// blank
|
||||
}
|
||||
|
||||
private ExtendedMasterSecretSpec(ByteBuffer m) throws IOException {
|
||||
// Parse the extension.
|
||||
if (m.hasRemaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid extended_master_secret extension data: " +
|
||||
"not empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<empty>";
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class ExtendedMasterSecretStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new ExtendedMasterSecretSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "extended_master_secret" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHExtendedMasterSecretProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHExtendedMasterSecretProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
|
||||
!SSLConfiguration.useExtendedMasterSecret ||
|
||||
!chc.conContext.protocolVersion.useTLS10PlusSpec()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable extended_master_secret extension");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (chc.handshakeSession == null ||
|
||||
chc.handshakeSession.useExtendedMasterSecret) {
|
||||
byte[] extData = new byte[0];
|
||||
chc.handshakeExtensions.put(CH_EXTENDED_MASTER_SECRET,
|
||||
ExtendedMasterSecretSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "extended_master_secret" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHExtendedMasterSecretConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHExtendedMasterSecretConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
|
||||
!SSLConfiguration.useExtendedMasterSecret ||
|
||||
!shc.negotiatedProtocol.useTLS10PlusSpec()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Ignore unavailable extension: " +
|
||||
CH_EXTENDED_MASTER_SECRET.name);
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
ExtendedMasterSecretSpec spec;
|
||||
try {
|
||||
spec = new ExtendedMasterSecretSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
if (shc.isResumption && shc.resumingSession != null &&
|
||||
!shc.resumingSession.useExtendedMasterSecret) {
|
||||
// For abbreviated handshake request, If the original
|
||||
// session did not use the "extended_master_secret"
|
||||
// extension but the new ClientHello contains the
|
||||
// extension, then the server MUST NOT perform the
|
||||
// abbreviated handshake. Instead, it SHOULD continue
|
||||
// with a full handshake.
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"abort session resumption which did not use " +
|
||||
"Extended Master Secret extension");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
//
|
||||
shc.handshakeExtensions.put(
|
||||
CH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if a "extended_master_secret" extension is
|
||||
* not present in the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHExtendedMasterSecretAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(CH_EXTENDED_MASTER_SECRET) ||
|
||||
!SSLConfiguration.useExtendedMasterSecret) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Ignore unavailable extension: " +
|
||||
CH_EXTENDED_MASTER_SECRET.name);
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
if (shc.negotiatedProtocol.useTLS10PlusSpec() &&
|
||||
!SSLConfiguration.allowLegacyMasterSecret) {
|
||||
// For full handshake, if the server receives a ClientHello
|
||||
// without the extension, it SHOULD abort the handshake if
|
||||
// it does not wish to interoperate with legacy clients.
|
||||
//
|
||||
// As if extended master extension is required for full
|
||||
// handshake, it MUST be used in abbreviated handshake too.
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Extended Master Secret extension is required");
|
||||
}
|
||||
|
||||
if (shc.isResumption && shc.resumingSession != null) {
|
||||
if (shc.resumingSession.useExtendedMasterSecret) {
|
||||
// For abbreviated handshake request, if the original
|
||||
// session used the "extended_master_secret" extension
|
||||
// but the new ClientHello does not contain it, the
|
||||
// server MUST abort the abbreviated handshake.
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Missing Extended Master Secret extension " +
|
||||
"on session resumption");
|
||||
} else {
|
||||
// For abbreviated handshake request, if neither the
|
||||
// original session nor the new ClientHello uses the
|
||||
// extension, the server SHOULD abort the handshake.
|
||||
if (!SSLConfiguration.allowLegacyResumption) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Missing Extended Master Secret extension " +
|
||||
"on session resumption");
|
||||
} else { // Otherwise, continue with a full handshake.
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"abort session resumption, " +
|
||||
"missing Extended Master Secret extension");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "extended_master_secret" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHExtendedMasterSecretProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHExtendedMasterSecretProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
if (shc.handshakeSession.useExtendedMasterSecret) {
|
||||
byte[] extData = new byte[0];
|
||||
shc.handshakeExtensions.put(SH_EXTENDED_MASTER_SECRET,
|
||||
ExtendedMasterSecretSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "extended_master_secret" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHExtendedMasterSecretConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHExtendedMasterSecretConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to the client extended_master_secret extension
|
||||
// request, which is mandatory for ClientHello message.
|
||||
ExtendedMasterSecretSpec requstedSpec = (ExtendedMasterSecretSpec)
|
||||
chc.handshakeExtensions.get(CH_EXTENDED_MASTER_SECRET);
|
||||
if (requstedSpec == null) {
|
||||
throw chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
|
||||
"Server sent the extended_master_secret " +
|
||||
"extension improperly");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
ExtendedMasterSecretSpec spec;
|
||||
try {
|
||||
spec = new ExtendedMasterSecretSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
if (chc.isResumption && chc.resumingSession != null &&
|
||||
!chc.resumingSession.useExtendedMasterSecret) {
|
||||
throw chc.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
|
||||
"Server sent an unexpected extended_master_secret " +
|
||||
"extension on session resumption");
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SH_EXTENDED_MASTER_SECRET, ExtendedMasterSecretSpec.NOMINAL);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if a "extended_master_secret" extension is
|
||||
* not present in the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHExtendedMasterSecretAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
if (SSLConfiguration.useExtendedMasterSecret &&
|
||||
!SSLConfiguration.allowLegacyMasterSecret) {
|
||||
// For full handshake, if a client receives a ServerHello
|
||||
// without the extension, it SHOULD abort the handshake if
|
||||
// it does not wish to interoperate with legacy servers.
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Extended Master Secret extension is required");
|
||||
}
|
||||
|
||||
if (chc.isResumption && chc.resumingSession != null) {
|
||||
if (chc.resumingSession.useExtendedMasterSecret) {
|
||||
// For abbreviated handshake, if the original session used
|
||||
// the "extended_master_secret" extension but the new
|
||||
// ServerHello does not contain the extension, the client
|
||||
// MUST abort the handshake.
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Missing Extended Master Secret extension " +
|
||||
"on session resumption");
|
||||
} else if (SSLConfiguration.useExtendedMasterSecret &&
|
||||
!SSLConfiguration.allowLegacyResumption &&
|
||||
chc.negotiatedProtocol.useTLS10PlusSpec()) {
|
||||
// Unlikely, abbreviated handshake should be discarded.
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Extended Master Secret extension is required");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1121
jdkSrc/jdk8/sun/security/ssl/Finished.java
Normal file
1121
jdkSrc/jdk8/sun/security/ssl/Finished.java
Normal file
File diff suppressed because it is too large
Load Diff
186
jdkSrc/jdk8/sun/security/ssl/HKDF.java
Normal file
186
jdkSrc/jdk8/sun/security/ssl/HKDF.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.InvalidKeyException;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.ShortBufferException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
|
||||
* specific to the needs of TLS 1.3 key derivation in JSSE. This is not a
|
||||
* general purpose HKDF implementation and is suited only to single-key output
|
||||
* derivations.
|
||||
*
|
||||
* HKDF objects are created by specifying a message digest algorithm. That
|
||||
* digest algorithm will be used by the HMAC function as part of the HKDF
|
||||
* derivation process.
|
||||
*/
|
||||
final class HKDF {
|
||||
private final String hmacAlg;
|
||||
private final Mac hmacObj;
|
||||
private final int hmacLen;
|
||||
|
||||
/**
|
||||
* Create an HDKF object, specifying the underlying message digest
|
||||
* algorithm.
|
||||
*
|
||||
* @param hashAlg a standard name corresponding to a supported message
|
||||
* digest algorithm.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if that message digest algorithm does
|
||||
* not have an HMAC variant supported on any available provider.
|
||||
*/
|
||||
HKDF(String hashAlg) throws NoSuchAlgorithmException {
|
||||
Objects.requireNonNull(hashAlg,
|
||||
"Must provide underlying HKDF Digest algorithm.");
|
||||
hmacAlg = "Hmac" + hashAlg.replace("-", "");
|
||||
hmacObj = JsseJce.getMac(hmacAlg);
|
||||
hmacLen = hmacObj.getMacLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HMAC-Extract derivation.
|
||||
*
|
||||
* @param salt a salt value, implemented as a {@code SecretKey}. A
|
||||
* {@code null} value is allowed, which will internally use an array of
|
||||
* zero bytes the same size as the underlying hash output length.
|
||||
* @param inputKey the input keying material provided as a
|
||||
* {@code SecretKey}.
|
||||
* @param keyAlg the algorithm name assigned to the resulting
|
||||
* {@code SecretKey} object.
|
||||
*
|
||||
* @return a {@code SecretKey} that is the result of the HKDF extract
|
||||
* operation.
|
||||
*
|
||||
* @throws InvalidKeyException if the {@code salt} parameter cannot be
|
||||
* used to initialize the underlying HMAC.
|
||||
*/
|
||||
SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
|
||||
throws InvalidKeyException {
|
||||
if (salt == null) {
|
||||
salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
|
||||
}
|
||||
hmacObj.init(salt);
|
||||
|
||||
return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
|
||||
keyAlg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HMAC-Extract derivation.
|
||||
*
|
||||
* @param salt a salt value as cleartext bytes. A {@code null} value is
|
||||
* allowed, which will internally use an array of zero bytes the same
|
||||
* size as the underlying hash output length.
|
||||
* @param inputKey the input keying material provided as a
|
||||
* {@code SecretKey}.
|
||||
* @param keyAlg the algorithm name assigned to the resulting
|
||||
* {@code SecretKey} object.
|
||||
*
|
||||
* @return a {@code SecretKey} that is the result of the HKDF extract
|
||||
* operation.
|
||||
*
|
||||
* @throws InvalidKeyException if the {@code salt} parameter cannot be
|
||||
* used to initialize the underlying HMAC.
|
||||
*/
|
||||
SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
|
||||
throws InvalidKeyException {
|
||||
if (salt == null) {
|
||||
salt = new byte[hmacLen];
|
||||
}
|
||||
return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HKDF-Expand derivation for a single-key output.
|
||||
*
|
||||
* @param pseudoRandKey the pseudo random key (PRK).
|
||||
* @param info optional context-specific info. A {@code null} value is
|
||||
* allowed in which case a zero-length byte array will be used.
|
||||
* @param outLen the length of the resulting {@code SecretKey}
|
||||
* @param keyAlg the algorithm name applied to the resulting
|
||||
* {@code SecretKey}
|
||||
*
|
||||
* @return the resulting key derivation as a {@code SecretKey} object
|
||||
*
|
||||
* @throws InvalidKeyException if the underlying HMAC operation cannot
|
||||
* be initialized using the provided {@code pseudoRandKey} object.
|
||||
*/
|
||||
SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
|
||||
String keyAlg) throws InvalidKeyException {
|
||||
byte[] kdfOutput;
|
||||
|
||||
// Calculate the number of rounds of HMAC that are needed to
|
||||
// meet the requested data. Then set up the buffers we will need.
|
||||
Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
|
||||
|
||||
// Output from the expand operation must be <= 255 * hmac length
|
||||
if (outLen > 255 * hmacLen) {
|
||||
throw new IllegalArgumentException("Requested output length " +
|
||||
"exceeds maximum length allowed for HKDF expansion");
|
||||
}
|
||||
hmacObj.init(pseudoRandKey);
|
||||
if (info == null) {
|
||||
info = new byte[0];
|
||||
}
|
||||
int rounds = (outLen + hmacLen - 1) / hmacLen;
|
||||
kdfOutput = new byte[rounds * hmacLen];
|
||||
int offset = 0;
|
||||
int tLength = 0;
|
||||
|
||||
for (int i = 0; i < rounds ; i++) {
|
||||
|
||||
// Calculate this round
|
||||
try {
|
||||
// Add T(i). This will be an empty string on the first
|
||||
// iteration since tLength starts at zero. After the first
|
||||
// iteration, tLength is changed to the HMAC length for the
|
||||
// rest of the loop.
|
||||
hmacObj.update(kdfOutput,
|
||||
Math.max(0, offset - hmacLen), tLength);
|
||||
hmacObj.update(info); // Add info
|
||||
hmacObj.update((byte)(i + 1)); // Add round number
|
||||
hmacObj.doFinal(kdfOutput, offset);
|
||||
|
||||
tLength = hmacLen;
|
||||
offset += hmacLen; // For next iteration
|
||||
} catch (ShortBufferException sbe) {
|
||||
// This really shouldn't happen given that we've
|
||||
// sized the buffers to their largest possible size up-front,
|
||||
// but just in case...
|
||||
throw new RuntimeException(sbe);
|
||||
}
|
||||
}
|
||||
|
||||
return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
|
||||
}
|
||||
}
|
||||
|
||||
42
jdkSrc/jdk8/sun/security/ssl/HandshakeAbsence.java
Normal file
42
jdkSrc/jdk8/sun/security/ssl/HandshakeAbsence.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Interface for handshake message or extension absence on handshake
|
||||
* message processing.
|
||||
*
|
||||
* This is typically used after the SSLSession object created, so that the
|
||||
* extension can update/impact the session object.
|
||||
*/
|
||||
interface HandshakeAbsence {
|
||||
void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException;
|
||||
}
|
||||
|
||||
35
jdkSrc/jdk8/sun/security/ssl/HandshakeConsumer.java
Normal file
35
jdkSrc/jdk8/sun/security/ssl/HandshakeConsumer.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
interface HandshakeConsumer {
|
||||
// message: the handshake message to be consumed.
|
||||
void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException;
|
||||
}
|
||||
551
jdkSrc/jdk8/sun/security/ssl/HandshakeContext.java
Normal file
551
jdkSrc/jdk8/sun/security/ssl/HandshakeContext.java
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.AlgorithmConstraints;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
|
||||
abstract class HandshakeContext implements ConnectionContext {
|
||||
// System properties
|
||||
|
||||
// By default, disable the unsafe legacy session renegotiation.
|
||||
static final boolean allowUnsafeRenegotiation =
|
||||
Utilities.getBooleanProperty(
|
||||
"sun.security.ssl.allowUnsafeRenegotiation", false);
|
||||
|
||||
// For maximum interoperability and backward compatibility, RFC 5746
|
||||
// allows server (or client) to accept ClientHello (or ServerHello)
|
||||
// message without the secure renegotiation_info extension or SCSV.
|
||||
//
|
||||
// For maximum security, RFC 5746 also allows server (or client) to
|
||||
// reject such message with a fatal "handshake_failure" alert.
|
||||
//
|
||||
// By default, allow such legacy hello messages.
|
||||
static final boolean allowLegacyHelloMessages =
|
||||
Utilities.getBooleanProperty(
|
||||
"sun.security.ssl.allowLegacyHelloMessages", true);
|
||||
|
||||
// registered handshake message actors
|
||||
LinkedHashMap<Byte, SSLConsumer> handshakeConsumers;
|
||||
final HashMap<Byte, HandshakeProducer> handshakeProducers;
|
||||
|
||||
// context
|
||||
final SSLContextImpl sslContext;
|
||||
final TransportContext conContext;
|
||||
final SSLConfiguration sslConfig;
|
||||
|
||||
// consolidated parameters
|
||||
final List<ProtocolVersion> activeProtocols;
|
||||
final List<CipherSuite> activeCipherSuites;
|
||||
final AlgorithmConstraints algorithmConstraints;
|
||||
final ProtocolVersion maximumActiveProtocol;
|
||||
|
||||
// output stream
|
||||
final HandshakeOutStream handshakeOutput;
|
||||
|
||||
// handshake transcript hash
|
||||
final HandshakeHash handshakeHash;
|
||||
|
||||
// negotiated security parameters
|
||||
SSLSessionImpl handshakeSession;
|
||||
boolean handshakeFinished;
|
||||
// boolean isInvalidated;
|
||||
|
||||
boolean kickstartMessageDelivered;
|
||||
|
||||
// Resumption
|
||||
boolean isResumption;
|
||||
SSLSessionImpl resumingSession;
|
||||
|
||||
final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
|
||||
volatile boolean taskDelegated = false;
|
||||
volatile Exception delegatedThrown = null;
|
||||
|
||||
ProtocolVersion negotiatedProtocol;
|
||||
CipherSuite negotiatedCipherSuite;
|
||||
final List<SSLPossession> handshakePossessions;
|
||||
final List<SSLCredentials> handshakeCredentials;
|
||||
SSLKeyDerivation handshakeKeyDerivation;
|
||||
SSLKeyExchange handshakeKeyExchange;
|
||||
SecretKey baseReadSecret;
|
||||
SecretKey baseWriteSecret;
|
||||
|
||||
// protocol version being established
|
||||
int clientHelloVersion;
|
||||
String applicationProtocol;
|
||||
|
||||
RandomCookie clientHelloRandom;
|
||||
RandomCookie serverHelloRandom;
|
||||
byte[] certRequestContext;
|
||||
|
||||
////////////////////
|
||||
// Extensions
|
||||
|
||||
// the extensions used in the handshake
|
||||
final Map<SSLExtension, SSLExtension.SSLExtensionSpec>
|
||||
handshakeExtensions;
|
||||
|
||||
// MaxFragmentLength
|
||||
int maxFragmentLength;
|
||||
|
||||
// SignatureScheme
|
||||
List<SignatureScheme> localSupportedSignAlgs;
|
||||
List<SignatureScheme> peerRequestedSignatureSchemes;
|
||||
List<SignatureScheme> peerRequestedCertSignSchemes;
|
||||
|
||||
// Known authorities
|
||||
X500Principal[] peerSupportedAuthorities = null;
|
||||
|
||||
// SupportedGroups
|
||||
List<NamedGroup> clientRequestedNamedGroups;
|
||||
|
||||
// HelloRetryRequest
|
||||
NamedGroup serverSelectedNamedGroup;
|
||||
|
||||
// if server name indicator is negotiated
|
||||
//
|
||||
// May need a public API for the indication in the future.
|
||||
List<SNIServerName> requestedServerNames;
|
||||
SNIServerName negotiatedServerName;
|
||||
|
||||
// OCSP Stapling info
|
||||
boolean staplingActive = false;
|
||||
|
||||
protected HandshakeContext(SSLContextImpl sslContext,
|
||||
TransportContext conContext) throws IOException {
|
||||
this.sslContext = sslContext;
|
||||
this.conContext = conContext;
|
||||
this.sslConfig = (SSLConfiguration)conContext.sslConfig.clone();
|
||||
|
||||
this.algorithmConstraints = new SSLAlgorithmConstraints(
|
||||
sslConfig.userSpecifiedAlgorithmConstraints);
|
||||
this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
|
||||
sslConfig.enabledCipherSuites, algorithmConstraints);
|
||||
if (activeProtocols.isEmpty()) {
|
||||
throw new SSLHandshakeException(
|
||||
"No appropriate protocol (protocol is disabled or " +
|
||||
"cipher suites are inappropriate)");
|
||||
}
|
||||
|
||||
ProtocolVersion maximumVersion = ProtocolVersion.NONE;
|
||||
for (ProtocolVersion pv : this.activeProtocols) {
|
||||
if (maximumVersion == ProtocolVersion.NONE ||
|
||||
pv.compare(maximumVersion) > 0) {
|
||||
maximumVersion = pv;
|
||||
}
|
||||
}
|
||||
this.maximumActiveProtocol = maximumVersion;
|
||||
this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
|
||||
sslConfig.enabledCipherSuites, algorithmConstraints);
|
||||
if (activeCipherSuites.isEmpty()) {
|
||||
throw new SSLHandshakeException("No appropriate cipher suite");
|
||||
}
|
||||
|
||||
this.handshakeConsumers = new LinkedHashMap<>();
|
||||
this.handshakeProducers = new HashMap<>();
|
||||
this.handshakeHash = conContext.inputRecord.handshakeHash;
|
||||
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
|
||||
|
||||
this.handshakeFinished = false;
|
||||
this.kickstartMessageDelivered = false;
|
||||
|
||||
this.delegatedActions = new LinkedList<>();
|
||||
this.handshakeExtensions = new HashMap<>();
|
||||
this.handshakePossessions = new LinkedList<>();
|
||||
this.handshakeCredentials = new LinkedList<>();
|
||||
this.requestedServerNames = null;
|
||||
this.negotiatedServerName = null;
|
||||
this.negotiatedCipherSuite = conContext.cipherSuite;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for PostHandshakeContext
|
||||
*/
|
||||
protected HandshakeContext(TransportContext conContext) {
|
||||
this.sslContext = conContext.sslContext;
|
||||
this.conContext = conContext;
|
||||
this.sslConfig = conContext.sslConfig;
|
||||
|
||||
this.negotiatedProtocol = conContext.protocolVersion;
|
||||
this.negotiatedCipherSuite = conContext.cipherSuite;
|
||||
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
|
||||
this.delegatedActions = new LinkedList<>();
|
||||
|
||||
this.handshakeConsumers = new LinkedHashMap<>();
|
||||
this.handshakeProducers = null;
|
||||
this.handshakeHash = null;
|
||||
this.activeProtocols = null;
|
||||
this.activeCipherSuites = null;
|
||||
this.algorithmConstraints = null;
|
||||
this.maximumActiveProtocol = null;
|
||||
this.handshakeExtensions = Collections.emptyMap(); // Not in TLS13
|
||||
this.handshakePossessions = null;
|
||||
this.handshakeCredentials = null;
|
||||
}
|
||||
|
||||
// Initialize the non-final class variables.
|
||||
private void initialize() {
|
||||
ProtocolVersion inputHelloVersion;
|
||||
ProtocolVersion outputHelloVersion;
|
||||
if (conContext.isNegotiated) {
|
||||
inputHelloVersion = conContext.protocolVersion;
|
||||
outputHelloVersion = conContext.protocolVersion;
|
||||
} else {
|
||||
if (activeProtocols.contains(ProtocolVersion.SSL20Hello)) {
|
||||
inputHelloVersion = ProtocolVersion.SSL20Hello;
|
||||
|
||||
// Per TLS 1.3 protocol, implementation MUST NOT send an SSL
|
||||
// version 2.0 compatible CLIENT-HELLO.
|
||||
if (maximumActiveProtocol.useTLS13PlusSpec()) {
|
||||
outputHelloVersion = maximumActiveProtocol;
|
||||
} else {
|
||||
outputHelloVersion = ProtocolVersion.SSL20Hello;
|
||||
}
|
||||
} else {
|
||||
inputHelloVersion = maximumActiveProtocol;
|
||||
outputHelloVersion = maximumActiveProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
conContext.inputRecord.setHelloVersion(inputHelloVersion);
|
||||
conContext.outputRecord.setHelloVersion(outputHelloVersion);
|
||||
|
||||
if (!conContext.isNegotiated) {
|
||||
conContext.protocolVersion = maximumActiveProtocol;
|
||||
}
|
||||
conContext.outputRecord.setVersion(conContext.protocolVersion);
|
||||
}
|
||||
|
||||
private static List<ProtocolVersion> getActiveProtocols(
|
||||
List<ProtocolVersion> enabledProtocols,
|
||||
List<CipherSuite> enabledCipherSuites,
|
||||
AlgorithmConstraints algorithmConstraints) {
|
||||
boolean enabledSSL20Hello = false;
|
||||
ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
|
||||
for (ProtocolVersion protocol : enabledProtocols) {
|
||||
if (!enabledSSL20Hello && protocol == ProtocolVersion.SSL20Hello) {
|
||||
enabledSSL20Hello = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
protocol.name, null)) {
|
||||
// Ignore disabled protocol.
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean found = false;
|
||||
Map<NamedGroupType, Boolean> cachedStatus =
|
||||
new EnumMap<>(NamedGroupType.class);
|
||||
for (CipherSuite suite : enabledCipherSuites) {
|
||||
if (suite.isAvailable() && suite.supports(protocol)) {
|
||||
if (isActivatable(suite,
|
||||
algorithmConstraints, cachedStatus)) {
|
||||
protocols.add(protocol);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unsupported cipher suite: " + suite +
|
||||
" for " + protocol);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && (SSLLogger.isOn) && SSLLogger.isOn("handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No available cipher suite for " + protocol);
|
||||
}
|
||||
}
|
||||
|
||||
if (!protocols.isEmpty()) {
|
||||
if (enabledSSL20Hello) {
|
||||
protocols.add(ProtocolVersion.SSL20Hello);
|
||||
}
|
||||
Collections.sort(protocols);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(protocols);
|
||||
}
|
||||
|
||||
private static List<CipherSuite> getActiveCipherSuites(
|
||||
List<ProtocolVersion> enabledProtocols,
|
||||
List<CipherSuite> enabledCipherSuites,
|
||||
AlgorithmConstraints algorithmConstraints) {
|
||||
|
||||
List<CipherSuite> suites = new LinkedList<>();
|
||||
if (enabledProtocols != null && !enabledProtocols.isEmpty()) {
|
||||
Map<NamedGroupType, Boolean> cachedStatus =
|
||||
new EnumMap<>(NamedGroupType.class);
|
||||
for (CipherSuite suite : enabledCipherSuites) {
|
||||
if (!suite.isAvailable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isSupported = false;
|
||||
for (ProtocolVersion protocol : enabledProtocols) {
|
||||
if (!suite.supports(protocol)) {
|
||||
continue;
|
||||
}
|
||||
if (isActivatable(suite,
|
||||
algorithmConstraints, cachedStatus)) {
|
||||
suites.add(suite);
|
||||
isSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSupported &&
|
||||
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.finest(
|
||||
"Ignore unsupported cipher suite: " + suite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(suites);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the handshake record and return the contentType
|
||||
*/
|
||||
static byte getHandshakeType(TransportContext conContext,
|
||||
Plaintext plaintext) throws IOException {
|
||||
// struct {
|
||||
// HandshakeType msg_type; /* handshake type */
|
||||
// uint24 length; /* bytes in message */
|
||||
// select (HandshakeType) {
|
||||
// ...
|
||||
// } body;
|
||||
// } Handshake;
|
||||
|
||||
if (plaintext.contentType != ContentType.HANDSHAKE.id) {
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Unexpected operation for record: " + plaintext.contentType);
|
||||
}
|
||||
|
||||
if (plaintext.fragment == null || plaintext.fragment.remaining() < 4) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid handshake message: insufficient data");
|
||||
}
|
||||
|
||||
byte handshakeType = (byte)Record.getInt8(plaintext.fragment);
|
||||
int handshakeLen = Record.getInt24(plaintext.fragment);
|
||||
if (handshakeLen != plaintext.fragment.remaining()) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid handshake message: insufficient handshake body");
|
||||
}
|
||||
|
||||
return handshakeType;
|
||||
}
|
||||
|
||||
void dispatch(byte handshakeType, Plaintext plaintext) throws IOException {
|
||||
if (conContext.transport.useDelegatedTask()) {
|
||||
boolean hasDelegated = !delegatedActions.isEmpty();
|
||||
if (hasDelegated ||
|
||||
(handshakeType != SSLHandshake.FINISHED.id &&
|
||||
handshakeType != SSLHandshake.KEY_UPDATE.id &&
|
||||
handshakeType != SSLHandshake.NEW_SESSION_TICKET.id)) {
|
||||
if (!hasDelegated) {
|
||||
taskDelegated = false;
|
||||
delegatedThrown = null;
|
||||
}
|
||||
|
||||
// Clone the fragment for delegated actions.
|
||||
//
|
||||
// The plaintext may share the application buffers. It is
|
||||
// fine to use shared buffers if no delegated actions.
|
||||
// However, for delegated actions, the shared buffers may be
|
||||
// polluted in application layer before the delegated actions
|
||||
// executed.
|
||||
ByteBuffer fragment = ByteBuffer.wrap(
|
||||
new byte[plaintext.fragment.remaining()]);
|
||||
fragment.put(plaintext.fragment);
|
||||
fragment = (ByteBuffer)fragment.rewind();
|
||||
|
||||
delegatedActions.add(new SimpleImmutableEntry<>(
|
||||
handshakeType,
|
||||
fragment
|
||||
));
|
||||
} else {
|
||||
dispatch(handshakeType, plaintext.fragment);
|
||||
}
|
||||
} else {
|
||||
dispatch(handshakeType, plaintext.fragment);
|
||||
}
|
||||
}
|
||||
|
||||
void dispatch(byte handshakeType,
|
||||
ByteBuffer fragment) throws IOException {
|
||||
SSLConsumer consumer;
|
||||
if (handshakeType == SSLHandshake.HELLO_REQUEST.id) {
|
||||
// For TLS 1.2 and prior versions, the HelloRequest message MAY
|
||||
// be sent by the server at any time.
|
||||
consumer = SSLHandshake.HELLO_REQUEST;
|
||||
} else {
|
||||
consumer = handshakeConsumers.get(handshakeType);
|
||||
}
|
||||
|
||||
if (consumer == null) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType));
|
||||
}
|
||||
|
||||
try {
|
||||
consumer.consume(this, fragment);
|
||||
} catch (UnsupportedOperationException unsoe) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType), unsoe);
|
||||
} catch (BufferUnderflowException | BufferOverflowException be) {
|
||||
throw conContext.fatal(Alert.DECODE_ERROR,
|
||||
"Illegal handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType), be);
|
||||
}
|
||||
|
||||
// update handshake hash after handshake message consumption.
|
||||
handshakeHash.consume();
|
||||
}
|
||||
|
||||
abstract void kickstart() throws IOException;
|
||||
|
||||
/**
|
||||
* Check if the given cipher suite is enabled and available within
|
||||
* the current active cipher suites.
|
||||
*
|
||||
* Does not check if the required server certificates are available.
|
||||
*/
|
||||
boolean isNegotiable(CipherSuite cs) {
|
||||
return isNegotiable(activeCipherSuites, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given cipher suite is enabled and available within
|
||||
* the proposed cipher suite list.
|
||||
*
|
||||
* Does not check if the required server certificates are available.
|
||||
*/
|
||||
static final boolean isNegotiable(
|
||||
List<CipherSuite> proposed, CipherSuite cs) {
|
||||
return proposed.contains(cs) && cs.isNegotiable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given cipher suite is enabled and available within
|
||||
* the proposed cipher suite list and specific protocol version.
|
||||
*
|
||||
* Does not check if the required server certificates are available.
|
||||
*/
|
||||
static final boolean isNegotiable(List<CipherSuite> proposed,
|
||||
ProtocolVersion protocolVersion, CipherSuite cs) {
|
||||
return proposed.contains(cs) &&
|
||||
cs.isNegotiable() && cs.supports(protocolVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given protocol version is enabled and available.
|
||||
*/
|
||||
boolean isNegotiable(ProtocolVersion protocolVersion) {
|
||||
return activeProtocols.contains(protocolVersion);
|
||||
}
|
||||
|
||||
private static boolean isActivatable(CipherSuite suite,
|
||||
AlgorithmConstraints algorithmConstraints,
|
||||
Map<NamedGroupType, Boolean> cachedStatus) {
|
||||
|
||||
if (algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
|
||||
if (suite.keyExchange == null) {
|
||||
// TLS 1.3, no definition of key exchange in cipher suite.
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean available;
|
||||
NamedGroupType groupType = suite.keyExchange.groupType;
|
||||
if (groupType != NAMED_GROUP_NONE) {
|
||||
Boolean checkedStatus = cachedStatus.get(groupType);
|
||||
if (checkedStatus == null) {
|
||||
available = SupportedGroups.isActivatable(
|
||||
algorithmConstraints, groupType);
|
||||
cachedStatus.put(groupType, available);
|
||||
|
||||
if (!available &&
|
||||
SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine("No activated named group");
|
||||
}
|
||||
} else {
|
||||
available = checkedStatus;
|
||||
}
|
||||
|
||||
if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine(
|
||||
"No active named group, ignore " + suite);
|
||||
}
|
||||
return available;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
|
||||
SSLLogger.fine("Ignore disabled cipher suite: " + suite);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
List<SNIServerName> getRequestedServerNames() {
|
||||
if (requestedServerNames == null) {
|
||||
return Collections.<SNIServerName>emptyList();
|
||||
}
|
||||
return requestedServerNames;
|
||||
}
|
||||
}
|
||||
|
||||
634
jdkSrc/jdk8/sun/security/ssl/HandshakeHash.java
Normal file
634
jdkSrc/jdk8/sun/security/ssl/HandshakeHash.java
Normal file
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import javax.crypto.SecretKey;
|
||||
import sun.security.util.MessageDigestSpi2;
|
||||
|
||||
final class HandshakeHash {
|
||||
private TranscriptHash transcriptHash;
|
||||
private LinkedList<byte[]> reserves; // one handshake message per entry
|
||||
private boolean hasBeenUsed;
|
||||
|
||||
HandshakeHash() {
|
||||
this.transcriptHash = new CacheOnlyHash();
|
||||
this.reserves = new LinkedList<>();
|
||||
this.hasBeenUsed = false;
|
||||
}
|
||||
|
||||
// fix the negotiated protocol version and cipher suite
|
||||
void determine(ProtocolVersion protocolVersion,
|
||||
CipherSuite cipherSuite) {
|
||||
if (!(transcriptHash instanceof CacheOnlyHash)) {
|
||||
throw new IllegalStateException(
|
||||
"Not expected instance of transcript hash");
|
||||
}
|
||||
|
||||
CacheOnlyHash coh = (CacheOnlyHash)transcriptHash;
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
transcriptHash = new T13HandshakeHash(cipherSuite);
|
||||
} else if (protocolVersion.useTLS12PlusSpec()) {
|
||||
transcriptHash = new T12HandshakeHash(cipherSuite);
|
||||
} else if (protocolVersion.useTLS10PlusSpec()) {
|
||||
transcriptHash = new T10HandshakeHash(cipherSuite);
|
||||
} else {
|
||||
transcriptHash = new S30HandshakeHash(cipherSuite);
|
||||
}
|
||||
|
||||
byte[] reserved = coh.baos.toByteArray();
|
||||
if (reserved.length != 0) {
|
||||
transcriptHash.update(reserved, 0, reserved.length);
|
||||
}
|
||||
}
|
||||
|
||||
HandshakeHash copy() {
|
||||
if (transcriptHash instanceof CacheOnlyHash) {
|
||||
HandshakeHash result = new HandshakeHash();
|
||||
result.transcriptHash = ((CacheOnlyHash)transcriptHash).copy();
|
||||
result.reserves = new LinkedList<>(reserves);
|
||||
result.hasBeenUsed = hasBeenUsed;
|
||||
return result;
|
||||
} else {
|
||||
throw new IllegalStateException("Hash does not support copying");
|
||||
}
|
||||
}
|
||||
|
||||
void receive(byte[] input) {
|
||||
reserves.add(Arrays.copyOf(input, input.length));
|
||||
}
|
||||
|
||||
void receive(ByteBuffer input, int length) {
|
||||
if (input.hasArray()) {
|
||||
int from = input.position() + input.arrayOffset();
|
||||
int to = from + length;
|
||||
reserves.add(Arrays.copyOfRange(input.array(), from, to));
|
||||
} else {
|
||||
int inPos = input.position();
|
||||
byte[] holder = new byte[length];
|
||||
input.get(holder);
|
||||
input.position(inPos);
|
||||
reserves.add(Arrays.copyOf(holder, holder.length));
|
||||
}
|
||||
}
|
||||
void receive(ByteBuffer input) {
|
||||
receive(input, input.remaining());
|
||||
}
|
||||
|
||||
// For HelloRetryRequest only! Please use this method very carefully!
|
||||
void push(byte[] input) {
|
||||
reserves.push(Arrays.copyOf(input, input.length));
|
||||
}
|
||||
|
||||
// For PreSharedKey to modify the state of the PSK binder hash
|
||||
byte[] removeLastReceived() {
|
||||
return reserves.removeLast();
|
||||
}
|
||||
|
||||
void deliver(byte[] input) {
|
||||
update();
|
||||
transcriptHash.update(input, 0, input.length);
|
||||
}
|
||||
|
||||
void deliver(byte[] input, int offset, int length) {
|
||||
update();
|
||||
transcriptHash.update(input, offset, length);
|
||||
}
|
||||
|
||||
void deliver(ByteBuffer input) {
|
||||
update();
|
||||
if (input.hasArray()) {
|
||||
transcriptHash.update(input.array(),
|
||||
input.position() + input.arrayOffset(), input.remaining());
|
||||
} else {
|
||||
int inPos = input.position();
|
||||
byte[] holder = new byte[input.remaining()];
|
||||
input.get(holder);
|
||||
input.position(inPos);
|
||||
transcriptHash.update(holder, 0, holder.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Use one handshake message if it has not been used.
|
||||
void utilize() {
|
||||
if (hasBeenUsed) {
|
||||
return;
|
||||
}
|
||||
if (reserves.size() != 0) {
|
||||
byte[] holder = reserves.remove();
|
||||
transcriptHash.update(holder, 0, holder.length);
|
||||
hasBeenUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Consume one handshake message if it has not been consumed.
|
||||
void consume() {
|
||||
if (hasBeenUsed) {
|
||||
hasBeenUsed = false;
|
||||
return;
|
||||
}
|
||||
if (reserves.size() != 0) {
|
||||
byte[] holder = reserves.remove();
|
||||
transcriptHash.update(holder, 0, holder.length);
|
||||
}
|
||||
}
|
||||
|
||||
void update() {
|
||||
while (reserves.size() != 0) {
|
||||
byte[] holder = reserves.remove();
|
||||
transcriptHash.update(holder, 0, holder.length);
|
||||
}
|
||||
hasBeenUsed = false;
|
||||
}
|
||||
|
||||
byte[] digest() {
|
||||
// Note that the reserve handshake message may be not a part of
|
||||
// the expected digest.
|
||||
return transcriptHash.digest();
|
||||
}
|
||||
|
||||
void finish() {
|
||||
this.transcriptHash = new CacheOnlyHash();
|
||||
this.reserves = new LinkedList<>();
|
||||
this.hasBeenUsed = false;
|
||||
}
|
||||
|
||||
// Optional
|
||||
byte[] archived() {
|
||||
// Note that the reserve handshake message may be not a part of
|
||||
// the expected digest.
|
||||
return transcriptHash.archived();
|
||||
}
|
||||
|
||||
// Optional, TLS 1.0/1.1 only
|
||||
byte[] digest(String algorithm) {
|
||||
T10HandshakeHash hh = (T10HandshakeHash)transcriptHash;
|
||||
return hh.digest(algorithm);
|
||||
}
|
||||
|
||||
// Optional, SSL 3.0 only
|
||||
byte[] digest(String algorithm, SecretKey masterSecret) {
|
||||
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
|
||||
return hh.digest(algorithm, masterSecret);
|
||||
}
|
||||
|
||||
// Optional, SSL 3.0 only
|
||||
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
|
||||
S30HandshakeHash hh = (S30HandshakeHash)transcriptHash;
|
||||
return hh.digest(useClientLabel, masterSecret);
|
||||
}
|
||||
|
||||
public boolean isHashable(byte handshakeType) {
|
||||
return handshakeType != SSLHandshake.HELLO_REQUEST.id;
|
||||
}
|
||||
|
||||
interface TranscriptHash {
|
||||
void update(byte[] input, int offset, int length);
|
||||
byte[] digest();
|
||||
byte[] archived(); // optional
|
||||
}
|
||||
|
||||
// For cache only.
|
||||
private static final class CacheOnlyHash implements TranscriptHash {
|
||||
private final ByteArrayOutputStream baos;
|
||||
|
||||
CacheOnlyHash() {
|
||||
this.baos = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
baos.write(input, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
throw new IllegalStateException(
|
||||
"Not expected call to handshake hash digest");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
CacheOnlyHash copy() {
|
||||
CacheOnlyHash result = new CacheOnlyHash();
|
||||
try {
|
||||
baos.writeTo(result.baos);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("unable to to clone hash state");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static final class S30HandshakeHash implements TranscriptHash {
|
||||
static final byte[] MD5_pad1 = genPad(0x36, 48);
|
||||
static final byte[] MD5_pad2 = genPad(0x5c, 48);
|
||||
|
||||
static final byte[] SHA_pad1 = genPad(0x36, 40);
|
||||
static final byte[] SHA_pad2 = genPad(0x5c, 40);
|
||||
|
||||
private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 };
|
||||
private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 };
|
||||
|
||||
private final MessageDigest mdMD5;
|
||||
private final MessageDigest mdSHA;
|
||||
private final TranscriptHash md5;
|
||||
private final TranscriptHash sha;
|
||||
private final ByteArrayOutputStream baos;
|
||||
|
||||
S30HandshakeHash(CipherSuite cipherSuite) {
|
||||
this.mdMD5 = JsseJce.getMessageDigest("MD5");
|
||||
this.mdSHA = JsseJce.getMessageDigest("SHA");
|
||||
|
||||
boolean hasArchived = false;
|
||||
if (mdMD5 instanceof Cloneable) {
|
||||
md5 = new CloneableHash(mdMD5);
|
||||
} else {
|
||||
hasArchived = true;
|
||||
md5 = new NonCloneableHash(mdMD5);
|
||||
}
|
||||
if (mdSHA instanceof Cloneable) {
|
||||
sha = new CloneableHash(mdSHA);
|
||||
} else {
|
||||
hasArchived = true;
|
||||
sha = new NonCloneableHash(mdSHA);
|
||||
}
|
||||
|
||||
if (hasArchived) {
|
||||
this.baos = null;
|
||||
} else {
|
||||
this.baos = new ByteArrayOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
md5.update(input, offset, length);
|
||||
sha.update(input, offset, length);
|
||||
if (baos != null) {
|
||||
baos.write(input, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
byte[] digest = new byte[36];
|
||||
System.arraycopy(md5.digest(), 0, digest, 0, 16);
|
||||
System.arraycopy(sha.digest(), 0, digest, 16, 20);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
if (baos != null) {
|
||||
return baos.toByteArray();
|
||||
} else if (md5 instanceof NonCloneableHash) {
|
||||
return md5.archived();
|
||||
} else {
|
||||
return sha.archived();
|
||||
}
|
||||
}
|
||||
|
||||
byte[] digest(boolean useClientLabel, SecretKey masterSecret) {
|
||||
MessageDigest md5Clone = cloneMd5();
|
||||
MessageDigest shaClone = cloneSha();
|
||||
|
||||
if (useClientLabel) {
|
||||
md5Clone.update(SSL_CLIENT);
|
||||
shaClone.update(SSL_CLIENT);
|
||||
} else {
|
||||
md5Clone.update(SSL_SERVER);
|
||||
shaClone.update(SSL_SERVER);
|
||||
}
|
||||
|
||||
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
|
||||
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
|
||||
|
||||
byte[] digest = new byte[36];
|
||||
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
|
||||
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
byte[] digest(String algorithm, SecretKey masterSecret) {
|
||||
if ("RSA".equalsIgnoreCase(algorithm)) {
|
||||
MessageDigest md5Clone = cloneMd5();
|
||||
MessageDigest shaClone = cloneSha();
|
||||
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterSecret);
|
||||
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
|
||||
|
||||
byte[] digest = new byte[36];
|
||||
System.arraycopy(md5Clone.digest(), 0, digest, 0, 16);
|
||||
System.arraycopy(shaClone.digest(), 0, digest, 16, 20);
|
||||
|
||||
return digest;
|
||||
} else {
|
||||
MessageDigest shaClone = cloneSha();
|
||||
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterSecret);
|
||||
return shaClone.digest();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] genPad(int b, int count) {
|
||||
byte[] padding = new byte[count];
|
||||
Arrays.fill(padding, (byte)b);
|
||||
return padding;
|
||||
}
|
||||
|
||||
private MessageDigest cloneMd5() {
|
||||
MessageDigest md5Clone;
|
||||
if (mdMD5 instanceof Cloneable) {
|
||||
try {
|
||||
md5Clone = (MessageDigest)mdMD5.clone();
|
||||
} catch (CloneNotSupportedException ex) { // unlikely
|
||||
throw new RuntimeException(
|
||||
"MessageDigest does no support clone operation");
|
||||
}
|
||||
} else {
|
||||
md5Clone = JsseJce.getMessageDigest("MD5");
|
||||
md5Clone.update(md5.archived());
|
||||
}
|
||||
|
||||
return md5Clone;
|
||||
}
|
||||
|
||||
private MessageDigest cloneSha() {
|
||||
MessageDigest shaClone;
|
||||
if (mdSHA instanceof Cloneable) {
|
||||
try {
|
||||
shaClone = (MessageDigest)mdSHA.clone();
|
||||
} catch (CloneNotSupportedException ex) { // unlikely
|
||||
throw new RuntimeException(
|
||||
"MessageDigest does no support clone operation");
|
||||
}
|
||||
} else {
|
||||
shaClone = JsseJce.getMessageDigest("SHA");
|
||||
shaClone.update(sha.archived());
|
||||
}
|
||||
|
||||
return shaClone;
|
||||
}
|
||||
|
||||
private static void updateDigest(MessageDigest md,
|
||||
byte[] pad1, byte[] pad2, SecretKey masterSecret) {
|
||||
byte[] keyBytes = "RAW".equals(masterSecret.getFormat())
|
||||
? masterSecret.getEncoded() : null;
|
||||
if (keyBytes != null) {
|
||||
md.update(keyBytes);
|
||||
} else {
|
||||
digestKey(md, masterSecret);
|
||||
}
|
||||
md.update(pad1);
|
||||
byte[] temp = md.digest();
|
||||
|
||||
if (keyBytes != null) {
|
||||
md.update(keyBytes);
|
||||
} else {
|
||||
digestKey(md, masterSecret);
|
||||
}
|
||||
md.update(pad2);
|
||||
md.update(temp);
|
||||
}
|
||||
|
||||
private static void digestKey(MessageDigest md, SecretKey key) {
|
||||
try {
|
||||
if (md instanceof MessageDigestSpi2) {
|
||||
((MessageDigestSpi2)md).engineUpdate(key);
|
||||
} else {
|
||||
throw new Exception(
|
||||
"Digest does not support implUpdate(SecretKey)");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Could not obtain encoded key and "
|
||||
+ "MessageDigest cannot digest key", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TLS 1.0 and TLS 1.1
|
||||
static final class T10HandshakeHash implements TranscriptHash {
|
||||
private final TranscriptHash md5;
|
||||
private final TranscriptHash sha;
|
||||
private final ByteArrayOutputStream baos;
|
||||
|
||||
T10HandshakeHash(CipherSuite cipherSuite) {
|
||||
MessageDigest mdMD5 = JsseJce.getMessageDigest("MD5");
|
||||
MessageDigest mdSHA = JsseJce.getMessageDigest("SHA");
|
||||
|
||||
boolean hasArchived = false;
|
||||
if (mdMD5 instanceof Cloneable) {
|
||||
md5 = new CloneableHash(mdMD5);
|
||||
} else {
|
||||
hasArchived = true;
|
||||
md5 = new NonCloneableHash(mdMD5);
|
||||
}
|
||||
if (mdSHA instanceof Cloneable) {
|
||||
sha = new CloneableHash(mdSHA);
|
||||
} else {
|
||||
hasArchived = true;
|
||||
sha = new NonCloneableHash(mdSHA);
|
||||
}
|
||||
|
||||
if (hasArchived) {
|
||||
this.baos = null;
|
||||
} else {
|
||||
this.baos = new ByteArrayOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
md5.update(input, offset, length);
|
||||
sha.update(input, offset, length);
|
||||
if (baos != null) {
|
||||
baos.write(input, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
byte[] digest = new byte[36];
|
||||
System.arraycopy(md5.digest(), 0, digest, 0, 16);
|
||||
System.arraycopy(sha.digest(), 0, digest, 16, 20);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
byte[] digest(String algorithm) {
|
||||
if ("RSA".equalsIgnoreCase(algorithm)) {
|
||||
return digest();
|
||||
} else {
|
||||
return sha.digest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
if (baos != null) {
|
||||
return baos.toByteArray();
|
||||
} else if (md5 instanceof NonCloneableHash) {
|
||||
return md5.archived();
|
||||
} else {
|
||||
return sha.archived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class T12HandshakeHash implements TranscriptHash {
|
||||
private final TranscriptHash transcriptHash;
|
||||
private final ByteArrayOutputStream baos;
|
||||
|
||||
T12HandshakeHash(CipherSuite cipherSuite) {
|
||||
MessageDigest md =
|
||||
JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
|
||||
if (md instanceof Cloneable) {
|
||||
transcriptHash = new CloneableHash(md);
|
||||
this.baos = new ByteArrayOutputStream();
|
||||
} else {
|
||||
transcriptHash = new NonCloneableHash(md);
|
||||
this.baos = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
transcriptHash.update(input, offset, length);
|
||||
if (baos != null) {
|
||||
baos.write(input, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
return transcriptHash.digest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
if (baos != null) {
|
||||
return baos.toByteArray();
|
||||
} else {
|
||||
return transcriptHash.archived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class T13HandshakeHash implements TranscriptHash {
|
||||
private final TranscriptHash transcriptHash;
|
||||
|
||||
T13HandshakeHash(CipherSuite cipherSuite) {
|
||||
MessageDigest md =
|
||||
JsseJce.getMessageDigest(cipherSuite.hashAlg.name);
|
||||
if (md instanceof Cloneable) {
|
||||
transcriptHash = new CloneableHash(md);
|
||||
} else {
|
||||
transcriptHash = new NonCloneableHash(md);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
transcriptHash.update(input, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
return transcriptHash.digest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
// This method is not necessary in T13
|
||||
throw new UnsupportedOperationException(
|
||||
"TLS 1.3 does not require archived.");
|
||||
}
|
||||
}
|
||||
|
||||
static final class CloneableHash implements TranscriptHash {
|
||||
private final MessageDigest md;
|
||||
|
||||
CloneableHash(MessageDigest md) {
|
||||
this.md = md;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
md.update(input, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
try {
|
||||
return ((MessageDigest)md.clone()).digest();
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
// unlikely
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
static final class NonCloneableHash implements TranscriptHash {
|
||||
private final MessageDigest md;
|
||||
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
NonCloneableHash(MessageDigest md) {
|
||||
this.md = md;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(byte[] input, int offset, int length) {
|
||||
baos.write(input, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] digest() {
|
||||
byte[] bytes = baos.toByteArray();
|
||||
md.reset();
|
||||
return md.digest(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] archived() {
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
169
jdkSrc/jdk8/sun/security/ssl/HandshakeOutStream.java
Normal file
169
jdkSrc/jdk8/sun/security/ssl/HandshakeOutStream.java
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Output stream for handshake data. This is used only internally
|
||||
* to the SSL classes.
|
||||
*
|
||||
* MT note: one thread at a time is presumed be writing handshake
|
||||
* messages, but (after initial connection setup) it's possible to
|
||||
* have other threads reading/writing application data. It's the
|
||||
* SSLSocketImpl class that synchronizes record writes.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class HandshakeOutStream extends ByteArrayOutputStream {
|
||||
|
||||
OutputRecord outputRecord; // May be null if not actually used to
|
||||
// output handshake message records.
|
||||
|
||||
HandshakeOutStream(OutputRecord outputRecord) {
|
||||
super();
|
||||
this.outputRecord = outputRecord;
|
||||
}
|
||||
|
||||
// Complete a handshaking message write. Called by HandshakeMessage.
|
||||
void complete() throws IOException {
|
||||
if (size() < 4) { // 4: handshake message header size
|
||||
// internal_error alert will be triggered
|
||||
throw new RuntimeException("handshake message is not available");
|
||||
}
|
||||
|
||||
if (outputRecord != null) {
|
||||
if (!outputRecord.isClosed()) {
|
||||
outputRecord.encodeHandshake(buf, 0, count);
|
||||
} else {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"handshake messages", ByteBuffer.wrap(buf, 0, count));
|
||||
}
|
||||
}
|
||||
|
||||
// reset the byte array output stream
|
||||
reset();
|
||||
} // otherwise, the handshake outstream is temporarily used only.
|
||||
}
|
||||
|
||||
//
|
||||
// overridden ByteArrayOutputStream methods
|
||||
//
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
// The maximum fragment size is 24 bytes.
|
||||
checkOverflow(len, Record.OVERFLOW_OF_INT24);
|
||||
super.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (outputRecord != null) {
|
||||
outputRecord.flush();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// handshake output stream management functions
|
||||
//
|
||||
|
||||
/*
|
||||
* Put integers encoded in standard 8, 16, 24, and 32 bit
|
||||
* big endian formats. Note that OutputStream.write(int) only
|
||||
* writes the least significant 8 bits and ignores the rest.
|
||||
*/
|
||||
void putInt8(int i) throws IOException {
|
||||
checkOverflow(i, Record.OVERFLOW_OF_INT08);
|
||||
super.write(i);
|
||||
}
|
||||
|
||||
void putInt16(int i) throws IOException {
|
||||
checkOverflow(i, Record.OVERFLOW_OF_INT16);
|
||||
super.write(i >> 8);
|
||||
super.write(i);
|
||||
}
|
||||
|
||||
void putInt24(int i) throws IOException {
|
||||
checkOverflow(i, Record.OVERFLOW_OF_INT24);
|
||||
super.write(i >> 16);
|
||||
super.write(i >> 8);
|
||||
super.write(i);
|
||||
}
|
||||
|
||||
void putInt32(int i) throws IOException {
|
||||
super.write(i >> 24);
|
||||
super.write(i >> 16);
|
||||
super.write(i >> 8);
|
||||
super.write(i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Put byte arrays with length encoded as 8, 16, 24 bit
|
||||
* integers in big-endian format.
|
||||
*/
|
||||
void putBytes8(byte[] b) throws IOException {
|
||||
if (b == null) {
|
||||
putInt8(0);
|
||||
} else {
|
||||
putInt8(b.length);
|
||||
super.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void putBytes16(byte[] b) throws IOException {
|
||||
if (b == null) {
|
||||
putInt16(0);
|
||||
} else {
|
||||
putInt16(b.length);
|
||||
super.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
void putBytes24(byte[] b) throws IOException {
|
||||
if (b == null) {
|
||||
putInt24(0);
|
||||
} else {
|
||||
putInt24(b.length);
|
||||
super.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the specified length overflow the limitation?
|
||||
*/
|
||||
private static void checkOverflow(int length, int limit) {
|
||||
if (length >= limit) {
|
||||
// internal_error alert will be triggered
|
||||
throw new RuntimeException(
|
||||
"Field length overflow, the field length (" +
|
||||
length + ") should be less than " + limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
jdkSrc/jdk8/sun/security/ssl/HandshakeProducer.java
Normal file
38
jdkSrc/jdk8/sun/security/ssl/HandshakeProducer.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
interface HandshakeProducer {
|
||||
// return the encoded producing if it has not been dumped to the context
|
||||
//
|
||||
// message: the handshake message responded to, can be null for producing
|
||||
// of kickstart handshake message
|
||||
byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException;
|
||||
}
|
||||
215
jdkSrc/jdk8/sun/security/ssl/HelloCookieManager.java
Normal file
215
jdkSrc/jdk8/sun/security/ssl/HelloCookieManager.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import static sun.security.ssl.ClientHello.ClientHelloMessage;
|
||||
|
||||
/**
|
||||
* TLS handshake cookie manager
|
||||
*/
|
||||
abstract class HelloCookieManager {
|
||||
|
||||
static class Builder {
|
||||
|
||||
final SecureRandom secureRandom;
|
||||
|
||||
private volatile T13HelloCookieManager t13HelloCookieManager;
|
||||
|
||||
Builder(SecureRandom secureRandom) {
|
||||
this.secureRandom = secureRandom;
|
||||
}
|
||||
|
||||
HelloCookieManager valueOf(ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
if (t13HelloCookieManager != null) {
|
||||
return t13HelloCookieManager;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (t13HelloCookieManager == null) {
|
||||
t13HelloCookieManager =
|
||||
new T13HelloCookieManager(secureRandom);
|
||||
}
|
||||
}
|
||||
|
||||
return t13HelloCookieManager;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract byte[] createCookie(ServerHandshakeContext context,
|
||||
ClientHelloMessage clientHello) throws IOException;
|
||||
|
||||
abstract boolean isCookieValid(ServerHandshakeContext context,
|
||||
ClientHelloMessage clientHello, byte[] cookie) throws IOException;
|
||||
|
||||
private static final
|
||||
class T13HelloCookieManager extends HelloCookieManager {
|
||||
|
||||
final SecureRandom secureRandom;
|
||||
private int cookieVersion; // version + sequence
|
||||
private final byte[] cookieSecret;
|
||||
private final byte[] legacySecret;
|
||||
|
||||
T13HelloCookieManager(SecureRandom secureRandom) {
|
||||
this.secureRandom = secureRandom;
|
||||
this.cookieVersion = secureRandom.nextInt();
|
||||
this.cookieSecret = new byte[64];
|
||||
this.legacySecret = new byte[64];
|
||||
|
||||
secureRandom.nextBytes(cookieSecret);
|
||||
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] createCookie(ServerHandshakeContext context,
|
||||
ClientHelloMessage clientHello) throws IOException {
|
||||
int version;
|
||||
byte[] secret;
|
||||
|
||||
synchronized (this) {
|
||||
version = cookieVersion;
|
||||
secret = cookieSecret;
|
||||
|
||||
// the cookie secret usage limit is 2^24
|
||||
if ((cookieVersion & 0xFFFFFF) == 0) { // reset the secret
|
||||
System.arraycopy(cookieSecret, 0, legacySecret, 0, 64);
|
||||
secureRandom.nextBytes(cookieSecret);
|
||||
}
|
||||
|
||||
cookieVersion++; // allow wrapped version number
|
||||
}
|
||||
|
||||
MessageDigest md = JsseJce.getMessageDigest(
|
||||
context.negotiatedCipherSuite.hashAlg.name);
|
||||
byte[] headerBytes = clientHello.getHeaderBytes();
|
||||
md.update(headerBytes);
|
||||
byte[] headerCookie = md.digest(secret);
|
||||
|
||||
// hash of ClientHello handshake message
|
||||
context.handshakeHash.update();
|
||||
byte[] clientHelloHash = context.handshakeHash.digest();
|
||||
|
||||
// version and cipher suite
|
||||
//
|
||||
// Store the negotiated cipher suite in the cookie as well.
|
||||
// cookie[0]/[1]: cipher suite
|
||||
// cookie[2]: cookie version
|
||||
// + (hash length): Mac(ClientHello header)
|
||||
// + (hash length): Hash(ClientHello)
|
||||
byte[] prefix = new byte[] {
|
||||
(byte)((context.negotiatedCipherSuite.id >> 8) & 0xFF),
|
||||
(byte)(context.negotiatedCipherSuite.id & 0xFF),
|
||||
(byte)((version >> 24) & 0xFF)
|
||||
};
|
||||
|
||||
byte[] cookie = Arrays.copyOf(prefix,
|
||||
prefix.length + headerCookie.length + clientHelloHash.length);
|
||||
System.arraycopy(headerCookie, 0, cookie,
|
||||
prefix.length, headerCookie.length);
|
||||
System.arraycopy(clientHelloHash, 0, cookie,
|
||||
prefix.length + headerCookie.length, clientHelloHash.length);
|
||||
|
||||
return cookie;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCookieValid(ServerHandshakeContext context,
|
||||
ClientHelloMessage clientHello, byte[] cookie) throws IOException {
|
||||
// no cookie exchange or not a valid cookie length
|
||||
if ((cookie == null) || (cookie.length <= 32)) { // 32: roughly
|
||||
return false;
|
||||
}
|
||||
|
||||
int csId = ((cookie[0] & 0xFF) << 8) | (cookie[1] & 0xFF);
|
||||
CipherSuite cs = CipherSuite.valueOf(csId);
|
||||
if (cs == null || cs.hashAlg == null || cs.hashAlg.hashLength == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int hashLen = cs.hashAlg.hashLength;
|
||||
if (cookie.length != (3 + hashLen * 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] prevHeadCookie =
|
||||
Arrays.copyOfRange(cookie, 3, 3 + hashLen);
|
||||
byte[] prevClientHelloHash =
|
||||
Arrays.copyOfRange(cookie, 3 + hashLen, cookie.length);
|
||||
|
||||
byte[] secret;
|
||||
synchronized (this) {
|
||||
if ((byte)((cookieVersion >> 24) & 0xFF) == cookie[2]) {
|
||||
secret = cookieSecret;
|
||||
} else {
|
||||
secret = legacySecret; // including out of window cookies
|
||||
}
|
||||
}
|
||||
|
||||
MessageDigest md = JsseJce.getMessageDigest(cs.hashAlg.name);
|
||||
byte[] headerBytes = clientHello.getHeaderBytes();
|
||||
md.update(headerBytes);
|
||||
byte[] headerCookie = md.digest(secret);
|
||||
|
||||
if (!MessageDigest.isEqual(headerCookie, prevHeadCookie)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the ClientHello hash in the cookie for transtript
|
||||
// hash calculation for stateless HelloRetryRequest.
|
||||
//
|
||||
// Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
|
||||
// Hash(message_hash || /* Handshake type */
|
||||
// 00 00 Hash.length || /* Handshake message length (bytes) */
|
||||
// Hash(ClientHello1) || /* Hash of ClientHello1 */
|
||||
// HelloRetryRequest || ... || Mn)
|
||||
|
||||
// Reproduce HelloRetryRequest handshake message
|
||||
byte[] hrrMessage =
|
||||
ServerHello.hrrReproducer.produce(context, clientHello);
|
||||
context.handshakeHash.push(hrrMessage);
|
||||
|
||||
// Construct the 1st ClientHello message for transcript hash
|
||||
byte[] hashedClientHello = new byte[4 + hashLen];
|
||||
hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;
|
||||
hashedClientHello[1] = (byte)0x00;
|
||||
hashedClientHello[2] = (byte)0x00;
|
||||
hashedClientHello[3] = (byte)(hashLen & 0xFF);
|
||||
System.arraycopy(prevClientHelloHash, 0,
|
||||
hashedClientHello, 4, hashLen);
|
||||
|
||||
context.handshakeHash.push(hashedClientHello);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
217
jdkSrc/jdk8/sun/security/ssl/HelloRequest.java
Normal file
217
jdkSrc/jdk8/sun/security/ssl/HelloRequest.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the HelloRequest handshake message.
|
||||
*/
|
||||
final class HelloRequest {
|
||||
static final SSLProducer kickstartProducer =
|
||||
new HelloRequestKickstartProducer();
|
||||
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new HelloRequestConsumer();
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new HelloRequestProducer();
|
||||
|
||||
/**
|
||||
* The HelloRequest handshake message.
|
||||
*
|
||||
* [RFC 5246] The HelloRequest message MAY be sent by the server at any
|
||||
* time. HelloRequest is a simple notification that the client should
|
||||
* begin the negotiation process anew.
|
||||
*
|
||||
* struct { } HelloRequest;
|
||||
*/
|
||||
static final class HelloRequestMessage extends HandshakeMessage {
|
||||
HelloRequestMessage(HandshakeContext handshakeContext) {
|
||||
super(handshakeContext);
|
||||
}
|
||||
|
||||
HelloRequestMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
if (m.hasRemaining()) {
|
||||
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Error parsing HelloRequest message: not empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.HELLO_REQUEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream s) throws IOException {
|
||||
// empty, nothing to send
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<empty>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "HelloRequest" handshake message kick start producer.
|
||||
*/
|
||||
private static final
|
||||
class HelloRequestKickstartProducer implements SSLProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HelloRequestKickstartProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
HelloRequestMessage hrm = new HelloRequestMessage(shc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Produced HelloRequest handshake message", hrm);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
hrm.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// update the context
|
||||
|
||||
// What's the expected response?
|
||||
shc.handshakeConsumers.put(
|
||||
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "HelloRequest" handshake message producer.
|
||||
*/
|
||||
private static final class HelloRequestProducer
|
||||
implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HelloRequestProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
HelloRequestMessage hrm = new HelloRequestMessage(shc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Produced HelloRequest handshake message", hrm);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
hrm.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// update the context
|
||||
|
||||
// What's the expected response?
|
||||
shc.handshakeConsumers.put(
|
||||
SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "HelloRequest" handshake message consumer.
|
||||
*/
|
||||
private static final class HelloRequestConsumer
|
||||
implements SSLConsumer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private HelloRequestConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// For TLS 1.2 and prior versions, the HelloRequest message MAY
|
||||
// be sent by the server at any time. Please don't clean up this
|
||||
// handshake consumer.
|
||||
HelloRequestMessage hrm = new HelloRequestMessage(chc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming HelloRequest handshake message", hrm);
|
||||
}
|
||||
|
||||
if (!chc.kickstartMessageDelivered) {
|
||||
if (!chc.conContext.secureRenegotiation &&
|
||||
!HandshakeContext.allowUnsafeRenegotiation) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unsafe renegotiation is not allowed");
|
||||
}
|
||||
|
||||
if (!chc.conContext.secureRenegotiation) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Continue with insecure renegotiation");
|
||||
}
|
||||
}
|
||||
|
||||
// update the responders
|
||||
chc.handshakeProducers.put(
|
||||
SSLHandshake.CLIENT_HELLO.id,
|
||||
SSLHandshake.CLIENT_HELLO);
|
||||
|
||||
//
|
||||
// produce response handshake message
|
||||
//
|
||||
SSLHandshake.CLIENT_HELLO.produce(context, hrm);
|
||||
} else {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ingore HelloRequest, handshaking is in progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
369
jdkSrc/jdk8/sun/security/ssl/InputRecord.java
Normal file
369
jdkSrc/jdk8/sun/security/ssl/InputRecord.java
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
|
||||
/**
|
||||
* {@code InputRecord} takes care of the management of SSL/TLS input
|
||||
* records, including buffering, decryption, handshake messages marshal, etc.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
abstract class InputRecord implements Record, Closeable {
|
||||
SSLReadCipher readCipher;
|
||||
// Needed for KeyUpdate, used after Handshake.Finished
|
||||
TransportContext tc;
|
||||
|
||||
final HandshakeHash handshakeHash;
|
||||
boolean isClosed;
|
||||
|
||||
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
|
||||
// and the first message we read is a ClientHello in V2 format, we convert
|
||||
// it to V3. Otherwise we throw an exception when encountering a V2 hello.
|
||||
ProtocolVersion helloVersion;
|
||||
|
||||
// fragment size
|
||||
int fragmentSize;
|
||||
|
||||
InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
|
||||
this.readCipher = readCipher;
|
||||
this.helloVersion = ProtocolVersion.TLS10;
|
||||
this.handshakeHash = handshakeHash;
|
||||
this.isClosed = false;
|
||||
this.fragmentSize = Record.maxDataSize;
|
||||
}
|
||||
|
||||
void setHelloVersion(ProtocolVersion helloVersion) {
|
||||
this.helloVersion = helloVersion;
|
||||
}
|
||||
|
||||
boolean seqNumIsHuge() {
|
||||
return (readCipher.authenticator != null) &&
|
||||
readCipher.authenticator.seqNumIsHuge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent any more data from being read into this record,
|
||||
* and flag the record as holding no data.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!isClosed) {
|
||||
isClosed = true;
|
||||
readCipher.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized boolean isClosed() {
|
||||
return isClosed;
|
||||
}
|
||||
|
||||
// apply to SSLSocket and SSLEngine
|
||||
void changeReadCiphers(SSLReadCipher readCipher) {
|
||||
|
||||
/*
|
||||
* Dispose of any intermediate state in the underlying cipher.
|
||||
* For PKCS11 ciphers, this will release any attached sessions,
|
||||
* and thus make finalization faster.
|
||||
*
|
||||
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
|
||||
* not necessary to do the same with MAC's.
|
||||
*/
|
||||
readCipher.dispose();
|
||||
|
||||
this.readCipher = readCipher;
|
||||
}
|
||||
|
||||
// change fragment size
|
||||
void changeFragmentSize(int fragmentSize) {
|
||||
this.fragmentSize = fragmentSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is enough inbound data in the ByteBuffer to make
|
||||
* a inbound packet.
|
||||
*
|
||||
* @return -1 if there are not enough bytes to tell (small header),
|
||||
*/
|
||||
// apply to SSLEngine only
|
||||
int bytesInCompletePacket(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
|
||||
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
// apply to SSLSocket only
|
||||
int bytesInCompletePacket() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// apply to SSLSocket only
|
||||
void setReceiverStream(InputStream inputStream) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// read, decrypt and decompress the network record.
|
||||
//
|
||||
abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
|
||||
int srcsLength) throws IOException, BadPaddingException;
|
||||
|
||||
// apply to SSLSocket only
|
||||
void setDeliverStream(OutputStream outputStream) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// calculate plaintext fragment size
|
||||
//
|
||||
// apply to SSLEngine only
|
||||
int estimateFragmentSize(int packetSize) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
//
|
||||
// shared helpers
|
||||
//
|
||||
|
||||
static ByteBuffer convertToClientHello(ByteBuffer packet) {
|
||||
int srcPos = packet.position();
|
||||
|
||||
byte firstByte = packet.get();
|
||||
byte secondByte = packet.get();
|
||||
int recordLen = (((firstByte & 0x7F) << 8) | (secondByte & 0xFF)) + 2;
|
||||
|
||||
packet.position(srcPos + 3); // the V2ClientHello record header
|
||||
|
||||
byte majorVersion = packet.get();
|
||||
byte minorVersion = packet.get();
|
||||
|
||||
int cipherSpecLen = ((packet.get() & 0xFF) << 8) +
|
||||
(packet.get() & 0xFF);
|
||||
int sessionIdLen = ((packet.get() & 0xFF) << 8) +
|
||||
(packet.get() & 0xFF);
|
||||
int nonceLen = ((packet.get() & 0xFF) << 8) +
|
||||
(packet.get() & 0xFF);
|
||||
|
||||
// Required space for the target SSLv3 ClientHello message.
|
||||
// 5: record header size
|
||||
// 4: handshake header size
|
||||
// 2: ClientHello.client_version
|
||||
// 32: ClientHello.random
|
||||
// 1: length byte of ClientHello.session_id
|
||||
// 2: length bytes of ClientHello.cipher_suites
|
||||
// 2: empty ClientHello.compression_methods
|
||||
int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
|
||||
byte[] converted = new byte[requiredSize];
|
||||
|
||||
/*
|
||||
* Build the first part of the V3 record header from the V2 one
|
||||
* that's now buffered up. (Lengths are fixed up later).
|
||||
*/
|
||||
// Note: need not to set the header actually.
|
||||
converted[0] = ContentType.HANDSHAKE.id;
|
||||
converted[1] = majorVersion;
|
||||
converted[2] = minorVersion;
|
||||
// header [3..4] for handshake message length
|
||||
// required size is 5;
|
||||
|
||||
/*
|
||||
* Store the generic V3 handshake header: 4 bytes
|
||||
*/
|
||||
converted[5] = 1; // HandshakeMessage.ht_client_hello
|
||||
// buf [6..8] for length of ClientHello (int24)
|
||||
// required size += 4;
|
||||
|
||||
/*
|
||||
* ClientHello header starts with SSL version
|
||||
*/
|
||||
converted[9] = majorVersion;
|
||||
converted[10] = minorVersion;
|
||||
// required size += 2;
|
||||
int pointer = 11;
|
||||
|
||||
/*
|
||||
* Copy Random value/nonce ... if less than the 32 bytes of
|
||||
* a V3 "Random", right justify and zero pad to the left. Else
|
||||
* just take the last 32 bytes.
|
||||
*/
|
||||
int offset = srcPos + 11 + cipherSpecLen + sessionIdLen;
|
||||
|
||||
if (nonceLen < 32) {
|
||||
for (int i = 0; i < (32 - nonceLen); i++) {
|
||||
converted[pointer++] = 0;
|
||||
}
|
||||
packet.position(offset);
|
||||
packet.get(converted, pointer, nonceLen);
|
||||
|
||||
pointer += nonceLen;
|
||||
} else {
|
||||
packet.position(offset + nonceLen - 32);
|
||||
packet.get(converted, pointer, 32);
|
||||
|
||||
pointer += 32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy session ID (only one byte length!)
|
||||
*/
|
||||
offset -= sessionIdLen;
|
||||
converted[pointer++] = (byte)(sessionIdLen & 0xFF);
|
||||
packet.position(offset);
|
||||
packet.get(converted, pointer, sessionIdLen);
|
||||
|
||||
/*
|
||||
* Copy and translate cipher suites ... V2 specs with first byte zero
|
||||
* are really V3 specs (in the last 2 bytes), just copy those and drop
|
||||
* the other ones. Preference order remains unchanged.
|
||||
*
|
||||
* Example: Netscape Navigator 3.0 (exportable) says:
|
||||
*
|
||||
* 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
|
||||
* 0/6, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5
|
||||
*
|
||||
* Microsoft Internet Explorer 3.0 (exportable) supports only
|
||||
*
|
||||
* 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5
|
||||
*/
|
||||
int j;
|
||||
|
||||
offset -= cipherSpecLen;
|
||||
packet.position(offset);
|
||||
|
||||
j = pointer + 2;
|
||||
for (int i = 0; i < cipherSpecLen; i += 3) {
|
||||
if (packet.get() != 0) {
|
||||
// Ignore version 2.0 specific cipher suite. Clients
|
||||
// should also include the version 3.0 equivalent in
|
||||
// the V2ClientHello message.
|
||||
packet.get(); // ignore the 2nd byte
|
||||
packet.get(); // ignore the 3rd byte
|
||||
continue;
|
||||
}
|
||||
|
||||
converted[j++] = packet.get();
|
||||
converted[j++] = packet.get();
|
||||
}
|
||||
|
||||
j -= pointer + 2;
|
||||
converted[pointer++] = (byte)((j >>> 8) & 0xFF);
|
||||
converted[pointer++] = (byte)(j & 0xFF);
|
||||
pointer += j;
|
||||
|
||||
/*
|
||||
* Append compression methods (default/null only)
|
||||
*/
|
||||
converted[pointer++] = 1;
|
||||
converted[pointer++] = 0; // Session.compression_null
|
||||
|
||||
/*
|
||||
* Fill in lengths of the messages we synthesized (nested:
|
||||
* V3 handshake message within V3 record).
|
||||
*/
|
||||
// Note: need not to set the header actually.
|
||||
int fragLen = pointer - 5; // TLSPlaintext.length
|
||||
converted[3] = (byte)((fragLen >>> 8) & 0xFF);
|
||||
converted[4] = (byte)(fragLen & 0xFF);
|
||||
|
||||
/*
|
||||
* Handshake.length, length of ClientHello message
|
||||
*/
|
||||
fragLen = pointer - 9; // Handshake.length
|
||||
converted[6] = (byte)((fragLen >>> 16) & 0xFF);
|
||||
converted[7] = (byte)((fragLen >>> 8) & 0xFF);
|
||||
converted[8] = (byte)(fragLen & 0xFF);
|
||||
|
||||
// consume the full record
|
||||
packet.position(srcPos + recordLen);
|
||||
|
||||
// Need no header bytes.
|
||||
return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size
|
||||
}
|
||||
|
||||
// Extract an SSL/TLS record from the specified source buffers.
|
||||
static ByteBuffer extract(
|
||||
ByteBuffer[] buffers, int offset, int length, int headerSize) {
|
||||
|
||||
boolean hasFullHeader = false;
|
||||
int contentLen = -1;
|
||||
for (int i = offset, j = 0;
|
||||
i < (offset + length) && j < headerSize; i++) {
|
||||
int remains = buffers[i].remaining();
|
||||
int pos = buffers[i].position();
|
||||
for (int k = 0; k < remains && j < headerSize; j++, k++) {
|
||||
byte b = buffers[i].get(pos + k);
|
||||
if (j == (headerSize - 2)) {
|
||||
contentLen = ((b & 0xFF) << 8);
|
||||
} else if (j == (headerSize -1)) {
|
||||
contentLen |= (b & 0xFF);
|
||||
hasFullHeader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFullHeader) {
|
||||
throw new BufferUnderflowException();
|
||||
}
|
||||
|
||||
int packetLen = headerSize + contentLen;
|
||||
int remains = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
remains += buffers[i].remaining();
|
||||
if (remains >= packetLen) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remains < packetLen) {
|
||||
throw new BufferUnderflowException();
|
||||
}
|
||||
|
||||
byte[] packet = new byte[packetLen];
|
||||
int packetOffset = 0;
|
||||
int packetSpaces = packetLen;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
if (buffers[i].hasRemaining()) {
|
||||
int len = Math.min(packetSpaces, buffers[i].remaining());
|
||||
buffers[i].get(packet, packetOffset, len);
|
||||
packetOffset += len;
|
||||
packetSpaces -= len;
|
||||
}
|
||||
|
||||
if (packetSpaces <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ByteBuffer.wrap(packet);
|
||||
}
|
||||
}
|
||||
435
jdkSrc/jdk8/sun/security/ssl/JsseJce.java
Normal file
435
jdkSrc/jdk8/sun/security/ssl/JsseJce.java
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.*;
|
||||
import java.util.*;
|
||||
import javax.crypto.*;
|
||||
import sun.security.jca.ProviderList;
|
||||
import sun.security.jca.Providers;
|
||||
import static sun.security.ssl.SunJSSE.cryptoProvider;
|
||||
import sun.security.util.ECUtil;
|
||||
import static sun.security.util.SecurityConstants.PROVIDER_VER;
|
||||
|
||||
/**
|
||||
* This class contains a few static methods for interaction with the JCA/JCE
|
||||
* to obtain implementations, etc.
|
||||
*
|
||||
* @author Andreas Sterbenz
|
||||
*/
|
||||
final class JsseJce {
|
||||
static final boolean ALLOW_ECC =
|
||||
Utilities.getBooleanProperty("com.sun.net.ssl.enableECC", true);
|
||||
|
||||
private static final ProviderList fipsProviderList;
|
||||
|
||||
// Flag indicating whether Kerberos crypto is available.
|
||||
// If true, then all the Kerberos-based crypto we need is available.
|
||||
private final static boolean kerberosAvailable;
|
||||
static {
|
||||
boolean temp;
|
||||
try {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
// Test for Kerberos using the bootstrap class loader
|
||||
Class.forName("sun.security.krb5.PrincipalName", true,
|
||||
null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
temp = true;
|
||||
|
||||
} catch (Exception e) {
|
||||
temp = false;
|
||||
}
|
||||
kerberosAvailable = temp;
|
||||
}
|
||||
|
||||
static {
|
||||
// force FIPS flag initialization
|
||||
// Because isFIPS() is synchronized and cryptoProvider is not modified
|
||||
// after it completes, this also eliminates the need for any further
|
||||
// synchronization when accessing cryptoProvider
|
||||
if (SunJSSE.isFIPS() == false) {
|
||||
fipsProviderList = null;
|
||||
} else {
|
||||
// Setup a ProviderList that can be used by the trust manager
|
||||
// during certificate chain validation. All the crypto must be
|
||||
// from the FIPS provider, but we also allow the required
|
||||
// certificate related services from the SUN provider.
|
||||
Provider sun = Security.getProvider("SUN");
|
||||
if (sun == null) {
|
||||
throw new RuntimeException
|
||||
("FIPS mode: SUN provider must be installed");
|
||||
}
|
||||
Provider sunCerts = new SunCertificates(sun);
|
||||
fipsProviderList = ProviderList.newList(cryptoProvider, sunCerts);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SunCertificates extends Provider {
|
||||
private static final long serialVersionUID = -3284138292032213752L;
|
||||
|
||||
SunCertificates(final Provider p) {
|
||||
super("SunCertificates", PROVIDER_VER, "SunJSSE internal");
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
// copy certificate related services from the Sun provider
|
||||
for (Map.Entry<Object,Object> entry : p.entrySet()) {
|
||||
String key = (String)entry.getKey();
|
||||
if (key.startsWith("CertPathValidator.")
|
||||
|| key.startsWith("CertPathBuilder.")
|
||||
|| key.startsWith("CertStore.")
|
||||
|| key.startsWith("CertificateFactory.")) {
|
||||
put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JCE transformation string for RSA with PKCS#1 v1.5 padding.
|
||||
* Can be used for encryption, decryption, signing, verifying.
|
||||
*/
|
||||
static final String CIPHER_RSA_PKCS1 = "RSA/ECB/PKCS1Padding";
|
||||
|
||||
/**
|
||||
* JCE transformation string for the stream cipher RC4.
|
||||
*/
|
||||
static final String CIPHER_RC4 = "RC4";
|
||||
|
||||
/**
|
||||
* JCE transformation string for DES in CBC mode without padding.
|
||||
*/
|
||||
static final String CIPHER_DES = "DES/CBC/NoPadding";
|
||||
|
||||
/**
|
||||
* JCE transformation string for (3-key) Triple DES in CBC mode
|
||||
* without padding.
|
||||
*/
|
||||
static final String CIPHER_3DES = "DESede/CBC/NoPadding";
|
||||
|
||||
/**
|
||||
* JCE transformation string for AES in CBC mode
|
||||
* without padding.
|
||||
*/
|
||||
static final String CIPHER_AES = "AES/CBC/NoPadding";
|
||||
|
||||
/**
|
||||
* JCE transformation string for AES in GCM mode
|
||||
* without padding.
|
||||
*/
|
||||
static final String CIPHER_AES_GCM = "AES/GCM/NoPadding";
|
||||
|
||||
/**
|
||||
* JCA identifier string for DSA, i.e. a DSA with SHA-1.
|
||||
*/
|
||||
static final String SIGNATURE_DSA = "DSA";
|
||||
|
||||
/**
|
||||
* JCA identifier string for ECDSA, i.e. a ECDSA with SHA-1.
|
||||
*/
|
||||
static final String SIGNATURE_ECDSA = "SHA1withECDSA";
|
||||
|
||||
/**
|
||||
* JCA identifier string for Raw DSA, i.e. a DSA signature without
|
||||
* hashing where the application provides the SHA-1 hash of the data.
|
||||
* Note that the standard name is "NONEwithDSA" but we use "RawDSA"
|
||||
* for compatibility.
|
||||
*/
|
||||
static final String SIGNATURE_RAWDSA = "RawDSA";
|
||||
|
||||
/**
|
||||
* JCA identifier string for Raw ECDSA, i.e. a DSA signature without
|
||||
* hashing where the application provides the SHA-1 hash of the data.
|
||||
*/
|
||||
static final String SIGNATURE_RAWECDSA = "NONEwithECDSA";
|
||||
|
||||
/**
|
||||
* JCA identifier string for Raw RSA, i.e. a RSA PKCS#1 v1.5 signature
|
||||
* without hashing where the application provides the hash of the data.
|
||||
* Used for RSA client authentication with a 36 byte hash.
|
||||
*/
|
||||
static final String SIGNATURE_RAWRSA = "NONEwithRSA";
|
||||
|
||||
/**
|
||||
* JCA identifier string for the SSL/TLS style RSA Signature. I.e.
|
||||
* an signature using RSA with PKCS#1 v1.5 padding signing a
|
||||
* concatenation of an MD5 and SHA-1 digest.
|
||||
*/
|
||||
static final String SIGNATURE_SSLRSA = "MD5andSHA1withRSA";
|
||||
|
||||
private JsseJce() {
|
||||
// no instantiation of this class
|
||||
}
|
||||
|
||||
static boolean isEcAvailable() {
|
||||
return EcAvailability.isAvailable;
|
||||
}
|
||||
|
||||
static boolean isKerberosAvailable() {
|
||||
return kerberosAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an JCE cipher implementation for the specified algorithm.
|
||||
*/
|
||||
static Cipher getCipher(String transformation)
|
||||
throws NoSuchAlgorithmException {
|
||||
try {
|
||||
if (cryptoProvider == null) {
|
||||
return Cipher.getInstance(transformation);
|
||||
} else {
|
||||
return Cipher.getInstance(transformation, cryptoProvider);
|
||||
}
|
||||
} catch (NoSuchPaddingException e) {
|
||||
throw new NoSuchAlgorithmException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an JCA signature implementation for the specified algorithm.
|
||||
* The algorithm string should be one of the constants defined
|
||||
* in this class.
|
||||
*/
|
||||
static Signature getSignature(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return Signature.getInstance(algorithm);
|
||||
} else {
|
||||
// reference equality
|
||||
if (algorithm == SIGNATURE_SSLRSA) {
|
||||
// The SunPKCS11 provider currently does not support this
|
||||
// special algorithm. We allow a fallback in this case because
|
||||
// the SunJSSE implementation does the actual crypto using
|
||||
// a NONEwithRSA signature obtained from the cryptoProvider.
|
||||
if (cryptoProvider.getService("Signature", algorithm) == null) {
|
||||
// Calling Signature.getInstance() and catching the
|
||||
// exception would be cleaner, but exceptions are a little
|
||||
// expensive. So we check directly via getService().
|
||||
try {
|
||||
return Signature.getInstance(algorithm, "SunJSSE");
|
||||
} catch (NoSuchProviderException e) {
|
||||
throw new NoSuchAlgorithmException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Signature.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static KeyGenerator getKeyGenerator(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return KeyGenerator.getInstance(algorithm);
|
||||
} else {
|
||||
return KeyGenerator.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static KeyPairGenerator getKeyPairGenerator(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return KeyPairGenerator.getInstance(algorithm);
|
||||
} else {
|
||||
return KeyPairGenerator.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static KeyAgreement getKeyAgreement(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return KeyAgreement.getInstance(algorithm);
|
||||
} else {
|
||||
return KeyAgreement.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static Mac getMac(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return Mac.getInstance(algorithm);
|
||||
} else {
|
||||
return Mac.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static KeyFactory getKeyFactory(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return KeyFactory.getInstance(algorithm);
|
||||
} else {
|
||||
return KeyFactory.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static AlgorithmParameters getAlgorithmParameters(String algorithm)
|
||||
throws NoSuchAlgorithmException {
|
||||
if (cryptoProvider == null) {
|
||||
return AlgorithmParameters.getInstance(algorithm);
|
||||
} else {
|
||||
return AlgorithmParameters.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
}
|
||||
|
||||
static SecureRandom getSecureRandom() throws KeyManagementException {
|
||||
if (cryptoProvider == null) {
|
||||
return new SecureRandom();
|
||||
}
|
||||
// Try "PKCS11" first. If that is not supported, iterate through
|
||||
// the provider and return the first working implementation.
|
||||
try {
|
||||
return SecureRandom.getInstance("PKCS11", cryptoProvider);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// ignore
|
||||
}
|
||||
for (Provider.Service s : cryptoProvider.getServices()) {
|
||||
if (s.getType().equals("SecureRandom")) {
|
||||
try {
|
||||
return SecureRandom.getInstance(
|
||||
s.getAlgorithm(), cryptoProvider);
|
||||
} catch (NoSuchAlgorithmException ee) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new KeyManagementException("FIPS mode: no SecureRandom "
|
||||
+ " implementation found in provider " + cryptoProvider.getName());
|
||||
}
|
||||
|
||||
static MessageDigest getMD5() {
|
||||
return getMessageDigest("MD5");
|
||||
}
|
||||
|
||||
static MessageDigest getSHA() {
|
||||
return getMessageDigest("SHA");
|
||||
}
|
||||
|
||||
static MessageDigest getMessageDigest(String algorithm) {
|
||||
try {
|
||||
if (cryptoProvider == null) {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
} else {
|
||||
return MessageDigest.getInstance(algorithm, cryptoProvider);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException
|
||||
("Algorithm " + algorithm + " not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
static int getRSAKeyLength(PublicKey key) {
|
||||
BigInteger modulus;
|
||||
if (key instanceof RSAPublicKey) {
|
||||
modulus = ((RSAPublicKey)key).getModulus();
|
||||
} else {
|
||||
RSAPublicKeySpec spec = getRSAPublicKeySpec(key);
|
||||
modulus = spec.getModulus();
|
||||
}
|
||||
return modulus.bitLength();
|
||||
}
|
||||
|
||||
static RSAPublicKeySpec getRSAPublicKeySpec(PublicKey key) {
|
||||
if (key instanceof RSAPublicKey) {
|
||||
RSAPublicKey rsaKey = (RSAPublicKey)key;
|
||||
return new RSAPublicKeySpec(rsaKey.getModulus(),
|
||||
rsaKey.getPublicExponent());
|
||||
}
|
||||
try {
|
||||
KeyFactory factory = JsseJce.getKeyFactory("RSA");
|
||||
return factory.getKeySpec(key, RSAPublicKeySpec.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static ECParameterSpec getECParameterSpec(String namedCurveOid) {
|
||||
return ECUtil.getECParameterSpec(cryptoProvider, namedCurveOid);
|
||||
}
|
||||
|
||||
static String getNamedCurveOid(ECParameterSpec params) {
|
||||
return ECUtil.getCurveName(cryptoProvider, params);
|
||||
}
|
||||
|
||||
static ECPoint decodePoint(byte[] encoded, EllipticCurve curve)
|
||||
throws java.io.IOException {
|
||||
return ECUtil.decodePoint(encoded, curve);
|
||||
}
|
||||
|
||||
static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
|
||||
return ECUtil.encodePoint(point, curve);
|
||||
}
|
||||
|
||||
// In FIPS mode, set thread local providers; otherwise a no-op.
|
||||
// Must be paired with endFipsProvider.
|
||||
static Object beginFipsProvider() {
|
||||
if (fipsProviderList == null) {
|
||||
return null;
|
||||
} else {
|
||||
return Providers.beginThreadProviderList(fipsProviderList);
|
||||
}
|
||||
}
|
||||
|
||||
static void endFipsProvider(Object o) {
|
||||
if (fipsProviderList != null) {
|
||||
Providers.endThreadProviderList((ProviderList)o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// lazy initialization holder class idiom for static default parameters
|
||||
//
|
||||
// See Effective Java Second Edition: Item 71.
|
||||
private static class EcAvailability {
|
||||
// Is EC crypto available?
|
||||
private static final boolean isAvailable;
|
||||
|
||||
static {
|
||||
boolean mediator = true;
|
||||
try {
|
||||
JsseJce.getSignature(SIGNATURE_ECDSA);
|
||||
JsseJce.getSignature(SIGNATURE_RAWECDSA);
|
||||
JsseJce.getKeyAgreement("ECDH");
|
||||
JsseJce.getKeyFactory("EC");
|
||||
JsseJce.getKeyPairGenerator("EC");
|
||||
JsseJce.getAlgorithmParameters("EC");
|
||||
} catch (Exception e) {
|
||||
mediator = false;
|
||||
}
|
||||
|
||||
isAvailable = mediator;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
jdkSrc/jdk8/sun/security/ssl/KeyManagerFactoryImpl.java
Normal file
130
jdkSrc/jdk8/sun/security/ssl/KeyManagerFactoryImpl.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.KeyStore.*;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
|
||||
abstract class KeyManagerFactoryImpl extends KeyManagerFactorySpi {
|
||||
|
||||
X509ExtendedKeyManager keyManager;
|
||||
boolean isInitialized;
|
||||
|
||||
KeyManagerFactoryImpl() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one key manager for each type of key material.
|
||||
*/
|
||||
@Override
|
||||
protected KeyManager[] engineGetKeyManagers() {
|
||||
if (!isInitialized) {
|
||||
throw new IllegalStateException(
|
||||
"KeyManagerFactoryImpl is not initialized");
|
||||
}
|
||||
return new KeyManager[] { keyManager };
|
||||
}
|
||||
|
||||
// Factory for the SunX509 keymanager
|
||||
public static final class SunX509 extends KeyManagerFactoryImpl {
|
||||
|
||||
@Override
|
||||
protected void engineInit(KeyStore ks, char[] password) throws
|
||||
KeyStoreException, NoSuchAlgorithmException,
|
||||
UnrecoverableKeyException {
|
||||
if ((ks != null) && SunJSSE.isFIPS()) {
|
||||
if (ks.getProvider() != SunJSSE.cryptoProvider) {
|
||||
throw new KeyStoreException("FIPS mode: KeyStore must be "
|
||||
+ "from provider " + SunJSSE.cryptoProvider.getName());
|
||||
}
|
||||
}
|
||||
keyManager = new SunX509KeyManagerImpl(ks, password);
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInit(ManagerFactoryParameters spec) throws
|
||||
InvalidAlgorithmParameterException {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"SunX509KeyManager does not use ManagerFactoryParameters");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Factory for the X509 keymanager
|
||||
public static final class X509 extends KeyManagerFactoryImpl {
|
||||
|
||||
@Override
|
||||
protected void engineInit(KeyStore ks, char[] password) throws
|
||||
KeyStoreException, NoSuchAlgorithmException,
|
||||
UnrecoverableKeyException {
|
||||
if (ks == null) {
|
||||
keyManager = new X509KeyManagerImpl(
|
||||
Collections.<Builder>emptyList());
|
||||
} else {
|
||||
if (SunJSSE.isFIPS() &&
|
||||
(ks.getProvider() != SunJSSE.cryptoProvider)) {
|
||||
throw new KeyStoreException(
|
||||
"FIPS mode: KeyStore must be " +
|
||||
"from provider " + SunJSSE.cryptoProvider.getName());
|
||||
}
|
||||
try {
|
||||
Builder builder = Builder.newInstance(ks,
|
||||
new PasswordProtection(password));
|
||||
keyManager = new X509KeyManagerImpl(builder);
|
||||
} catch (RuntimeException e) {
|
||||
throw new KeyStoreException("initialization failed", e);
|
||||
}
|
||||
}
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInit(ManagerFactoryParameters params) throws
|
||||
InvalidAlgorithmParameterException {
|
||||
if (params instanceof KeyStoreBuilderParameters == false) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Parameters must be instance of KeyStoreBuilderParameters");
|
||||
}
|
||||
if (SunJSSE.isFIPS()) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("FIPS mode: KeyStoreBuilderParameters not supported");
|
||||
}
|
||||
List<Builder> builders =
|
||||
((KeyStoreBuilderParameters)params).getParameters();
|
||||
keyManager = new X509KeyManagerImpl(builders);
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
954
jdkSrc/jdk8/sun/security/ssl/KeyShareExtension.java
Normal file
954
jdkSrc/jdk8/sun/security/ssl/KeyShareExtension.java
Normal file
@@ -0,0 +1,954 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import sun.security.ssl.DHKeyExchange.DHECredentials;
|
||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
||||
import sun.security.ssl.KeyShareExtension.CHKeyShareSpec;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the "key_share" extensions.
|
||||
*/
|
||||
final class KeyShareExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHKeyShareProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHKeyShareConsumer();
|
||||
static final SSLStringizer chStringizer =
|
||||
new CHKeyShareStringizer();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer =
|
||||
new SHKeyShareProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHKeyShareConsumer();
|
||||
static final HandshakeAbsence shOnLoadAbsence =
|
||||
new SHKeyShareAbsence();
|
||||
static final SSLStringizer shStringizer =
|
||||
new SHKeyShareStringizer();
|
||||
|
||||
static final HandshakeProducer hrrNetworkProducer =
|
||||
new HRRKeyShareProducer();
|
||||
static final ExtensionConsumer hrrOnLoadConsumer =
|
||||
new HRRKeyShareConsumer();
|
||||
static final HandshakeProducer hrrNetworkReproducer =
|
||||
new HRRKeyShareReproducer();
|
||||
static final SSLStringizer hrrStringizer =
|
||||
new HRRKeyShareStringizer();
|
||||
|
||||
/**
|
||||
* The key share entry used in "key_share" extensions.
|
||||
*/
|
||||
private static final class KeyShareEntry {
|
||||
final int namedGroupId;
|
||||
final byte[] keyExchange;
|
||||
|
||||
private KeyShareEntry(int namedGroupId, byte[] keyExchange) {
|
||||
this.namedGroupId = namedGroupId;
|
||||
this.keyExchange = keyExchange;
|
||||
}
|
||||
|
||||
private byte[] getEncoded() {
|
||||
byte[] buffer = new byte[keyExchange.length + 4];
|
||||
// 2: named group id
|
||||
// +2: key exchange length
|
||||
ByteBuffer m = ByteBuffer.wrap(buffer);
|
||||
try {
|
||||
Record.putInt16(m, namedGroupId);
|
||||
Record.putBytes16(m, keyExchange);
|
||||
} catch (IOException ioe) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Unlikely IOException", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private int getEncodedSize() {
|
||||
return keyExchange.length + 4; // 2: named group id
|
||||
// +2: key exchange length
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\n'{'\n" +
|
||||
" \"named group\": {0}\n" +
|
||||
" \"key_exchange\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}'\n" +
|
||||
"'}',", Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
NamedGroup.nameOf(namedGroupId),
|
||||
Utilities.indent(hexEncoder.encode(keyExchange), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "key_share" extension in a ClientHello handshake message.
|
||||
*/
|
||||
static final class CHKeyShareSpec implements SSLExtensionSpec {
|
||||
final List<KeyShareEntry> clientShares;
|
||||
|
||||
private CHKeyShareSpec(List<KeyShareEntry> clientShares) {
|
||||
this.clientShares = clientShares;
|
||||
}
|
||||
|
||||
private CHKeyShareSpec(ByteBuffer buffer) throws IOException {
|
||||
// struct {
|
||||
// KeyShareEntry client_shares<0..2^16-1>;
|
||||
// } KeyShareClientHello;
|
||||
if (buffer.remaining() < 2) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: " +
|
||||
"insufficient data (length=" + buffer.remaining() + ")");
|
||||
}
|
||||
|
||||
int listLen = Record.getInt16(buffer);
|
||||
if (listLen != buffer.remaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: " +
|
||||
"incorrect list length (length=" + listLen + ")");
|
||||
}
|
||||
|
||||
List<KeyShareEntry> keyShares = new LinkedList<>();
|
||||
while (buffer.hasRemaining()) {
|
||||
int namedGroupId = Record.getInt16(buffer);
|
||||
byte[] keyExchange = Record.getBytes16(buffer);
|
||||
if (keyExchange.length == 0) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: empty key_exchange");
|
||||
}
|
||||
|
||||
keyShares.add(new KeyShareEntry(namedGroupId, keyExchange));
|
||||
}
|
||||
|
||||
this.clientShares = Collections.unmodifiableList(keyShares);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"client_shares\": '['{0}\n']'", Locale.ENGLISH);
|
||||
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
for (KeyShareEntry entry : clientShares) {
|
||||
builder.append(entry.toString());
|
||||
}
|
||||
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(builder.toString())
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CHKeyShareStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new CHKeyShareSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension in a ClientHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHKeyShareProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHKeyShareProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable key_share extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<NamedGroup> namedGroups;
|
||||
if (chc.serverSelectedNamedGroup != null) {
|
||||
// Response to HelloRetryRequest
|
||||
namedGroups = Arrays.asList(chc.serverSelectedNamedGroup);
|
||||
} else {
|
||||
namedGroups = chc.clientRequestedNamedGroups;
|
||||
if (namedGroups == null || namedGroups.isEmpty()) {
|
||||
// No supported groups.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore key_share extension, no supported groups");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
List<KeyShareEntry> keyShares = new LinkedList<>();
|
||||
for (NamedGroup ng : namedGroups) {
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
|
||||
if (ke == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"No key exchange for named group " + ng.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SSLPossession[] poses = ke.createPossessions(chc);
|
||||
for (SSLPossession pos : poses) {
|
||||
// update the context
|
||||
chc.handshakePossessions.add(pos);
|
||||
if (!(pos instanceof ECDHEPossession) &&
|
||||
!(pos instanceof DHEPossession)) {
|
||||
// May need more possesion types in the future.
|
||||
continue;
|
||||
}
|
||||
|
||||
keyShares.add(new KeyShareEntry(ng.id, pos.encode()));
|
||||
}
|
||||
|
||||
// One key share entry only. Too much key share entries makes
|
||||
// the ClientHello handshake message really big.
|
||||
if (!keyShares.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int listLen = 0;
|
||||
for (KeyShareEntry entry : keyShares) {
|
||||
listLen += entry.getEncodedSize();
|
||||
}
|
||||
byte[] extData = new byte[listLen + 2]; // 2: list length
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt16(m, listLen);
|
||||
for (KeyShareEntry entry : keyShares) {
|
||||
m.put(entry.getEncoded());
|
||||
}
|
||||
|
||||
// update the context
|
||||
chc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE,
|
||||
new CHKeyShareSpec(keyShares));
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of the extension in a ClientHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class CHKeyShareConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHKeyShareConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"The key_share extension has been loaded");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable key_share extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension
|
||||
CHKeyShareSpec spec;
|
||||
try {
|
||||
spec = new CHKeyShareSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
List<SSLCredentials> credentials = new LinkedList<>();
|
||||
for (KeyShareEntry entry : spec.clientShares) {
|
||||
NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
|
||||
if (ng == null || !SupportedGroups.isActivatable(
|
||||
shc.algorithmConstraints, ng)) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unsupported named group: " +
|
||||
NamedGroup.nameOf(entry.namedGroupId));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||
try {
|
||||
ECDHECredentials ecdhec =
|
||||
ECDHECredentials.valueOf(ng, entry.keyExchange);
|
||||
if (ecdhec != null) {
|
||||
if (shc.algorithmConstraints != null &&
|
||||
!shc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
ecdhec.popPublicKey)) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"ECDHE key share entry does not " +
|
||||
"comply to algorithm constraints");
|
||||
}
|
||||
} else {
|
||||
credentials.add(ecdhec);
|
||||
}
|
||||
}
|
||||
} catch (IOException | GeneralSecurityException ex) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Cannot decode named group: " +
|
||||
NamedGroup.nameOf(entry.namedGroupId));
|
||||
}
|
||||
}
|
||||
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||
try {
|
||||
DHECredentials dhec =
|
||||
DHECredentials.valueOf(ng, entry.keyExchange);
|
||||
if (dhec != null) {
|
||||
if (shc.algorithmConstraints != null &&
|
||||
!shc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
dhec.popPublicKey)) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"DHE key share entry does not " +
|
||||
"comply to algorithm constraints");
|
||||
}
|
||||
} else {
|
||||
credentials.add(dhec);
|
||||
}
|
||||
}
|
||||
} catch (IOException | GeneralSecurityException ex) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Cannot decode named group: " +
|
||||
NamedGroup.nameOf(entry.namedGroupId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!credentials.isEmpty()) {
|
||||
shc.handshakeCredentials.addAll(credentials);
|
||||
} else {
|
||||
// New handshake credentials are required from the client side.
|
||||
shc.handshakeProducers.put(
|
||||
SSLHandshake.HELLO_RETRY_REQUEST.id,
|
||||
SSLHandshake.HELLO_RETRY_REQUEST);
|
||||
}
|
||||
|
||||
// update the context
|
||||
shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The key share entry used in ServerHello "key_share" extensions.
|
||||
*/
|
||||
static final class SHKeyShareSpec implements SSLExtensionSpec {
|
||||
final KeyShareEntry serverShare;
|
||||
|
||||
SHKeyShareSpec(KeyShareEntry serverShare) {
|
||||
this.serverShare = serverShare;
|
||||
}
|
||||
|
||||
private SHKeyShareSpec(ByteBuffer buffer) throws IOException {
|
||||
// struct {
|
||||
// KeyShareEntry server_share;
|
||||
// } KeyShareServerHello;
|
||||
if (buffer.remaining() < 5) { // 5: minimal server_share
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: " +
|
||||
"insufficient data (length=" + buffer.remaining() + ")");
|
||||
}
|
||||
|
||||
int namedGroupId = Record.getInt16(buffer);
|
||||
byte[] keyExchange = Record.getBytes16(buffer);
|
||||
|
||||
if (buffer.hasRemaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: unknown extra data");
|
||||
}
|
||||
|
||||
this.serverShare = new KeyShareEntry(namedGroupId, keyExchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"server_share\": '{'\n" +
|
||||
" \"named group\": {0}\n" +
|
||||
" \"key_exchange\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}'\n" +
|
||||
"'}',", Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
NamedGroup.nameOf(serverShare.namedGroupId),
|
||||
Utilities.indent(
|
||||
hexEncoder.encode(serverShare.keyExchange), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SHKeyShareStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new SHKeyShareSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension in a ServerHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class SHKeyShareProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHKeyShareProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// In response to key_share request only
|
||||
CHKeyShareSpec kss =
|
||||
(CHKeyShareSpec)shc.handshakeExtensions.get(
|
||||
SSLExtension.CH_KEY_SHARE);
|
||||
if (kss == null) {
|
||||
// Unlikely, no key_share extension requested.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore, no client key_share extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore, no available server key_share extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// use requested key share entries
|
||||
if ((shc.handshakeCredentials == null) ||
|
||||
shc.handshakeCredentials.isEmpty()) {
|
||||
// Unlikely, HelloRetryRequest should be used ealier.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"No available client key share entries");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyShareEntry keyShare = null;
|
||||
for (SSLCredentials cd : shc.handshakeCredentials) {
|
||||
NamedGroup ng = null;
|
||||
if (cd instanceof ECDHECredentials) {
|
||||
ng = ((ECDHECredentials)cd).namedGroup;
|
||||
} else if (cd instanceof DHECredentials) {
|
||||
ng = ((DHECredentials)cd).namedGroup;
|
||||
}
|
||||
|
||||
if (ng == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
|
||||
if (ke == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"No key exchange for named group " + ng.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SSLPossession[] poses = ke.createPossessions(shc);
|
||||
for (SSLPossession pos : poses) {
|
||||
if (!(pos instanceof ECDHEPossession) &&
|
||||
!(pos instanceof DHEPossession)) {
|
||||
// May need more possesion types in the future.
|
||||
continue;
|
||||
}
|
||||
|
||||
// update the context
|
||||
shc.handshakeKeyExchange = ke;
|
||||
shc.handshakePossessions.add(pos);
|
||||
keyShare = new KeyShareEntry(ng.id, pos.encode());
|
||||
break;
|
||||
}
|
||||
|
||||
if (keyShare != null) {
|
||||
for (Map.Entry<Byte, HandshakeProducer> me :
|
||||
ke.getHandshakeProducers(shc)) {
|
||||
shc.handshakeProducers.put(
|
||||
me.getKey(), me.getValue());
|
||||
}
|
||||
|
||||
// We have got one! Don't forgor to break.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyShare == null) {
|
||||
// Unlikely, HelloRetryRequest should be used instead ealier.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"No available server key_share extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] extData = keyShare.getEncoded();
|
||||
|
||||
// update the context
|
||||
SHKeyShareSpec spec = new SHKeyShareSpec(keyShare);
|
||||
shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of the extension in a ServerHello
|
||||
* handshake message.
|
||||
*/
|
||||
private static final class SHKeyShareConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHKeyShareConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// Happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
if (chc.clientRequestedNamedGroups == null ||
|
||||
chc.clientRequestedNamedGroups.isEmpty()) {
|
||||
// No supported groups.
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected key_share extension in ServerHello");
|
||||
}
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported key_share extension in ServerHello");
|
||||
}
|
||||
|
||||
// Parse the extension
|
||||
SHKeyShareSpec spec;
|
||||
try {
|
||||
spec = new SHKeyShareSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
KeyShareEntry keyShare = spec.serverShare;
|
||||
NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
|
||||
if (ng == null || !SupportedGroups.isActivatable(
|
||||
chc.algorithmConstraints, ng)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported named group: " +
|
||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(ng);
|
||||
if (ke == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"No key exchange for named group " + ng.name);
|
||||
}
|
||||
|
||||
SSLCredentials credentials = null;
|
||||
if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||
try {
|
||||
ECDHECredentials ecdhec =
|
||||
ECDHECredentials.valueOf(ng, keyShare.keyExchange);
|
||||
if (ecdhec != null) {
|
||||
if (chc.algorithmConstraints != null &&
|
||||
!chc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
ecdhec.popPublicKey)) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"ECDHE key share entry does not " +
|
||||
"comply to algorithm constraints");
|
||||
} else {
|
||||
credentials = ecdhec;
|
||||
}
|
||||
}
|
||||
} catch (IOException | GeneralSecurityException ex) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Cannot decode named group: " +
|
||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||
}
|
||||
} else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||
try {
|
||||
DHECredentials dhec =
|
||||
DHECredentials.valueOf(ng, keyShare.keyExchange);
|
||||
if (dhec != null) {
|
||||
if (chc.algorithmConstraints != null &&
|
||||
!chc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
dhec.popPublicKey)) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"DHE key share entry does not " +
|
||||
"comply to algorithm constraints");
|
||||
} else {
|
||||
credentials = dhec;
|
||||
}
|
||||
}
|
||||
} catch (IOException | GeneralSecurityException ex) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Cannot decode named group: " +
|
||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||
}
|
||||
} else {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported named group: " +
|
||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||
}
|
||||
|
||||
if (credentials == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported named group: " + ng.name);
|
||||
}
|
||||
|
||||
// update the context
|
||||
chc.handshakeKeyExchange = ke;
|
||||
chc.handshakeCredentials.add(credentials);
|
||||
chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if the extension is not present in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final class SHKeyShareAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Cannot use the previous requested key shares any more.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No key_share extension in ServerHello, " +
|
||||
"cleanup the key shares if necessary");
|
||||
}
|
||||
chc.handshakePossessions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The key share entry used in HelloRetryRequest "key_share" extensions.
|
||||
*/
|
||||
static final class HRRKeyShareSpec implements SSLExtensionSpec {
|
||||
final int selectedGroup;
|
||||
|
||||
HRRKeyShareSpec(NamedGroup serverGroup) {
|
||||
this.selectedGroup = serverGroup.id;
|
||||
}
|
||||
|
||||
private HRRKeyShareSpec(ByteBuffer buffer) throws IOException {
|
||||
// struct {
|
||||
// NamedGroup selected_group;
|
||||
// } KeyShareHelloRetryRequest;
|
||||
if (buffer.remaining() != 2) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid key_share extension: " +
|
||||
"improper data (length=" + buffer.remaining() + ")");
|
||||
}
|
||||
|
||||
this.selectedGroup = Record.getInt16(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"selected group\": '['{0}']'", Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
NamedGroup.nameOf(selectedGroup)
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HRRKeyShareStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new HRRKeyShareSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension in a HelloRetryRequest
|
||||
* handshake message.
|
||||
*/
|
||||
private static final
|
||||
class HRRKeyShareProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRKeyShareProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext) context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported key_share extension in HelloRetryRequest");
|
||||
}
|
||||
|
||||
if (shc.clientRequestedNamedGroups == null ||
|
||||
shc.clientRequestedNamedGroups.isEmpty()) {
|
||||
// No supported groups.
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected key_share extension in HelloRetryRequest");
|
||||
}
|
||||
|
||||
NamedGroup selectedGroup = null;
|
||||
for (NamedGroup ng : shc.clientRequestedNamedGroups) {
|
||||
if (SupportedGroups.isActivatable(
|
||||
shc.algorithmConstraints, ng)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"HelloRetryRequest selected named group: " +
|
||||
ng.name);
|
||||
}
|
||||
|
||||
selectedGroup = ng;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedGroup == null) {
|
||||
throw shc.conContext.fatal(
|
||||
Alert.UNEXPECTED_MESSAGE, "No common named group");
|
||||
}
|
||||
|
||||
byte[] extdata = new byte[] {
|
||||
(byte)((selectedGroup.id >> 8) & 0xFF),
|
||||
(byte)(selectedGroup.id & 0xFF)
|
||||
};
|
||||
|
||||
// update the context
|
||||
shc.serverSelectedNamedGroup = selectedGroup;
|
||||
shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE,
|
||||
new HRRKeyShareSpec(selectedGroup));
|
||||
|
||||
return extdata;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of the extension for stateless
|
||||
* HelloRetryRequest reconstruction.
|
||||
*/
|
||||
private static final
|
||||
class HRRKeyShareReproducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRKeyShareReproducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext) context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported key_share extension in HelloRetryRequest");
|
||||
}
|
||||
|
||||
CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get(
|
||||
SSLExtension.CH_KEY_SHARE);
|
||||
if (spec != null && spec.clientShares != null &&
|
||||
spec.clientShares.size() == 1) {
|
||||
int namedGroupId = spec.clientShares.get(0).namedGroupId;
|
||||
|
||||
byte[] extdata = new byte[] {
|
||||
(byte)((namedGroupId >> 8) & 0xFF),
|
||||
(byte)(namedGroupId & 0xFF)
|
||||
};
|
||||
|
||||
return extdata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of the extension in a HelloRetryRequest
|
||||
* handshake message.
|
||||
*/
|
||||
private static final
|
||||
class HRRKeyShareConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private HRRKeyShareConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported key_share extension in HelloRetryRequest");
|
||||
}
|
||||
|
||||
if (chc.clientRequestedNamedGroups == null ||
|
||||
chc.clientRequestedNamedGroups.isEmpty()) {
|
||||
// No supported groups.
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected key_share extension in HelloRetryRequest");
|
||||
}
|
||||
|
||||
// Parse the extension
|
||||
HRRKeyShareSpec spec;
|
||||
try {
|
||||
spec = new HRRKeyShareSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup);
|
||||
if (serverGroup == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported HelloRetryRequest selected group: " +
|
||||
NamedGroup.nameOf(spec.selectedGroup));
|
||||
}
|
||||
|
||||
if (!chc.clientRequestedNamedGroups.contains(serverGroup)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected HelloRetryRequest selected group: " +
|
||||
serverGroup.name);
|
||||
}
|
||||
|
||||
// update the context
|
||||
|
||||
// When sending the new ClientHello, the client MUST replace the
|
||||
// original "key_share" extension with one containing only a new
|
||||
// KeyShareEntry for the group indicated in the selected_group
|
||||
// field of the triggering HelloRetryRequest.
|
||||
//
|
||||
chc.serverSelectedNamedGroup = serverGroup;
|
||||
chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
335
jdkSrc/jdk8/sun/security/ssl/KeyUpdate.java
Normal file
335
jdkSrc/jdk8/sun/security/ssl/KeyUpdate.java
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
/**
|
||||
* Pack of the KeyUpdate handshake message.
|
||||
*/
|
||||
final class KeyUpdate {
|
||||
static final SSLProducer kickstartProducer =
|
||||
new KeyUpdateKickstartProducer();
|
||||
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new KeyUpdateConsumer();
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new KeyUpdateProducer();
|
||||
|
||||
/**
|
||||
* The KeyUpdate handshake message.
|
||||
*
|
||||
* The KeyUpdate handshake message is used to indicate that the sender is
|
||||
* updating its sending cryptographic keys.
|
||||
*
|
||||
* enum {
|
||||
* update_not_requested(0), update_requested(1), (255)
|
||||
* } KeyUpdateRequest;
|
||||
*
|
||||
* struct {
|
||||
* KeyUpdateRequest request_update;
|
||||
* } KeyUpdate;
|
||||
*/
|
||||
static final class KeyUpdateMessage extends HandshakeMessage {
|
||||
private final KeyUpdateRequest status;
|
||||
|
||||
KeyUpdateMessage(PostHandshakeContext context,
|
||||
KeyUpdateRequest status) {
|
||||
super(context);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
KeyUpdateMessage(PostHandshakeContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(context);
|
||||
|
||||
if (m.remaining() != 1) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"KeyUpdate has an unexpected length of "+
|
||||
m.remaining());
|
||||
}
|
||||
|
||||
byte request = m.get();
|
||||
this.status = KeyUpdateRequest.valueOf(request);
|
||||
if (status == null) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid KeyUpdate message value: " +
|
||||
KeyUpdateRequest.nameOf(request));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.KEY_UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
// one byte enum
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream s) throws IOException {
|
||||
s.putInt8(status.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"KeyUpdate\": '{'\n" +
|
||||
" \"request_update\": {0}\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
status.name
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
enum KeyUpdateRequest {
|
||||
NOTREQUESTED ((byte)0, "update_not_requested"),
|
||||
REQUESTED ((byte)1, "update_requested");
|
||||
|
||||
final byte id;
|
||||
final String name;
|
||||
|
||||
private KeyUpdateRequest(byte id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static KeyUpdateRequest valueOf(byte id) {
|
||||
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
|
||||
if (kur.id == id) {
|
||||
return kur;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(byte id) {
|
||||
for (KeyUpdateRequest kur : KeyUpdateRequest.values()) {
|
||||
if (kur.id == id) {
|
||||
return kur.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "<UNKNOWN KeyUpdateRequest TYPE: " + (id & 0x0FF) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class KeyUpdateKickstartProducer implements SSLProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private KeyUpdateKickstartProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
// Produce kickstart handshake message.
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context) throws IOException {
|
||||
PostHandshakeContext hc = (PostHandshakeContext)context;
|
||||
return handshakeProducer.produce(context,
|
||||
new KeyUpdateMessage(hc, hc.conContext.isInboundClosed() ?
|
||||
KeyUpdateRequest.NOTREQUESTED :
|
||||
KeyUpdateRequest.REQUESTED));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "KeyUpdate" handshake message consumer.
|
||||
*/
|
||||
private static final class KeyUpdateConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private KeyUpdateConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
PostHandshakeContext hc = (PostHandshakeContext)context;
|
||||
KeyUpdateMessage km = new KeyUpdateMessage(hc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming KeyUpdate post-handshake message", km);
|
||||
}
|
||||
|
||||
// Update read key and IV.
|
||||
SSLTrafficKeyDerivation kdg =
|
||||
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
|
||||
if (kdg == null) {
|
||||
// unlikely
|
||||
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
hc.conContext.protocolVersion);
|
||||
}
|
||||
|
||||
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
|
||||
hc.conContext.inputRecord.readCipher.baseSecret);
|
||||
if (skd == null) {
|
||||
// unlikely
|
||||
throw hc.conContext.fatal(
|
||||
Alert.INTERNAL_ERROR, "no key derivation");
|
||||
}
|
||||
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
|
||||
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
|
||||
SecretKey key = kd.deriveKey("TlsKey", null);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(
|
||||
kd.deriveKey("TlsIv", null).getEncoded());
|
||||
try {
|
||||
SSLReadCipher rc =
|
||||
hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
|
||||
Authenticator.valueOf(hc.conContext.protocolVersion),
|
||||
hc.conContext.protocolVersion, key, ivSpec,
|
||||
hc.sslContext.getSecureRandom());
|
||||
|
||||
if (rc == null) {
|
||||
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
|
||||
") and protocol version (" + hc.negotiatedProtocol +
|
||||
")");
|
||||
}
|
||||
|
||||
rc.baseSecret = nplus1;
|
||||
hc.conContext.inputRecord.changeReadCiphers(rc);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine("KeyUpdate: read key updated");
|
||||
}
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failure to derive read secrets", gse);
|
||||
}
|
||||
|
||||
if (km.status == KeyUpdateRequest.REQUESTED) {
|
||||
// Update the write key and IV.
|
||||
handshakeProducer.produce(hc,
|
||||
new KeyUpdateMessage(hc, KeyUpdateRequest.NOTREQUESTED));
|
||||
return;
|
||||
}
|
||||
|
||||
// clean handshake context
|
||||
hc.conContext.finishPostHandshake();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "KeyUpdate" handshake message producer.
|
||||
*/
|
||||
private static final class KeyUpdateProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private KeyUpdateProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
PostHandshakeContext hc = (PostHandshakeContext)context;
|
||||
KeyUpdateMessage km = (KeyUpdateMessage)message;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced KeyUpdate post-handshake message", km);
|
||||
}
|
||||
|
||||
// Update the write key and IV.
|
||||
SSLTrafficKeyDerivation kdg =
|
||||
SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
|
||||
if (kdg == null) {
|
||||
// unlikely
|
||||
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
hc.conContext.protocolVersion);
|
||||
}
|
||||
|
||||
SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
|
||||
hc.conContext.outputRecord.writeCipher.baseSecret);
|
||||
if (skd == null) {
|
||||
// unlikely
|
||||
throw hc.conContext.fatal(
|
||||
Alert.INTERNAL_ERROR, "no key derivation");
|
||||
}
|
||||
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
|
||||
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
|
||||
SecretKey key = kd.deriveKey("TlsKey", null);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(
|
||||
kd.deriveKey("TlsIv", null).getEncoded());
|
||||
|
||||
SSLWriteCipher wc;
|
||||
try {
|
||||
wc = hc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
|
||||
Authenticator.valueOf(hc.conContext.protocolVersion),
|
||||
hc.conContext.protocolVersion, key, ivSpec,
|
||||
hc.sslContext.getSecureRandom());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw hc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failure to derive write secrets", gse);
|
||||
}
|
||||
|
||||
if (wc == null) {
|
||||
throw hc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Illegal cipher suite (" + hc.negotiatedCipherSuite +
|
||||
") and protocol version (" + hc.negotiatedProtocol + ")");
|
||||
}
|
||||
|
||||
// Output the handshake message and change the write cipher.
|
||||
//
|
||||
// The KeyUpdate handshake message SHALL be delivered in the
|
||||
// changeWriteCiphers() implementation.
|
||||
wc.baseSecret = nplus1;
|
||||
hc.conContext.outputRecord.changeWriteCiphers(wc, km.status.id);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine("KeyUpdate: write key updated");
|
||||
}
|
||||
|
||||
// clean handshake context
|
||||
hc.conContext.finishPostHandshake();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
jdkSrc/jdk8/sun/security/ssl/Krb5Helper.java
Normal file
128
jdkSrc/jdk8/sun/security/ssl/Krb5Helper.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Permission;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
/**
|
||||
* A helper class for Kerberos APIs.
|
||||
*/
|
||||
public final class Krb5Helper {
|
||||
|
||||
private Krb5Helper() { }
|
||||
|
||||
// loads Krb5Proxy implementation class if available
|
||||
private static final String IMPL_CLASS =
|
||||
"sun.security.ssl.krb5.Krb5ProxyImpl";
|
||||
|
||||
private static final Krb5Proxy proxy =
|
||||
AccessController.doPrivileged(new PrivilegedAction<Krb5Proxy>() {
|
||||
@Override
|
||||
public Krb5Proxy run() {
|
||||
try {
|
||||
Class<?> c = Class.forName(IMPL_CLASS, true, null);
|
||||
return (Krb5Proxy)c.newInstance();
|
||||
} catch (ClassNotFoundException cnf) {
|
||||
return null;
|
||||
} catch (InstantiationException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}});
|
||||
|
||||
private static void ensureAvailable() {
|
||||
if (proxy == null)
|
||||
throw new AssertionError("Kerberos should be available");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Subject associated with client-side of the SSL socket.
|
||||
*/
|
||||
public static Subject getClientSubject(AccessControlContext acc)
|
||||
throws LoginException {
|
||||
ensureAvailable();
|
||||
return proxy.getClientSubject(acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Subject associated with server-side of the SSL socket.
|
||||
*/
|
||||
public static Subject getServerSubject(AccessControlContext acc)
|
||||
throws LoginException {
|
||||
ensureAvailable();
|
||||
return proxy.getServerSubject(acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the KerberosKeys for the default server-side principal.
|
||||
*/
|
||||
public static Object getServiceCreds(AccessControlContext acc)
|
||||
throws LoginException {
|
||||
ensureAvailable();
|
||||
return proxy.getServiceCreds(acc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server-side principal name associated with the KerberosKey.
|
||||
*/
|
||||
public static String getServerPrincipalName(Object serviceCreds) {
|
||||
ensureAvailable();
|
||||
return proxy.getServerPrincipalName(serviceCreds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hostname embedded in the principal name.
|
||||
*/
|
||||
public static String getPrincipalHostName(Principal principal) {
|
||||
ensureAvailable();
|
||||
return proxy.getPrincipalHostName(principal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ServicePermission for the principal name and action.
|
||||
*/
|
||||
public static Permission getServicePermission(String principalName,
|
||||
String action) {
|
||||
ensureAvailable();
|
||||
return proxy.getServicePermission(principalName, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the Subject might contain creds for princ.
|
||||
*/
|
||||
public static boolean isRelated(Subject subject, Principal princ) {
|
||||
ensureAvailable();
|
||||
return proxy.isRelated(subject, princ);
|
||||
}
|
||||
}
|
||||
75
jdkSrc/jdk8/sun/security/ssl/Krb5Proxy.java
Normal file
75
jdkSrc/jdk8/sun/security/ssl/Krb5Proxy.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.Permission;
|
||||
import java.security.Principal;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
/**
|
||||
* An interface to a subset of the Kerberos APIs to avoid a static dependency
|
||||
* on the types defined by these APIs.
|
||||
*/
|
||||
public interface Krb5Proxy {
|
||||
|
||||
/**
|
||||
* Returns the Subject associated with the client-side of the SSL socket.
|
||||
*/
|
||||
Subject getClientSubject(AccessControlContext acc) throws LoginException;
|
||||
|
||||
/**
|
||||
* Returns the Subject associated with the server-side of the SSL socket.
|
||||
*/
|
||||
Subject getServerSubject(AccessControlContext acc) throws LoginException;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Kerberos ServiceCreds for the default server-side principal.
|
||||
*/
|
||||
Object getServiceCreds(AccessControlContext acc) throws LoginException;
|
||||
|
||||
/**
|
||||
* Returns the server-side principal name associated with the KerberosKey.
|
||||
*/
|
||||
String getServerPrincipalName(Object serviceCreds);
|
||||
|
||||
/**
|
||||
* Returns the hostname embedded in the principal name.
|
||||
*/
|
||||
String getPrincipalHostName(Principal principal);
|
||||
|
||||
/**
|
||||
* Returns a ServicePermission for the principal name and action.
|
||||
*/
|
||||
Permission getServicePermission(String principalName, String action);
|
||||
|
||||
/**
|
||||
* Determines if the Subject might contain creds for princ.
|
||||
*/
|
||||
boolean isRelated(Subject subject, Principal princ);
|
||||
}
|
||||
379
jdkSrc/jdk8/sun/security/ssl/KrbClientKeyExchange.java
Normal file
379
jdkSrc/jdk8/sun/security/ssl/KrbClientKeyExchange.java
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2020, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SNIHostName;
|
||||
import javax.net.ssl.StandardConstants;
|
||||
|
||||
import sun.misc.HexDumpEncoder;
|
||||
import sun.security.ssl.KrbKeyExchange.KrbPremasterSecret;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "ClientKeyExchange" handshake message.
|
||||
*/
|
||||
final class KrbClientKeyExchange {
|
||||
static final SSLConsumer krbHandshakeConsumer =
|
||||
new KrbClientKeyExchangeConsumer();
|
||||
static final HandshakeProducer krbHandshakeProducer =
|
||||
new KrbClientKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The KRB5 ClientKeyExchange handshake message (CLIENT -> SERVER).
|
||||
* It holds the Kerberos ticket and the encrypted pre-master secret
|
||||
* encrypted with the session key sealed in the ticket.
|
||||
*
|
||||
* From RFC 2712:
|
||||
*
|
||||
* struct
|
||||
* {
|
||||
* opaque Ticket;
|
||||
* opaque authenticator; // optional, ignored
|
||||
* opaque EncryptedPreMasterSecret; // encrypted with the session key
|
||||
* // sealed in the ticket
|
||||
* } KerberosWrapper;
|
||||
*
|
||||
*/
|
||||
private static final
|
||||
class KrbClientKeyExchangeMessage extends HandshakeMessage {
|
||||
|
||||
private static final String KRB5_CLASS_NAME =
|
||||
"sun.security.ssl.krb5.KrbClientKeyExchangeHelperImpl";
|
||||
|
||||
private static final Class<?> krb5Class = AccessController.doPrivileged(
|
||||
new PrivilegedAction<Class<?>>() {
|
||||
@Override
|
||||
public Class<?> run() {
|
||||
try {
|
||||
return Class.forName(KRB5_CLASS_NAME, true, null);
|
||||
} catch (ClassNotFoundException cnf) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private static KrbClientKeyExchangeHelper newKrb5Instance() {
|
||||
if (krb5Class != null) {
|
||||
try {
|
||||
return (KrbClientKeyExchangeHelper)krb5Class.
|
||||
getDeclaredConstructor().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException |
|
||||
NoSuchMethodException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final KrbClientKeyExchangeHelper krb5Helper;
|
||||
|
||||
private KrbClientKeyExchangeMessage(HandshakeContext context) {
|
||||
super(context);
|
||||
if ((krb5Helper = newKrb5Instance()) == null)
|
||||
throw new IllegalStateException("Kerberos is unavailable");
|
||||
}
|
||||
|
||||
KrbClientKeyExchangeMessage(HandshakeContext context,
|
||||
byte[] preMaster, String serverName,
|
||||
AccessControlContext acc) throws IOException {
|
||||
this(context);
|
||||
krb5Helper.init(preMaster, serverName, acc);
|
||||
}
|
||||
|
||||
KrbClientKeyExchangeMessage(HandshakeContext context,
|
||||
ByteBuffer message, Object serverKeys,
|
||||
AccessControlContext acc) throws IOException {
|
||||
this(context);
|
||||
byte[] encodedTicket = Record.getBytes16(message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("encoded Kerberos service ticket",
|
||||
encodedTicket);
|
||||
}
|
||||
// Read and ignore the authenticator
|
||||
Record.getBytes16(message);
|
||||
byte[] encryptedPreMasterSecret = Record.getBytes16(message);
|
||||
if (encryptedPreMasterSecret != null && SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("encrypted Kerberos pre-master secret",
|
||||
encryptedPreMasterSecret);
|
||||
}
|
||||
krb5Helper.init(encodedTicket, encryptedPreMasterSecret,
|
||||
serverKeys, acc);
|
||||
}
|
||||
|
||||
@Override
|
||||
SSLHandshake handshakeType() {
|
||||
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
int messageLength() {
|
||||
return (6 + krb5Helper.getEncodedTicket().length +
|
||||
krb5Helper.getEncryptedPreMasterSecret().length);
|
||||
}
|
||||
|
||||
@Override
|
||||
void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putBytes16(krb5Helper.getEncodedTicket());
|
||||
hos.putBytes16(null); // XXX no authenticator
|
||||
hos.putBytes16(krb5Helper.getEncryptedPreMasterSecret());
|
||||
}
|
||||
|
||||
byte[] getPlainPreMasterSecret() {
|
||||
return krb5Helper.getPlainPreMasterSecret();
|
||||
}
|
||||
|
||||
Principal getPeerPrincipal() {
|
||||
return krb5Helper.getPeerPrincipal();
|
||||
}
|
||||
|
||||
Principal getLocalPrincipal() {
|
||||
return krb5Helper.getLocalPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"KRB5 ClientKeyExchange\": '{'\n" +
|
||||
" \"ticket\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}'\n" +
|
||||
" \"pre-master\": '{'\n" +
|
||||
" \"plain\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}'\n" +
|
||||
" \"encrypted\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}'\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(
|
||||
krb5Helper.getEncodedTicket()), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(
|
||||
krb5Helper.getPlainPreMasterSecret()), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(
|
||||
krb5Helper.getEncryptedPreMasterSecret()), " "),
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The KRB5 "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class KrbClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private KrbClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
KrbClientKeyExchangeMessage kerberosMsg = null;
|
||||
String hostName = null;
|
||||
if (chc.negotiatedServerName != null) {
|
||||
if (chc.negotiatedServerName.getType() ==
|
||||
StandardConstants.SNI_HOST_NAME) {
|
||||
SNIHostName sniHostName = null;
|
||||
if (chc.negotiatedServerName instanceof SNIHostName) {
|
||||
sniHostName = (SNIHostName) chc.negotiatedServerName;
|
||||
} else {
|
||||
try {
|
||||
sniHostName = new SNIHostName(
|
||||
chc.negotiatedServerName.getEncoded());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// unlikely to happen, just in case ...
|
||||
}
|
||||
}
|
||||
if (sniHostName != null)
|
||||
hostName = sniHostName.getAsciiName();
|
||||
}
|
||||
} else {
|
||||
hostName = chc.handshakeSession.getPeerHost();
|
||||
}
|
||||
try {
|
||||
KrbPremasterSecret premasterSecret =
|
||||
KrbPremasterSecret.createPremasterSecret(
|
||||
chc.negotiatedProtocol,
|
||||
chc.sslContext.getSecureRandom());
|
||||
kerberosMsg = new KrbClientKeyExchangeMessage(chc,
|
||||
premasterSecret.preMaster, hostName,
|
||||
chc.conContext.acc);
|
||||
chc.handshakePossessions.add(premasterSecret);
|
||||
} catch (IOException e) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Error generating KRB premaster secret." +
|
||||
" Hostname: " + hostName + " - Negotiated" +
|
||||
" server name: " + chc.negotiatedServerName);
|
||||
}
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Cannot generate KRB premaster secret", e);
|
||||
}
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced KRB5 ClientKeyExchange handshake message",
|
||||
kerberosMsg);
|
||||
}
|
||||
|
||||
// Record the principals involved in the exchange
|
||||
chc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
|
||||
chc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
|
||||
|
||||
// Output the handshake message.
|
||||
kerberosMsg.write(chc.handshakeOutput);
|
||||
chc.handshakeOutput.flush();
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kd == null) {
|
||||
// unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
chc.negotiatedProtocol);
|
||||
} else {
|
||||
chc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(chc, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The KRB5 "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class KrbClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private KrbClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
Object serviceCreds = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof KrbKeyExchange.KrbServiceCreds) {
|
||||
serviceCreds = ((KrbKeyExchange.KrbServiceCreds) possession)
|
||||
.serviceCreds;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (serviceCreds == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No Kerberos service credentials for KRB Client Key Exchange");
|
||||
}
|
||||
|
||||
KrbClientKeyExchangeMessage kerberosMsg =
|
||||
new KrbClientKeyExchangeMessage(shc,
|
||||
message, serviceCreds, shc.conContext.acc);
|
||||
KrbPremasterSecret premasterSecret = KrbPremasterSecret.decode(
|
||||
shc.negotiatedProtocol,
|
||||
ProtocolVersion.valueOf(shc.clientHelloVersion),
|
||||
kerberosMsg.getPlainPreMasterSecret(),
|
||||
shc.sslContext.getSecureRandom());
|
||||
shc.handshakeSession.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
|
||||
shc.handshakeSession.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
|
||||
shc.handshakeCredentials.add(premasterSecret);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming KRB5 ClientKeyExchange handshake message",
|
||||
kerberosMsg);
|
||||
}
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
|
||||
// update the states
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
if (kd == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
shc.negotiatedProtocol);
|
||||
} else {
|
||||
shc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(shc, masterSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
jdkSrc/jdk8/sun/security/ssl/KrbClientKeyExchangeHelper.java
Normal file
56
jdkSrc/jdk8/sun/security/ssl/KrbClientKeyExchangeHelper.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Helper interface for KrbClientKeyExchange SSL classes to access
|
||||
* the Kerberos implementation without static-linking. This enables
|
||||
* Java SE Embedded 8 compilation using 'compact1' profile -where
|
||||
* SSL classes are available but Kerberos are not-.
|
||||
*/
|
||||
public interface KrbClientKeyExchangeHelper {
|
||||
|
||||
void init(byte[] preMaster, String serverName,
|
||||
AccessControlContext acc) throws IOException;
|
||||
|
||||
void init(byte[] encodedTicket, byte[] preMasterEnc,
|
||||
Object serviceCreds, AccessControlContext acc)
|
||||
throws IOException;
|
||||
|
||||
byte[] getEncodedTicket();
|
||||
|
||||
byte[] getEncryptedPreMasterSecret();
|
||||
|
||||
byte[] getPlainPreMasterSecret();
|
||||
|
||||
Principal getPeerPrincipal();
|
||||
|
||||
Principal getLocalPrincipal();
|
||||
}
|
||||
257
jdkSrc/jdk8/sun/security/ssl/KrbKeyExchange.java
Normal file
257
jdkSrc/jdk8/sun/security/ssl/KrbKeyExchange.java
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2020, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
final class KrbKeyExchange {
|
||||
static final SSLPossessionGenerator poGenerator =
|
||||
new KrbPossessionGenerator();
|
||||
static final SSLKeyAgreementGenerator kaGenerator =
|
||||
new KrbKAGenerator();
|
||||
|
||||
static final
|
||||
class KrbPossessionGenerator implements SSLPossessionGenerator {
|
||||
/**
|
||||
* Kerberos service credentials. Having this possession in
|
||||
* the server side will enable the use of a KRB cipher suite in
|
||||
* T12ServerHelloProducer::chooseCipherSuite.
|
||||
*/
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext handshakeContext) {
|
||||
Object serviceCreds = null;
|
||||
try {
|
||||
final AccessControlContext acc = handshakeContext.conContext.acc;
|
||||
serviceCreds = AccessController.doPrivileged(
|
||||
// Eliminate dependency on KerberosKey
|
||||
new PrivilegedExceptionAction<Object>() {
|
||||
@Override
|
||||
public Object run() throws Exception {
|
||||
// get kerberos key for the default principal
|
||||
return Krb5Helper.getServiceCreds(acc);
|
||||
}});
|
||||
|
||||
// check permission to access and use the secret key of the
|
||||
// Kerberized "host" service
|
||||
if (serviceCreds != null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Using Kerberos creds");
|
||||
}
|
||||
String serverPrincipal =
|
||||
Krb5Helper.getServerPrincipalName(serviceCreds);
|
||||
if (serverPrincipal != null) {
|
||||
// When service is bound, we check ASAP. Otherwise,
|
||||
// will check after client request is received
|
||||
// in Kerberos ClientKeyExchange
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
try {
|
||||
if (sm != null) {
|
||||
// Eliminate dependency on ServicePermission
|
||||
sm.checkPermission(Krb5Helper.getServicePermission(
|
||||
serverPrincipal, "accept"), acc);
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
// Do not destroy keys. Will affect Subject
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Permission to access Kerberos"
|
||||
+ " secret key denied");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new KrbServiceCreds(serviceCreds);
|
||||
}
|
||||
} catch (PrivilegedActionException e) {
|
||||
// Likely exception here is LoginException
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Attempt to obtain Kerberos key failed: "
|
||||
+ e.toString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final
|
||||
class KrbServiceCreds implements SSLPossession {
|
||||
final Object serviceCreds;
|
||||
|
||||
KrbServiceCreds(Object serviceCreds) {
|
||||
this.serviceCreds = serviceCreds;
|
||||
}
|
||||
}
|
||||
|
||||
static final
|
||||
class KrbPremasterSecret implements SSLPossession, SSLCredentials {
|
||||
final byte[] preMaster; // 48 bytes
|
||||
|
||||
KrbPremasterSecret(byte[] premasterSecret) {
|
||||
preMaster = premasterSecret;
|
||||
}
|
||||
|
||||
static KrbPremasterSecret createPremasterSecret(
|
||||
ProtocolVersion protocolVersion, SecureRandom rand) {
|
||||
byte[] pm = new byte[48];
|
||||
rand.nextBytes(pm);
|
||||
pm[0] = protocolVersion.major;
|
||||
pm[1] = protocolVersion.minor;
|
||||
return new KrbPremasterSecret(pm);
|
||||
}
|
||||
|
||||
static KrbPremasterSecret decode(ProtocolVersion protocolVersion,
|
||||
ProtocolVersion clientVersion, byte[] preMaster,
|
||||
SecureRandom rand) {
|
||||
KrbPremasterSecret preMasterSecret = null;
|
||||
boolean versionMismatch = true;
|
||||
ProtocolVersion preMasterProtocolVersion = null;
|
||||
if (preMaster != null && preMaster.length == 48) {
|
||||
preMasterProtocolVersion =
|
||||
ProtocolVersion.valueOf(preMaster[0], preMaster[1]);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Kerberos pre-master secret protocol" +
|
||||
" version: " + preMasterProtocolVersion);
|
||||
}
|
||||
|
||||
// Check if the pre-master secret protocol version is valid.
|
||||
// The specification states that it must be the maximum version
|
||||
// supported by the client from its ClientHello message. However,
|
||||
// many old implementations send the negotiated version, so accept
|
||||
// both for SSL v3.0 and TLS v1.0.
|
||||
versionMismatch =
|
||||
(preMasterProtocolVersion.compare(clientVersion) != 0);
|
||||
if (versionMismatch &&
|
||||
(clientVersion.compare(ProtocolVersion.TLS10) <= 0)) {
|
||||
versionMismatch =
|
||||
(preMasterProtocolVersion.compare(protocolVersion) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (versionMismatch) {
|
||||
/*
|
||||
* Bogus decrypted ClientKeyExchange? If so, conjure a
|
||||
* a random pre-master secret that will fail later during
|
||||
* Finished message processing. This is a countermeasure against
|
||||
* the "interactive RSA PKCS#1 encryption envelop attack" reported
|
||||
* in June 1998. Preserving the execution path will
|
||||
* mitigate timing attacks and force consistent error handling.
|
||||
*/
|
||||
preMasterSecret = createPremasterSecret(clientVersion, rand);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Kerberos pre-master secret error," +
|
||||
" generating random secret for safe failure.");
|
||||
}
|
||||
} else {
|
||||
preMasterSecret = new KrbPremasterSecret(preMaster);
|
||||
}
|
||||
return preMasterSecret;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class KrbKAGenerator implements SSLKeyAgreementGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private KrbKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
|
||||
KrbPremasterSecret premaster = null;
|
||||
if (context instanceof ClientHandshakeContext) {
|
||||
for (SSLPossession possession : context.handshakePossessions) {
|
||||
if (possession instanceof KrbPremasterSecret) {
|
||||
premaster = (KrbPremasterSecret)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SSLCredentials credential : context.handshakeCredentials) {
|
||||
if (credential instanceof KrbPremasterSecret) {
|
||||
premaster = (KrbPremasterSecret)credential;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (premaster == null) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient KRB key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new KRBKAKeyDerivation(context, premaster.preMaster);
|
||||
|
||||
}
|
||||
|
||||
private static final
|
||||
class KRBKAKeyDerivation implements SSLKeyDerivation {
|
||||
private final HandshakeContext context;
|
||||
private final byte[] secretBytes;
|
||||
|
||||
KRBKAKeyDerivation(HandshakeContext context,
|
||||
byte[] secret) {
|
||||
this.context = context;
|
||||
this.secretBytes = secret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
try {
|
||||
SecretKey preMasterSecret = new SecretKeySpec(secretBytes,
|
||||
"TlsPremasterSecret");
|
||||
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
"No expected master key derivation for protocol: " +
|
||||
context.negotiatedProtocol.name);
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
} catch (Exception gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
609
jdkSrc/jdk8/sun/security/ssl/MaxFragExtension.java
Normal file
609
jdkSrc/jdk8/sun/security/ssl/MaxFragExtension.java
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import static sun.security.ssl.SSLExtension.CH_MAX_FRAGMENT_LENGTH;
|
||||
import static sun.security.ssl.SSLExtension.EE_MAX_FRAGMENT_LENGTH;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import static sun.security.ssl.SSLExtension.SH_MAX_FRAGMENT_LENGTH;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "max_fragment_length" extensions [RFC6066].
|
||||
*/
|
||||
final class MaxFragExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHMaxFragmentLengthProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHMaxFragmentLengthConsumer();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer =
|
||||
new SHMaxFragmentLengthProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHMaxFragmentLengthConsumer();
|
||||
static final HandshakeConsumer shOnTradeConsumer =
|
||||
new SHMaxFragmentLengthUpdate();
|
||||
|
||||
static final HandshakeProducer eeNetworkProducer =
|
||||
new EEMaxFragmentLengthProducer();
|
||||
static final ExtensionConsumer eeOnLoadConsumer =
|
||||
new EEMaxFragmentLengthConsumer();
|
||||
static final HandshakeConsumer eeOnTradeConsumer =
|
||||
new EEMaxFragmentLengthUpdate();
|
||||
|
||||
static final SSLStringizer maxFragLenStringizer =
|
||||
new MaxFragLenStringizer();
|
||||
|
||||
/**
|
||||
* The "max_fragment_length" extension [RFC 6066].
|
||||
*/
|
||||
static final class MaxFragLenSpec implements SSLExtensionSpec {
|
||||
byte id;
|
||||
|
||||
private MaxFragLenSpec(byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private MaxFragLenSpec(ByteBuffer buffer) throws IOException {
|
||||
if (buffer.remaining() != 1) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid max_fragment_length extension data");
|
||||
}
|
||||
|
||||
this.id = buffer.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MaxFragLenEnum.nameOf(id);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MaxFragLenStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new MaxFragLenSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum MaxFragLenEnum {
|
||||
MFL_512 ((byte)0x01, 512, "2^9"),
|
||||
MFL_1024 ((byte)0x02, 1024, "2^10"),
|
||||
MFL_2048 ((byte)0x03, 2048, "2^11"),
|
||||
MFL_4096 ((byte)0x04, 4096, "2^12");
|
||||
|
||||
final byte id;
|
||||
final int fragmentSize;
|
||||
final String description;
|
||||
|
||||
private MaxFragLenEnum(byte id, int fragmentSize, String description) {
|
||||
this.id = id;
|
||||
this.fragmentSize = fragmentSize;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
private static MaxFragLenEnum valueOf(byte id) {
|
||||
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
|
||||
if (mfl.id == id) {
|
||||
return mfl;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String nameOf(byte id) {
|
||||
for (MaxFragLenEnum mfl : MaxFragLenEnum.values()) {
|
||||
if (mfl.id == id) {
|
||||
return mfl.description;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNDEFINED-MAX-FRAGMENT-LENGTH(" + id + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best match enum constant of the specified
|
||||
* fragment size.
|
||||
*/
|
||||
static MaxFragLenEnum valueOf(int fragmentSize) {
|
||||
if (fragmentSize <= 0) {
|
||||
return null;
|
||||
} else if (fragmentSize < 1024) {
|
||||
return MFL_512;
|
||||
} else if (fragmentSize < 2048) {
|
||||
return MFL_1024;
|
||||
} else if (fragmentSize < 4096) {
|
||||
return MFL_2048;
|
||||
} else if (fragmentSize == 4096) {
|
||||
return MFL_4096;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "max_fragment_length" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHMaxFragmentLengthProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHMaxFragmentLengthProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable max_fragment_length extension");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Produce the extension and update the context.
|
||||
int requestedMFLength;
|
||||
if (chc.isResumption && (chc.resumingSession != null)) {
|
||||
// The same extension should be sent for resumption.
|
||||
requestedMFLength =
|
||||
chc.resumingSession.getNegotiatedMaxFragSize();
|
||||
} else if (chc.sslConfig.maximumPacketSize != 0) {
|
||||
// Maybe we can calculate the fragment size more accurate
|
||||
// by condering the enabled cipher suites in the future.
|
||||
requestedMFLength = chc.sslConfig.maximumPacketSize -
|
||||
SSLRecord.maxPlaintextPlusSize;
|
||||
} else {
|
||||
// Need no max_fragment_length extension.
|
||||
requestedMFLength = -1;
|
||||
}
|
||||
|
||||
MaxFragLenEnum mfl = MaxFragLenEnum.valueOf(requestedMFLength);
|
||||
if (mfl != null) {
|
||||
// update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
CH_MAX_FRAGMENT_LENGTH, new MaxFragLenSpec(mfl.id));
|
||||
|
||||
return new byte[] { mfl.id };
|
||||
} else {
|
||||
// log and ignore, no MFL extension.
|
||||
chc.maxFragmentLength = -1;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No available max_fragment_length extension can " +
|
||||
"be used for fragment size of " +
|
||||
requestedMFLength + "bytes");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "max_fragment_length" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHMaxFragmentLengthConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHMaxFragmentLengthConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
if (!shc.sslConfig.isAvailable(CH_MAX_FRAGMENT_LENGTH)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable max_fragment_length extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
MaxFragLenSpec spec;
|
||||
try {
|
||||
spec = new MaxFragLenSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
|
||||
if (mfle == null) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"the requested maximum fragment length is other " +
|
||||
"than the allowed values");
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.maxFragmentLength = mfle.fragmentSize;
|
||||
shc.handshakeExtensions.put(CH_MAX_FRAGMENT_LENGTH, spec);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "max_fragment_length" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHMaxFragmentLengthProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHMaxFragmentLengthProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// In response to "max_fragment_length" extension request only
|
||||
MaxFragLenSpec spec = (MaxFragLenSpec)
|
||||
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
|
||||
if (spec == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest(
|
||||
"Ignore unavailable max_fragment_length extension");
|
||||
}
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
if ((shc.maxFragmentLength > 0) &&
|
||||
(shc.sslConfig.maximumPacketSize != 0)) {
|
||||
int estimatedMaxFragSize =
|
||||
shc.negotiatedCipherSuite.calculatePacketSize(
|
||||
shc.maxFragmentLength, shc.negotiatedProtocol);
|
||||
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
|
||||
// For better interoperability, abort the maximum
|
||||
// fragment length negotiation, rather than terminate
|
||||
// the connection with a fatal alert.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Abort the maximum fragment length negotiation, " +
|
||||
"may overflow the maximum packet size limit.");
|
||||
}
|
||||
shc.maxFragmentLength = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// update the context
|
||||
if (shc.maxFragmentLength > 0) {
|
||||
shc.handshakeSession.setNegotiatedMaxFragSize(
|
||||
shc.maxFragmentLength);
|
||||
shc.conContext.inputRecord.changeFragmentSize(
|
||||
shc.maxFragmentLength);
|
||||
shc.conContext.outputRecord.changeFragmentSize(
|
||||
shc.maxFragmentLength);
|
||||
|
||||
// The response extension data is the same as the requested one.
|
||||
shc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
|
||||
return new byte[] { spec.id };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "max_fragment_length" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHMaxFragmentLengthConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHMaxFragmentLengthConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to "max_fragment_length" extension request only
|
||||
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
|
||||
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
|
||||
if (requestedSpec == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected max_fragment_length extension in ServerHello");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
MaxFragLenSpec spec;
|
||||
try {
|
||||
spec = new MaxFragLenSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
if (spec.id != requestedSpec.id) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"The maximum fragment length response is not requested");
|
||||
}
|
||||
|
||||
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
|
||||
if (mfle == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"the requested maximum fragment length is other " +
|
||||
"than the allowed values");
|
||||
}
|
||||
|
||||
// update the context
|
||||
chc.maxFragmentLength = mfle.fragmentSize;
|
||||
chc.handshakeExtensions.put(SH_MAX_FRAGMENT_LENGTH, spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After session creation consuming of a "max_fragment_length"
|
||||
* extension in the ClientHello handshake message.
|
||||
*/
|
||||
private static final class SHMaxFragmentLengthUpdate
|
||||
implements HandshakeConsumer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private SHMaxFragmentLengthUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
MaxFragLenSpec spec = (MaxFragLenSpec)
|
||||
chc.handshakeExtensions.get(SH_MAX_FRAGMENT_LENGTH);
|
||||
if (spec == null) {
|
||||
// Ignore, no "max_fragment_length" extension response.
|
||||
return;
|
||||
}
|
||||
|
||||
if ((chc.maxFragmentLength > 0) &&
|
||||
(chc.sslConfig.maximumPacketSize != 0)) {
|
||||
int estimatedMaxFragSize =
|
||||
chc.negotiatedCipherSuite.calculatePacketSize(
|
||||
chc.maxFragmentLength, chc.negotiatedProtocol);
|
||||
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
|
||||
// For better interoperability, abort the maximum
|
||||
// fragment length negotiation, rather than terminate
|
||||
// the connection with a fatal alert.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Abort the maximum fragment length negotiation, " +
|
||||
"may overflow the maximum packet size limit.");
|
||||
}
|
||||
chc.maxFragmentLength = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// update the context
|
||||
if (chc.maxFragmentLength > 0) {
|
||||
chc.handshakeSession.setNegotiatedMaxFragSize(
|
||||
chc.maxFragmentLength);
|
||||
chc.conContext.inputRecord.changeFragmentSize(
|
||||
chc.maxFragmentLength);
|
||||
chc.conContext.outputRecord.changeFragmentSize(
|
||||
chc.maxFragmentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "max_fragment_length" extension in
|
||||
* the EncryptedExtensions handshake message.
|
||||
*/
|
||||
private static final
|
||||
class EEMaxFragmentLengthProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private EEMaxFragmentLengthProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// In response to "max_fragment_length" extension request only
|
||||
MaxFragLenSpec spec = (MaxFragLenSpec)
|
||||
shc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
|
||||
if (spec == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest(
|
||||
"Ignore unavailable max_fragment_length extension");
|
||||
}
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
if ((shc.maxFragmentLength > 0) &&
|
||||
(shc.sslConfig.maximumPacketSize != 0)) {
|
||||
int estimatedMaxFragSize =
|
||||
shc.negotiatedCipherSuite.calculatePacketSize(
|
||||
shc.maxFragmentLength, shc.negotiatedProtocol);
|
||||
if (estimatedMaxFragSize > shc.sslConfig.maximumPacketSize) {
|
||||
// For better interoperability, abort the maximum
|
||||
// fragment length negotiation, rather than terminate
|
||||
// the connection with a fatal alert.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Abort the maximum fragment length negotiation, " +
|
||||
"may overflow the maximum packet size limit.");
|
||||
}
|
||||
shc.maxFragmentLength = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// update the context
|
||||
if (shc.maxFragmentLength > 0) {
|
||||
shc.handshakeSession.setNegotiatedMaxFragSize(
|
||||
shc.maxFragmentLength);
|
||||
shc.conContext.inputRecord.changeFragmentSize(
|
||||
shc.maxFragmentLength);
|
||||
shc.conContext.outputRecord.changeFragmentSize(
|
||||
shc.maxFragmentLength);
|
||||
|
||||
// The response extension data is the same as the requested one.
|
||||
shc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
|
||||
return new byte[] { spec.id };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "max_fragment_length" extension in the
|
||||
* EncryptedExtensions handshake message.
|
||||
*/
|
||||
private static final
|
||||
class EEMaxFragmentLengthConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private EEMaxFragmentLengthConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to "max_fragment_length" extension request only
|
||||
MaxFragLenSpec requestedSpec = (MaxFragLenSpec)
|
||||
chc.handshakeExtensions.get(CH_MAX_FRAGMENT_LENGTH);
|
||||
if (requestedSpec == null) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected max_fragment_length extension in ServerHello");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
MaxFragLenSpec spec;
|
||||
try {
|
||||
spec = new MaxFragLenSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
if (spec.id != requestedSpec.id) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"The maximum fragment length response is not requested");
|
||||
}
|
||||
|
||||
MaxFragLenEnum mfle = MaxFragLenEnum.valueOf(spec.id);
|
||||
if (mfle == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"the requested maximum fragment length is other " +
|
||||
"than the allowed values");
|
||||
}
|
||||
|
||||
// update the context
|
||||
chc.maxFragmentLength = mfle.fragmentSize;
|
||||
chc.handshakeExtensions.put(EE_MAX_FRAGMENT_LENGTH, spec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After session creation consuming of a "max_fragment_length"
|
||||
* extension in the EncryptedExtensions handshake message.
|
||||
*/
|
||||
private static final
|
||||
class EEMaxFragmentLengthUpdate implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private EEMaxFragmentLengthUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
MaxFragLenSpec spec = (MaxFragLenSpec)
|
||||
chc.handshakeExtensions.get(EE_MAX_FRAGMENT_LENGTH);
|
||||
if (spec == null) {
|
||||
// Ignore, no "max_fragment_length" extension response.
|
||||
return;
|
||||
}
|
||||
|
||||
if ((chc.maxFragmentLength > 0) &&
|
||||
(chc.sslConfig.maximumPacketSize != 0)) {
|
||||
int estimatedMaxFragSize =
|
||||
chc.negotiatedCipherSuite.calculatePacketSize(
|
||||
chc.maxFragmentLength, chc.negotiatedProtocol);
|
||||
if (estimatedMaxFragSize > chc.sslConfig.maximumPacketSize) {
|
||||
// For better interoperability, abort the maximum
|
||||
// fragment length negotiation, rather than terminate
|
||||
// the connection with a fatal alert.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Abort the maximum fragment length negotiation, " +
|
||||
"may overflow the maximum packet size limit.");
|
||||
}
|
||||
chc.maxFragmentLength = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// update the context
|
||||
if (chc.maxFragmentLength > 0) {
|
||||
chc.handshakeSession.setNegotiatedMaxFragSize(
|
||||
chc.maxFragmentLength);
|
||||
chc.conContext.inputRecord.changeFragmentSize(
|
||||
chc.maxFragmentLength);
|
||||
chc.conContext.outputRecord.changeFragmentSize(
|
||||
chc.maxFragmentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
389
jdkSrc/jdk8/sun/security/ssl/NewSessionTicket.java
Normal file
389
jdkSrc/jdk8/sun/security/ssl/NewSessionTicket.java
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
|
||||
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the NewSessionTicket handshake message.
|
||||
*/
|
||||
final class NewSessionTicket {
|
||||
private static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
|
||||
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new NewSessionTicketConsumer();
|
||||
static final SSLProducer kickstartProducer =
|
||||
new NewSessionTicketKickstartProducer();
|
||||
static final HandshakeProducer handshakeProducer =
|
||||
new NewSessionTicketProducer();
|
||||
|
||||
/**
|
||||
* The NewSessionTicketMessage handshake message.
|
||||
*/
|
||||
static final class NewSessionTicketMessage extends HandshakeMessage {
|
||||
final int ticketLifetime;
|
||||
final int ticketAgeAdd;
|
||||
final byte[] ticketNonce;
|
||||
final byte[] ticket;
|
||||
final SSLExtensions extensions;
|
||||
|
||||
NewSessionTicketMessage(HandshakeContext context,
|
||||
int ticketLifetime, SecureRandom generator,
|
||||
byte[] ticketNonce, byte[] ticket) {
|
||||
super(context);
|
||||
|
||||
this.ticketLifetime = ticketLifetime;
|
||||
this.ticketAgeAdd = generator.nextInt();
|
||||
this.ticketNonce = ticketNonce;
|
||||
this.ticket = ticket;
|
||||
this.extensions = new SSLExtensions(this);
|
||||
}
|
||||
|
||||
NewSessionTicketMessage(HandshakeContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(context);
|
||||
|
||||
// struct {
|
||||
// uint32 ticket_lifetime;
|
||||
// uint32 ticket_age_add;
|
||||
// opaque ticket_nonce<0..255>;
|
||||
// opaque ticket<1..2^16-1>;
|
||||
// Extension extensions<0..2^16-2>;
|
||||
// } NewSessionTicket;
|
||||
if (m.remaining() < 14) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
}
|
||||
|
||||
this.ticketLifetime = Record.getInt32(m);
|
||||
this.ticketAgeAdd = Record.getInt32(m);
|
||||
this.ticketNonce = Record.getBytes8(m);
|
||||
|
||||
if (m.remaining() < 5) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
}
|
||||
|
||||
this.ticket = Record.getBytes16(m);
|
||||
if (ticket.length == 0) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No ticket in the NewSessionTicket handshake message");
|
||||
}
|
||||
|
||||
if (m.remaining() < 2) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
}
|
||||
|
||||
SSLExtension[] supportedExtensions =
|
||||
context.sslConfig.getEnabledExtensions(
|
||||
SSLHandshake.NEW_SESSION_TICKET);
|
||||
this.extensions = new SSLExtensions(this, m, supportedExtensions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.NEW_SESSION_TICKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
int extLen = extensions.length();
|
||||
if (extLen == 0) {
|
||||
extLen = 2; // empty extensions
|
||||
}
|
||||
|
||||
return 8 + ticketNonce.length + 1 +
|
||||
ticket.length + 2 + extLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putInt32(ticketLifetime);
|
||||
hos.putInt32(ticketAgeAdd);
|
||||
hos.putBytes8(ticketNonce);
|
||||
hos.putBytes16(ticket);
|
||||
|
||||
// Is it an empty extensions?
|
||||
if (extensions.length() == 0) {
|
||||
hos.putInt16(0);
|
||||
} else {
|
||||
extensions.send(hos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"NewSessionTicket\": '{'\n" +
|
||||
" \"ticket_lifetime\" : \"{0}\",\n" +
|
||||
" \"ticket_age_add\" : \"{1}\",\n" +
|
||||
" \"ticket_nonce\" : \"{2}\",\n" +
|
||||
" \"ticket\" : \"{3}\",\n" +
|
||||
" \"extensions\" : [\n" +
|
||||
"{4}\n" +
|
||||
" ]\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
ticketLifetime,
|
||||
"<omitted>", //ticketAgeAdd should not be logged
|
||||
Utilities.toHexString(ticketNonce),
|
||||
Utilities.toHexString(ticket),
|
||||
Utilities.indent(extensions.toString(), " ")
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretKey derivePreSharedKey(CipherSuite.HashAlg hashAlg,
|
||||
SecretKey resumptionMasterSecret, byte[] nonce) throws IOException {
|
||||
try {
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
"tls13 resumption".getBytes(), nonce, hashAlg.hashLength);
|
||||
return hkdf.expand(resumptionMasterSecret, hkdfInfo,
|
||||
hashAlg.hashLength, "TlsPreSharedKey");
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not derive PSK").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class NewSessionTicketKickstartProducer implements SSLProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private NewSessionTicketKickstartProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is this session resumable?
|
||||
if (!shc.handshakeSession.isRejoinable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// What's the requested PSK key exchange modes?
|
||||
//
|
||||
// Note that currently, the NewSessionTicket post-handshake is
|
||||
// produced and delivered only in the current handshake context
|
||||
// if required.
|
||||
PskKeyExchangeModesSpec pkemSpec =
|
||||
(PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES);
|
||||
if (pkemSpec == null || !pkemSpec.contains(
|
||||
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
|
||||
// Client doesn't support PSK with (EC)DHE key establishment.
|
||||
return null;
|
||||
}
|
||||
|
||||
// get a new session ID
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
shc.sslContext.engineGetServerSessionContext();
|
||||
SessionId newId = new SessionId(true,
|
||||
shc.sslContext.getSecureRandom());
|
||||
|
||||
SecretKey resumptionMasterSecret =
|
||||
shc.handshakeSession.getResumptionMasterSecret();
|
||||
if (resumptionMasterSecret == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Session has no resumption secret. No ticket sent.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// construct the PSK and handshake message
|
||||
BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
|
||||
byte[] nonceArr = nonce.toByteArray();
|
||||
SecretKey psk = derivePreSharedKey(
|
||||
shc.negotiatedCipherSuite.hashAlg,
|
||||
resumptionMasterSecret, nonceArr);
|
||||
|
||||
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
|
||||
if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Session timeout is too long. No ticket sent.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
NewSessionTicketMessage nstm = new NewSessionTicketMessage(shc,
|
||||
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
|
||||
nonceArr, newId.getId());
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced NewSessionTicket handshake message", nstm);
|
||||
}
|
||||
|
||||
// create and cache the new session
|
||||
// The new session must be a child of the existing session so
|
||||
// they will be invalidated together, etc.
|
||||
SSLSessionImpl sessionCopy =
|
||||
new SSLSessionImpl(shc.handshakeSession, newId);
|
||||
shc.handshakeSession.addChild(sessionCopy);
|
||||
sessionCopy.setPreSharedKey(psk);
|
||||
sessionCopy.setPskIdentity(newId.getId());
|
||||
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
|
||||
sessionCache.put(sessionCopy);
|
||||
|
||||
// Output the handshake message.
|
||||
nstm.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "NewSessionTicket" handshake message producer.
|
||||
*/
|
||||
private static final class NewSessionTicketProducer
|
||||
implements HandshakeProducer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private NewSessionTicketProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
|
||||
// NSTM may be sent in response to handshake messages.
|
||||
// For example: key update
|
||||
|
||||
throw new ProviderException(
|
||||
"NewSessionTicket handshake producer not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class NewSessionTicketConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private NewSessionTicketConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
|
||||
// Note: Although the resumption master secret depends on the
|
||||
// client's second flight, servers which do not request client
|
||||
// authentication MAY compute the remainder of the transcript
|
||||
// independently and then send a NewSessionTicket immediately
|
||||
// upon sending its Finished rather than waiting for the client
|
||||
// Finished.
|
||||
//
|
||||
// The consuming happens in client side only. As the server
|
||||
// may send the NewSessionTicket before handshake complete, the
|
||||
// context may be a PostHandshakeContext or HandshakeContext
|
||||
// instance.
|
||||
HandshakeContext hc = (HandshakeContext)context;
|
||||
NewSessionTicketMessage nstm =
|
||||
new NewSessionTicketMessage(hc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming NewSessionTicket message", nstm);
|
||||
}
|
||||
|
||||
// discard tickets with timeout 0
|
||||
if (nstm.ticketLifetime <= 0 ||
|
||||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Discarding NewSessionTicket with lifetime "
|
||||
+ nstm.ticketLifetime, nstm);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
hc.sslContext.engineGetClientSessionContext();
|
||||
|
||||
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Session cache lifetime is too long. Discarding ticket.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SSLSessionImpl sessionToSave = hc.conContext.conSession;
|
||||
|
||||
SecretKey resumptionMasterSecret =
|
||||
sessionToSave.getResumptionMasterSecret();
|
||||
if (resumptionMasterSecret == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Session has no resumption master secret. Ignoring ticket.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// derive the PSK
|
||||
SecretKey psk = derivePreSharedKey(
|
||||
sessionToSave.getSuite().hashAlg, resumptionMasterSecret,
|
||||
nstm.ticketNonce);
|
||||
|
||||
// create and cache the new session
|
||||
// The new session must be a child of the existing session so
|
||||
// they will be invalidated together, etc.
|
||||
SessionId newId =
|
||||
new SessionId(true, hc.sslContext.getSecureRandom());
|
||||
SSLSessionImpl sessionCopy = new SSLSessionImpl(sessionToSave,
|
||||
newId);
|
||||
sessionToSave.addChild(sessionCopy);
|
||||
sessionCopy.setPreSharedKey(psk);
|
||||
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
|
||||
sessionCopy.setPskIdentity(nstm.ticket);
|
||||
sessionCache.put(sessionCopy);
|
||||
|
||||
// clean handshake context
|
||||
hc.conContext.finishPostHandshake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
521
jdkSrc/jdk8/sun/security/ssl/OutputRecord.java
Normal file
521
jdkSrc/jdk8/sun/security/ssl/OutputRecord.java
Normal file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||
|
||||
/**
|
||||
* {@code OutputRecord} takes care of the management of SSL/TLS
|
||||
* output records, including buffering, encryption, handshake
|
||||
* messages marshal, etc.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
abstract class OutputRecord
|
||||
extends ByteArrayOutputStream implements Record, Closeable {
|
||||
SSLWriteCipher writeCipher;
|
||||
// Needed for KeyUpdate, used after Handshake.Finished
|
||||
TransportContext tc;
|
||||
|
||||
final HandshakeHash handshakeHash;
|
||||
boolean firstMessage;
|
||||
|
||||
// current protocol version, sent as record version
|
||||
ProtocolVersion protocolVersion;
|
||||
|
||||
// version for the ClientHello message. Only relevant if this is a
|
||||
// client handshake record. If set to ProtocolVersion.SSL20Hello,
|
||||
// the V3 client hello is converted to V2 format.
|
||||
ProtocolVersion helloVersion;
|
||||
|
||||
// Is it the first application record to write?
|
||||
boolean isFirstAppOutputRecord = true;
|
||||
|
||||
// packet size
|
||||
int packetSize;
|
||||
|
||||
// fragment size
|
||||
private int fragmentSize;
|
||||
|
||||
// closed or not?
|
||||
volatile boolean isClosed;
|
||||
|
||||
/*
|
||||
* Mappings from V3 cipher suite encodings to their pure V2 equivalents.
|
||||
* This is taken from the SSL V3 specification, Appendix E.
|
||||
*/
|
||||
private static final int[] V3toV2CipherMap1 =
|
||||
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
|
||||
private static final int[] V3toV2CipherMap3 =
|
||||
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
|
||||
private static final byte[] HANDSHAKE_MESSAGE_KEY_UPDATE =
|
||||
{SSLHandshake.KEY_UPDATE.id, 0x00, 0x00, 0x01, 0x00};
|
||||
|
||||
OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
|
||||
this.writeCipher = writeCipher;
|
||||
this.firstMessage = true;
|
||||
this.fragmentSize = Record.maxDataSize;
|
||||
|
||||
this.handshakeHash = handshakeHash;
|
||||
|
||||
// Please set packetSize and protocolVersion in the implementation.
|
||||
}
|
||||
|
||||
synchronized void setVersion(ProtocolVersion protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates helloVersion of this record.
|
||||
*/
|
||||
synchronized void setHelloVersion(ProtocolVersion helloVersion) {
|
||||
this.helloVersion = helloVersion;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true iff the record is empty -- to avoid doing the work
|
||||
* of sending empty records over the network.
|
||||
*/
|
||||
boolean isEmpty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized boolean seqNumIsHuge() {
|
||||
return (writeCipher.authenticator != null) &&
|
||||
writeCipher.authenticator.seqNumIsHuge();
|
||||
}
|
||||
|
||||
// SSLEngine and SSLSocket
|
||||
abstract void encodeAlert(byte level, byte description) throws IOException;
|
||||
|
||||
// SSLEngine and SSLSocket
|
||||
abstract void encodeHandshake(byte[] buffer,
|
||||
int offset, int length) throws IOException;
|
||||
|
||||
// SSLEngine and SSLSocket
|
||||
abstract void encodeChangeCipherSpec() throws IOException;
|
||||
|
||||
// apply to SSLEngine only
|
||||
Ciphertext encode(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// apply to SSLEngine only
|
||||
void encodeV2NoCipher() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// apply to SSLSocket only
|
||||
void deliver(
|
||||
byte[] source, int offset, int length) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// apply to SSLSocket only
|
||||
void setDeliverStream(OutputStream outputStream) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Change write ciphers, may use change_cipher_spec record.
|
||||
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
|
||||
boolean useChangeCipherSpec) throws IOException {
|
||||
if (isClosed()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"change_cipher_spec message");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (useChangeCipherSpec) {
|
||||
encodeChangeCipherSpec();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of any intermediate state in the underlying cipher.
|
||||
* For PKCS11 ciphers, this will release any attached sessions,
|
||||
* and thus make finalization faster.
|
||||
*
|
||||
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
|
||||
* not necessary to do the same with MAC's.
|
||||
*/
|
||||
writeCipher.dispose();
|
||||
|
||||
this.writeCipher = writeCipher;
|
||||
this.isFirstAppOutputRecord = true;
|
||||
}
|
||||
|
||||
// Change write ciphers using key_update handshake message.
|
||||
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
|
||||
byte keyUpdateRequest) throws IOException {
|
||||
if (isClosed()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"key_update handshake message");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// encode the handshake message, KeyUpdate
|
||||
byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
|
||||
hm[hm.length - 1] = keyUpdateRequest;
|
||||
encodeHandshake(hm, 0, hm.length);
|
||||
flush();
|
||||
|
||||
// Dispose of any intermediate state in the underlying cipher.
|
||||
writeCipher.dispose();
|
||||
|
||||
this.writeCipher = writeCipher;
|
||||
this.isFirstAppOutputRecord = true;
|
||||
}
|
||||
|
||||
synchronized void changePacketSize(int packetSize) {
|
||||
this.packetSize = packetSize;
|
||||
}
|
||||
|
||||
synchronized void changeFragmentSize(int fragmentSize) {
|
||||
this.fragmentSize = fragmentSize;
|
||||
}
|
||||
|
||||
synchronized int getMaxPacketSize() {
|
||||
return packetSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
isClosed = true;
|
||||
writeCipher.dispose();
|
||||
}
|
||||
|
||||
boolean isClosed() {
|
||||
return isClosed;
|
||||
}
|
||||
|
||||
//
|
||||
// shared helpers
|
||||
//
|
||||
|
||||
private static final class T13PaddingHolder {
|
||||
private static final byte[] zeros = new byte[16];
|
||||
}
|
||||
|
||||
int calculateFragmentSize(int fragmentLimit) {
|
||||
if (fragmentSize > 0) {
|
||||
fragmentLimit = Math.min(fragmentLimit, fragmentSize);
|
||||
}
|
||||
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
// No negative integer checking as the fragment capacity should
|
||||
// have been ensured.
|
||||
return fragmentLimit - T13PaddingHolder.zeros.length - 1;
|
||||
}
|
||||
|
||||
return fragmentLimit;
|
||||
}
|
||||
|
||||
// Encrypt a fragment and wrap up a record.
|
||||
//
|
||||
// To be consistent with the spec of SSLEngine.wrap() methods, the
|
||||
// destination ByteBuffer's position is updated to reflect the amount
|
||||
// of data produced. The limit remains the same.
|
||||
static long encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
|
||||
int headerOffset, int dstLim, int headerSize,
|
||||
ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
return t13Encrypt(encCipher,
|
||||
contentType, destination, headerOffset,
|
||||
dstLim, headerSize, protocolVersion);
|
||||
} else {
|
||||
return t10Encrypt(encCipher,
|
||||
contentType, destination, headerOffset,
|
||||
dstLim, headerSize, protocolVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private static long t13Encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
|
||||
int headerOffset, int dstLim, int headerSize,
|
||||
ProtocolVersion protocolVersion) {
|
||||
if (!encCipher.isNullCipher()) {
|
||||
// inner plaintext, using zero length padding.
|
||||
int endOfPt = destination.limit();
|
||||
int startOfPt = destination.position();
|
||||
destination.position(endOfPt);
|
||||
destination.limit(endOfPt + 1 + T13PaddingHolder.zeros.length);
|
||||
destination.put(contentType);
|
||||
destination.put(T13PaddingHolder.zeros);
|
||||
destination.position(startOfPt);
|
||||
}
|
||||
|
||||
// use the right TLSCiphertext.opaque_type and legacy_record_version
|
||||
ProtocolVersion pv = protocolVersion;
|
||||
if (!encCipher.isNullCipher()) {
|
||||
pv = ProtocolVersion.TLS12;
|
||||
contentType = ContentType.APPLICATION_DATA.id;
|
||||
} else if (protocolVersion.useTLS13PlusSpec()) {
|
||||
pv = ProtocolVersion.TLS12;
|
||||
}
|
||||
|
||||
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
|
||||
encCipher.encrypt(contentType, destination);
|
||||
|
||||
// Finish out the record header.
|
||||
int fragLen = destination.limit() - headerOffset - headerSize;
|
||||
destination.put(headerOffset, contentType);
|
||||
destination.put(headerOffset + 1, pv.major);
|
||||
destination.put(headerOffset + 2, pv.minor);
|
||||
|
||||
// fragment length
|
||||
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
|
||||
destination.put(headerOffset + 4, (byte)fragLen);
|
||||
|
||||
// Update destination position to reflect the amount of data produced.
|
||||
destination.position(destination.limit());
|
||||
|
||||
return Authenticator.toLong(sequenceNumber);
|
||||
}
|
||||
|
||||
private static long t10Encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
|
||||
int headerOffset, int dstLim, int headerSize,
|
||||
ProtocolVersion protocolVersion) {
|
||||
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
|
||||
encCipher.encrypt(contentType, destination);
|
||||
|
||||
// Finish out the record header.
|
||||
int fragLen = destination.limit() - headerOffset - headerSize;
|
||||
|
||||
destination.put(headerOffset, contentType); // content type
|
||||
destination.put(headerOffset + 1, protocolVersion.major);
|
||||
destination.put(headerOffset + 2, protocolVersion.minor);
|
||||
|
||||
// fragment length
|
||||
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
|
||||
destination.put(headerOffset + 4, (byte)fragLen);
|
||||
|
||||
// Update destination position to reflect the amount of data produced.
|
||||
destination.position(destination.limit());
|
||||
|
||||
return Authenticator.toLong(sequenceNumber);
|
||||
}
|
||||
|
||||
// Encrypt a fragment and wrap up a record.
|
||||
//
|
||||
// Uses the internal expandable buf variable and the current
|
||||
// protocolVersion variable.
|
||||
long encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, int headerSize) {
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
return t13Encrypt(encCipher, contentType, headerSize);
|
||||
} else {
|
||||
return t10Encrypt(encCipher, contentType, headerSize);
|
||||
}
|
||||
}
|
||||
|
||||
private long t13Encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, int headerSize) {
|
||||
if (!encCipher.isNullCipher()) {
|
||||
// inner plaintext
|
||||
write(contentType);
|
||||
write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
|
||||
}
|
||||
|
||||
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
|
||||
int position = headerSize;
|
||||
int contentLen = count - position;
|
||||
|
||||
// ensure the capacity
|
||||
int requiredPacketSize =
|
||||
encCipher.calculatePacketSize(contentLen, headerSize);
|
||||
if (requiredPacketSize > buf.length) {
|
||||
byte[] newBuf = new byte[requiredPacketSize];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
buf = newBuf;
|
||||
}
|
||||
|
||||
// use the right TLSCiphertext.opaque_type and legacy_record_version
|
||||
ProtocolVersion pv = protocolVersion;
|
||||
if (!encCipher.isNullCipher()) {
|
||||
pv = ProtocolVersion.TLS12;
|
||||
contentType = ContentType.APPLICATION_DATA.id;
|
||||
} else {
|
||||
pv = ProtocolVersion.TLS12;
|
||||
}
|
||||
|
||||
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
|
||||
count = headerSize + encCipher.encrypt(contentType, destination);
|
||||
|
||||
// Fill out the header, write it and the message.
|
||||
int fragLen = count - headerSize;
|
||||
|
||||
buf[0] = contentType;
|
||||
buf[1] = pv.major;
|
||||
buf[2] = pv.minor;
|
||||
buf[3] = (byte)((fragLen >> 8) & 0xFF);
|
||||
buf[4] = (byte)(fragLen & 0xFF);
|
||||
|
||||
return Authenticator.toLong(sequenceNumber);
|
||||
}
|
||||
|
||||
private long t10Encrypt(
|
||||
SSLWriteCipher encCipher, byte contentType, int headerSize) {
|
||||
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
|
||||
int position = headerSize + writeCipher.getExplicitNonceSize();
|
||||
int contentLen = count - position;
|
||||
|
||||
// ensure the capacity
|
||||
int requiredPacketSize =
|
||||
encCipher.calculatePacketSize(contentLen, headerSize);
|
||||
if (requiredPacketSize > buf.length) {
|
||||
byte[] newBuf = new byte[requiredPacketSize];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
buf = newBuf;
|
||||
}
|
||||
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
|
||||
count = headerSize + encCipher.encrypt(contentType, destination);
|
||||
|
||||
// Fill out the header, write it and the message.
|
||||
int fragLen = count - headerSize;
|
||||
buf[0] = contentType;
|
||||
buf[1] = protocolVersion.major;
|
||||
buf[2] = protocolVersion.minor;
|
||||
buf[3] = (byte)((fragLen >> 8) & 0xFF);
|
||||
buf[4] = (byte)(fragLen & 0xFF);
|
||||
|
||||
return Authenticator.toLong(sequenceNumber);
|
||||
}
|
||||
|
||||
static ByteBuffer encodeV2ClientHello(
|
||||
byte[] fragment, int offset, int length) throws IOException {
|
||||
int v3SessIdLenOffset = offset + 34; // 2: client_version
|
||||
// 32: random
|
||||
|
||||
int v3SessIdLen = fragment[v3SessIdLenOffset];
|
||||
int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
|
||||
int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
|
||||
(fragment[v3CSLenOffset + 1] & 0xff);
|
||||
int cipherSpecs = v3CSLen / 2; // 2: cipher spec size
|
||||
|
||||
// Estimate the max V2ClientHello message length
|
||||
//
|
||||
// 11: header size
|
||||
// (cipherSpecs * 6): cipher_specs
|
||||
// 6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
|
||||
// 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
|
||||
// signaling cipher suite
|
||||
// 32: challenge size
|
||||
int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
|
||||
|
||||
// Create a ByteBuffer backed by an accessible byte array.
|
||||
byte[] dstBytes = new byte[v2MaxMsgLen];
|
||||
ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
|
||||
|
||||
/*
|
||||
* Copy over the cipher specs. We don't care about actually
|
||||
* translating them for use with an actual V2 server since
|
||||
* we only talk V3. Therefore, just copy over the V3 cipher
|
||||
* spec values with a leading 0.
|
||||
*/
|
||||
int v3CSOffset = v3CSLenOffset + 2; // skip length field
|
||||
int v2CSLen = 0;
|
||||
|
||||
dstBuf.position(11);
|
||||
boolean containsRenegoInfoSCSV = false;
|
||||
for (int i = 0; i < cipherSpecs; i++) {
|
||||
byte byte1, byte2;
|
||||
|
||||
byte1 = fragment[v3CSOffset++];
|
||||
byte2 = fragment[v3CSOffset++];
|
||||
v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
|
||||
if (!containsRenegoInfoSCSV &&
|
||||
byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
|
||||
containsRenegoInfoSCSV = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!containsRenegoInfoSCSV) {
|
||||
v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy in the nonce.
|
||||
*/
|
||||
dstBuf.put(fragment, (offset + 2), 32);
|
||||
|
||||
/*
|
||||
* Build the first part of the V3 record header from the V2 one
|
||||
* that's now buffered up. (Lengths are fixed up later).
|
||||
*/
|
||||
int msgLen = dstBuf.position() - 2; // Exclude the legth field itself
|
||||
dstBuf.position(0);
|
||||
dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
|
||||
dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
|
||||
dstBuf.put(SSLHandshake.CLIENT_HELLO.id); // pos: 2
|
||||
dstBuf.put(fragment[offset]); // major version, pos: 3
|
||||
dstBuf.put(fragment[offset + 1]); // minor version, pos: 4
|
||||
dstBuf.put((byte)(v2CSLen >>> 8)); // pos: 5
|
||||
dstBuf.put((byte)(v2CSLen & 0xFF)); // pos: 6
|
||||
dstBuf.put((byte)0x00); // session_id_length, pos: 7
|
||||
dstBuf.put((byte)0x00); // pos: 8
|
||||
dstBuf.put((byte)0x00); // challenge_length, pos: 9
|
||||
dstBuf.put((byte)32); // pos: 10
|
||||
|
||||
dstBuf.position(0);
|
||||
dstBuf.limit(msgLen + 2);
|
||||
|
||||
return dstBuf;
|
||||
}
|
||||
|
||||
private static int V3toV2CipherSuite(ByteBuffer dstBuf,
|
||||
byte byte1, byte byte2) {
|
||||
dstBuf.put((byte)0);
|
||||
dstBuf.put(byte1);
|
||||
dstBuf.put(byte2);
|
||||
|
||||
if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
dstBuf.put((byte)V3toV2CipherMap1[byte2]);
|
||||
dstBuf.put((byte)0);
|
||||
dstBuf.put((byte)V3toV2CipherMap3[byte2]);
|
||||
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
79
jdkSrc/jdk8/sun/security/ssl/Plaintext.java
Normal file
79
jdkSrc/jdk8/sun/security/ssl/Plaintext.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
|
||||
/*
|
||||
* Plaintext
|
||||
*/
|
||||
final class Plaintext {
|
||||
static final Plaintext PLAINTEXT_NULL = new Plaintext();
|
||||
|
||||
final byte contentType;
|
||||
final byte majorVersion;
|
||||
final byte minorVersion;
|
||||
final int recordEpoch; // increments on every cipher state change
|
||||
final long recordSN; // epoch | sequence number
|
||||
final ByteBuffer fragment; // null if need to be reassembled
|
||||
|
||||
HandshakeStatus handshakeStatus; // null if not used or not handshaking
|
||||
|
||||
private Plaintext() {
|
||||
this.contentType = 0;
|
||||
this.majorVersion = 0;
|
||||
this.minorVersion = 0;
|
||||
this.recordEpoch = -1;
|
||||
this.recordSN = -1;
|
||||
this.fragment = null;
|
||||
this.handshakeStatus = null;
|
||||
}
|
||||
|
||||
Plaintext(byte contentType,
|
||||
byte majorVersion, byte minorVersion,
|
||||
int recordEpoch, long recordSN, ByteBuffer fragment) {
|
||||
|
||||
this.contentType = contentType;
|
||||
this.majorVersion = majorVersion;
|
||||
this.minorVersion = minorVersion;
|
||||
this.recordEpoch = recordEpoch;
|
||||
this.recordSN = recordSN;
|
||||
this.fragment = fragment;
|
||||
|
||||
this.handshakeStatus = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "contentType: " + contentType + "/" +
|
||||
"majorVersion: " + majorVersion + "/" +
|
||||
"minorVersion: " + minorVersion + "/" +
|
||||
"recordEpoch: " + recordEpoch + "/" +
|
||||
"recordSN: 0x" + Long.toHexString(recordSN) + "/" +
|
||||
"fragment: " + fragment;
|
||||
}
|
||||
}
|
||||
109
jdkSrc/jdk8/sun/security/ssl/PostHandshakeContext.java
Normal file
109
jdkSrc/jdk8/sun/security/ssl/PostHandshakeContext.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A compact implementation of HandshakeContext for post-handshake messages
|
||||
*/
|
||||
final class PostHandshakeContext extends HandshakeContext {
|
||||
PostHandshakeContext(TransportContext context) throws IOException {
|
||||
super(context);
|
||||
|
||||
if (!negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Post-handshake not supported in " + negotiatedProtocol.name);
|
||||
}
|
||||
|
||||
this.localSupportedSignAlgs = new ArrayList<>(
|
||||
context.conSession.getLocalSupportedSignatureSchemes());
|
||||
|
||||
// Add the potential post-handshake consumers.
|
||||
if (context.sslConfig.isClientMode) {
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.KEY_UPDATE.id,
|
||||
SSLHandshake.KEY_UPDATE);
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.NEW_SESSION_TICKET.id,
|
||||
SSLHandshake.NEW_SESSION_TICKET);
|
||||
} else {
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.KEY_UPDATE.id,
|
||||
SSLHandshake.KEY_UPDATE);
|
||||
}
|
||||
|
||||
handshakeFinished = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void kickstart() throws IOException {
|
||||
SSLHandshake.kickstart(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void dispatch(byte handshakeType, ByteBuffer fragment) throws IOException {
|
||||
SSLConsumer consumer = handshakeConsumers.get(handshakeType);
|
||||
if (consumer == null) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected post-handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType));
|
||||
}
|
||||
|
||||
try {
|
||||
consumer.consume(this, fragment);
|
||||
} catch (UnsupportedOperationException unsoe) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported post-handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType), unsoe);
|
||||
} catch (BufferUnderflowException | BufferOverflowException be) {
|
||||
throw conContext.fatal(Alert.DECODE_ERROR,
|
||||
"Illegal handshake message: " +
|
||||
SSLHandshake.nameOf(handshakeType), be);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isConsumable(TransportContext context, byte handshakeType) {
|
||||
if (handshakeType == SSLHandshake.KEY_UPDATE.id) {
|
||||
// The KeyUpdate handshake message does not apply to TLS 1.2 and
|
||||
// previous protocols.
|
||||
return context.protocolVersion.useTLS13PlusSpec();
|
||||
}
|
||||
|
||||
if (handshakeType == SSLHandshake.NEW_SESSION_TICKET.id) {
|
||||
// The new session ticket handshake message could be consumer in
|
||||
// client side only.
|
||||
return context.sslConfig.isClientMode;
|
||||
}
|
||||
|
||||
// No more post-handshake message supported currently.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
892
jdkSrc/jdk8/sun/security/ssl/PreSharedKeyExtension.java
Normal file
892
jdkSrc/jdk8/sun/security/ssl/PreSharedKeyExtension.java
Normal file
@@ -0,0 +1,892 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Collection;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
|
||||
import sun.security.ssl.ClientHello.ClientHelloMessage;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import static sun.security.ssl.SSLExtension.*;
|
||||
|
||||
/**
|
||||
* Pack of the "pre_shared_key" extension.
|
||||
*/
|
||||
final class PreSharedKeyExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHPreSharedKeyProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHPreSharedKeyConsumer();
|
||||
static final HandshakeAbsence chOnLoadAbsence =
|
||||
new CHPreSharedKeyAbsence();
|
||||
static final HandshakeConsumer chOnTradeConsumer =
|
||||
new CHPreSharedKeyUpdate();
|
||||
static final SSLStringizer chStringizer =
|
||||
new CHPreSharedKeyStringizer();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer =
|
||||
new SHPreSharedKeyProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHPreSharedKeyConsumer();
|
||||
static final HandshakeAbsence shOnLoadAbsence =
|
||||
new SHPreSharedKeyAbsence();
|
||||
static final SSLStringizer shStringizer =
|
||||
new SHPreSharedKeyStringizer();
|
||||
|
||||
private static final class PskIdentity {
|
||||
final byte[] identity;
|
||||
final int obfuscatedAge;
|
||||
|
||||
PskIdentity(byte[] identity, int obfuscatedAge) {
|
||||
this.identity = identity;
|
||||
this.obfuscatedAge = obfuscatedAge;
|
||||
}
|
||||
|
||||
int getEncodedLength() {
|
||||
return 2 + identity.length + 4;
|
||||
}
|
||||
|
||||
void writeEncoded(ByteBuffer m) throws IOException {
|
||||
Record.putBytes16(m, identity);
|
||||
Record.putInt32(m, obfuscatedAge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + Utilities.toHexString(identity) + "," +
|
||||
obfuscatedAge + "}";
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeySpec implements SSLExtensionSpec {
|
||||
final List<PskIdentity> identities;
|
||||
final List<byte[]> binders;
|
||||
|
||||
CHPreSharedKeySpec(List<PskIdentity> identities, List<byte[]> binders) {
|
||||
this.identities = identities;
|
||||
this.binders = binders;
|
||||
}
|
||||
|
||||
CHPreSharedKeySpec(HandshakeContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
// struct {
|
||||
// PskIdentity identities<7..2^16-1>;
|
||||
// PskBinderEntry binders<33..2^16-1>;
|
||||
// } OfferedPsks;
|
||||
if (m.remaining() < 44) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient data (length=" + m.remaining() + ")");
|
||||
}
|
||||
|
||||
int idEncodedLength = Record.getInt16(m);
|
||||
if (idEncodedLength < 7) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient identities (length=" + idEncodedLength + ")");
|
||||
}
|
||||
|
||||
identities = new ArrayList<>();
|
||||
int idReadLength = 0;
|
||||
while (idReadLength < idEncodedLength) {
|
||||
byte[] id = Record.getBytes16(m);
|
||||
if (id.length < 1) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient identity (length=" + id.length + ")");
|
||||
}
|
||||
int obfuscatedTicketAge = Record.getInt32(m);
|
||||
|
||||
PskIdentity pskId = new PskIdentity(id, obfuscatedTicketAge);
|
||||
identities.add(pskId);
|
||||
idReadLength += pskId.getEncodedLength();
|
||||
}
|
||||
|
||||
if (m.remaining() < 35) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient binders data (length=" +
|
||||
m.remaining() + ")");
|
||||
}
|
||||
|
||||
int bindersEncodedLen = Record.getInt16(m);
|
||||
if (bindersEncodedLen < 33) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient binders (length=" +
|
||||
bindersEncodedLen + ")");
|
||||
}
|
||||
|
||||
binders = new ArrayList<>();
|
||||
int bindersReadLength = 0;
|
||||
while (bindersReadLength < bindersEncodedLen) {
|
||||
byte[] binder = Record.getBytes8(m);
|
||||
if (binder.length < 32) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient binder entry (length=" +
|
||||
binder.length + ")");
|
||||
}
|
||||
binders.add(binder);
|
||||
bindersReadLength += 1 + binder.length;
|
||||
}
|
||||
}
|
||||
|
||||
int getIdsEncodedLength() {
|
||||
int idEncodedLength = 0;
|
||||
for (PskIdentity curId : identities) {
|
||||
idEncodedLength += curId.getEncodedLength();
|
||||
}
|
||||
|
||||
return idEncodedLength;
|
||||
}
|
||||
|
||||
int getBindersEncodedLength() {
|
||||
int binderEncodedLength = 0;
|
||||
for (byte[] curBinder : binders) {
|
||||
binderEncodedLength += 1 + curBinder.length;
|
||||
}
|
||||
|
||||
return binderEncodedLength;
|
||||
}
|
||||
|
||||
byte[] getEncoded() throws IOException {
|
||||
int idsEncodedLength = getIdsEncodedLength();
|
||||
int bindersEncodedLength = getBindersEncodedLength();
|
||||
int encodedLength = 4 + idsEncodedLength + bindersEncodedLength;
|
||||
byte[] buffer = new byte[encodedLength];
|
||||
ByteBuffer m = ByteBuffer.wrap(buffer);
|
||||
Record.putInt16(m, idsEncodedLength);
|
||||
for (PskIdentity curId : identities) {
|
||||
curId.writeEncoded(m);
|
||||
}
|
||||
Record.putInt16(m, bindersEncodedLength);
|
||||
for (byte[] curBinder : binders) {
|
||||
Record.putBytes8(m, curBinder);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"PreSharedKey\": '{'\n" +
|
||||
" \"identities\" : \"{0}\",\n" +
|
||||
" \"binders\" : \"{1}\",\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(identitiesString()),
|
||||
Utilities.indent(bindersString())
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
|
||||
String identitiesString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (PskIdentity curId : identities) {
|
||||
result.append(curId.toString() + "\n");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
String bindersString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte[] curBinder : binders) {
|
||||
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeyStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
// As the HandshakeContext parameter of CHPreSharedKeySpec
|
||||
// constructor is used for fatal alert only, we can use
|
||||
// null HandshakeContext here as we don't care about exception.
|
||||
//
|
||||
// Please take care of this code if the CHPreSharedKeySpec
|
||||
// constructor is updated in the future.
|
||||
return (new CHPreSharedKeySpec(null, buffer)).toString();
|
||||
} catch (Exception ex) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ex.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class SHPreSharedKeySpec implements SSLExtensionSpec {
|
||||
final int selectedIdentity;
|
||||
|
||||
SHPreSharedKeySpec(int selectedIdentity) {
|
||||
this.selectedIdentity = selectedIdentity;
|
||||
}
|
||||
|
||||
SHPreSharedKeySpec(HandshakeContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
if (m.remaining() < 2) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid pre_shared_key extension: " +
|
||||
"insufficient selected_identity (length=" +
|
||||
m.remaining() + ")");
|
||||
}
|
||||
this.selectedIdentity = Record.getInt16(m);
|
||||
}
|
||||
|
||||
byte[] getEncoded() throws IOException {
|
||||
return new byte[] {
|
||||
(byte)((selectedIdentity >> 8) & 0xFF),
|
||||
(byte)(selectedIdentity & 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"PreSharedKey\": '{'\n" +
|
||||
" \"selected_identity\" : \"{0}\",\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
Object[] messageFields = {
|
||||
Utilities.byte16HexString(selectedIdentity)
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class SHPreSharedKeyStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
// As the HandshakeContext parameter of SHPreSharedKeySpec
|
||||
// constructor is used for fatal alert only, we can use
|
||||
// null HandshakeContext here as we don't care about exception.
|
||||
//
|
||||
// Please take care of this code if the SHPreSharedKeySpec
|
||||
// constructor is updated in the future.
|
||||
return (new SHPreSharedKeySpec(null, buffer)).toString();
|
||||
} catch (Exception ex) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ex.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeyConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHPreSharedKeyConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message,
|
||||
ByteBuffer buffer) throws IOException {
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage) message;
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable pre_shared_key extension");
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
CHPreSharedKeySpec pskSpec = null;
|
||||
try {
|
||||
pskSpec = new CHPreSharedKeySpec(shc, buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// The "psk_key_exchange_modes" extension should have been loaded.
|
||||
if (!shc.handshakeExtensions.containsKey(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Client sent PSK but not PSK modes, or the PSK " +
|
||||
"extension is not the last extension");
|
||||
}
|
||||
|
||||
// error if id and binder lists are not the same length
|
||||
if (pskSpec.identities.size() != pskSpec.binders.size()) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"PSK extension has incorrect number of binders");
|
||||
}
|
||||
|
||||
if (shc.isResumption) { // resumingSession may not be set
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
shc.sslContext.engineGetServerSessionContext();
|
||||
int idIndex = 0;
|
||||
for (PskIdentity requestedId : pskSpec.identities) {
|
||||
SSLSessionImpl s = sessionCache.pull(requestedId.identity);
|
||||
if (s != null && canRejoin(clientHello, shc, s)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Resuming session: ", s);
|
||||
}
|
||||
|
||||
// binder will be checked later
|
||||
shc.resumingSession = s;
|
||||
shc.handshakeExtensions.put(SH_PRE_SHARED_KEY,
|
||||
new SHPreSharedKeySpec(idIndex)); // for the index
|
||||
break;
|
||||
}
|
||||
|
||||
++idIndex;
|
||||
}
|
||||
|
||||
if (idIndex == pskSpec.identities.size()) {
|
||||
// no resumable session
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the context
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canRejoin(ClientHelloMessage clientHello,
|
||||
ServerHandshakeContext shc, SSLSessionImpl s) {
|
||||
|
||||
boolean result = s.isRejoinable() && (s.getPreSharedKey() != null);
|
||||
|
||||
// Check protocol version
|
||||
if (result && s.getProtocolVersion() != shc.negotiatedProtocol) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake,verbose")) {
|
||||
|
||||
SSLLogger.finest("Can't resume, incorrect protocol version");
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Make sure that the server handshake context's localSupportedSignAlgs
|
||||
// field is populated. This is particularly important when
|
||||
// client authentication was used in an initial session and it is
|
||||
// now being resumed.
|
||||
if (shc.localSupportedSignAlgs == null) {
|
||||
shc.localSupportedSignAlgs =
|
||||
SignatureScheme.getSupportedAlgorithms(
|
||||
shc.sslConfig,
|
||||
shc.algorithmConstraints, shc.activeProtocols);
|
||||
}
|
||||
|
||||
// Validate the required client authentication.
|
||||
if (result &&
|
||||
(shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
|
||||
try {
|
||||
s.getPeerPrincipal();
|
||||
} catch (SSLPeerUnverifiedException e) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake,verbose")) {
|
||||
SSLLogger.finest(
|
||||
"Can't resume, " +
|
||||
"client authentication is required");
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Make sure the list of supported signature algorithms matches
|
||||
Collection<SignatureScheme> sessionSigAlgs =
|
||||
s.getLocalSupportedSignatureSchemes();
|
||||
if (result &&
|
||||
!shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Can't resume. Session uses different " +
|
||||
"signature algorithms");
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the endpoint identification algorithm matches the
|
||||
// one in the session
|
||||
String identityAlg = shc.sslConfig.identificationProtocol;
|
||||
if (result && identityAlg != null) {
|
||||
String sessionIdentityAlg = s.getIdentificationProtocol();
|
||||
if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake,verbose")) {
|
||||
|
||||
SSLLogger.finest("Can't resume, endpoint id" +
|
||||
" algorithm does not match, requested: " +
|
||||
identityAlg + ", cached: " + sessionIdentityAlg);
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure cipher suite can be negotiated
|
||||
if (result && (!shc.isNegotiable(s.getSuite()) ||
|
||||
!clientHello.cipherSuites.contains(s.getSuite()))) {
|
||||
if (SSLLogger.isOn &&
|
||||
SSLLogger.isOn("ssl,handshake,verbose")) {
|
||||
SSLLogger.finest(
|
||||
"Can't resume, unavailable session cipher suite");
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeyUpdate implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHPreSharedKeyUpdate() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
if (!shc.isResumption || shc.resumingSession == null) {
|
||||
// not resuming---nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
CHPreSharedKeySpec chPsk = (CHPreSharedKeySpec)
|
||||
shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
|
||||
SHPreSharedKeySpec shPsk = (SHPreSharedKeySpec)
|
||||
shc.handshakeExtensions.get(SSLExtension.SH_PRE_SHARED_KEY);
|
||||
if (chPsk == null || shPsk == null) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Required extensions are unavailable");
|
||||
}
|
||||
|
||||
byte[] binder = chPsk.binders.get(shPsk.selectedIdentity);
|
||||
|
||||
// set up PSK binder hash
|
||||
HandshakeHash pskBinderHash = shc.handshakeHash.copy();
|
||||
byte[] lastMessage = pskBinderHash.removeLastReceived();
|
||||
ByteBuffer messageBuf = ByteBuffer.wrap(lastMessage);
|
||||
// skip the type and length
|
||||
messageBuf.position(4);
|
||||
// read to find the beginning of the binders
|
||||
ClientHelloMessage.readPartial(shc.conContext, messageBuf);
|
||||
int length = messageBuf.position();
|
||||
messageBuf.position(0);
|
||||
pskBinderHash.receive(messageBuf, length);
|
||||
|
||||
checkBinder(shc, shc.resumingSession, pskBinderHash, binder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkBinder(ServerHandshakeContext shc,
|
||||
SSLSessionImpl session,
|
||||
HandshakeHash pskBinderHash, byte[] binder) throws IOException {
|
||||
SecretKey psk = session.getPreSharedKey();
|
||||
if (psk == null) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Session has no PSK");
|
||||
}
|
||||
|
||||
SecretKey binderKey = deriveBinderKey(shc, psk, session);
|
||||
byte[] computedBinder =
|
||||
computeBinder(shc, binderKey, session, pskBinderHash);
|
||||
if (!MessageDigest.isEqual(binder, computedBinder)) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Incorect PSK binder value");
|
||||
}
|
||||
}
|
||||
|
||||
// Class that produces partial messages used to compute binder hash
|
||||
static final class PartialClientHelloMessage extends HandshakeMessage {
|
||||
|
||||
private final ClientHello.ClientHelloMessage msg;
|
||||
private final CHPreSharedKeySpec psk;
|
||||
|
||||
PartialClientHelloMessage(HandshakeContext ctx,
|
||||
ClientHello.ClientHelloMessage msg,
|
||||
CHPreSharedKeySpec psk) {
|
||||
super(ctx);
|
||||
|
||||
this.msg = msg;
|
||||
this.psk = psk;
|
||||
}
|
||||
|
||||
@Override
|
||||
SSLHandshake handshakeType() {
|
||||
return msg.handshakeType();
|
||||
}
|
||||
|
||||
private int pskTotalLength() {
|
||||
return psk.getIdsEncodedLength() +
|
||||
psk.getBindersEncodedLength() + 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
int messageLength() {
|
||||
|
||||
if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) != null) {
|
||||
return msg.messageLength();
|
||||
} else {
|
||||
return msg.messageLength() + pskTotalLength();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void send(HandshakeOutStream hos) throws IOException {
|
||||
msg.sendCore(hos);
|
||||
|
||||
// complete extensions
|
||||
int extsLen = msg.extensions.length();
|
||||
if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) == null) {
|
||||
extsLen += pskTotalLength();
|
||||
}
|
||||
hos.putInt16(extsLen - 2);
|
||||
// write the complete extensions
|
||||
for (SSLExtension ext : SSLExtension.values()) {
|
||||
byte[] extData = msg.extensions.get(ext);
|
||||
if (extData == null) {
|
||||
continue;
|
||||
}
|
||||
// the PSK could be there from an earlier round
|
||||
if (ext == SSLExtension.CH_PRE_SHARED_KEY) {
|
||||
continue;
|
||||
}
|
||||
int extID = ext.id;
|
||||
hos.putInt16(extID);
|
||||
hos.putBytes16(extData);
|
||||
}
|
||||
|
||||
// partial PSK extension
|
||||
int extID = SSLExtension.CH_PRE_SHARED_KEY.id;
|
||||
hos.putInt16(extID);
|
||||
byte[] encodedPsk = psk.getEncoded();
|
||||
hos.putInt16(encodedPsk.length);
|
||||
hos.write(encodedPsk, 0, psk.getIdsEncodedLength() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeyProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHPreSharedKeyProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
if (!chc.isResumption || chc.resumingSession == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("No session to resume.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure the list of supported signature algorithms matches
|
||||
Collection<SignatureScheme> sessionSigAlgs =
|
||||
chc.resumingSession.getLocalSupportedSignatureSchemes();
|
||||
if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Existing session uses different " +
|
||||
"signature algorithms");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// The session must have a pre-shared key
|
||||
SecretKey psk = chc.resumingSession.getPreSharedKey();
|
||||
if (psk == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Existing session has no PSK.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// The PSK ID can only be used in one connections, but this method
|
||||
// may be called twice in a connection if the server sends HRR.
|
||||
// ID is saved in the context so it can be used in the second call.
|
||||
if (chc.pskIdentity == null) {
|
||||
chc.pskIdentity = chc.resumingSession.consumePskIdentity();
|
||||
}
|
||||
|
||||
if (chc.pskIdentity == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"PSK has no identity, or identity was already used");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//The session cannot be used again. Remove it from the cache.
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
chc.sslContext.engineGetClientSessionContext();
|
||||
sessionCache.remove(chc.resumingSession.getSessionId());
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Found resumable session. Preparing PSK message.");
|
||||
}
|
||||
|
||||
List<PskIdentity> identities = new ArrayList<>();
|
||||
int ageMillis = (int)(System.currentTimeMillis() -
|
||||
chc.resumingSession.getTicketCreationTime());
|
||||
int obfuscatedAge =
|
||||
ageMillis + chc.resumingSession.getTicketAgeAdd();
|
||||
identities.add(new PskIdentity(chc.pskIdentity, obfuscatedAge));
|
||||
|
||||
SecretKey binderKey =
|
||||
deriveBinderKey(chc, psk, chc.resumingSession);
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||
CHPreSharedKeySpec pskPrototype = createPskPrototype(
|
||||
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
|
||||
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
|
||||
|
||||
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
|
||||
chc.resumingSession, chc, clientHello, pskPrototype);
|
||||
|
||||
List<byte[]> binders = new ArrayList<>();
|
||||
binders.add(binder);
|
||||
|
||||
CHPreSharedKeySpec pskMessage =
|
||||
new CHPreSharedKeySpec(identities, binders);
|
||||
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
|
||||
return pskMessage.getEncoded();
|
||||
}
|
||||
|
||||
private CHPreSharedKeySpec createPskPrototype(
|
||||
int hashLength, List<PskIdentity> identities) {
|
||||
List<byte[]> binders = new ArrayList<>();
|
||||
byte[] binderProto = new byte[hashLength];
|
||||
for (PskIdentity curId : identities) {
|
||||
binders.add(binderProto);
|
||||
}
|
||||
|
||||
return new CHPreSharedKeySpec(identities, binders);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] computeBinder(
|
||||
HandshakeContext context, SecretKey binderKey,
|
||||
SSLSessionImpl session,
|
||||
HandshakeHash pskBinderHash) throws IOException {
|
||||
|
||||
pskBinderHash.determine(
|
||||
session.getProtocolVersion(), session.getSuite());
|
||||
pskBinderHash.update();
|
||||
byte[] digest = pskBinderHash.digest();
|
||||
|
||||
return computeBinder(context, binderKey, session, digest);
|
||||
}
|
||||
|
||||
private static byte[] computeBinder(
|
||||
HandshakeContext context, SecretKey binderKey,
|
||||
HandshakeHash hash, SSLSessionImpl session,
|
||||
HandshakeContext ctx, ClientHello.ClientHelloMessage hello,
|
||||
CHPreSharedKeySpec pskPrototype) throws IOException {
|
||||
|
||||
PartialClientHelloMessage partialMsg =
|
||||
new PartialClientHelloMessage(ctx, hello, pskPrototype);
|
||||
|
||||
SSLEngineOutputRecord record = new SSLEngineOutputRecord(hash);
|
||||
HandshakeOutStream hos = new HandshakeOutStream(record);
|
||||
partialMsg.write(hos);
|
||||
|
||||
hash.determine(session.getProtocolVersion(), session.getSuite());
|
||||
hash.update();
|
||||
byte[] digest = hash.digest();
|
||||
|
||||
return computeBinder(context, binderKey, session, digest);
|
||||
}
|
||||
|
||||
private static byte[] computeBinder(HandshakeContext context,
|
||||
SecretKey binderKey,
|
||||
SSLSessionImpl session, byte[] digest) throws IOException {
|
||||
try {
|
||||
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
byte[] label = ("tls13 finished").getBytes();
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
label, new byte[0], hashAlg.hashLength);
|
||||
SecretKey finishedKey = hkdf.expand(
|
||||
binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
|
||||
|
||||
String hmacAlg =
|
||||
"Hmac" + hashAlg.name.replace("-", "");
|
||||
try {
|
||||
Mac hmac = JsseJce.getMac(hmacAlg);
|
||||
hmac.init(finishedKey);
|
||||
return hmac.doFinal(digest);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
}
|
||||
} catch (GeneralSecurityException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretKey deriveBinderKey(HandshakeContext context,
|
||||
SecretKey psk, SSLSessionImpl session) throws IOException {
|
||||
try {
|
||||
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
|
||||
|
||||
byte[] label = ("tls13 res binder").getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance(hashAlg.name);
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
label, md.digest(new byte[0]), hashAlg.hashLength);
|
||||
return hkdf.expand(earlySecret,
|
||||
hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
|
||||
} catch (GeneralSecurityException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class CHPreSharedKeyAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Handling pre_shared_key absence.");
|
||||
}
|
||||
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Resumption is only determined by PSK, when enabled
|
||||
shc.resumingSession = null;
|
||||
shc.isResumption = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class SHPreSharedKeyConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHPreSharedKeyConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a response of the specific request?
|
||||
if (!chc.handshakeExtensions.containsKey(
|
||||
SSLExtension.CH_PRE_SHARED_KEY)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Server sent unexpected pre_shared_key extension");
|
||||
}
|
||||
|
||||
SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Received pre_shared_key extension: ", shPsk);
|
||||
}
|
||||
|
||||
if (shPsk.selectedIdentity != 0) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Selected identity index is not in correct range.");
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Resuming session: ", chc.resumingSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class SHPreSharedKeyAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Handling pre_shared_key absence.");
|
||||
}
|
||||
|
||||
// The server refused to resume, or the client did not
|
||||
// request 1.3 resumption.
|
||||
chc.resumingSession = null;
|
||||
chc.isResumption = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class SHPreSharedKeyProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHPreSharedKeyProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
SHPreSharedKeySpec psk = (SHPreSharedKeySpec)
|
||||
shc.handshakeExtensions.get(SH_PRE_SHARED_KEY);
|
||||
if (psk == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return psk.getEncoded();
|
||||
}
|
||||
}
|
||||
}
|
||||
314
jdkSrc/jdk8/sun/security/ssl/PredefinedDHParameterSpecs.java
Normal file
314
jdkSrc/jdk8/sun/security/ssl/PredefinedDHParameterSpecs.java
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import sun.security.util.SafeDHParameterSpec;
|
||||
|
||||
/**
|
||||
* Predefined default DH ephemeral parameters.
|
||||
*/
|
||||
final class PredefinedDHParameterSpecs {
|
||||
|
||||
//
|
||||
// Default DH ephemeral parameters
|
||||
//
|
||||
private static final BigInteger p512 = new BigInteger( // generated
|
||||
"D87780E15FF50B4ABBE89870188B049406B5BEA98AB23A02" +
|
||||
"41D88EA75B7755E669C08093D3F0CA7FC3A5A25CF067DCB9" +
|
||||
"A43DD89D1D90921C6328884461E0B6D3", 16);
|
||||
private static final BigInteger p768 = new BigInteger( // RFC 2409
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
|
||||
"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16);
|
||||
|
||||
private static final BigInteger p1024 = new BigInteger( // RFC 2409
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" +
|
||||
"FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p1536 = new BigInteger( // RFC 3526
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
|
||||
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
|
||||
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
|
||||
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
|
||||
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
|
||||
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
|
||||
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p2048 = new BigInteger( // TLS FFDHE
|
||||
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
|
||||
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
|
||||
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
|
||||
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
|
||||
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
|
||||
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
|
||||
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
|
||||
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
|
||||
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
|
||||
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
|
||||
"886B423861285C97FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p3072 = new BigInteger( // TLS FFDHE
|
||||
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
|
||||
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
|
||||
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
|
||||
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
|
||||
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
|
||||
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
|
||||
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
|
||||
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
|
||||
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
|
||||
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
|
||||
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
|
||||
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
|
||||
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
|
||||
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
|
||||
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
|
||||
"3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p4096 = new BigInteger( // TLS FFDHE
|
||||
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
|
||||
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
|
||||
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
|
||||
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
|
||||
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
|
||||
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
|
||||
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
|
||||
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
|
||||
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
|
||||
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
|
||||
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
|
||||
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
|
||||
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
|
||||
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
|
||||
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
|
||||
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
|
||||
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
|
||||
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
|
||||
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
|
||||
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
|
||||
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6A" +
|
||||
"FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p6144 = new BigInteger( // TLS FFDHE
|
||||
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
|
||||
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
|
||||
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
|
||||
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
|
||||
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
|
||||
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
|
||||
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
|
||||
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
|
||||
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
|
||||
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
|
||||
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
|
||||
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
|
||||
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
|
||||
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
|
||||
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
|
||||
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
|
||||
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
|
||||
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
|
||||
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
|
||||
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
|
||||
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
|
||||
"0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
|
||||
"3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
|
||||
"CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
|
||||
"A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
|
||||
"0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
|
||||
"763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
|
||||
"B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
|
||||
"D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
|
||||
"E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
|
||||
"5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
|
||||
"A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", 16);
|
||||
private static final BigInteger p8192 = new BigInteger( // TLS FFDHE
|
||||
"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1" +
|
||||
"D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF9" +
|
||||
"7D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD6561" +
|
||||
"2433F51F5F066ED0856365553DED1AF3B557135E7F57C935" +
|
||||
"984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE735" +
|
||||
"30ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FB" +
|
||||
"B96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB19" +
|
||||
"0B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F61" +
|
||||
"9172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD73" +
|
||||
"3BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA" +
|
||||
"886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C0238" +
|
||||
"61B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91C" +
|
||||
"AEFE130985139270B4130C93BC437944F4FD4452E2D74DD3" +
|
||||
"64F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0D" +
|
||||
"ABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF" +
|
||||
"3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB" +
|
||||
"7930E9E4E58857B6AC7D5F42D69F6D187763CF1D55034004" +
|
||||
"87F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832" +
|
||||
"A907600A918130C46DC778F971AD0038092999A333CB8B7A" +
|
||||
"1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF" +
|
||||
"8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD902" +
|
||||
"0BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA6" +
|
||||
"3BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3A" +
|
||||
"CDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477" +
|
||||
"A52471F7A9A96910B855322EDB6340D8A00EF092350511E3" +
|
||||
"0ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4" +
|
||||
"763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6" +
|
||||
"B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538C" +
|
||||
"D72B03746AE77F5E62292C311562A846505DC82DB854338A" +
|
||||
"E49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B04" +
|
||||
"5B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1" +
|
||||
"A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C838" +
|
||||
"1E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E" +
|
||||
"0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665" +
|
||||
"CB2C0F1CC01BD70229388839D2AF05E454504AC78B758282" +
|
||||
"2846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022" +
|
||||
"BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C" +
|
||||
"51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9" +
|
||||
"D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA457" +
|
||||
"1EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30" +
|
||||
"FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D" +
|
||||
"97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88C" +
|
||||
"D68C8BB7C5C6424CFFFFFFFFFFFFFFFF", 16);
|
||||
|
||||
private static final BigInteger[] supportedPrimes = {
|
||||
p512, p768, p1024, p1536, p2048, p3072, p4096, p6144, p8192};
|
||||
|
||||
private static final BigInteger[] ffdhePrimes = {
|
||||
p2048, p3072, p4096, p6144, p8192};
|
||||
|
||||
// a measure of the uncertainty that prime modulus p is not a prime
|
||||
//
|
||||
// see BigInteger.isProbablePrime(int certainty)
|
||||
private static final int PRIME_CERTAINTY = 120;
|
||||
|
||||
// the known security property, jdk.tls.server.defaultDHEParameters
|
||||
private static final String PROPERTY_NAME =
|
||||
"jdk.tls.server.defaultDHEParameters";
|
||||
|
||||
private static final Pattern spacesPattern = Pattern.compile("\\s+");
|
||||
|
||||
private static final Pattern syntaxPattern = Pattern.compile(
|
||||
"(\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})" +
|
||||
"(,\\{[0-9A-Fa-f]+,[0-9A-Fa-f]+\\})*");
|
||||
|
||||
private static final Pattern paramsPattern = Pattern.compile(
|
||||
"\\{([0-9A-Fa-f]+),([0-9A-Fa-f]+)\\}");
|
||||
|
||||
// cache of predefined default DH ephemeral parameters
|
||||
static final Map<Integer, DHParameterSpec> definedParams;
|
||||
|
||||
// cache of Finite Field DH Ephemeral parameters (RFC 7919/FFDHE)
|
||||
static final Map<Integer, DHParameterSpec> ffdheParams;
|
||||
|
||||
static {
|
||||
String property = AccessController.doPrivileged(
|
||||
new PrivilegedAction<String>() {
|
||||
public String run() {
|
||||
return Security.getProperty(PROPERTY_NAME);
|
||||
}
|
||||
});
|
||||
|
||||
if (property != null && !property.isEmpty()) {
|
||||
// remove double quote marks from beginning/end of the property
|
||||
if (property.length() >= 2 && property.charAt(0) == '"' &&
|
||||
property.charAt(property.length() - 1) == '"') {
|
||||
property = property.substring(1, property.length() - 1);
|
||||
}
|
||||
|
||||
property = property.trim();
|
||||
}
|
||||
|
||||
if (property != null && !property.isEmpty()) {
|
||||
Matcher spacesMatcher = spacesPattern.matcher(property);
|
||||
property = spacesMatcher.replaceAll("");
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
|
||||
SSLLogger.fine(
|
||||
"The Security Property " +
|
||||
PROPERTY_NAME + ": " + property);
|
||||
}
|
||||
}
|
||||
|
||||
Map<Integer,DHParameterSpec> defaultParams = new HashMap<>();
|
||||
if (property != null && !property.isEmpty()) {
|
||||
Matcher syntaxMatcher = syntaxPattern.matcher(property);
|
||||
if (syntaxMatcher.matches()) {
|
||||
Matcher paramsFinder = paramsPattern.matcher(property);
|
||||
while(paramsFinder.find()) {
|
||||
String primeModulus = paramsFinder.group(1);
|
||||
BigInteger p = new BigInteger(primeModulus, 16);
|
||||
if (!p.isProbablePrime(PRIME_CERTAINTY)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
|
||||
SSLLogger.fine(
|
||||
"Prime modulus p in Security Property, " +
|
||||
PROPERTY_NAME + ", is not a prime: " +
|
||||
primeModulus);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
String baseGenerator = paramsFinder.group(2);
|
||||
BigInteger g = new BigInteger(baseGenerator, 16);
|
||||
|
||||
int primeLen = p.bitLength();
|
||||
DHParameterSpec spec = new DHParameterSpec(p, g);
|
||||
defaultParams.put(primeLen, spec);
|
||||
}
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("sslctx")) {
|
||||
SSLLogger.fine("Invalid Security Property, " +
|
||||
PROPERTY_NAME + ", definition");
|
||||
}
|
||||
}
|
||||
BigInteger TWO = BigInteger.valueOf(2);
|
||||
|
||||
Map<Integer,DHParameterSpec> tempFFDHEs = new HashMap<>();
|
||||
for (BigInteger p : ffdhePrimes) {
|
||||
int primeLen = p.bitLength();
|
||||
DHParameterSpec dhps = new SafeDHParameterSpec(p, TWO);
|
||||
tempFFDHEs.put(primeLen, dhps);
|
||||
defaultParams.putIfAbsent(primeLen, dhps);
|
||||
}
|
||||
|
||||
for (BigInteger p : supportedPrimes) {
|
||||
int primeLen = p.bitLength();
|
||||
if (defaultParams.get(primeLen) == null) {
|
||||
defaultParams.put(primeLen, new SafeDHParameterSpec(p, TWO));
|
||||
}
|
||||
}
|
||||
|
||||
ffdheParams =
|
||||
Collections.<Integer,DHParameterSpec>unmodifiableMap(tempFFDHEs);
|
||||
definedParams =
|
||||
Collections.<Integer,DHParameterSpec>unmodifiableMap(defaultParams);
|
||||
}
|
||||
}
|
||||
395
jdkSrc/jdk8/sun/security/ssl/ProtocolVersion.java
Normal file
395
jdkSrc/jdk8/sun/security/ssl/ProtocolVersion.java
Normal file
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Enum for an SSL/TLS protocol version.
|
||||
*
|
||||
* @author Andreas Sterbenz
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public enum ProtocolVersion {
|
||||
TLS13 (0x0304, "TLSv1.3"),
|
||||
TLS12 (0x0303, "TLSv1.2"),
|
||||
TLS11 (0x0302, "TLSv1.1"),
|
||||
TLS10 (0x0301, "TLSv1"),
|
||||
SSL30 (0x0300, "SSLv3"),
|
||||
SSL20Hello (0x0002, "SSLv2Hello"),
|
||||
|
||||
// Dummy protocol version value for invalid SSLSession
|
||||
NONE (-1, "NONE");
|
||||
|
||||
|
||||
final int id;
|
||||
final String name;
|
||||
final byte major;
|
||||
final byte minor;
|
||||
final boolean isAvailable;
|
||||
|
||||
// The limit of maximum protocol version
|
||||
static final int LIMIT_MAX_VALUE = 0xFFFF;
|
||||
|
||||
// The limit of minimum protocol version
|
||||
static final int LIMIT_MIN_VALUE = 0x0000;
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.0 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_10 = new ProtocolVersion[] {
|
||||
TLS10, SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.1 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_11 = new ProtocolVersion[] {
|
||||
TLS11, TLS10, SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.2 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_12 = new ProtocolVersion[] {
|
||||
TLS12, TLS11, TLS10, SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.3 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_13 = new ProtocolVersion[] {
|
||||
TLS13, TLS12, TLS11, TLS10, SSL30
|
||||
};
|
||||
|
||||
// No protocol version specified.
|
||||
static final ProtocolVersion[] PROTOCOLS_OF_NONE = new ProtocolVersion[] {
|
||||
NONE
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for SSL 3.0.
|
||||
static final ProtocolVersion[] PROTOCOLS_OF_30 = new ProtocolVersion[] {
|
||||
SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.1.
|
||||
static final ProtocolVersion[] PROTOCOLS_OF_11 = new ProtocolVersion[] {
|
||||
TLS11
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.2.
|
||||
static final ProtocolVersion[] PROTOCOLS_OF_12 = new ProtocolVersion[] {
|
||||
TLS12
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.3.
|
||||
static final ProtocolVersion[] PROTOCOLS_OF_13 = new ProtocolVersion[] {
|
||||
TLS13
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TSL 1.0/1.1.
|
||||
static final ProtocolVersion[] PROTOCOLS_10_11 = new ProtocolVersion[] {
|
||||
TLS11, TLS10
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TSL 1.1/1.2.
|
||||
static final ProtocolVersion[] PROTOCOLS_11_12 = new ProtocolVersion[] {
|
||||
TLS12, TLS11
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TSL 1.2/1.3.
|
||||
static final ProtocolVersion[] PROTOCOLS_12_13 = new ProtocolVersion[] {
|
||||
TLS13, TLS12
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TSL 1.0/1.1/1.2.
|
||||
static final ProtocolVersion[] PROTOCOLS_10_12 = new ProtocolVersion[] {
|
||||
TLS12, TLS11, TLS10
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.2 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_TLS12 = new ProtocolVersion[] {
|
||||
TLS12, TLS11, TLS10, SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.1 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_TLS11 = new ProtocolVersion[] {
|
||||
TLS11, TLS10, SSL30
|
||||
};
|
||||
|
||||
// TLS ProtocolVersion array for TLS 1.0 and previous versions.
|
||||
static final ProtocolVersion[] PROTOCOLS_TO_TLS10 = new ProtocolVersion[] {
|
||||
TLS10, SSL30
|
||||
};
|
||||
|
||||
// Empty ProtocolVersion array
|
||||
static final ProtocolVersion[] PROTOCOLS_EMPTY = new ProtocolVersion[0];
|
||||
|
||||
private ProtocolVersion(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.major = (byte)((id >>> 8) & 0xFF);
|
||||
this.minor = (byte)(id & 0xFF);
|
||||
|
||||
this.isAvailable = SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits(
|
||||
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT),
|
||||
name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ProtocolVersion with the specified major and minor
|
||||
* version numbers.
|
||||
*/
|
||||
static ProtocolVersion valueOf(byte major, byte minor) {
|
||||
for (ProtocolVersion pv : ProtocolVersion.values()) {
|
||||
if ((pv.major == major) && (pv.minor == minor)) {
|
||||
return pv;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ProtocolVersion with the specified version number.
|
||||
*/
|
||||
static ProtocolVersion valueOf(int id) {
|
||||
for (ProtocolVersion pv : ProtocolVersion.values()) {
|
||||
if (pv.id == id) {
|
||||
return pv;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return name of a TLS protocol specified by major and
|
||||
* minor version numbers.
|
||||
*/
|
||||
static String nameOf(byte major, byte minor) {
|
||||
for (ProtocolVersion pv : ProtocolVersion.values()) {
|
||||
if ((pv.major == major) && (pv.minor == minor)) {
|
||||
return pv.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "TLS-" + major + "." + minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return name of a TLS protocol specified by a protocol number.
|
||||
*/
|
||||
static String nameOf(int id) {
|
||||
return nameOf((byte)((id >>> 8) & 0xFF), (byte)(id & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a ProtocolVersion for the given TLS protocol name.
|
||||
*/
|
||||
static ProtocolVersion nameOf(String name) {
|
||||
for (ProtocolVersion pv : ProtocolVersion.values()) {
|
||||
if (pv.name.equals(name)) {
|
||||
return pv;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specific TLS protocol is negotiable.
|
||||
*
|
||||
* Used to filter out SSLv2Hello and protocol numbers less than the
|
||||
* minimal supported protocol versions.
|
||||
*/
|
||||
static boolean isNegotiable(
|
||||
byte major, byte minor, boolean allowSSL20Hello) {
|
||||
int v = ((major & 0xFF) << 8) | (minor & 0xFF);
|
||||
if (v < SSL30.id) {
|
||||
if (!allowSSL20Hello || (v != SSL20Hello.id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get names of a list of ProtocolVersion objects.
|
||||
*/
|
||||
static String[] toStringArray(List<ProtocolVersion> protocolVersions) {
|
||||
if ((protocolVersions != null) && !protocolVersions.isEmpty()) {
|
||||
String[] protocolNames = new String[protocolVersions.size()];
|
||||
int i = 0;
|
||||
for (ProtocolVersion pv : protocolVersions) {
|
||||
protocolNames[i++] = pv.name;
|
||||
}
|
||||
|
||||
return protocolNames;
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get names of a list of protocol version identifiers.
|
||||
*/
|
||||
static String[] toStringArray(int[] protocolVersions) {
|
||||
if ((protocolVersions != null) && protocolVersions.length != 0) {
|
||||
String[] protocolNames = new String[protocolVersions.length];
|
||||
int i = 0;
|
||||
for (int pv : protocolVersions) {
|
||||
protocolNames[i++] = ProtocolVersion.nameOf(pv);
|
||||
}
|
||||
|
||||
return protocolNames;
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ProtocolVersion objects of an array protocol
|
||||
* version names.
|
||||
*/
|
||||
static List<ProtocolVersion> namesOf(String[] protocolNames) {
|
||||
if (protocolNames == null || protocolNames.length == 0) {
|
||||
return Collections.<ProtocolVersion>emptyList();
|
||||
}
|
||||
|
||||
List<ProtocolVersion> pvs = new ArrayList<>(protocolNames.length);
|
||||
for (String pn : protocolNames) {
|
||||
ProtocolVersion pv = ProtocolVersion.nameOf(pn);
|
||||
if (pv == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported protocol" + pn);
|
||||
}
|
||||
|
||||
pvs.add(pv);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(pvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specific protocol version name is
|
||||
* of TLS 1.2 or newer version.
|
||||
*/
|
||||
static boolean useTLS12PlusSpec(String name) {
|
||||
ProtocolVersion pv = ProtocolVersion.nameOf(name);
|
||||
if (pv != null && pv != NONE) {
|
||||
return pv.id >= TLS12.id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this object with the specified ProtocolVersion.
|
||||
*
|
||||
* @see java.lang.Comparable
|
||||
*/
|
||||
int compare(ProtocolVersion that) {
|
||||
if (this == that) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this == ProtocolVersion.NONE) {
|
||||
return -1;
|
||||
} else if (that == ProtocolVersion.NONE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return this.id - that.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of TLS 1.3 or
|
||||
* newer version.
|
||||
*/
|
||||
boolean useTLS13PlusSpec() {
|
||||
return this.id >= TLS13.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of TLS 1.2 or
|
||||
* newer version.
|
||||
*/
|
||||
boolean useTLS12PlusSpec() {
|
||||
return this.id >= TLS12.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of
|
||||
* TLS 1.1 or newer version.
|
||||
*/
|
||||
boolean useTLS11PlusSpec() {
|
||||
return this.id >= TLS11.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of TLS 1.0 or
|
||||
* newer version.
|
||||
*/
|
||||
boolean useTLS10PlusSpec() {
|
||||
return this.id >= TLS10.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of TLS 1.0 or
|
||||
* newer version.
|
||||
*/
|
||||
static boolean useTLS10PlusSpec(int id) {
|
||||
return id >= TLS10.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this ProtocolVersion object is of TLS 1.3 or
|
||||
* newer version.
|
||||
*/
|
||||
static boolean useTLS13PlusSpec(int id) {
|
||||
return id >= TLS13.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the lower of that suggested protocol version and
|
||||
* the highest of the listed protocol versions.
|
||||
*
|
||||
* @param listedVersions the listed protocol version
|
||||
* @param suggestedVersion the suggested protocol version
|
||||
*/
|
||||
static ProtocolVersion selectedFrom(
|
||||
List<ProtocolVersion> listedVersions, int suggestedVersion) {
|
||||
ProtocolVersion selectedVersion = ProtocolVersion.NONE;
|
||||
for (ProtocolVersion pv : listedVersions) {
|
||||
if (pv.id == suggestedVersion) {
|
||||
return pv;
|
||||
} else {
|
||||
if (pv.id < suggestedVersion && pv.id > selectedVersion.id) {
|
||||
selectedVersion = pv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedVersion;
|
||||
}
|
||||
}
|
||||
332
jdkSrc/jdk8/sun/security/ssl/PskKeyExchangeModesExtension.java
Normal file
332
jdkSrc/jdk8/sun/security/ssl/PskKeyExchangeModesExtension.java
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
|
||||
/**
|
||||
* Pack of the "psk_key_exchange_modes" extensions.
|
||||
*/
|
||||
final class PskKeyExchangeModesExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new PskKeyExchangeModesProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new PskKeyExchangeModesConsumer();
|
||||
static final HandshakeAbsence chOnLoadAbsence =
|
||||
new PskKeyExchangeModesOnLoadAbsence();
|
||||
static final HandshakeAbsence chOnTradeAbsence =
|
||||
new PskKeyExchangeModesOnTradeAbsence();
|
||||
|
||||
static final SSLStringizer pkemStringizer =
|
||||
new PskKeyExchangeModesStringizer();
|
||||
|
||||
enum PskKeyExchangeMode {
|
||||
PSK_KE ((byte)0, "psk_ke"),
|
||||
PSK_DHE_KE ((byte)1, "psk_dhe_ke");
|
||||
|
||||
final byte id;
|
||||
final String name;
|
||||
|
||||
PskKeyExchangeMode(byte id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static PskKeyExchangeMode valueOf(byte id) {
|
||||
for(PskKeyExchangeMode pkem : values()) {
|
||||
if (pkem.id == id) {
|
||||
return pkem;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(byte id) {
|
||||
for (PskKeyExchangeMode pkem : PskKeyExchangeMode.values()) {
|
||||
if (pkem.id == id) {
|
||||
return pkem.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "<UNKNOWN PskKeyExchangeMode TYPE: " + (id & 0x0FF) + ">";
|
||||
}
|
||||
}
|
||||
|
||||
static final
|
||||
class PskKeyExchangeModesSpec implements SSLExtensionSpec {
|
||||
private static final PskKeyExchangeModesSpec DEFAULT =
|
||||
new PskKeyExchangeModesSpec(new byte[] {
|
||||
PskKeyExchangeMode.PSK_DHE_KE.id});
|
||||
|
||||
final byte[] modes;
|
||||
|
||||
PskKeyExchangeModesSpec(byte[] modes) {
|
||||
this.modes = modes;
|
||||
}
|
||||
|
||||
PskKeyExchangeModesSpec(ByteBuffer m) throws IOException {
|
||||
if (m.remaining() < 2) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid psk_key_exchange_modes extension: " +
|
||||
"insufficient data");
|
||||
}
|
||||
|
||||
this.modes = Record.getBytes8(m);
|
||||
}
|
||||
|
||||
boolean contains(PskKeyExchangeMode mode) {
|
||||
if (modes != null) {
|
||||
for (byte m : modes) {
|
||||
if (mode.id == m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"ke_modes\": '['{0}']'", Locale.ENGLISH);
|
||||
if (modes == null || modes.length == 0) {
|
||||
Object[] messageFields = {
|
||||
"<no PSK key exchange modes specified>"
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder(64);
|
||||
boolean isFirst = true;
|
||||
for (byte mode : modes) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(PskKeyExchangeMode.nameOf(mode));
|
||||
}
|
||||
|
||||
Object[] messageFields = {
|
||||
builder.toString()
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class PskKeyExchangeModesStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new PskKeyExchangeModesSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "psk_key_exchange_modes" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class PskKeyExchangeModesConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private PskKeyExchangeModesConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable psk_key_exchange_modes extension");
|
||||
}
|
||||
|
||||
// No session resumption is allowed.
|
||||
if (shc.isResumption && shc.resumingSession != null) {
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
}
|
||||
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
PskKeyExchangeModesSpec spec;
|
||||
try {
|
||||
spec = new PskKeyExchangeModesSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
shc.handshakeExtensions.put(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES, spec);
|
||||
|
||||
// Impact on session resumption.
|
||||
//
|
||||
// Do the requested modes support session resumption?
|
||||
if (shc.isResumption) { // resumingSession may not be set
|
||||
// Note: psk_dhe_ke is the only supported mode now. If the
|
||||
// psk_ke mode is supported in the future, may need an update
|
||||
// here.
|
||||
if (!spec.contains(PskKeyExchangeMode.PSK_DHE_KE)) {
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"abort session resumption, " +
|
||||
"no supported psk_dhe_ke PSK key exchange mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "psk_key_exchange_modes" extension in the
|
||||
* ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class PskKeyExchangeModesProducer implements HandshakeProducer {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private PskKeyExchangeModesProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore unavailable psk_key_exchange_modes extension");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] extData = new byte[] {0x01, 0x01}; // psk_dhe_ke
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES,
|
||||
PskKeyExchangeModesSpec.DEFAULT);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The absence processing if a "psk_key_exchange_modes" extension is
|
||||
* not present in the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class PskKeyExchangeModesOnLoadAbsence implements HandshakeAbsence {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private PskKeyExchangeModesOnLoadAbsence() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// No session resumptio is allowed.
|
||||
if (shc.isResumption) { // resumingSession may not be set
|
||||
shc.isResumption = false;
|
||||
shc.resumingSession = null;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"abort session resumption, " +
|
||||
"no supported psk_dhe_ke PSK key exchange mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if a "signature_algorithms" extension is
|
||||
* not present in the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class PskKeyExchangeModesOnTradeAbsence implements HandshakeAbsence {
|
||||
|
||||
// Prevent instantiation of this class.
|
||||
private PskKeyExchangeModesOnTradeAbsence() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// A client MUST provide a "psk_key_exchange_modes" extension if
|
||||
// it offers a "pre_shared_key" extension. If clients offer
|
||||
// "pre_shared_key" without a "psk_key_exchange_modes" extension,
|
||||
// servers MUST abort the handshake.
|
||||
SSLExtensionSpec spec =
|
||||
shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
|
||||
if (spec != null) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"pre_shared_key key extension is offered " +
|
||||
"without a psk_key_exchange_modes extension");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
319
jdkSrc/jdk8/sun/security/ssl/RSAClientKeyExchange.java
Normal file
319
jdkSrc/jdk8/sun/security/ssl/RSAClientKeyExchange.java
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.SecretKey;
|
||||
import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
|
||||
import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
|
||||
import sun.security.ssl.RSAKeyExchange.RSAPremasterSecret;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the "ClientKeyExchange" handshake message.
|
||||
*/
|
||||
final class RSAClientKeyExchange {
|
||||
static final SSLConsumer rsaHandshakeConsumer =
|
||||
new RSAClientKeyExchangeConsumer();
|
||||
static final HandshakeProducer rsaHandshakeProducer =
|
||||
new RSAClientKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The RSA ClientKeyExchange handshake message.
|
||||
*/
|
||||
private static final
|
||||
class RSAClientKeyExchangeMessage extends HandshakeMessage {
|
||||
final int protocolVersion;
|
||||
final boolean useTLS10PlusSpec;
|
||||
final byte[] encrypted;
|
||||
|
||||
RSAClientKeyExchangeMessage(HandshakeContext context,
|
||||
RSAPremasterSecret premaster,
|
||||
PublicKey publicKey) throws GeneralSecurityException {
|
||||
super(context);
|
||||
this.protocolVersion = context.clientHelloVersion;
|
||||
this.encrypted = premaster.getEncoded(
|
||||
publicKey, context.sslContext.getSecureRandom());
|
||||
this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
|
||||
protocolVersion);
|
||||
}
|
||||
|
||||
RSAClientKeyExchangeMessage(HandshakeContext context,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(context);
|
||||
|
||||
if (m.remaining() < 2) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid RSA ClientKeyExchange message: insufficient data");
|
||||
}
|
||||
|
||||
this.protocolVersion = context.clientHelloVersion;
|
||||
this.useTLS10PlusSpec = ProtocolVersion.useTLS10PlusSpec(
|
||||
protocolVersion);
|
||||
if (useTLS10PlusSpec) {
|
||||
this.encrypted = Record.getBytes16(m);
|
||||
} else { // SSL 3.0
|
||||
this.encrypted = new byte[m.remaining()];
|
||||
m.get(encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake handshakeType() {
|
||||
return SSLHandshake.CLIENT_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int messageLength() {
|
||||
if (useTLS10PlusSpec) {
|
||||
return encrypted.length + 2;
|
||||
} else {
|
||||
return encrypted.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(HandshakeOutStream hos) throws IOException {
|
||||
if (useTLS10PlusSpec) {
|
||||
hos.putBytes16(encrypted);
|
||||
} else {
|
||||
hos.write(encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"RSA ClientKeyExchange\": '{'\n" +
|
||||
" \"client_version\": {0}\n" +
|
||||
" \"encncrypted\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
ProtocolVersion.nameOf(protocolVersion),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(encrypted), " "),
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA "ClientKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class RSAClientKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private RSAClientKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
EphemeralRSACredentials rsaCredentials = null;
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLCredentials credential : chc.handshakeCredentials) {
|
||||
if (credential instanceof EphemeralRSACredentials) {
|
||||
rsaCredentials = (EphemeralRSACredentials)credential;
|
||||
if (x509Credentials != null) {
|
||||
break;
|
||||
}
|
||||
} else if (credential instanceof X509Credentials) {
|
||||
x509Credentials = (X509Credentials)credential;
|
||||
if (rsaCredentials != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rsaCredentials == null && x509Credentials == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No RSA credentials negotiated for client key exchange");
|
||||
}
|
||||
|
||||
PublicKey publicKey = (rsaCredentials != null) ?
|
||||
rsaCredentials.popPublicKey : x509Credentials.popPublicKey;
|
||||
if (!publicKey.getAlgorithm().equals("RSA")) { // unlikely
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Not RSA public key for client key exchange");
|
||||
}
|
||||
|
||||
RSAPremasterSecret premaster;
|
||||
RSAClientKeyExchangeMessage ckem;
|
||||
try {
|
||||
premaster = RSAPremasterSecret.createPremasterSecret(chc);
|
||||
chc.handshakePossessions.add(premaster);
|
||||
ckem = new RSAClientKeyExchangeMessage(
|
||||
chc, premaster, publicKey);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Cannot generate RSA premaster secret", gse);
|
||||
}
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced RSA ClientKeyExchange handshake message", ckem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
ckem.write(chc.handshakeOutput);
|
||||
chc.handshakeOutput.flush();
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
chc.negotiatedCipherSuite.keyExchange,
|
||||
chc.negotiatedProtocol);
|
||||
if (ke == null) { // unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
|
||||
// update the states
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kd == null) { // unlikely
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
chc.negotiatedProtocol);
|
||||
} else {
|
||||
chc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(chc, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA "ClientKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class RSAClientKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private RSAClientKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
EphemeralRSAPossession rsaPossession = null;
|
||||
X509Possession x509Possession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof EphemeralRSAPossession) {
|
||||
rsaPossession = (EphemeralRSAPossession)possession;
|
||||
break;
|
||||
} else if (possession instanceof X509Possession) {
|
||||
x509Possession = (X509Possession)possession;
|
||||
if (rsaPossession != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rsaPossession == null && x509Possession == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No RSA possessions negotiated for client key exchange");
|
||||
}
|
||||
|
||||
PrivateKey privateKey = (rsaPossession != null) ?
|
||||
rsaPossession.popPrivateKey : x509Possession.popPrivateKey;
|
||||
if (!privateKey.getAlgorithm().equals("RSA")) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Not RSA private key for client key exchange");
|
||||
}
|
||||
|
||||
RSAClientKeyExchangeMessage ckem =
|
||||
new RSAClientKeyExchangeMessage(shc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming RSA ClientKeyExchange handshake message", ckem);
|
||||
}
|
||||
|
||||
// create the credentials
|
||||
RSAPremasterSecret premaster;
|
||||
try {
|
||||
premaster =
|
||||
RSAPremasterSecret.decode(shc, privateKey, ckem.encrypted);
|
||||
shc.handshakeCredentials.add(premaster);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Cannot decode RSA premaster secret", gse);
|
||||
}
|
||||
|
||||
// update the states
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
if (ke == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
|
||||
// update the states
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
SSLTrafficKeyDerivation kd =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
if (kd == null) { // unlikely
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Not supported key derivation: " +
|
||||
shc.negotiatedProtocol);
|
||||
} else {
|
||||
shc.handshakeKeyDerivation =
|
||||
kd.createKeyDerivation(shc, masterSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
313
jdkSrc/jdk8/sun/security/ssl/RSAKeyExchange.java
Normal file
313
jdkSrc/jdk8/sun/security/ssl/RSAKeyExchange.java
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
final class RSAKeyExchange {
|
||||
static final SSLPossessionGenerator poGenerator =
|
||||
new EphemeralRSAPossessionGenerator();
|
||||
static final SSLKeyAgreementGenerator kaGenerator =
|
||||
new RSAKAGenerator();
|
||||
|
||||
static final class EphemeralRSAPossession implements SSLPossession {
|
||||
// Proof of possession of the private key corresponding to the public
|
||||
// key for which a certificate is being provided for authentication.
|
||||
final RSAPublicKey popPublicKey;
|
||||
final PrivateKey popPrivateKey;
|
||||
|
||||
EphemeralRSAPossession(PrivateKey popPrivateKey,
|
||||
RSAPublicKey popPublicKey) {
|
||||
this.popPublicKey = popPublicKey;
|
||||
this.popPrivateKey = popPrivateKey;
|
||||
}
|
||||
}
|
||||
|
||||
static final class EphemeralRSACredentials implements SSLCredentials {
|
||||
final RSAPublicKey popPublicKey;
|
||||
|
||||
EphemeralRSACredentials(RSAPublicKey popPublicKey) {
|
||||
this.popPublicKey = popPublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EphemeralRSAPossessionGenerator
|
||||
implements SSLPossessionGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private EphemeralRSAPossessionGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext context) {
|
||||
try {
|
||||
EphemeralKeyManager ekm =
|
||||
context.sslContext.getEphemeralKeyManager();
|
||||
KeyPair kp = ekm.getRSAKeyPair(
|
||||
true, context.sslContext.getSecureRandom());
|
||||
if (kp != null) {
|
||||
return new EphemeralRSAPossession(
|
||||
kp.getPrivate(), (RSAPublicKey)kp.getPublic());
|
||||
} else {
|
||||
// Could not generate the ephemeral key, ignore.
|
||||
return null;
|
||||
}
|
||||
} catch (RuntimeException rte) {
|
||||
// Could not determine keylength, ignore.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final
|
||||
class RSAPremasterSecret implements SSLPossession, SSLCredentials {
|
||||
final SecretKey premasterSecret;
|
||||
|
||||
RSAPremasterSecret(SecretKey premasterSecret) {
|
||||
this.premasterSecret = premasterSecret;
|
||||
}
|
||||
|
||||
byte[] getEncoded(PublicKey publicKey,
|
||||
SecureRandom secureRandom) throws GeneralSecurityException {
|
||||
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
|
||||
cipher.init(Cipher.WRAP_MODE, publicKey, secureRandom);
|
||||
return cipher.wrap(premasterSecret);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
static RSAPremasterSecret createPremasterSecret(
|
||||
ClientHandshakeContext chc) throws GeneralSecurityException {
|
||||
String algorithm = chc.negotiatedProtocol.useTLS12PlusSpec() ?
|
||||
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
|
||||
KeyGenerator kg = JsseJce.getKeyGenerator(algorithm);
|
||||
TlsRsaPremasterSecretParameterSpec spec =
|
||||
new TlsRsaPremasterSecretParameterSpec(
|
||||
chc.clientHelloVersion,
|
||||
chc.negotiatedProtocol.id);
|
||||
kg.init(spec, chc.sslContext.getSecureRandom());
|
||||
|
||||
return new RSAPremasterSecret(kg.generateKey());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
static RSAPremasterSecret decode(ServerHandshakeContext shc,
|
||||
PrivateKey privateKey,
|
||||
byte[] encrypted) throws GeneralSecurityException {
|
||||
|
||||
byte[] encoded = null;
|
||||
boolean needFailover = false;
|
||||
Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
|
||||
try {
|
||||
// Try UNWRAP_MODE mode firstly.
|
||||
cipher.init(Cipher.UNWRAP_MODE, privateKey,
|
||||
new TlsRsaPremasterSecretParameterSpec(
|
||||
shc.clientHelloVersion,
|
||||
shc.negotiatedProtocol.id),
|
||||
shc.sslContext.getSecureRandom());
|
||||
|
||||
// The provider selection can be delayed, please don't call
|
||||
// any Cipher method before the call to Cipher.init().
|
||||
needFailover = !KeyUtil.isOracleJCEProvider(
|
||||
cipher.getProvider().getName());
|
||||
} catch (InvalidKeyException | UnsupportedOperationException iue) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("The Cipher provider "
|
||||
+ safeProviderName(cipher)
|
||||
+ " caused exception: " + iue.getMessage());
|
||||
}
|
||||
|
||||
needFailover = true;
|
||||
}
|
||||
|
||||
SecretKey preMaster;
|
||||
if (needFailover) {
|
||||
// The cipher might be spoiled by unsuccessful call to init(),
|
||||
// so request a fresh instance
|
||||
cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1);
|
||||
|
||||
// Use DECRYPT_MODE and dispose the previous initialization.
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
boolean failed = false;
|
||||
try {
|
||||
encoded = cipher.doFinal(encrypted);
|
||||
} catch (BadPaddingException bpe) {
|
||||
// Note: encoded == null
|
||||
failed = true;
|
||||
}
|
||||
encoded = KeyUtil.checkTlsPreMasterSecretKey(
|
||||
shc.clientHelloVersion, shc.negotiatedProtocol.id,
|
||||
shc.sslContext.getSecureRandom(), encoded, failed);
|
||||
preMaster = generatePremasterSecret(
|
||||
shc.clientHelloVersion, shc.negotiatedProtocol.id,
|
||||
encoded, shc.sslContext.getSecureRandom());
|
||||
} else {
|
||||
// the cipher should have been initialized
|
||||
preMaster = (SecretKey)cipher.unwrap(encrypted,
|
||||
"TlsRsaPremasterSecret", Cipher.SECRET_KEY);
|
||||
}
|
||||
|
||||
return new RSAPremasterSecret(preMaster);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieving the cipher's provider name for the debug purposes
|
||||
* can throw an exception by itself.
|
||||
*/
|
||||
private static String safeProviderName(Cipher cipher) {
|
||||
try {
|
||||
return cipher.getProvider().toString();
|
||||
} catch (Exception e) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Retrieving The Cipher provider name" +
|
||||
" caused exception ", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return cipher.toString() + " (provider name not available)";
|
||||
} catch (Exception e) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Retrieving The Cipher name" +
|
||||
" caused exception ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return "(cipher/provider names not available)";
|
||||
}
|
||||
|
||||
// generate a premaster secret with the specified version number
|
||||
@SuppressWarnings("deprecation")
|
||||
private static SecretKey generatePremasterSecret(
|
||||
int clientVersion, int serverVersion, byte[] encodedSecret,
|
||||
SecureRandom generator) throws GeneralSecurityException {
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Generating a premaster secret");
|
||||
}
|
||||
|
||||
try {
|
||||
String s = ((clientVersion >= ProtocolVersion.TLS12.id) ?
|
||||
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret");
|
||||
KeyGenerator kg = JsseJce.getKeyGenerator(s);
|
||||
kg.init(new TlsRsaPremasterSecretParameterSpec(
|
||||
clientVersion, serverVersion, encodedSecret),
|
||||
generator);
|
||||
return kg.generateKey();
|
||||
} catch (InvalidAlgorithmParameterException |
|
||||
NoSuchAlgorithmException iae) {
|
||||
// unlikely to happen, otherwise, must be a provider exception
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("RSA premaster secret generation error:");
|
||||
iae.printStackTrace(System.out);
|
||||
}
|
||||
|
||||
throw new GeneralSecurityException(
|
||||
"Could not generate premaster secret", iae);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class RSAKAGenerator implements SSLKeyAgreementGenerator {
|
||||
// Prevent instantiation of this class.
|
||||
private RSAKAGenerator() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
RSAPremasterSecret premaster = null;
|
||||
if (context instanceof ClientHandshakeContext) {
|
||||
for (SSLPossession possession : context.handshakePossessions) {
|
||||
if (possession instanceof RSAPremasterSecret) {
|
||||
premaster = (RSAPremasterSecret)possession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SSLCredentials credential : context.handshakeCredentials) {
|
||||
if (credential instanceof RSAPremasterSecret) {
|
||||
premaster = (RSAPremasterSecret)credential;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (premaster == null) {
|
||||
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"No sufficient RSA key agreement parameters negotiated");
|
||||
}
|
||||
|
||||
return new RSAKAKeyDerivation(context, premaster.premasterSecret);
|
||||
}
|
||||
|
||||
private static final
|
||||
class RSAKAKeyDerivation implements SSLKeyDerivation {
|
||||
private final HandshakeContext context;
|
||||
private final SecretKey preMasterSecret;
|
||||
|
||||
RSAKAKeyDerivation(
|
||||
HandshakeContext context, SecretKey preMasterSecret) {
|
||||
this.context = context;
|
||||
this.preMasterSecret = preMasterSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
"No expected master key derivation for protocol: " +
|
||||
context.negotiatedProtocol.name);
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
339
jdkSrc/jdk8/sun/security/ssl/RSAServerKeyExchange.java
Normal file
339
jdkSrc/jdk8/sun/security/ssl/RSAServerKeyExchange.java
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Locale;
|
||||
import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
|
||||
import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.X509Authentication.X509Credentials;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* Pack of the ServerKeyExchange handshake message.
|
||||
*/
|
||||
final class RSAServerKeyExchange {
|
||||
static final SSLConsumer rsaHandshakeConsumer =
|
||||
new RSAServerKeyExchangeConsumer();
|
||||
static final HandshakeProducer rsaHandshakeProducer =
|
||||
new RSAServerKeyExchangeProducer();
|
||||
|
||||
/**
|
||||
* The ephemeral RSA ServerKeyExchange handshake message.
|
||||
*
|
||||
* Used for RSA_EXPORT, SSL 3.0 and TLS 1.0 only.
|
||||
*/
|
||||
private static final
|
||||
class RSAServerKeyExchangeMessage extends HandshakeMessage {
|
||||
// public key encapsulated in this message
|
||||
private final byte[] modulus; // 1 to 2^16 - 1 bytes
|
||||
private final byte[] exponent; // 1 to 2^16 - 1 bytes
|
||||
|
||||
// signature bytes, none-null as no anonymous RSA key exchange.
|
||||
private final byte[] paramsSignature;
|
||||
|
||||
private RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
X509Possession x509Possession,
|
||||
EphemeralRSAPossession rsaPossession) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in server side only.
|
||||
ServerHandshakeContext shc =
|
||||
(ServerHandshakeContext)handshakeContext;
|
||||
|
||||
RSAPublicKey publicKey = rsaPossession.popPublicKey;
|
||||
RSAPublicKeySpec spec = JsseJce.getRSAPublicKeySpec(publicKey);
|
||||
this.modulus = Utilities.toByteArray(spec.getModulus());
|
||||
this.exponent = Utilities.toByteArray(spec.getPublicExponent());
|
||||
byte[] signature = null;
|
||||
try {
|
||||
Signature signer = RSASignature.getInstance();
|
||||
signer.initSign(x509Possession.popPrivateKey,
|
||||
shc.sslContext.getSecureRandom());
|
||||
updateSignature(signer,
|
||||
shc.clientHelloRandom.randomBytes,
|
||||
shc.serverHelloRandom.randomBytes);
|
||||
signature = signer.sign();
|
||||
} catch (NoSuchAlgorithmException |
|
||||
InvalidKeyException | SignatureException ex) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failed to sign ephemeral RSA parameters", ex);
|
||||
}
|
||||
|
||||
this.paramsSignature = signature;
|
||||
}
|
||||
|
||||
RSAServerKeyExchangeMessage(HandshakeContext handshakeContext,
|
||||
ByteBuffer m) throws IOException {
|
||||
super(handshakeContext);
|
||||
|
||||
// This happens in client side only.
|
||||
ClientHandshakeContext chc =
|
||||
(ClientHandshakeContext)handshakeContext;
|
||||
|
||||
this.modulus = Record.getBytes16(m);
|
||||
this.exponent = Record.getBytes16(m);
|
||||
this.paramsSignature = Record.getBytes16(m);
|
||||
|
||||
X509Credentials x509Credentials = null;
|
||||
for (SSLCredentials cd : chc.handshakeCredentials) {
|
||||
if (cd instanceof X509Credentials) {
|
||||
x509Credentials = (X509Credentials)cd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x509Credentials == null) {
|
||||
throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No RSA credentials negotiated for server key exchange");
|
||||
}
|
||||
|
||||
try {
|
||||
Signature signer = RSASignature.getInstance();
|
||||
signer.initVerify(x509Credentials.popPublicKey);
|
||||
updateSignature(signer,
|
||||
chc.clientHelloRandom.randomBytes,
|
||||
chc.serverHelloRandom.randomBytes);
|
||||
if (!signer.verify(paramsSignature)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid signature of RSA ServerKeyExchange message");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException |
|
||||
InvalidKeyException | SignatureException ex) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failed to sign ephemeral RSA parameters", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
SSLHandshake handshakeType() {
|
||||
return SSLHandshake.SERVER_KEY_EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
int messageLength() {
|
||||
return 6 + modulus.length + exponent.length
|
||||
+ paramsSignature.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
void send(HandshakeOutStream hos) throws IOException {
|
||||
hos.putBytes16(modulus);
|
||||
hos.putBytes16(exponent);
|
||||
hos.putBytes16(paramsSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"RSA ServerKeyExchange\": '{'\n" +
|
||||
" \"parameters\": '{'\n" +
|
||||
" \"rsa_modulus\": '{'\n" +
|
||||
"{0}\n" +
|
||||
" '}',\n" +
|
||||
" \"rsa_exponent\": '{'\n" +
|
||||
"{1}\n" +
|
||||
" '}'\n" +
|
||||
" '}',\n" +
|
||||
" \"digital signature\": '{'\n" +
|
||||
" \"signature\": '{'\n" +
|
||||
"{2}\n" +
|
||||
" '}',\n" +
|
||||
" '}'\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
Object[] messageFields = {
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(modulus), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(exponent), " "),
|
||||
Utilities.indent(
|
||||
hexEncoder.encodeBuffer(paramsSignature), " ")
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash the nonces and the ephemeral RSA public key.
|
||||
*/
|
||||
private void updateSignature(Signature signature,
|
||||
byte[] clntNonce, byte[] svrNonce) throws SignatureException {
|
||||
signature.update(clntNonce);
|
||||
signature.update(svrNonce);
|
||||
|
||||
signature.update((byte)(modulus.length >> 8));
|
||||
signature.update((byte)(modulus.length & 0x0ff));
|
||||
signature.update(modulus);
|
||||
|
||||
signature.update((byte)(exponent.length >> 8));
|
||||
signature.update((byte)(exponent.length & 0x0ff));
|
||||
signature.update(exponent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA "ServerKeyExchange" handshake message producer.
|
||||
*/
|
||||
private static final
|
||||
class RSAServerKeyExchangeProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private RSAServerKeyExchangeProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
EphemeralRSAPossession rsaPossession = null;
|
||||
X509Possession x509Possession = null;
|
||||
for (SSLPossession possession : shc.handshakePossessions) {
|
||||
if (possession instanceof EphemeralRSAPossession) {
|
||||
rsaPossession = (EphemeralRSAPossession)possession;
|
||||
if (x509Possession != null) {
|
||||
break;
|
||||
}
|
||||
} else if (possession instanceof X509Possession) {
|
||||
x509Possession = (X509Possession)possession;
|
||||
if (rsaPossession != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rsaPossession == null) {
|
||||
// The X.509 certificate itself should be used for RSA_EXPORT
|
||||
// key exchange. The ServerKeyExchange handshake message is
|
||||
// not needed.
|
||||
return null;
|
||||
} else if (x509Possession == null) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No RSA certificate negotiated for server key exchange");
|
||||
} else if (!"RSA".equals(
|
||||
x509Possession.popPrivateKey.getAlgorithm())) {
|
||||
// unlikely
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"No X.509 possession can be used for " +
|
||||
"ephemeral RSA ServerKeyExchange");
|
||||
}
|
||||
|
||||
RSAServerKeyExchangeMessage skem =
|
||||
new RSAServerKeyExchangeMessage(
|
||||
shc, x509Possession, rsaPossession);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced RSA ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
skem.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
|
||||
// The handshake message has been delivered.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The RSA "ServerKeyExchange" handshake message consumer.
|
||||
*/
|
||||
private static final
|
||||
class RSAServerKeyExchangeConsumer implements SSLConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private RSAServerKeyExchangeConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
// The consuming happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
RSAServerKeyExchangeMessage skem =
|
||||
new RSAServerKeyExchangeMessage(chc, message);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Consuming RSA ServerKeyExchange handshake message", skem);
|
||||
}
|
||||
|
||||
//
|
||||
// validate
|
||||
//
|
||||
// check constraints of RSA PublicKey
|
||||
RSAPublicKey publicKey;
|
||||
try {
|
||||
KeyFactory kf = JsseJce.getKeyFactory("RSA");
|
||||
RSAPublicKeySpec spec = new RSAPublicKeySpec(
|
||||
new BigInteger(1, skem.modulus),
|
||||
new BigInteger(1, skem.exponent));
|
||||
publicKey = (RSAPublicKey)kf.generatePublic(spec);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"Could not generate RSAPublicKey", gse);
|
||||
}
|
||||
|
||||
if (!chc.algorithmConstraints.permits(
|
||||
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) {
|
||||
throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
|
||||
"RSA ServerKeyExchange does not comply to " +
|
||||
"algorithm constraints");
|
||||
}
|
||||
|
||||
//
|
||||
// update
|
||||
//
|
||||
chc.handshakeCredentials.add(
|
||||
new EphemeralRSACredentials(publicKey));
|
||||
|
||||
//
|
||||
// produce
|
||||
//
|
||||
// Need no new handshake message producers here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
168
jdkSrc/jdk8/sun/security/ssl/RSASignature.java
Normal file
168
jdkSrc/jdk8/sun/security/ssl/RSASignature.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
/**
|
||||
* Signature implementation for the SSL/TLS RSA Signature variant with both
|
||||
* MD5 and SHA-1 MessageDigests. Used for explicit RSA server authentication
|
||||
* (RSA signed server key exchange for RSA_EXPORT and DHE_RSA) and RSA client
|
||||
* authentication (RSA signed certificate verify message).
|
||||
*
|
||||
* It conforms to the standard JCA Signature API. It is registered in the
|
||||
* SunJSSE provider to avoid more complicated getInstance() code and
|
||||
* negative interaction with the JCA mechanisms for hardware providers.
|
||||
*
|
||||
* The class should be instantiated via the getInstance() method in this class,
|
||||
* which returns the implementation from the preferred provider. The internal
|
||||
* implementation allows the hashes to be explicitly set, which is required
|
||||
* for RSA client authentication. It can be obtained via the
|
||||
* getInternalInstance() method.
|
||||
*
|
||||
* This class is not thread safe.
|
||||
*/
|
||||
public final class RSASignature extends SignatureSpi {
|
||||
private final Signature rawRsa;
|
||||
private final MessageDigest mdMD5;
|
||||
private final MessageDigest mdSHA;
|
||||
|
||||
public RSASignature() throws NoSuchAlgorithmException {
|
||||
super();
|
||||
rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA);
|
||||
this.mdMD5 = JsseJce.getMessageDigest("MD5");
|
||||
this.mdSHA = JsseJce.getMessageDigest("SHA");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an implementation for the RSA signature.
|
||||
*
|
||||
* Follows the standard JCA getInstance() model, so it return the
|
||||
* implementation from the provider with the highest precedence,
|
||||
* which may be this class.
|
||||
*/
|
||||
static Signature getInstance() throws NoSuchAlgorithmException {
|
||||
return JsseJce.getSignature(JsseJce.SIGNATURE_SSLRSA);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey)
|
||||
throws InvalidKeyException {
|
||||
if (publicKey == null) {
|
||||
throw new InvalidKeyException("Public key must not be null");
|
||||
}
|
||||
mdMD5.reset();
|
||||
mdSHA.reset();
|
||||
rawRsa.initVerify(publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey)
|
||||
throws InvalidKeyException {
|
||||
engineInitSign(privateKey, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
if (privateKey == null) {
|
||||
throw new InvalidKeyException("Private key must not be null");
|
||||
}
|
||||
mdMD5.reset();
|
||||
mdSHA.reset();
|
||||
rawRsa.initSign(privateKey, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte b) {
|
||||
mdMD5.update(b);
|
||||
mdSHA.update(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len) {
|
||||
mdMD5.update(b, off, len);
|
||||
mdSHA.update(b, off, len);
|
||||
}
|
||||
|
||||
private byte[] getDigest() throws SignatureException {
|
||||
try {
|
||||
byte[] data = new byte[36];
|
||||
mdMD5.digest(data, 0, 16);
|
||||
mdSHA.digest(data, 16, 20);
|
||||
return data;
|
||||
} catch (DigestException e) {
|
||||
// should never occur
|
||||
throw new SignatureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
rawRsa.update(getDigest());
|
||||
return rawRsa.sign();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
|
||||
return engineVerify(sigBytes, 0, sigBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean engineVerify(byte[] sigBytes, int offset, int length)
|
||||
throws SignatureException {
|
||||
rawRsa.update(getDigest());
|
||||
return rawRsa.verify(sigBytes, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void engineSetParameter(String param,
|
||||
Object value) throws InvalidParameterException {
|
||||
throw new InvalidParameterException("Parameters not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
if (params != null) {
|
||||
throw new InvalidAlgorithmParameterException("No parameters accepted");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Object engineGetParameter(
|
||||
String param) throws InvalidParameterException {
|
||||
throw new InvalidParameterException("Parameters not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlgorithmParameters engineGetParameters() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
141
jdkSrc/jdk8/sun/security/ssl/RandomCookie.java
Normal file
141
jdkSrc/jdk8/sun/security/ssl/RandomCookie.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import sun.security.util.ByteArrays;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/*
|
||||
* RandomCookie ... SSL hands standard format random cookies (nonces)
|
||||
* around. These know how to encode/decode themselves on SSL streams,
|
||||
* and can be created and printed.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
final class RandomCookie {
|
||||
final byte[] randomBytes = new byte[32]; // exactly 32 bytes
|
||||
|
||||
private static final byte[] hrrRandomBytes = new byte[] {
|
||||
(byte)0xCF, (byte)0x21, (byte)0xAD, (byte)0x74,
|
||||
(byte)0xE5, (byte)0x9A, (byte)0x61, (byte)0x11,
|
||||
(byte)0xBE, (byte)0x1D, (byte)0x8C, (byte)0x02,
|
||||
(byte)0x1E, (byte)0x65, (byte)0xB8, (byte)0x91,
|
||||
(byte)0xC2, (byte)0xA2, (byte)0x11, (byte)0x16,
|
||||
(byte)0x7A, (byte)0xBB, (byte)0x8C, (byte)0x5E,
|
||||
(byte)0x07, (byte)0x9E, (byte)0x09, (byte)0xE2,
|
||||
(byte)0xC8, (byte)0xA8, (byte)0x33, (byte)0x9C
|
||||
};
|
||||
|
||||
private static final byte[] t12Protection = new byte[] {
|
||||
(byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
|
||||
(byte)0x47, (byte)0x52, (byte)0x44, (byte)0x01
|
||||
};
|
||||
|
||||
private static final byte[] t11Protection = new byte[] {
|
||||
(byte)0x44, (byte)0x4F, (byte)0x57, (byte)0x4E,
|
||||
(byte)0x47, (byte)0x52, (byte)0x44, (byte)0x00
|
||||
};
|
||||
|
||||
static final RandomCookie hrrRandom = new RandomCookie(hrrRandomBytes);
|
||||
|
||||
RandomCookie(SecureRandom generator) {
|
||||
generator.nextBytes(randomBytes);
|
||||
}
|
||||
|
||||
// Used for server random generation with version downgrade protection.
|
||||
RandomCookie(HandshakeContext context) {
|
||||
SecureRandom generator = context.sslContext.getSecureRandom();
|
||||
generator.nextBytes(randomBytes);
|
||||
|
||||
// TLS 1.3 has a downgrade protection mechanism embedded in the
|
||||
// server's random value. TLS 1.3 servers which negotiate TLS 1.2
|
||||
// or below in response to a ClientHello MUST set the last eight
|
||||
// bytes of their Random value specially.
|
||||
byte[] protection = null;
|
||||
if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
if (context.negotiatedProtocol.useTLS12PlusSpec()) {
|
||||
protection = t12Protection;
|
||||
} else {
|
||||
protection = t11Protection;
|
||||
}
|
||||
}
|
||||
} else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
|
||||
if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
|
||||
protection = t11Protection;
|
||||
}
|
||||
}
|
||||
|
||||
if (protection != null) {
|
||||
System.arraycopy(protection, 0, randomBytes,
|
||||
randomBytes.length - protection.length, protection.length);
|
||||
}
|
||||
}
|
||||
|
||||
RandomCookie(ByteBuffer m) throws IOException {
|
||||
m.get(randomBytes);
|
||||
}
|
||||
|
||||
private RandomCookie(byte[] randomBytes) {
|
||||
System.arraycopy(randomBytes, 0, this.randomBytes, 0, 32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "random_bytes = {" + Utilities.toHexString(randomBytes) + "}";
|
||||
}
|
||||
|
||||
boolean isHelloRetryRequest() {
|
||||
return MessageDigest.isEqual(hrrRandomBytes, randomBytes);
|
||||
}
|
||||
|
||||
// Used for client random validation of version downgrade protection.
|
||||
boolean isVersionDowngrade(HandshakeContext context) {
|
||||
if (context.maximumActiveProtocol.useTLS13PlusSpec()) {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return isT12Downgrade() || isT11Downgrade();
|
||||
}
|
||||
} else if (context.maximumActiveProtocol.useTLS12PlusSpec()) {
|
||||
if (!context.negotiatedProtocol.useTLS12PlusSpec()) {
|
||||
return isT11Downgrade();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isT12Downgrade() {
|
||||
return ByteArrays.isEqual(randomBytes, 24, 32, t12Protection, 0, 8);
|
||||
}
|
||||
|
||||
private boolean isT11Downgrade() {
|
||||
return ByteArrays.isEqual(randomBytes, 24, 32, t11Protection, 0, 8);
|
||||
}
|
||||
}
|
||||
196
jdkSrc/jdk8/sun/security/ssl/Record.java
Normal file
196
jdkSrc/jdk8/sun/security/ssl/Record.java
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
/**
|
||||
* SSL/TLS record.
|
||||
*
|
||||
* This is the base interface, which defines common information and interfaces
|
||||
* used by both Input and Output records.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
interface Record {
|
||||
static final int maxMacSize = 48; // the max supported MAC or
|
||||
// AEAD tag size
|
||||
static final int maxDataSize = 16384; // 2^14 bytes of data
|
||||
static final int maxPadding = 256; // block cipher padding
|
||||
static final int maxIVLength = 16; // the max supported IV length
|
||||
|
||||
static final int maxFragmentSize = 18432; // the max fragment size
|
||||
// 2^14 + 2048
|
||||
|
||||
/*
|
||||
* System property to enable/disable CBC protection in SSL3/TLS1.
|
||||
*/
|
||||
static final boolean enableCBCProtection =
|
||||
Utilities.getBooleanProperty("jsse.enableCBCProtection", true);
|
||||
|
||||
/*
|
||||
* The overflow values of integers of 8, 16 and 24 bits.
|
||||
*/
|
||||
static final int OVERFLOW_OF_INT08 = (0x01 << 8);
|
||||
static final int OVERFLOW_OF_INT16 = (0x01 << 16);
|
||||
static final int OVERFLOW_OF_INT24 = (0x01 << 24);
|
||||
|
||||
/*
|
||||
* Read 8, 16, 24, and 32 bit integer data types, encoded
|
||||
* in standard big-endian form.
|
||||
*/
|
||||
static int getInt8(ByteBuffer m) throws IOException {
|
||||
verifyLength(m, 1);
|
||||
return (m.get() & 0xFF);
|
||||
}
|
||||
|
||||
static int getInt16(ByteBuffer m) throws IOException {
|
||||
verifyLength(m, 2);
|
||||
return ((m.get() & 0xFF) << 8) |
|
||||
(m.get() & 0xFF);
|
||||
}
|
||||
|
||||
static int getInt24(ByteBuffer m) throws IOException {
|
||||
verifyLength(m, 3);
|
||||
return ((m.get() & 0xFF) << 16) |
|
||||
((m.get() & 0xFF) << 8) |
|
||||
(m.get() & 0xFF);
|
||||
}
|
||||
|
||||
static int getInt32(ByteBuffer m) throws IOException {
|
||||
verifyLength(m, 4);
|
||||
return ((m.get() & 0xFF) << 24) |
|
||||
((m.get() & 0xFF) << 16) |
|
||||
((m.get() & 0xFF) << 8) |
|
||||
(m.get() & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read byte vectors with 8, 16, and 24 bit length encodings.
|
||||
*/
|
||||
static byte[] getBytes8(ByteBuffer m) throws IOException {
|
||||
int len = Record.getInt8(m);
|
||||
verifyLength(m, len);
|
||||
byte[] b = new byte[len];
|
||||
|
||||
m.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
static byte[] getBytes16(ByteBuffer m) throws IOException {
|
||||
int len = Record.getInt16(m);
|
||||
verifyLength(m, len);
|
||||
byte[] b = new byte[len];
|
||||
|
||||
m.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
static byte[] getBytes24(ByteBuffer m) throws IOException {
|
||||
int len = Record.getInt24(m);
|
||||
verifyLength(m, len);
|
||||
byte[] b = new byte[len];
|
||||
|
||||
m.get(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write 8, 16, 24, and 32 bit integer data types, encoded
|
||||
* in standard big-endian form.
|
||||
*/
|
||||
static void putInt8(ByteBuffer m, int i) throws IOException {
|
||||
verifyLength(m, 1);
|
||||
m.put((byte)(i & 0xFF));
|
||||
}
|
||||
|
||||
static void putInt16(ByteBuffer m, int i) throws IOException {
|
||||
verifyLength(m, 2);
|
||||
m.put((byte)((i >> 8) & 0xFF));
|
||||
m.put((byte)(i & 0xFF));
|
||||
}
|
||||
|
||||
static void putInt24(ByteBuffer m, int i) throws IOException {
|
||||
verifyLength(m, 3);
|
||||
m.put((byte)((i >> 16) & 0xFF));
|
||||
m.put((byte)((i >> 8) & 0xFF));
|
||||
m.put((byte)(i & 0xFF));
|
||||
}
|
||||
|
||||
static void putInt32(ByteBuffer m, int i) throws IOException {
|
||||
m.put((byte)((i >> 24) & 0xFF));
|
||||
m.put((byte)((i >> 16) & 0xFF));
|
||||
m.put((byte)((i >> 8) & 0xFF));
|
||||
m.put((byte)(i & 0xFF));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write byte vectors with 8, 16, and 24 bit length encodings.
|
||||
*/
|
||||
static void putBytes8(ByteBuffer m, byte[] s) throws IOException {
|
||||
if (s == null || s.length == 0) {
|
||||
verifyLength(m, 1);
|
||||
putInt8(m, 0);
|
||||
} else {
|
||||
verifyLength(m, 1 + s.length);
|
||||
putInt8(m, s.length);
|
||||
m.put(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void putBytes16(ByteBuffer m, byte[] s) throws IOException {
|
||||
if (s == null || s.length == 0) {
|
||||
verifyLength(m, 2);
|
||||
putInt16(m, 0);
|
||||
} else {
|
||||
verifyLength(m, 2 + s.length);
|
||||
putInt16(m, s.length);
|
||||
m.put(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void putBytes24(ByteBuffer m, byte[] s) throws IOException {
|
||||
if (s == null || s.length == 0) {
|
||||
verifyLength(m, 3);
|
||||
putInt24(m, 0);
|
||||
} else {
|
||||
verifyLength(m, 3 + s.length);
|
||||
putInt24(m, s.length);
|
||||
m.put(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the buffer has sufficient remaining.
|
||||
static void verifyLength(
|
||||
ByteBuffer m, int len) throws SSLException {
|
||||
if (len > m.remaining()) {
|
||||
throw new SSLException("Insufficient space in the buffer, " +
|
||||
"may be cause by an unexpected end of handshake data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
556
jdkSrc/jdk8/sun/security/ssl/RenegoInfoExtension.java
Normal file
556
jdkSrc/jdk8/sun/security/ssl/RenegoInfoExtension.java
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import sun.security.ssl.ClientHello.ClientHelloMessage;
|
||||
import static sun.security.ssl.SSLExtension.CH_RENEGOTIATION_INFO;
|
||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||
import static sun.security.ssl.SSLExtension.SH_RENEGOTIATION_INFO;
|
||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.util.ByteArrays;
|
||||
|
||||
/**
|
||||
* Pack of the "renegotiation_info" extensions [RFC 5746].
|
||||
*/
|
||||
final class RenegoInfoExtension {
|
||||
static final HandshakeProducer chNetworkProducer =
|
||||
new CHRenegotiationInfoProducer();
|
||||
static final ExtensionConsumer chOnLoadConsumer =
|
||||
new CHRenegotiationInfoConsumer();
|
||||
static final HandshakeAbsence chOnLoadAbsence =
|
||||
new CHRenegotiationInfoAbsence();
|
||||
|
||||
static final HandshakeProducer shNetworkProducer =
|
||||
new SHRenegotiationInfoProducer();
|
||||
static final ExtensionConsumer shOnLoadConsumer =
|
||||
new SHRenegotiationInfoConsumer();
|
||||
static final HandshakeAbsence shOnLoadAbsence =
|
||||
new SHRenegotiationInfoAbsence();
|
||||
|
||||
static final SSLStringizer rniStringizer =
|
||||
new RenegotiationInfoStringizer();
|
||||
|
||||
/**
|
||||
* The "renegotiation_info" extension.
|
||||
*/
|
||||
static final class RenegotiationInfoSpec implements SSLExtensionSpec {
|
||||
// A nominal object that does not holding any real renegotiation info.
|
||||
static final RenegotiationInfoSpec NOMINAL =
|
||||
new RenegotiationInfoSpec(new byte[0]);
|
||||
|
||||
private final byte[] renegotiatedConnection;
|
||||
|
||||
private RenegotiationInfoSpec(byte[] renegotiatedConnection) {
|
||||
this.renegotiatedConnection = Arrays.copyOf(
|
||||
renegotiatedConnection, renegotiatedConnection.length);
|
||||
}
|
||||
|
||||
private RenegotiationInfoSpec(ByteBuffer m) throws IOException {
|
||||
// Parse the extension.
|
||||
if (!m.hasRemaining() || m.remaining() < 1) {
|
||||
throw new SSLProtocolException(
|
||||
"Invalid renegotiation_info extension data: " +
|
||||
"insufficient data");
|
||||
}
|
||||
this.renegotiatedConnection = Record.getBytes8(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"renegotiated connection\": '['{0}']'", Locale.ENGLISH);
|
||||
if (renegotiatedConnection.length == 0) {
|
||||
Object[] messageFields = {
|
||||
"<no renegotiated connection>"
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
} else {
|
||||
Object[] messageFields = {
|
||||
Utilities.toHexString(renegotiatedConnection)
|
||||
};
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
class RenegotiationInfoStringizer implements SSLStringizer {
|
||||
@Override
|
||||
public String toString(ByteBuffer buffer) {
|
||||
try {
|
||||
return (new RenegotiationInfoSpec(buffer)).toString();
|
||||
} catch (IOException ioe) {
|
||||
// For debug logging only, so please swallow exceptions.
|
||||
return ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "renegotiation_info" extension in
|
||||
* the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHRenegotiationInfoProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHRenegotiationInfoProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!chc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable renegotiation_info extension");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!chc.conContext.isNegotiated) {
|
||||
if (chc.activeCipherSuites.contains(
|
||||
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
|
||||
// Using the the TLS_EMPTY_RENEGOTIATION_INFO_SCSV instead.
|
||||
return null;
|
||||
}
|
||||
|
||||
// initial handshaking.
|
||||
//
|
||||
// If this is the initial handshake for a connection, then the
|
||||
// "renegotiated_connection" field is of zero length in both
|
||||
// the ClientHello and the ServerHello. [RFC 5746]
|
||||
byte[] extData = new byte[] { 0x00 };
|
||||
chc.handshakeExtensions.put(
|
||||
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
} else if (chc.conContext.secureRenegotiation) {
|
||||
// secure renegotiation
|
||||
//
|
||||
// For ClientHello handshake message in renegotiation, this
|
||||
// field contains the "client_verify_data".
|
||||
byte[] extData =
|
||||
new byte[chc.conContext.clientVerifyData.length + 1];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putBytes8(m, chc.conContext.clientVerifyData);
|
||||
|
||||
// The conContext.clientVerifyData will be used for further
|
||||
// processing, so it does not matter to save whatever in the
|
||||
// RenegotiationInfoSpec object.
|
||||
chc.handshakeExtensions.put(
|
||||
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
} else { // not secure renegotiation
|
||||
if (HandshakeContext.allowUnsafeRenegotiation) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("Using insecure renegotiation");
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
// terminate the session.
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"insecure renegotiation is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "renegotiation_info" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHRenegotiationInfoConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private CHRenegotiationInfoConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
|
||||
// The consuming happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// Is it a supported and enabled extension?
|
||||
if (!shc.sslConfig.isAvailable(CH_RENEGOTIATION_INFO)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Ignore unavailable extension: " +
|
||||
CH_RENEGOTIATION_INFO.name);
|
||||
}
|
||||
return; // ignore the extension
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
RenegotiationInfoSpec spec;
|
||||
try {
|
||||
spec = new RenegotiationInfoSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
if (!shc.conContext.isNegotiated) {
|
||||
// initial handshaking.
|
||||
if (spec.renegotiatedConnection.length != 0) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid renegotiation_info extension data: not empty");
|
||||
}
|
||||
shc.conContext.secureRenegotiation = true;
|
||||
} else {
|
||||
if (!shc.conContext.secureRenegotiation) {
|
||||
// Unexpected RI extension for insecure renegotiation,
|
||||
// abort the handshake with a fatal handshake_failure alert.
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"The renegotiation_info is present in a insecure " +
|
||||
"renegotiation");
|
||||
} else {
|
||||
// verify the client_verify_data value
|
||||
if (!MessageDigest.isEqual(shc.conContext.clientVerifyData,
|
||||
spec.renegotiatedConnection)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Invalid renegotiation_info extension data: " +
|
||||
"incorrect verify data in ClientHello");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
//
|
||||
// The conContext.clientVerifyData will be used for further
|
||||
// processing, so it does not matter to save whatever in the
|
||||
// RenegotiationInfoSpec object.
|
||||
shc.handshakeExtensions.put(
|
||||
CH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if a "renegotiation_info" extension is
|
||||
* not present in the ClientHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class CHRenegotiationInfoAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||
|
||||
if (!shc.conContext.isNegotiated) {
|
||||
// initial handshaking.
|
||||
for (int id : clientHello.cipherSuiteIds) {
|
||||
if (id ==
|
||||
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.id) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest(
|
||||
"Safe renegotiation, using the SCSV signgling");
|
||||
}
|
||||
shc.conContext.secureRenegotiation = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!HandshakeContext.allowLegacyHelloMessages) {
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Failed to negotiate the use of secure renegotiation");
|
||||
} // otherwise, allow legacy hello message
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("Warning: No renegotiation " +
|
||||
"indication in ClientHello, allow legacy ClientHello");
|
||||
}
|
||||
|
||||
shc.conContext.secureRenegotiation = false;
|
||||
} else if (shc.conContext.secureRenegotiation) {
|
||||
// Require secure renegotiation, terminate the connection.
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Inconsistent secure renegotiation indication");
|
||||
} else { // renegotiation, not secure
|
||||
if (HandshakeContext.allowUnsafeRenegotiation) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("Using insecure renegotiation");
|
||||
}
|
||||
} else {
|
||||
// Unsafe renegotiation should have been aborted in
|
||||
// ealier processes.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Terminate insecure renegotiation");
|
||||
}
|
||||
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unsafe renegotiation is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data producer of a "renegotiation_info" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHRenegotiationInfoProducer implements HandshakeProducer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHRenegotiationInfoProducer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
|
||||
// In response to "renegotiation_info" extension request only.
|
||||
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
|
||||
shc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
|
||||
if (requestedSpec == null && !shc.conContext.secureRenegotiation) {
|
||||
// Ignore, no renegotiation_info extension or SCSV signgling
|
||||
// requested.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest(
|
||||
"Ignore unavailable renegotiation_info extension");
|
||||
}
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
if (!shc.conContext.secureRenegotiation) {
|
||||
// Ignore, no secure renegotiation is negotiated.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest(
|
||||
"No secure renegotiation has been negotiated");
|
||||
}
|
||||
return null; // ignore the extension
|
||||
}
|
||||
|
||||
if (!shc.conContext.isNegotiated) {
|
||||
// initial handshaking.
|
||||
//
|
||||
// If this is the initial handshake for a connection, then the
|
||||
// "renegotiated_connection" field is of zero length in both
|
||||
// the ClientHello and the ServerHello. [RFC 5746]
|
||||
byte[] extData = new byte[] { 0x00 };
|
||||
|
||||
// The conContext.client/serverVerifyData will be used for
|
||||
// further processing, so it does not matter to save whatever
|
||||
// in the RenegotiationInfoSpec object.
|
||||
shc.handshakeExtensions.put(
|
||||
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
} else {
|
||||
// secure renegotiation
|
||||
//
|
||||
// For secure renegotiation, the server MUST include a
|
||||
// "renegotiation_info" extension containing the saved
|
||||
// client_verify_data and server_verify_data in the ServerHello.
|
||||
int infoLen = shc.conContext.clientVerifyData.length +
|
||||
shc.conContext.serverVerifyData.length;
|
||||
byte[] extData = new byte[infoLen + 1];
|
||||
ByteBuffer m = ByteBuffer.wrap(extData);
|
||||
Record.putInt8(m, infoLen);
|
||||
m.put(shc.conContext.clientVerifyData);
|
||||
m.put(shc.conContext.serverVerifyData);
|
||||
|
||||
// The conContext.client/serverVerifyData will be used for
|
||||
// further processing, so it does not matter to save whatever
|
||||
// in the RenegotiationInfoSpec object.
|
||||
shc.handshakeExtensions.put(
|
||||
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
return extData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network data consumer of a "renegotiation_info" extension in
|
||||
* the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHRenegotiationInfoConsumer implements ExtensionConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
private SHRenegotiationInfoConsumer() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to the client renegotiation_info extension request
|
||||
// or SCSV signling, which is mandatory for ClientHello message.
|
||||
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
|
||||
chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
|
||||
if (requestedSpec == null &&
|
||||
!chc.activeCipherSuites.contains(
|
||||
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Missing renegotiation_info and SCSV detected in " +
|
||||
"ClientHello");
|
||||
}
|
||||
|
||||
// Parse the extension.
|
||||
RenegotiationInfoSpec spec;
|
||||
try {
|
||||
spec = new RenegotiationInfoSpec(buffer);
|
||||
} catch (IOException ioe) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
|
||||
}
|
||||
|
||||
|
||||
if (!chc.conContext.isNegotiated) { // initial handshake
|
||||
// If the extension is present, set the secure_renegotiation
|
||||
// flag to TRUE. The client MUST then verify that the
|
||||
// length of the "renegotiated_connection" field is zero,
|
||||
// and if it is not, MUST abort the handshake (by sending
|
||||
// a fatal handshake_failure alert). [RFC 5746]
|
||||
if (spec.renegotiatedConnection.length != 0) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid renegotiation_info in ServerHello: " +
|
||||
"not empty renegotiated_connection");
|
||||
}
|
||||
|
||||
chc.conContext.secureRenegotiation = true;
|
||||
} else { // renegotiation
|
||||
// The client MUST then verify that the first half of the
|
||||
// "renegotiated_connection" field is equal to the saved
|
||||
// client_verify_data value, and the second half is equal to the
|
||||
// saved server_verify_data value. If they are not, the client
|
||||
// MUST abort the handshake. [RFC 5746]
|
||||
int infoLen = chc.conContext.clientVerifyData.length +
|
||||
chc.conContext.serverVerifyData.length;
|
||||
if (spec.renegotiatedConnection.length != infoLen) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid renegotiation_info in ServerHello: " +
|
||||
"invalid renegotiated_connection length (" +
|
||||
spec.renegotiatedConnection.length + ")");
|
||||
}
|
||||
|
||||
byte[] cvd = chc.conContext.clientVerifyData;
|
||||
if (!ByteArrays.isEqual(spec.renegotiatedConnection,
|
||||
0, cvd.length, cvd, 0, cvd.length)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid renegotiation_info in ServerHello: " +
|
||||
"unmatched client_verify_data value");
|
||||
}
|
||||
byte[] svd = chc.conContext.serverVerifyData;
|
||||
if (!ByteArrays.isEqual(spec.renegotiatedConnection,
|
||||
cvd.length, infoLen, svd, 0, svd.length)) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Invalid renegotiation_info in ServerHello: " +
|
||||
"unmatched server_verify_data value");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the context.
|
||||
chc.handshakeExtensions.put(
|
||||
SH_RENEGOTIATION_INFO, RenegotiationInfoSpec.NOMINAL);
|
||||
|
||||
// No impact on session resumption.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The absence processing if a "renegotiation_info" extension is
|
||||
* not present in the ServerHello handshake message.
|
||||
*/
|
||||
private static final
|
||||
class SHRenegotiationInfoAbsence implements HandshakeAbsence {
|
||||
@Override
|
||||
public void absent(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
// The producing happens in client side only.
|
||||
ClientHandshakeContext chc = (ClientHandshakeContext)context;
|
||||
|
||||
// In response to the client renegotiation_info extension request
|
||||
// or SCSV signling, which is mandatory for ClientHello message.
|
||||
RenegotiationInfoSpec requestedSpec = (RenegotiationInfoSpec)
|
||||
chc.handshakeExtensions.get(CH_RENEGOTIATION_INFO);
|
||||
if (requestedSpec == null &&
|
||||
!chc.activeCipherSuites.contains(
|
||||
CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Missing renegotiation_info and SCSV detected in " +
|
||||
"ClientHello");
|
||||
}
|
||||
|
||||
if (!chc.conContext.isNegotiated) {
|
||||
// initial handshaking.
|
||||
if (!HandshakeContext.allowLegacyHelloMessages) {
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Failed to negotiate the use of secure renegotiation");
|
||||
} // otherwise, allow legacy hello message
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("Warning: No renegotiation " +
|
||||
"indication in ServerHello, allow legacy ServerHello");
|
||||
}
|
||||
|
||||
chc.conContext.secureRenegotiation = false;
|
||||
} else if (chc.conContext.secureRenegotiation) {
|
||||
// Require secure renegotiation, terminate the connection.
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Inconsistent secure renegotiation indication");
|
||||
} else { // renegotiation, not secure
|
||||
if (HandshakeContext.allowUnsafeRenegotiation) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning("Using insecure renegotiation");
|
||||
}
|
||||
} else {
|
||||
// Unsafe renegotiation should have been aborted in
|
||||
// ealier processes.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Terminate insecure renegotiation");
|
||||
}
|
||||
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Unsafe renegotiation is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
289
jdkSrc/jdk8/sun/security/ssl/SSLAlgorithmConstraints.java
Normal file
289
jdkSrc/jdk8/sun/security/ssl/SSLAlgorithmConstraints.java
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.AlgorithmConstraints;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.CryptoPrimitive;
|
||||
import java.security.Key;
|
||||
import java.util.Set;
|
||||
import javax.net.ssl.*;
|
||||
import sun.security.util.DisabledAlgorithmConstraints;
|
||||
import static sun.security.util.DisabledAlgorithmConstraints.*;
|
||||
|
||||
/**
|
||||
* Algorithm constraints for disabled algorithms property
|
||||
*
|
||||
* See the "jdk.certpath.disabledAlgorithms" specification in java.security
|
||||
* for the syntax of the disabled algorithm string.
|
||||
*/
|
||||
final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
||||
|
||||
private static final AlgorithmConstraints tlsDisabledAlgConstraints =
|
||||
new DisabledAlgorithmConstraints(PROPERTY_TLS_DISABLED_ALGS,
|
||||
new SSLAlgorithmDecomposer());
|
||||
|
||||
private static final AlgorithmConstraints x509DisabledAlgConstraints =
|
||||
new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS,
|
||||
new SSLAlgorithmDecomposer(true));
|
||||
|
||||
private final AlgorithmConstraints userSpecifiedConstraints;
|
||||
private final AlgorithmConstraints peerSpecifiedConstraints;
|
||||
|
||||
private final boolean enabledX509DisabledAlgConstraints;
|
||||
|
||||
// the default algorithm constraints
|
||||
static final AlgorithmConstraints DEFAULT =
|
||||
new SSLAlgorithmConstraints(null);
|
||||
|
||||
// the default SSL only algorithm constraints
|
||||
static final AlgorithmConstraints DEFAULT_SSL_ONLY =
|
||||
new SSLAlgorithmConstraints((SSLSocket)null, false);
|
||||
|
||||
SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints) {
|
||||
this.userSpecifiedConstraints = userSpecifiedConstraints;
|
||||
this.peerSpecifiedConstraints = null;
|
||||
this.enabledX509DisabledAlgConstraints = true;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLSocket socket,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
|
||||
this.peerSpecifiedConstraints = null;
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLEngine engine,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
|
||||
this.peerSpecifiedConstraints = null;
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
|
||||
this.peerSpecifiedConstraints =
|
||||
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
|
||||
this.peerSpecifiedConstraints =
|
||||
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
private static AlgorithmConstraints getUserSpecifiedConstraints(
|
||||
SSLEngine engine) {
|
||||
if (engine != null) {
|
||||
// Note that the KeyManager or TrustManager implementation may be
|
||||
// not implemented in the same provider as SSLSocket/SSLEngine.
|
||||
// Please check the instance before casting to use SSLEngineImpl.
|
||||
if (engine instanceof SSLEngineImpl) {
|
||||
HandshakeContext hc =
|
||||
((SSLEngineImpl)engine).conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
return engine.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AlgorithmConstraints getUserSpecifiedConstraints(
|
||||
SSLSocket socket) {
|
||||
if (socket != null) {
|
||||
// Note that the KeyManager or TrustManager implementation may be
|
||||
// not implemented in the same provider as SSLSocket/SSLEngine.
|
||||
// Please check the instance before casting to use SSLSocketImpl.
|
||||
if (socket instanceof SSLSocketImpl) {
|
||||
HandshakeContext hc =
|
||||
((SSLSocketImpl)socket).conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
return socket.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||
String algorithm, AlgorithmParameters parameters) {
|
||||
|
||||
boolean permitted = true;
|
||||
|
||||
if (peerSpecifiedConstraints != null) {
|
||||
permitted = peerSpecifiedConstraints.permits(
|
||||
primitives, algorithm, parameters);
|
||||
}
|
||||
|
||||
if (permitted && userSpecifiedConstraints != null) {
|
||||
permitted = userSpecifiedConstraints.permits(
|
||||
primitives, algorithm, parameters);
|
||||
}
|
||||
|
||||
if (permitted) {
|
||||
permitted = tlsDisabledAlgConstraints.permits(
|
||||
primitives, algorithm, parameters);
|
||||
}
|
||||
|
||||
if (permitted && enabledX509DisabledAlgConstraints) {
|
||||
permitted = x509DisabledAlgConstraints.permits(
|
||||
primitives, algorithm, parameters);
|
||||
}
|
||||
|
||||
return permitted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
|
||||
|
||||
boolean permitted = true;
|
||||
|
||||
if (peerSpecifiedConstraints != null) {
|
||||
permitted = peerSpecifiedConstraints.permits(primitives, key);
|
||||
}
|
||||
|
||||
if (permitted && userSpecifiedConstraints != null) {
|
||||
permitted = userSpecifiedConstraints.permits(primitives, key);
|
||||
}
|
||||
|
||||
if (permitted) {
|
||||
permitted = tlsDisabledAlgConstraints.permits(primitives, key);
|
||||
}
|
||||
|
||||
if (permitted && enabledX509DisabledAlgConstraints) {
|
||||
permitted = x509DisabledAlgConstraints.permits(primitives, key);
|
||||
}
|
||||
|
||||
return permitted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||
String algorithm, Key key, AlgorithmParameters parameters) {
|
||||
|
||||
boolean permitted = true;
|
||||
|
||||
if (peerSpecifiedConstraints != null) {
|
||||
permitted = peerSpecifiedConstraints.permits(
|
||||
primitives, algorithm, key, parameters);
|
||||
}
|
||||
|
||||
if (permitted && userSpecifiedConstraints != null) {
|
||||
permitted = userSpecifiedConstraints.permits(
|
||||
primitives, algorithm, key, parameters);
|
||||
}
|
||||
|
||||
if (permitted) {
|
||||
permitted = tlsDisabledAlgConstraints.permits(
|
||||
primitives, algorithm, key, parameters);
|
||||
}
|
||||
|
||||
if (permitted && enabledX509DisabledAlgConstraints) {
|
||||
permitted = x509DisabledAlgConstraints.permits(
|
||||
primitives, algorithm, key, parameters);
|
||||
}
|
||||
|
||||
return permitted;
|
||||
}
|
||||
|
||||
|
||||
private static class SupportedSignatureAlgorithmConstraints
|
||||
implements AlgorithmConstraints {
|
||||
// supported signature algorithms
|
||||
private String[] supportedAlgorithms;
|
||||
|
||||
SupportedSignatureAlgorithmConstraints(String[] supportedAlgorithms) {
|
||||
if (supportedAlgorithms != null) {
|
||||
this.supportedAlgorithms = supportedAlgorithms.clone();
|
||||
} else {
|
||||
this.supportedAlgorithms = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||
String algorithm, AlgorithmParameters parameters) {
|
||||
|
||||
if (algorithm == null || algorithm.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"No algorithm name specified");
|
||||
}
|
||||
|
||||
if (primitives == null || primitives.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"No cryptographic primitive specified");
|
||||
}
|
||||
|
||||
if (supportedAlgorithms == null ||
|
||||
supportedAlgorithms.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// trim the MGF part: <digest>with<encryption>and<mgf>
|
||||
int position = algorithm.indexOf("and");
|
||||
if (position > 0) {
|
||||
algorithm = algorithm.substring(0, position);
|
||||
}
|
||||
|
||||
for (String supportedAlgorithm : supportedAlgorithms) {
|
||||
if (algorithm.equalsIgnoreCase(supportedAlgorithm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean permits(Set<CryptoPrimitive> primitives, Key key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean permits(Set<CryptoPrimitive> primitives,
|
||||
String algorithm, Key key, AlgorithmParameters parameters) {
|
||||
|
||||
if (algorithm == null || algorithm.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"No algorithm name specified");
|
||||
}
|
||||
|
||||
return permits(primitives, algorithm, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
280
jdkSrc/jdk8/sun/security/ssl/SSLAlgorithmDecomposer.java
Normal file
280
jdkSrc/jdk8/sun/security/ssl/SSLAlgorithmDecomposer.java
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import sun.security.ssl.CipherSuite.KeyExchange;
|
||||
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
||||
import sun.security.ssl.CipherSuite.MacAlg;
|
||||
import static sun.security.ssl.SSLCipher.*;
|
||||
import sun.security.util.AlgorithmDecomposer;
|
||||
|
||||
/**
|
||||
* The class decomposes standard SSL/TLS cipher suites into sub-elements.
|
||||
*/
|
||||
class SSLAlgorithmDecomposer extends AlgorithmDecomposer {
|
||||
|
||||
// indicates that only certification path algorithms need to be used
|
||||
private final boolean onlyX509;
|
||||
|
||||
SSLAlgorithmDecomposer(boolean onlyX509) {
|
||||
this.onlyX509 = onlyX509;
|
||||
}
|
||||
|
||||
SSLAlgorithmDecomposer() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
private Set<String> decomposes(CipherSuite.KeyExchange keyExchange) {
|
||||
Set<String> components = new HashSet<>();
|
||||
switch (keyExchange) {
|
||||
case K_NULL:
|
||||
if (!onlyX509) {
|
||||
components.add("K_NULL");
|
||||
}
|
||||
break;
|
||||
case K_RSA:
|
||||
components.add("RSA");
|
||||
break;
|
||||
case K_RSA_EXPORT:
|
||||
components.add("RSA");
|
||||
components.add("RSA_EXPORT");
|
||||
break;
|
||||
case K_DH_RSA:
|
||||
components.add("RSA");
|
||||
components.add("DH");
|
||||
components.add("DiffieHellman");
|
||||
components.add("DH_RSA");
|
||||
break;
|
||||
case K_DH_DSS:
|
||||
components.add("DSA");
|
||||
components.add("DSS");
|
||||
components.add("DH");
|
||||
components.add("DiffieHellman");
|
||||
components.add("DH_DSS");
|
||||
break;
|
||||
case K_DHE_DSS:
|
||||
components.add("DSA");
|
||||
components.add("DSS");
|
||||
components.add("DH");
|
||||
components.add("DHE");
|
||||
components.add("DiffieHellman");
|
||||
components.add("DHE_DSS");
|
||||
break;
|
||||
case K_DHE_RSA:
|
||||
components.add("RSA");
|
||||
components.add("DH");
|
||||
components.add("DHE");
|
||||
components.add("DiffieHellman");
|
||||
components.add("DHE_RSA");
|
||||
break;
|
||||
case K_DH_ANON:
|
||||
if (!onlyX509) {
|
||||
components.add("ANON");
|
||||
components.add("DH");
|
||||
components.add("DiffieHellman");
|
||||
components.add("DH_ANON");
|
||||
}
|
||||
break;
|
||||
case K_ECDH_ECDSA:
|
||||
components.add("ECDH");
|
||||
components.add("ECDSA");
|
||||
components.add("ECDH_ECDSA");
|
||||
break;
|
||||
case K_ECDH_RSA:
|
||||
components.add("ECDH");
|
||||
components.add("RSA");
|
||||
components.add("ECDH_RSA");
|
||||
break;
|
||||
case K_ECDHE_ECDSA:
|
||||
components.add("ECDHE");
|
||||
components.add("ECDSA");
|
||||
components.add("ECDHE_ECDSA");
|
||||
break;
|
||||
case K_ECDHE_RSA:
|
||||
components.add("ECDHE");
|
||||
components.add("RSA");
|
||||
components.add("ECDHE_RSA");
|
||||
break;
|
||||
case K_ECDH_ANON:
|
||||
if (!onlyX509) {
|
||||
components.add("ECDH");
|
||||
components.add("ANON");
|
||||
components.add("ECDH_ANON");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// otherwise ignore
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
private Set<String> decomposes(SSLCipher bulkCipher) {
|
||||
Set<String> components = new HashSet<>();
|
||||
|
||||
if (bulkCipher.transformation != null) {
|
||||
components.addAll(super.decompose(bulkCipher.transformation));
|
||||
}
|
||||
|
||||
switch (bulkCipher) {
|
||||
case B_NULL:
|
||||
components.add("C_NULL");
|
||||
break;
|
||||
case B_RC2_40:
|
||||
components.add("RC2_CBC_40");
|
||||
break;
|
||||
case B_RC4_40:
|
||||
components.add("RC4_40");
|
||||
break;
|
||||
case B_RC4_128:
|
||||
components.add("RC4_128");
|
||||
break;
|
||||
case B_DES_40:
|
||||
components.add("DES40_CBC");
|
||||
components.add("DES_CBC_40");
|
||||
break;
|
||||
case B_DES:
|
||||
components.add("DES_CBC");
|
||||
break;
|
||||
case B_3DES:
|
||||
components.add("3DES_EDE_CBC");
|
||||
break;
|
||||
case B_AES_128:
|
||||
components.add("AES_128_CBC");
|
||||
break;
|
||||
case B_AES_256:
|
||||
components.add("AES_256_CBC");
|
||||
break;
|
||||
case B_AES_128_GCM:
|
||||
components.add("AES_128_GCM");
|
||||
break;
|
||||
case B_AES_256_GCM:
|
||||
components.add("AES_256_GCM");
|
||||
break;
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
private Set<String> decomposes(CipherSuite.MacAlg macAlg,
|
||||
SSLCipher cipher) {
|
||||
Set<String> components = new HashSet<>();
|
||||
|
||||
if (macAlg == CipherSuite.MacAlg.M_NULL
|
||||
&& cipher.cipherType != CipherType.AEAD_CIPHER) {
|
||||
components.add("M_NULL");
|
||||
} else if (macAlg == CipherSuite.MacAlg.M_MD5) {
|
||||
components.add("MD5");
|
||||
components.add("HmacMD5");
|
||||
} else if (macAlg == CipherSuite.MacAlg.M_SHA) {
|
||||
components.add("SHA1");
|
||||
components.add("SHA-1");
|
||||
components.add("HmacSHA1");
|
||||
} else if (macAlg == CipherSuite.MacAlg.M_SHA256) {
|
||||
components.add("SHA256");
|
||||
components.add("SHA-256");
|
||||
components.add("HmacSHA256");
|
||||
} else if (macAlg == CipherSuite.MacAlg.M_SHA384) {
|
||||
components.add("SHA384");
|
||||
components.add("SHA-384");
|
||||
components.add("HmacSHA384");
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
private Set<String> decomposes(CipherSuite.HashAlg hashAlg) {
|
||||
Set<String> components = new HashSet<>();
|
||||
|
||||
if (hashAlg == CipherSuite.HashAlg.H_SHA256) {
|
||||
components.add("SHA256");
|
||||
components.add("SHA-256");
|
||||
components.add("HmacSHA256");
|
||||
} else if (hashAlg == CipherSuite.HashAlg.H_SHA384) {
|
||||
components.add("SHA384");
|
||||
components.add("SHA-384");
|
||||
components.add("HmacSHA384");
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
private Set<String> decompose(KeyExchange keyExchange,
|
||||
SSLCipher cipher,
|
||||
MacAlg macAlg,
|
||||
HashAlg hashAlg) {
|
||||
Set<String> components = new HashSet<>();
|
||||
|
||||
if (keyExchange != null) {
|
||||
components.addAll(decomposes(keyExchange));
|
||||
}
|
||||
|
||||
if (onlyX509) {
|
||||
// Certification path algorithm constraints do not apply
|
||||
// to cipher and macAlg.
|
||||
return components;
|
||||
}
|
||||
|
||||
if (cipher != null) {
|
||||
components.addAll(decomposes(cipher));
|
||||
}
|
||||
|
||||
if (macAlg != null) {
|
||||
components.addAll(decomposes(macAlg, cipher));
|
||||
}
|
||||
|
||||
if (hashAlg != null) {
|
||||
components.addAll(decomposes(hashAlg));
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> decompose(String algorithm) {
|
||||
if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) {
|
||||
CipherSuite cipherSuite = null;
|
||||
try {
|
||||
cipherSuite = CipherSuite.nameOf(algorithm);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// ignore: unknown or unsupported ciphersuite
|
||||
}
|
||||
|
||||
if (cipherSuite != null &&
|
||||
cipherSuite != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
|
||||
return decompose(cipherSuite.keyExchange,
|
||||
cipherSuite.bulkCipher,
|
||||
cipherSuite.macAlg,
|
||||
cipherSuite.hashAlg);
|
||||
}
|
||||
}
|
||||
|
||||
return super.decompose(algorithm);
|
||||
}
|
||||
}
|
||||
31
jdkSrc/jdk8/sun/security/ssl/SSLAuthentication.java
Normal file
31
jdkSrc/jdk8/sun/security/ssl/SSLAuthentication.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
interface SSLAuthentication
|
||||
extends SSLPossessionGenerator, SSLHandshakeBinding {
|
||||
// blank
|
||||
}
|
||||
81
jdkSrc/jdk8/sun/security/ssl/SSLBasicKeyDerivation.java
Normal file
81
jdkSrc/jdk8/sun/security/ssl/SSLBasicKeyDerivation.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
final class SSLBasicKeyDerivation implements SSLKeyDerivation {
|
||||
private final String hashAlg;
|
||||
private final SecretKey secret;
|
||||
private final byte[] hkdfInfo;
|
||||
|
||||
SSLBasicKeyDerivation(SecretKey secret, String hashAlg,
|
||||
byte[] label, byte[] context, int length) {
|
||||
this.hashAlg = hashAlg.replace("-", "");
|
||||
this.secret = secret;
|
||||
this.hkdfInfo = createHkdfInfo(label, context, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec keySpec) throws IOException {
|
||||
try {
|
||||
HKDF hkdf = new HKDF(hashAlg);
|
||||
return hkdf.expand(secret, hkdfInfo,
|
||||
((SecretSizeSpec)keySpec).length, algorithm);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] createHkdfInfo(
|
||||
byte[] label, byte[] context, int length) {
|
||||
byte[] info = new byte[4 + label.length + context.length];
|
||||
ByteBuffer m = ByteBuffer.wrap(info);
|
||||
try {
|
||||
Record.putInt16(m, length);
|
||||
Record.putBytes8(m, label);
|
||||
Record.putBytes8(m, context);
|
||||
} catch (IOException ioe) {
|
||||
// unlikely
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static class SecretSizeSpec implements AlgorithmParameterSpec {
|
||||
final int length;
|
||||
|
||||
SecretSizeSpec(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
2363
jdkSrc/jdk8/sun/security/ssl/SSLCipher.java
Normal file
2363
jdkSrc/jdk8/sun/security/ssl/SSLCipher.java
Normal file
File diff suppressed because it is too large
Load Diff
491
jdkSrc/jdk8/sun/security/ssl/SSLConfiguration.java
Normal file
491
jdkSrc/jdk8/sun/security/ssl/SSLConfiguration.java
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.AlgorithmConstraints;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.SNIMatcher;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import sun.security.action.GetIntegerAction;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.ssl.SSLExtension.ClientExtensions;
|
||||
import sun.security.ssl.SSLExtension.ServerExtensions;
|
||||
|
||||
/**
|
||||
* SSL/TLS configuration.
|
||||
*/
|
||||
final class SSLConfiguration implements Cloneable {
|
||||
// configurations with SSLParameters
|
||||
AlgorithmConstraints userSpecifiedAlgorithmConstraints;
|
||||
List<ProtocolVersion> enabledProtocols;
|
||||
List<CipherSuite> enabledCipherSuites;
|
||||
ClientAuthType clientAuthType;
|
||||
String identificationProtocol;
|
||||
List<SNIServerName> serverNames;
|
||||
Collection<SNIMatcher> sniMatchers;
|
||||
String[] applicationProtocols;
|
||||
boolean preferLocalCipherSuites;
|
||||
int maximumPacketSize = 0;
|
||||
|
||||
// The configured signature schemes for "signature_algorithms" and
|
||||
// "signature_algorithms_cert" extensions
|
||||
List<SignatureScheme> signatureSchemes;
|
||||
|
||||
// the maximum protocol version of enabled protocols
|
||||
ProtocolVersion maximumProtocolVersion;
|
||||
|
||||
// Configurations per SSLSocket or SSLEngine instance.
|
||||
boolean isClientMode;
|
||||
boolean enableSessionCreation;
|
||||
|
||||
// the application layer protocol negotiation configuration
|
||||
BiFunction<SSLSocket, List<String>, String> socketAPSelector;
|
||||
BiFunction<SSLEngine, List<String>, String> engineAPSelector;
|
||||
|
||||
HashMap<HandshakeCompletedListener, AccessControlContext>
|
||||
handshakeListeners;
|
||||
|
||||
boolean noSniExtension;
|
||||
boolean noSniMatcher;
|
||||
|
||||
// To switch off the extended_master_secret extension.
|
||||
static final boolean useExtendedMasterSecret;
|
||||
|
||||
// Allow session resumption without Extended Master Secret extension.
|
||||
static final boolean allowLegacyResumption =
|
||||
Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
|
||||
|
||||
// Allow full handshake without Extended Master Secret extension.
|
||||
static final boolean allowLegacyMasterSecret =
|
||||
Utilities.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
|
||||
|
||||
// Use TLS1.3 middlebox compatibility mode.
|
||||
static final boolean useCompatibilityMode = Utilities.getBooleanProperty(
|
||||
"jdk.tls.client.useCompatibilityMode", true);
|
||||
|
||||
// Respond a close_notify alert if receiving close_notify alert.
|
||||
static final boolean acknowledgeCloseNotify = Utilities.getBooleanProperty(
|
||||
"jdk.tls.acknowledgeCloseNotify", false);
|
||||
|
||||
// Set the max size limit for Handshake Message to 2^15
|
||||
static final int maxHandshakeMessageSize = AccessController.doPrivileged(
|
||||
new GetIntegerAction("jdk.tls.maxHandshakeMessageSize", 32768)).intValue();
|
||||
|
||||
// Set the max certificate chain length to 10
|
||||
static final int maxCertificateChainLength = AccessController.doPrivileged(
|
||||
new GetIntegerAction("jdk.tls.maxCertificateChainLength", 10)).intValue();
|
||||
|
||||
// Is the extended_master_secret extension supported?
|
||||
static {
|
||||
boolean supportExtendedMasterSecret = Utilities.getBooleanProperty(
|
||||
"jdk.tls.useExtendedMasterSecret", true);
|
||||
if (supportExtendedMasterSecret) {
|
||||
try {
|
||||
JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret");
|
||||
} catch (NoSuchAlgorithmException nae) {
|
||||
supportExtendedMasterSecret = false;
|
||||
}
|
||||
}
|
||||
useExtendedMasterSecret = supportExtendedMasterSecret;
|
||||
}
|
||||
|
||||
SSLConfiguration(SSLContextImpl sslContext, boolean isClientMode) {
|
||||
|
||||
// Configurations with SSLParameters, default values.
|
||||
this.userSpecifiedAlgorithmConstraints =
|
||||
SSLAlgorithmConstraints.DEFAULT;
|
||||
this.enabledProtocols =
|
||||
sslContext.getDefaultProtocolVersions(!isClientMode);
|
||||
this.enabledCipherSuites =
|
||||
sslContext.getDefaultCipherSuites(!isClientMode);
|
||||
this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
|
||||
|
||||
this.identificationProtocol = null;
|
||||
this.serverNames = Collections.<SNIServerName>emptyList();
|
||||
this.sniMatchers = Collections.<SNIMatcher>emptyList();
|
||||
this.preferLocalCipherSuites = false;
|
||||
|
||||
this.applicationProtocols = new String[0];
|
||||
|
||||
this.signatureSchemes = isClientMode ?
|
||||
CustomizedClientSignatureSchemes.signatureSchemes :
|
||||
CustomizedServerSignatureSchemes.signatureSchemes;
|
||||
this.maximumProtocolVersion = ProtocolVersion.NONE;
|
||||
for (ProtocolVersion pv : enabledProtocols) {
|
||||
if (pv.compareTo(maximumProtocolVersion) > 0) {
|
||||
this.maximumProtocolVersion = pv;
|
||||
}
|
||||
}
|
||||
|
||||
// Configurations per SSLSocket or SSLEngine instance.
|
||||
this.isClientMode = isClientMode;
|
||||
this.enableSessionCreation = true;
|
||||
this.socketAPSelector = null;
|
||||
this.engineAPSelector = null;
|
||||
|
||||
this.handshakeListeners = null;
|
||||
this.noSniExtension = false;
|
||||
this.noSniMatcher = false;
|
||||
}
|
||||
|
||||
SSLParameters getSSLParameters() {
|
||||
SSLParameters params = new SSLParameters();
|
||||
|
||||
params.setAlgorithmConstraints(this.userSpecifiedAlgorithmConstraints);
|
||||
params.setProtocols(ProtocolVersion.toStringArray(enabledProtocols));
|
||||
params.setCipherSuites(CipherSuite.namesOf(enabledCipherSuites));
|
||||
switch (this.clientAuthType) {
|
||||
case CLIENT_AUTH_REQUIRED:
|
||||
params.setNeedClientAuth(true);
|
||||
break;
|
||||
case CLIENT_AUTH_REQUESTED:
|
||||
params.setWantClientAuth(true);
|
||||
break;
|
||||
default:
|
||||
params.setWantClientAuth(false);
|
||||
}
|
||||
params.setEndpointIdentificationAlgorithm(this.identificationProtocol);
|
||||
|
||||
if (serverNames.isEmpty() && !noSniExtension) {
|
||||
// 'null' indicates none has been set
|
||||
params.setServerNames(null);
|
||||
} else {
|
||||
params.setServerNames(this.serverNames);
|
||||
}
|
||||
|
||||
if (sniMatchers.isEmpty() && !noSniMatcher) {
|
||||
// 'null' indicates none has been set
|
||||
params.setSNIMatchers(null);
|
||||
} else {
|
||||
params.setSNIMatchers(this.sniMatchers);
|
||||
}
|
||||
|
||||
params.setApplicationProtocols(this.applicationProtocols);
|
||||
params.setUseCipherSuitesOrder(this.preferLocalCipherSuites);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void setSSLParameters(SSLParameters params) {
|
||||
AlgorithmConstraints ac = params.getAlgorithmConstraints();
|
||||
if (ac != null) {
|
||||
this.userSpecifiedAlgorithmConstraints = ac;
|
||||
} // otherwise, use the default value
|
||||
|
||||
String[] sa = params.getCipherSuites();
|
||||
if (sa != null) {
|
||||
this.enabledCipherSuites = CipherSuite.validValuesOf(sa);
|
||||
} // otherwise, use the default values
|
||||
|
||||
sa = params.getProtocols();
|
||||
if (sa != null) {
|
||||
this.enabledProtocols = ProtocolVersion.namesOf(sa);
|
||||
|
||||
this.maximumProtocolVersion = ProtocolVersion.NONE;
|
||||
for (ProtocolVersion pv : enabledProtocols) {
|
||||
if (pv.compareTo(maximumProtocolVersion) > 0) {
|
||||
this.maximumProtocolVersion = pv;
|
||||
}
|
||||
}
|
||||
} // otherwise, use the default values
|
||||
|
||||
if (params.getNeedClientAuth()) {
|
||||
this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUIRED;
|
||||
} else if (params.getWantClientAuth()) {
|
||||
this.clientAuthType = ClientAuthType.CLIENT_AUTH_REQUESTED;
|
||||
} else {
|
||||
this.clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
|
||||
}
|
||||
|
||||
String s = params.getEndpointIdentificationAlgorithm();
|
||||
if (s != null) {
|
||||
this.identificationProtocol = s;
|
||||
} // otherwise, use the default value
|
||||
|
||||
List<SNIServerName> sniNames = params.getServerNames();
|
||||
if (sniNames != null) {
|
||||
this.noSniExtension = sniNames.isEmpty();
|
||||
this.serverNames = sniNames;
|
||||
} // null if none has been set
|
||||
|
||||
Collection<SNIMatcher> matchers = params.getSNIMatchers();
|
||||
if (matchers != null) {
|
||||
this.noSniMatcher = matchers.isEmpty();
|
||||
this.sniMatchers = matchers;
|
||||
} // null if none has been set
|
||||
|
||||
sa = params.getApplicationProtocols();
|
||||
if (sa != null) {
|
||||
this.applicationProtocols = sa;
|
||||
} // otherwise, use the default values
|
||||
|
||||
this.preferLocalCipherSuites = params.getUseCipherSuitesOrder();
|
||||
}
|
||||
|
||||
// SSLSocket only
|
||||
void addHandshakeCompletedListener(
|
||||
HandshakeCompletedListener listener) {
|
||||
|
||||
if (handshakeListeners == null) {
|
||||
handshakeListeners = new HashMap<>(4);
|
||||
}
|
||||
|
||||
handshakeListeners.put(listener, AccessController.getContext());
|
||||
}
|
||||
|
||||
// SSLSocket only
|
||||
void removeHandshakeCompletedListener(
|
||||
HandshakeCompletedListener listener) {
|
||||
|
||||
if (handshakeListeners == null) {
|
||||
throw new IllegalArgumentException("no listeners");
|
||||
}
|
||||
|
||||
if (handshakeListeners.remove(listener) == null) {
|
||||
throw new IllegalArgumentException("listener not registered");
|
||||
}
|
||||
|
||||
if (handshakeListeners.isEmpty()) {
|
||||
handshakeListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the extension is available.
|
||||
*/
|
||||
boolean isAvailable(SSLExtension extension) {
|
||||
for (ProtocolVersion protocolVersion : enabledProtocols) {
|
||||
if (extension.isAvailable(protocolVersion)) {
|
||||
if (isClientMode ?
|
||||
ClientExtensions.defaults.contains(extension) :
|
||||
ServerExtensions.defaults.contains(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the extension is available for the specific protocol.
|
||||
*/
|
||||
boolean isAvailable(SSLExtension extension,
|
||||
ProtocolVersion protocolVersion) {
|
||||
return extension.isAvailable(protocolVersion) &&
|
||||
(isClientMode ? ClientExtensions.defaults.contains(extension) :
|
||||
ServerExtensions.defaults.contains(extension));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled extensions for the specific handshake message.
|
||||
*
|
||||
* Used to consume handshake extensions.
|
||||
*/
|
||||
SSLExtension[] getEnabledExtensions(SSLHandshake handshakeType) {
|
||||
List<SSLExtension> extensions = new ArrayList<>();
|
||||
for (SSLExtension extension : SSLExtension.values()) {
|
||||
if (extension.handshakeType == handshakeType) {
|
||||
if (isAvailable(extension)) {
|
||||
extensions.add(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.toArray(new SSLExtension[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled extensions for the specific handshake message, excluding
|
||||
* the specified extensions.
|
||||
*
|
||||
* Used to consume handshake extensions.
|
||||
*/
|
||||
SSLExtension[] getExclusiveExtensions(SSLHandshake handshakeType,
|
||||
List<SSLExtension> excluded) {
|
||||
List<SSLExtension> extensions = new ArrayList<>();
|
||||
for (SSLExtension extension : SSLExtension.values()) {
|
||||
if (extension.handshakeType == handshakeType) {
|
||||
if (isAvailable(extension) && !excluded.contains(extension)) {
|
||||
extensions.add(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.toArray(new SSLExtension[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled extensions for the specific handshake message
|
||||
* and the specific protocol version.
|
||||
*
|
||||
* Used to produce handshake extensions after handshake protocol
|
||||
* version negotiation.
|
||||
*/
|
||||
SSLExtension[] getEnabledExtensions(
|
||||
SSLHandshake handshakeType, ProtocolVersion protocolVersion) {
|
||||
return getEnabledExtensions(
|
||||
handshakeType, Arrays.asList(protocolVersion));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled extensions for the specific handshake message
|
||||
* and the specific protocol versions.
|
||||
*
|
||||
* Used to produce ClientHello extensions before handshake protocol
|
||||
* version negotiation.
|
||||
*/
|
||||
SSLExtension[] getEnabledExtensions(
|
||||
SSLHandshake handshakeType, List<ProtocolVersion> activeProtocols) {
|
||||
List<SSLExtension> extensions = new ArrayList<>();
|
||||
for (SSLExtension extension : SSLExtension.values()) {
|
||||
if (extension.handshakeType == handshakeType) {
|
||||
if (!isAvailable(extension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (ProtocolVersion protocolVersion : activeProtocols) {
|
||||
if (extension.isAvailable(protocolVersion)) {
|
||||
extensions.add(extension);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.toArray(new SSLExtension[0]);
|
||||
}
|
||||
|
||||
void toggleClientMode() {
|
||||
this.isClientMode ^= true;
|
||||
|
||||
// reset the signature schemes
|
||||
this.signatureSchemes = isClientMode ?
|
||||
CustomizedClientSignatureSchemes.signatureSchemes :
|
||||
CustomizedServerSignatureSchemes.signatureSchemes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "CloneDeclaresCloneNotSupported"})
|
||||
public Object clone() {
|
||||
// Note that only references to the configurations are copied.
|
||||
try {
|
||||
SSLConfiguration config = (SSLConfiguration)super.clone();
|
||||
if (handshakeListeners != null) {
|
||||
config.handshakeListeners =
|
||||
(HashMap<HandshakeCompletedListener, AccessControlContext>)
|
||||
handshakeListeners.clone();
|
||||
}
|
||||
|
||||
return config;
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
// unlikely
|
||||
}
|
||||
|
||||
return null; // unlikely
|
||||
}
|
||||
|
||||
|
||||
// lazy initialization holder class idiom for static default parameters
|
||||
//
|
||||
// See Effective Java Second Edition: Item 71.
|
||||
private static final class CustomizedClientSignatureSchemes {
|
||||
private static List<SignatureScheme> signatureSchemes =
|
||||
getCustomizedSignatureScheme("jdk.tls.client.SignatureSchemes");
|
||||
}
|
||||
|
||||
// lazy initialization holder class idiom for static default parameters
|
||||
//
|
||||
// See Effective Java Second Edition: Item 71.
|
||||
private static final class CustomizedServerSignatureSchemes {
|
||||
private static List<SignatureScheme> signatureSchemes =
|
||||
getCustomizedSignatureScheme("jdk.tls.server.SignatureSchemes");
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the customized signature schemes specified by the given
|
||||
* system property.
|
||||
*/
|
||||
private static List<SignatureScheme> getCustomizedSignatureScheme(
|
||||
String propertyName) {
|
||||
|
||||
String property = GetPropertyAction.privilegedGetProperty(propertyName);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
|
||||
SSLLogger.fine(
|
||||
"System property " + propertyName + " is set to '" +
|
||||
property + "'");
|
||||
}
|
||||
if (property != null && !property.isEmpty()) {
|
||||
// remove double quote marks from beginning/end of the property
|
||||
if (property.length() > 1 && property.charAt(0) == '"' &&
|
||||
property.charAt(property.length() - 1) == '"') {
|
||||
property = property.substring(1, property.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (property != null && !property.isEmpty()) {
|
||||
String[] signatureSchemeNames = property.split(",");
|
||||
List<SignatureScheme> signatureSchemes =
|
||||
new ArrayList<>(signatureSchemeNames.length);
|
||||
for (int i = 0; i < signatureSchemeNames.length; i++) {
|
||||
signatureSchemeNames[i] = signatureSchemeNames[i].trim();
|
||||
if (signatureSchemeNames[i].isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SignatureScheme scheme =
|
||||
SignatureScheme.nameOf(signatureSchemeNames[i]);
|
||||
if (scheme != null && scheme.isAvailable) {
|
||||
signatureSchemes.add(scheme);
|
||||
} else {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,sslctx")) {
|
||||
SSLLogger.fine(
|
||||
"The current installed providers do not " +
|
||||
"support signature scheme: " +
|
||||
signatureSchemeNames[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return signatureSchemes;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
35
jdkSrc/jdk8/sun/security/ssl/SSLConsumer.java
Normal file
35
jdkSrc/jdk8/sun/security/ssl/SSLConsumer.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
interface SSLConsumer {
|
||||
void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException;
|
||||
}
|
||||
|
||||
1620
jdkSrc/jdk8/sun/security/ssl/SSLContextImpl.java
Normal file
1620
jdkSrc/jdk8/sun/security/ssl/SSLContextImpl.java
Normal file
File diff suppressed because it is too large
Load Diff
29
jdkSrc/jdk8/sun/security/ssl/SSLCredentials.java
Normal file
29
jdkSrc/jdk8/sun/security/ssl/SSLCredentials.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
interface SSLCredentials {
|
||||
}
|
||||
988
jdkSrc/jdk8/sun/security/ssl/SSLEngineImpl.java
Normal file
988
jdkSrc/jdk8/sun/security/ssl/SSLEngineImpl.java
Normal file
@@ -0,0 +1,988 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ReadOnlyBufferException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLEngineResult.Status;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLKeyException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* Implementation of an non-blocking SSLEngine.
|
||||
*
|
||||
* @author Brad Wetmore
|
||||
*/
|
||||
final class SSLEngineImpl extends SSLEngine implements SSLTransport {
|
||||
private final SSLContextImpl sslContext;
|
||||
final TransportContext conContext;
|
||||
|
||||
/**
|
||||
* Constructor for an SSLEngine from SSLContext, without
|
||||
* host/port hints.
|
||||
*
|
||||
* This Engine will not be able to cache sessions, but must renegotiate
|
||||
* everything by hand.
|
||||
*/
|
||||
SSLEngineImpl(SSLContextImpl sslContext) {
|
||||
this(sslContext, null, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for an SSLEngine from SSLContext.
|
||||
*/
|
||||
SSLEngineImpl(SSLContextImpl sslContext,
|
||||
String host, int port) {
|
||||
super(host, port);
|
||||
this.sslContext = sslContext;
|
||||
HandshakeHash handshakeHash = new HandshakeHash();
|
||||
this.conContext = new TransportContext(sslContext, this,
|
||||
new SSLEngineInputRecord(handshakeHash),
|
||||
new SSLEngineOutputRecord(handshakeHash));
|
||||
|
||||
// Server name indication is a connection scope extension.
|
||||
if (host != null) {
|
||||
this.conContext.sslConfig.serverNames =
|
||||
Utilities.addToSNIServerNameList(
|
||||
conContext.sslConfig.serverNames, host);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void beginHandshake() throws SSLException {
|
||||
if (conContext.isUnsureMode) {
|
||||
throw new IllegalStateException(
|
||||
"Client/Server mode has not yet been set.");
|
||||
}
|
||||
|
||||
try {
|
||||
conContext.kickstart();
|
||||
} catch (IOException ioe) {
|
||||
throw conContext.fatal(Alert.HANDSHAKE_FAILURE,
|
||||
"Couldn't kickstart handshaking", ioe);
|
||||
} catch (Exception ex) { // including RuntimeException
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Fail to begin handshake", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLEngineResult wrap(ByteBuffer[] appData,
|
||||
int offset, int length, ByteBuffer netData) throws SSLException {
|
||||
return wrap(appData, offset, length, new ByteBuffer[]{ netData }, 0, 1);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public synchronized SSLEngineResult wrap(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
|
||||
|
||||
if (conContext.isUnsureMode) {
|
||||
throw new IllegalStateException(
|
||||
"Client/Server mode has not yet been set.");
|
||||
}
|
||||
|
||||
// See if the handshaker needs to report back some SSLException.
|
||||
checkTaskThrown();
|
||||
|
||||
// check parameters
|
||||
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
|
||||
|
||||
try {
|
||||
return writeRecord(
|
||||
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
|
||||
} catch (SSLProtocolException spe) {
|
||||
// may be an unexpected handshake message
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, spe);
|
||||
} catch (IOException ioe) {
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"problem wrapping app data", ioe);
|
||||
} catch (Exception ex) { // including RuntimeException
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Fail to wrap application data", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLEngineResult writeRecord(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
// May need to deliver cached records.
|
||||
if (isOutboundDone()) {
|
||||
return new SSLEngineResult(
|
||||
Status.CLOSED, getHandshakeStatus(), 0, 0);
|
||||
}
|
||||
|
||||
HandshakeContext hc = conContext.handshakeContext;
|
||||
HandshakeStatus hsStatus = null;
|
||||
if (!conContext.isNegotiated && !conContext.isBroken &&
|
||||
!conContext.isInboundClosed() &&
|
||||
!conContext.isOutboundClosed()) {
|
||||
conContext.kickstart();
|
||||
|
||||
hsStatus = getHandshakeStatus();
|
||||
if (hsStatus == HandshakeStatus.NEED_UNWRAP) {
|
||||
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (hsStatus == null) {
|
||||
hsStatus = getHandshakeStatus();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a task outstanding, this *MUST* be done before
|
||||
* doing any more wrapping, because we could be in the middle
|
||||
* of receiving a handshake message, for example, a finished
|
||||
* message which would change the ciphers.
|
||||
*/
|
||||
if (hsStatus == HandshakeStatus.NEED_TASK) {
|
||||
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
|
||||
}
|
||||
|
||||
int dstsRemains = 0;
|
||||
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
|
||||
dstsRemains += dsts[i].remaining();
|
||||
}
|
||||
|
||||
// Check destination buffer size.
|
||||
//
|
||||
// We can be smarter about using smaller buffer sizes later. For
|
||||
// now, force it to be large enough to handle any valid record.
|
||||
if (dstsRemains < conContext.conSession.getPacketBufferSize()) {
|
||||
return new SSLEngineResult(
|
||||
Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0);
|
||||
}
|
||||
|
||||
int srcsRemains = 0;
|
||||
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
|
||||
srcsRemains += srcs[i].remaining();
|
||||
}
|
||||
|
||||
Ciphertext ciphertext = null;
|
||||
try {
|
||||
// Acquire the buffered to-be-delivered records or retransmissions.
|
||||
//
|
||||
// May have buffered records.
|
||||
if (!conContext.outputRecord.isEmpty()) {
|
||||
ciphertext = encode(null, 0, 0,
|
||||
dsts, dstsOffset, dstsLength);
|
||||
}
|
||||
|
||||
if (ciphertext == null && srcsRemains != 0) {
|
||||
ciphertext = encode(srcs, srcsOffset, srcsLength,
|
||||
dsts, dstsOffset, dstsLength);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (ioe instanceof SSLException) {
|
||||
throw ioe;
|
||||
} else {
|
||||
throw new SSLException("Write problems", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for status.
|
||||
*/
|
||||
Status status = (isOutboundDone() ? Status.CLOSED : Status.OK);
|
||||
if (ciphertext != null && ciphertext.handshakeStatus != null) {
|
||||
hsStatus = ciphertext.handshakeStatus;
|
||||
} else {
|
||||
hsStatus = getHandshakeStatus();
|
||||
if (ciphertext == null && !conContext.isNegotiated &&
|
||||
conContext.isInboundClosed() &&
|
||||
hsStatus == HandshakeStatus.NEED_WRAP) {
|
||||
// Even the outboud is open, no futher data could be wrapped as:
|
||||
// 1. the outbound is empty
|
||||
// 2. no negotiated connection
|
||||
// 3. the inbound has closed, cannot complete the handshake
|
||||
//
|
||||
// Mark the engine as closed if the handshake status is
|
||||
// NEED_WRAP. Otherwise, it could lead to dead loops in
|
||||
// applications.
|
||||
status = Status.CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
int deltaSrcs = srcsRemains;
|
||||
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
|
||||
deltaSrcs -= srcs[i].remaining();
|
||||
}
|
||||
|
||||
int deltaDsts = dstsRemains;
|
||||
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
|
||||
deltaDsts -= dsts[i].remaining();
|
||||
}
|
||||
|
||||
return new SSLEngineResult(status, hsStatus, deltaSrcs, deltaDsts);
|
||||
}
|
||||
|
||||
private Ciphertext encode(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
Ciphertext ciphertext = null;
|
||||
try {
|
||||
ciphertext = conContext.outputRecord.encode(
|
||||
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
|
||||
} catch (SSLHandshakeException she) {
|
||||
// may be record sequence number overflow
|
||||
throw conContext.fatal(Alert.HANDSHAKE_FAILURE, she);
|
||||
} catch (IOException e) {
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
|
||||
}
|
||||
|
||||
if (ciphertext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is the handshake completed?
|
||||
HandshakeStatus hsStatus =
|
||||
tryToFinishHandshake(ciphertext.contentType);
|
||||
|
||||
if (hsStatus == null) {
|
||||
hsStatus = conContext.getHandshakeStatus();
|
||||
}
|
||||
|
||||
// Is the sequence number is nearly overflow?
|
||||
if (conContext.outputRecord.seqNumIsHuge() ||
|
||||
conContext.outputRecord.writeCipher.atKeyLimit()) {
|
||||
hsStatus = tryKeyUpdate(hsStatus);
|
||||
}
|
||||
|
||||
// update context status
|
||||
ciphertext.handshakeStatus = hsStatus;
|
||||
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
private HandshakeStatus tryToFinishHandshake(byte contentType) {
|
||||
HandshakeStatus hsStatus = null;
|
||||
if ((contentType == ContentType.HANDSHAKE.id) &&
|
||||
conContext.outputRecord.isEmpty()) {
|
||||
if (conContext.handshakeContext == null) {
|
||||
hsStatus = HandshakeStatus.FINISHED;
|
||||
} else if (conContext.isPostHandshakeContext()) {
|
||||
// unlikely, but just in case.
|
||||
hsStatus = conContext.finishPostHandshake();
|
||||
} else if (conContext.handshakeContext.handshakeFinished) {
|
||||
hsStatus = conContext.finishHandshake();
|
||||
}
|
||||
} // Otherwise, the followed call to getHSStatus() will help.
|
||||
|
||||
return hsStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try key update for sequence number wrap or key usage limit.
|
||||
*
|
||||
* Note that in order to maintain the handshake status properly, we check
|
||||
* the sequence number and key usage limit after the last record
|
||||
* reading/writing process.
|
||||
*
|
||||
* As we request renegotiation or close the connection for wrapped sequence
|
||||
* number when there is enough sequence number space left to handle a few
|
||||
* more records, so the sequence number of the last record cannot be
|
||||
* wrapped.
|
||||
*/
|
||||
private HandshakeStatus tryKeyUpdate(
|
||||
HandshakeStatus currentHandshakeStatus) throws IOException {
|
||||
// Don't bother to kickstart if handshaking is in progress, or if
|
||||
// the write side of the connection is not open. We allow a half-
|
||||
// duplex write-only connection for key updates.
|
||||
if ((conContext.handshakeContext == null) &&
|
||||
!conContext.isOutboundClosed() &&
|
||||
!conContext.isBroken) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.finest("trigger key update");
|
||||
}
|
||||
beginHandshake();
|
||||
return conContext.getHandshakeStatus();
|
||||
}
|
||||
|
||||
return currentHandshakeStatus;
|
||||
}
|
||||
|
||||
private static void checkParams(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
|
||||
|
||||
if ((srcs == null) || (dsts == null)) {
|
||||
throw new IllegalArgumentException(
|
||||
"source or destination buffer is null");
|
||||
}
|
||||
|
||||
if ((dstsOffset < 0) || (dstsLength < 0) ||
|
||||
(dstsOffset > dsts.length - dstsLength)) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"index out of bound of the destination buffers");
|
||||
}
|
||||
|
||||
if ((srcsOffset < 0) || (srcsLength < 0) ||
|
||||
(srcsOffset > srcs.length - srcsLength)) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"index out of bound of the source buffers");
|
||||
}
|
||||
|
||||
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
|
||||
if (dsts[i] == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"destination buffer[" + i + "] == null");
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the destination bufffers are writable.
|
||||
*/
|
||||
if (dsts[i].isReadOnly()) {
|
||||
throw new ReadOnlyBufferException();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
|
||||
if (srcs[i] == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"source buffer[" + i + "] == null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLEngineResult unwrap(ByteBuffer src,
|
||||
ByteBuffer[] dsts, int offset, int length) throws SSLException {
|
||||
return unwrap(
|
||||
new ByteBuffer[]{src}, 0, 1, dsts, offset, length);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public synchronized SSLEngineResult unwrap(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws SSLException {
|
||||
|
||||
if (conContext.isUnsureMode) {
|
||||
throw new IllegalStateException(
|
||||
"Client/Server mode has not yet been set.");
|
||||
}
|
||||
|
||||
// See if the handshaker needs to report back some SSLException.
|
||||
checkTaskThrown();
|
||||
|
||||
// check parameters
|
||||
checkParams(srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
|
||||
|
||||
try {
|
||||
return readRecord(
|
||||
srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
|
||||
} catch (SSLProtocolException spe) {
|
||||
// may be an unexpected handshake message
|
||||
throw conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
spe.getMessage(), spe);
|
||||
} catch (IOException ioe) {
|
||||
/*
|
||||
* Don't reset position so it looks like we didn't
|
||||
* consume anything. We did consume something, and it
|
||||
* got us into this situation, so report that much back.
|
||||
* Our days of consuming are now over anyway.
|
||||
*/
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"problem unwrapping net record", ioe);
|
||||
} catch (Exception ex) { // including RuntimeException
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Fail to unwrap network record", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLEngineResult readRecord(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
/*
|
||||
* Check if we are closing/closed.
|
||||
*/
|
||||
if (isInboundDone()) {
|
||||
return new SSLEngineResult(
|
||||
Status.CLOSED, getHandshakeStatus(), 0, 0);
|
||||
}
|
||||
|
||||
HandshakeStatus hsStatus = null;
|
||||
if (!conContext.isNegotiated && !conContext.isBroken &&
|
||||
!conContext.isInboundClosed() &&
|
||||
!conContext.isOutboundClosed()) {
|
||||
conContext.kickstart();
|
||||
|
||||
/*
|
||||
* If there's still outbound data to flush, we
|
||||
* can return without trying to unwrap anything.
|
||||
*/
|
||||
hsStatus = getHandshakeStatus();
|
||||
if (hsStatus == HandshakeStatus.NEED_WRAP) {
|
||||
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (hsStatus == null) {
|
||||
hsStatus = getHandshakeStatus();
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a task outstanding, this *MUST* be done before
|
||||
* doing any more unwrapping, because we could be in the middle
|
||||
* of receiving a handshake message, for example, a finished
|
||||
* message which would change the ciphers.
|
||||
*/
|
||||
if (hsStatus == HandshakeStatus.NEED_TASK) {
|
||||
return new SSLEngineResult(Status.OK, hsStatus, 0, 0);
|
||||
}
|
||||
|
||||
int srcsRemains = 0;
|
||||
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
|
||||
srcsRemains += srcs[i].remaining();
|
||||
}
|
||||
|
||||
if (srcsRemains == 0) {
|
||||
return new SSLEngineResult(
|
||||
Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the packet to make sure enough is here.
|
||||
* This will also indirectly check for 0 len packets.
|
||||
*/
|
||||
int packetLen = conContext.inputRecord.bytesInCompletePacket(
|
||||
srcs, srcsOffset, srcsLength);
|
||||
|
||||
// Is this packet bigger than SSL/TLS normally allows?
|
||||
if (packetLen > conContext.conSession.getPacketBufferSize()) {
|
||||
int largestRecordSize = SSLRecord.maxLargeRecordSize;
|
||||
if (packetLen <= largestRecordSize) {
|
||||
// Expand the expected maximum packet/application buffer
|
||||
// sizes.
|
||||
//
|
||||
// Only apply to SSL/TLS protocols.
|
||||
|
||||
// Old behavior: shall we honor the System Property
|
||||
// "jsse.SSLEngine.acceptLargeFragments" if it is "false"?
|
||||
conContext.conSession.expandBufferSizes();
|
||||
}
|
||||
|
||||
// check the packet again
|
||||
largestRecordSize = conContext.conSession.getPacketBufferSize();
|
||||
if (packetLen > largestRecordSize) {
|
||||
throw new SSLProtocolException(
|
||||
"Input record too big: max = " +
|
||||
largestRecordSize + " len = " + packetLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for OVERFLOW.
|
||||
*
|
||||
* Delay enforcing the application buffer free space requirement
|
||||
* until after the initial handshaking.
|
||||
*/
|
||||
int dstsRemains = 0;
|
||||
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
|
||||
dstsRemains += dsts[i].remaining();
|
||||
}
|
||||
|
||||
if (conContext.isNegotiated) {
|
||||
int FragLen =
|
||||
conContext.inputRecord.estimateFragmentSize(packetLen);
|
||||
if (FragLen > dstsRemains) {
|
||||
return new SSLEngineResult(
|
||||
Status.BUFFER_OVERFLOW, hsStatus, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// check for UNDERFLOW.
|
||||
if ((packetLen == -1) || (srcsRemains < packetLen)) {
|
||||
return new SSLEngineResult(Status.BUFFER_UNDERFLOW, hsStatus, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* We're now ready to actually do the read.
|
||||
*/
|
||||
Plaintext plainText = null;
|
||||
try {
|
||||
plainText = decode(srcs, srcsOffset, srcsLength,
|
||||
dsts, dstsOffset, dstsLength);
|
||||
} catch (IOException ioe) {
|
||||
if (ioe instanceof SSLException) {
|
||||
throw ioe;
|
||||
} else {
|
||||
throw new SSLException("readRecord", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the various condition that we could be reporting.
|
||||
*
|
||||
* It's *possible* something might have happened between the
|
||||
* above and now, but it was better to minimally lock "this"
|
||||
* during the read process. We'll return the current
|
||||
* status, which is more representative of the current state.
|
||||
*
|
||||
* status above should cover: FINISHED, NEED_TASK
|
||||
*/
|
||||
Status status = (isInboundDone() ? Status.CLOSED : Status.OK);
|
||||
if (plainText.handshakeStatus != null) {
|
||||
hsStatus = plainText.handshakeStatus;
|
||||
} else {
|
||||
hsStatus = getHandshakeStatus();
|
||||
}
|
||||
|
||||
int deltaNet = srcsRemains;
|
||||
for (int i = srcsOffset; i < srcsOffset + srcsLength; i++) {
|
||||
deltaNet -= srcs[i].remaining();
|
||||
}
|
||||
|
||||
int deltaApp = dstsRemains;
|
||||
for (int i = dstsOffset; i < dstsOffset + dstsLength; i++) {
|
||||
deltaApp -= dsts[i].remaining();
|
||||
}
|
||||
|
||||
return new SSLEngineResult(status, hsStatus, deltaNet, deltaApp);
|
||||
}
|
||||
|
||||
private Plaintext decode(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
Plaintext pt = SSLTransport.decode(conContext,
|
||||
srcs, srcsOffset, srcsLength,
|
||||
dsts, dstsOffset, dstsLength);
|
||||
|
||||
// Is the handshake completed?
|
||||
if (pt != Plaintext.PLAINTEXT_NULL) {
|
||||
HandshakeStatus hsStatus = tryToFinishHandshake(pt.contentType);
|
||||
if (hsStatus == null) {
|
||||
pt.handshakeStatus = conContext.getHandshakeStatus();
|
||||
} else {
|
||||
pt.handshakeStatus = hsStatus;
|
||||
}
|
||||
|
||||
// Is the sequence number is nearly overflow?
|
||||
if (conContext.inputRecord.seqNumIsHuge() ||
|
||||
conContext.inputRecord.readCipher.atKeyLimit()) {
|
||||
pt.handshakeStatus =
|
||||
tryKeyUpdate(pt.handshakeStatus);
|
||||
}
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Runnable getDelegatedTask() {
|
||||
if (conContext.handshakeContext != null && // PRE or POST handshake
|
||||
!conContext.handshakeContext.taskDelegated &&
|
||||
!conContext.handshakeContext.delegatedActions.isEmpty()) {
|
||||
conContext.handshakeContext.taskDelegated = true;
|
||||
return new DelegatedTask(this);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void closeInbound() throws SSLException {
|
||||
if (isInboundDone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.finest("Closing inbound of SSLEngine");
|
||||
}
|
||||
|
||||
// Is it ready to close inbound?
|
||||
//
|
||||
// No need to throw exception if the initial handshake is not started.
|
||||
if (!conContext.isInputCloseNotified &&
|
||||
(conContext.isNegotiated || conContext.handshakeContext != null)) {
|
||||
|
||||
throw conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"closing inbound before receiving peer's close_notify");
|
||||
}
|
||||
|
||||
conContext.closeInbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isInboundDone() {
|
||||
return conContext.isInboundClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void closeOutbound() {
|
||||
if (conContext.isOutboundClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.finest("Closing outbound of SSLEngine");
|
||||
}
|
||||
|
||||
conContext.closeOutbound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isOutboundDone() {
|
||||
return conContext.isOutboundDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String[] getEnabledCipherSuites() {
|
||||
return CipherSuite.namesOf(conContext.sslConfig.enabledCipherSuites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnabledCipherSuites(String[] suites) {
|
||||
conContext.sslConfig.enabledCipherSuites =
|
||||
CipherSuite.validValuesOf(suites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return ProtocolVersion.toStringArray(
|
||||
sslContext.getSupportedProtocolVersions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String[] getEnabledProtocols() {
|
||||
return ProtocolVersion.toStringArray(
|
||||
conContext.sslConfig.enabledProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnabledProtocols(String[] protocols) {
|
||||
if (protocols == null) {
|
||||
throw new IllegalArgumentException("Protocols cannot be null");
|
||||
}
|
||||
|
||||
conContext.sslConfig.enabledProtocols =
|
||||
ProtocolVersion.namesOf(protocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLSession getSession() {
|
||||
return conContext.conSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLSession getHandshakeSession() {
|
||||
return conContext.handshakeContext == null ?
|
||||
null : conContext.handshakeContext.handshakeSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
|
||||
return conContext.getHandshakeStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setUseClientMode(boolean mode) {
|
||||
conContext.setUseClientMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getUseClientMode() {
|
||||
return conContext.sslConfig.isClientMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setNeedClientAuth(boolean need) {
|
||||
conContext.sslConfig.clientAuthType =
|
||||
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
|
||||
ClientAuthType.CLIENT_AUTH_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getNeedClientAuth() {
|
||||
return (conContext.sslConfig.clientAuthType ==
|
||||
ClientAuthType.CLIENT_AUTH_REQUIRED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setWantClientAuth(boolean want) {
|
||||
conContext.sslConfig.clientAuthType =
|
||||
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
|
||||
ClientAuthType.CLIENT_AUTH_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getWantClientAuth() {
|
||||
return (conContext.sslConfig.clientAuthType ==
|
||||
ClientAuthType.CLIENT_AUTH_REQUESTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnableSessionCreation(boolean flag) {
|
||||
conContext.sslConfig.enableSessionCreation = flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getEnableSessionCreation() {
|
||||
return conContext.sslConfig.enableSessionCreation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLParameters getSSLParameters() {
|
||||
return conContext.sslConfig.getSSLParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSSLParameters(SSLParameters params) {
|
||||
conContext.sslConfig.setSSLParameters(params);
|
||||
|
||||
if (conContext.sslConfig.maximumPacketSize != 0) {
|
||||
conContext.outputRecord.changePacketSize(
|
||||
conContext.sslConfig.maximumPacketSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getApplicationProtocol() {
|
||||
return conContext.applicationProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getHandshakeApplicationProtocol() {
|
||||
return conContext.handshakeContext == null ?
|
||||
null : conContext.handshakeContext.applicationProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setHandshakeApplicationProtocolSelector(
|
||||
BiFunction<SSLEngine, List<String>, String> selector) {
|
||||
conContext.sslConfig.engineAPSelector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized BiFunction<SSLEngine, List<String>, String>
|
||||
getHandshakeApplicationProtocolSelector() {
|
||||
return conContext.sslConfig.engineAPSelector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useDelegatedTask() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Depending on whether the error was just a warning and the
|
||||
* handshaker wasn't closed, or fatal and the handshaker is now
|
||||
* null, report back the Exception that happened in the delegated
|
||||
* task(s).
|
||||
*/
|
||||
private synchronized void checkTaskThrown() throws SSLException {
|
||||
|
||||
Exception exc = null;
|
||||
|
||||
// First check the handshake context.
|
||||
HandshakeContext hc = conContext.handshakeContext;
|
||||
if ((hc != null) && (hc.delegatedThrown != null)) {
|
||||
exc = hc.delegatedThrown;
|
||||
hc.delegatedThrown = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* hc.delegatedThrown and conContext.delegatedThrown are most likely
|
||||
* the same, but it's possible we could have had a non-fatal
|
||||
* exception and thus the new HandshakeContext is still valid
|
||||
* (alert warning). If so, then we may have a secondary exception
|
||||
* waiting to be reported from the TransportContext, so we will
|
||||
* need to clear that on a successive call. Otherwise, clear it now.
|
||||
*/
|
||||
if (conContext.delegatedThrown != null) {
|
||||
if (exc != null) {
|
||||
// hc object comparison
|
||||
if (conContext.delegatedThrown == exc) {
|
||||
// clear if/only if both are the same
|
||||
conContext.delegatedThrown = null;
|
||||
} // otherwise report the hc delegatedThrown
|
||||
} else {
|
||||
// Nothing waiting in HandshakeContext, but one is in the
|
||||
// TransportContext.
|
||||
exc = conContext.delegatedThrown;
|
||||
conContext.delegatedThrown = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Anything to report?
|
||||
if (exc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If it wasn't a RuntimeException/SSLException, need to wrap it.
|
||||
if (exc instanceof SSLException) {
|
||||
throw (SSLException)exc;
|
||||
} else if (exc instanceof RuntimeException) {
|
||||
throw (RuntimeException)exc;
|
||||
} else {
|
||||
throw getTaskThrown(exc);
|
||||
}
|
||||
}
|
||||
|
||||
private static SSLException getTaskThrown(Exception taskThrown) {
|
||||
String msg = taskThrown.getMessage();
|
||||
|
||||
if (msg == null) {
|
||||
msg = "Delegated task threw Exception or Error";
|
||||
}
|
||||
|
||||
if (taskThrown instanceof RuntimeException) {
|
||||
throw new RuntimeException(msg, taskThrown);
|
||||
} else if (taskThrown instanceof SSLHandshakeException) {
|
||||
return (SSLHandshakeException)
|
||||
new SSLHandshakeException(msg).initCause(taskThrown);
|
||||
} else if (taskThrown instanceof SSLKeyException) {
|
||||
return (SSLKeyException)
|
||||
new SSLKeyException(msg).initCause(taskThrown);
|
||||
} else if (taskThrown instanceof SSLPeerUnverifiedException) {
|
||||
return (SSLPeerUnverifiedException)
|
||||
new SSLPeerUnverifiedException(msg).initCause(taskThrown);
|
||||
} else if (taskThrown instanceof SSLProtocolException) {
|
||||
return (SSLProtocolException)
|
||||
new SSLProtocolException(msg).initCause(taskThrown);
|
||||
} else if (taskThrown instanceof SSLException) {
|
||||
return (SSLException)taskThrown;
|
||||
} else {
|
||||
return new SSLException(msg, taskThrown);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement a simple task delegator.
|
||||
*/
|
||||
private static class DelegatedTask implements Runnable {
|
||||
private final SSLEngineImpl engine;
|
||||
|
||||
DelegatedTask(SSLEngineImpl engineInstance) {
|
||||
this.engine = engineInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (engine) {
|
||||
HandshakeContext hc = engine.conContext.handshakeContext;
|
||||
if (hc == null || hc.delegatedActions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
AccessController.doPrivileged(
|
||||
new DelegatedAction(hc), engine.conContext.acc);
|
||||
} catch (PrivilegedActionException pae) {
|
||||
// Get the handshake context again in case the
|
||||
// handshaking has completed.
|
||||
Exception reportedException = pae.getException();
|
||||
|
||||
// Report to both the TransportContext...
|
||||
if (engine.conContext.delegatedThrown == null) {
|
||||
engine.conContext.delegatedThrown = reportedException;
|
||||
}
|
||||
|
||||
// ...and the HandshakeContext in case condition
|
||||
// wasn't fatal and the handshakeContext is still
|
||||
// around.
|
||||
hc = engine.conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
hc.delegatedThrown = reportedException;
|
||||
} else if (engine.conContext.closeReason != null) {
|
||||
// Update the reason in case there was a previous.
|
||||
engine.conContext.closeReason =
|
||||
getTaskThrown(reportedException);
|
||||
}
|
||||
} catch (RuntimeException rte) {
|
||||
// Get the handshake context again in case the
|
||||
// handshaking has completed.
|
||||
|
||||
// Report to both the TransportContext...
|
||||
if (engine.conContext.delegatedThrown == null) {
|
||||
engine.conContext.delegatedThrown = rte;
|
||||
}
|
||||
|
||||
// ...and the HandshakeContext in case condition
|
||||
// wasn't fatal and the handshakeContext is still
|
||||
// around.
|
||||
hc = engine.conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
hc.delegatedThrown = rte;
|
||||
} else if (engine.conContext.closeReason != null) {
|
||||
// Update the reason in case there was a previous.
|
||||
engine.conContext.closeReason = rte;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the handshake context again in case the
|
||||
// handshaking has completed.
|
||||
hc = engine.conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
hc.taskDelegated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelegatedAction
|
||||
implements PrivilegedExceptionAction<Void> {
|
||||
final HandshakeContext context;
|
||||
DelegatedAction(HandshakeContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
while (!context.delegatedActions.isEmpty()) {
|
||||
Map.Entry<Byte, ByteBuffer> me =
|
||||
context.delegatedActions.poll();
|
||||
if (me != null) {
|
||||
context.dispatch(me.getKey(), me.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
427
jdkSrc/jdk8/sun/security/ssl/SSLEngineInputRecord.java
Normal file
427
jdkSrc/jdk8/sun/security/ssl/SSLEngineInputRecord.java
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
|
||||
/**
|
||||
* {@code InputRecord} implementation for {@code SSLEngine}.
|
||||
*/
|
||||
final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
|
||||
private boolean formatVerified = false; // SSLv2 ruled out?
|
||||
|
||||
// Cache for incomplete handshake messages.
|
||||
private ByteBuffer handshakeBuffer = null;
|
||||
|
||||
SSLEngineInputRecord(HandshakeHash handshakeHash) {
|
||||
super(handshakeHash, SSLReadCipher.nullTlsReadCipher());
|
||||
}
|
||||
|
||||
@Override
|
||||
int estimateFragmentSize(int packetSize) {
|
||||
if (packetSize > 0) {
|
||||
return readCipher.estimateFragmentSize(packetSize, headerSize);
|
||||
} else {
|
||||
return Record.maxDataSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int bytesInCompletePacket(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
|
||||
|
||||
return bytesInCompletePacket(srcs[srcsOffset]);
|
||||
}
|
||||
|
||||
private int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
|
||||
/*
|
||||
* SSLv2 length field is in bytes 0/1
|
||||
* SSLv3/TLS length field is in bytes 3/4
|
||||
*/
|
||||
if (packet.remaining() < 5) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pos = packet.position();
|
||||
byte byteZero = packet.get(pos);
|
||||
|
||||
int len = 0;
|
||||
|
||||
/*
|
||||
* If we have already verified previous packets, we can
|
||||
* ignore the verifications steps, and jump right to the
|
||||
* determination. Otherwise, try one last heuristic to
|
||||
* see if it's SSL/TLS.
|
||||
*/
|
||||
if (formatVerified ||
|
||||
(byteZero == ContentType.HANDSHAKE.id) ||
|
||||
(byteZero == ContentType.ALERT.id)) {
|
||||
/*
|
||||
* Last sanity check that it's not a wild record
|
||||
*/
|
||||
byte majorVersion = packet.get(pos + 1);
|
||||
byte minorVersion = packet.get(pos + 2);
|
||||
if (!ProtocolVersion.isNegotiable(
|
||||
majorVersion, minorVersion, false)) {
|
||||
throw new SSLException("Unrecognized record version " +
|
||||
ProtocolVersion.nameOf(majorVersion, minorVersion) +
|
||||
" , plaintext connection?");
|
||||
}
|
||||
|
||||
/*
|
||||
* Reasonably sure this is a V3, disable further checks.
|
||||
* We can't do the same in the v2 check below, because
|
||||
* read still needs to parse/handle the v2 clientHello.
|
||||
*/
|
||||
formatVerified = true;
|
||||
|
||||
/*
|
||||
* One of the SSLv3/TLS message types.
|
||||
*/
|
||||
len = ((packet.get(pos + 3) & 0xFF) << 8) +
|
||||
(packet.get(pos + 4) & 0xFF) + headerSize;
|
||||
|
||||
} else {
|
||||
/*
|
||||
* Must be SSLv2 or something unknown.
|
||||
* Check if it's short (2 bytes) or
|
||||
* long (3) header.
|
||||
*
|
||||
* Internals can warn about unsupported SSLv2
|
||||
*/
|
||||
boolean isShort = ((byteZero & 0x80) != 0);
|
||||
|
||||
if (isShort &&
|
||||
((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
|
||||
|
||||
byte majorVersion = packet.get(pos + 3);
|
||||
byte minorVersion = packet.get(pos + 4);
|
||||
if (!ProtocolVersion.isNegotiable(
|
||||
majorVersion, minorVersion, false)) {
|
||||
throw new SSLException("Unrecognized record version " +
|
||||
ProtocolVersion.nameOf(majorVersion, minorVersion) +
|
||||
" , plaintext connection?");
|
||||
}
|
||||
|
||||
/*
|
||||
* Client or Server Hello
|
||||
*/
|
||||
int mask = (isShort ? 0x7F : 0x3F);
|
||||
len = ((byteZero & mask) << 8) +
|
||||
(packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
|
||||
|
||||
} else {
|
||||
// Gobblygook!
|
||||
throw new SSLException(
|
||||
"Unrecognized SSL message, plaintext connection?");
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@Override
|
||||
Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
|
||||
int srcsLength) throws IOException, BadPaddingException {
|
||||
if (srcs == null || srcs.length == 0 || srcsLength == 0) {
|
||||
return new Plaintext[0];
|
||||
} else if (srcsLength == 1) {
|
||||
return decode(srcs[srcsOffset]);
|
||||
} else {
|
||||
ByteBuffer packet = extract(srcs,
|
||||
srcsOffset, srcsLength, SSLRecord.headerSize);
|
||||
|
||||
return decode(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private Plaintext[] decode(ByteBuffer packet)
|
||||
throws IOException, BadPaddingException {
|
||||
|
||||
if (isClosed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
|
||||
SSLLogger.fine("Raw read", packet);
|
||||
}
|
||||
|
||||
// The caller should have validated the record.
|
||||
if (!formatVerified) {
|
||||
formatVerified = true;
|
||||
|
||||
/*
|
||||
* The first record must either be a handshake record or an
|
||||
* alert message. If it's not, it is either invalid or an
|
||||
* SSLv2 message.
|
||||
*/
|
||||
int pos = packet.position();
|
||||
byte byteZero = packet.get(pos);
|
||||
if (byteZero != ContentType.HANDSHAKE.id &&
|
||||
byteZero != ContentType.ALERT.id) {
|
||||
return handleUnknownRecord(packet);
|
||||
}
|
||||
}
|
||||
|
||||
return decodeInputRecord(packet);
|
||||
}
|
||||
|
||||
private Plaintext[] decodeInputRecord(ByteBuffer packet)
|
||||
throws IOException, BadPaddingException {
|
||||
//
|
||||
// The packet should be a complete record, or more.
|
||||
//
|
||||
int srcPos = packet.position();
|
||||
int srcLim = packet.limit();
|
||||
|
||||
byte contentType = packet.get(); // pos: 0
|
||||
byte majorVersion = packet.get(); // pos: 1
|
||||
byte minorVersion = packet.get(); // pos: 2
|
||||
int contentLen = Record.getInt16(packet); // pos: 3, 4
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
|
||||
SSLLogger.fine(
|
||||
"READ: " +
|
||||
ProtocolVersion.nameOf(majorVersion, minorVersion) +
|
||||
" " + ContentType.nameOf(contentType) + ", length = " +
|
||||
contentLen);
|
||||
}
|
||||
|
||||
//
|
||||
// Check for upper bound.
|
||||
//
|
||||
// Note: May check packetSize limit in the future.
|
||||
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
|
||||
throw new SSLProtocolException(
|
||||
"Bad input record size, TLSCiphertext.length = " + contentLen);
|
||||
}
|
||||
|
||||
//
|
||||
// Decrypt the fragment
|
||||
//
|
||||
int recLim = srcPos + SSLRecord.headerSize + contentLen;
|
||||
packet.limit(recLim);
|
||||
packet.position(srcPos + SSLRecord.headerSize);
|
||||
|
||||
ByteBuffer fragment;
|
||||
try {
|
||||
Plaintext plaintext =
|
||||
readCipher.decrypt(contentType, packet, null);
|
||||
fragment = plaintext.fragment;
|
||||
contentType = plaintext.contentType;
|
||||
} catch (BadPaddingException bpe) {
|
||||
throw bpe;
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLProtocolException)(new SSLProtocolException(
|
||||
"Unexpected exception")).initCause(gse);
|
||||
} finally {
|
||||
// consume a complete record
|
||||
packet.limit(srcLim);
|
||||
packet.position(recLim);
|
||||
}
|
||||
|
||||
//
|
||||
// check for handshake fragment
|
||||
//
|
||||
if (contentType != ContentType.HANDSHAKE.id &&
|
||||
handshakeBuffer != null && handshakeBuffer.hasRemaining()) {
|
||||
throw new SSLProtocolException(
|
||||
"Expecting a handshake fragment, but received " +
|
||||
ContentType.nameOf(contentType));
|
||||
}
|
||||
|
||||
//
|
||||
// parse handshake messages
|
||||
//
|
||||
if (contentType == ContentType.HANDSHAKE.id) {
|
||||
ByteBuffer handshakeFrag = fragment;
|
||||
if ((handshakeBuffer != null) &&
|
||||
(handshakeBuffer.remaining() != 0)) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(new byte[
|
||||
handshakeBuffer.remaining() + fragment.remaining()]);
|
||||
bb.put(handshakeBuffer);
|
||||
bb.put(fragment);
|
||||
handshakeFrag = (ByteBuffer)bb.rewind();
|
||||
handshakeBuffer = null;
|
||||
}
|
||||
|
||||
ArrayList<Plaintext> plaintexts = new ArrayList<>(5);
|
||||
while (handshakeFrag.hasRemaining()) {
|
||||
int remaining = handshakeFrag.remaining();
|
||||
if (remaining < handshakeHeaderSize) {
|
||||
handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
|
||||
handshakeBuffer.put(handshakeFrag);
|
||||
handshakeBuffer.rewind();
|
||||
break;
|
||||
}
|
||||
|
||||
handshakeFrag.mark();
|
||||
|
||||
// Fail fast for unknown handshake message.
|
||||
byte handshakeType = handshakeFrag.get();
|
||||
if (!SSLHandshake.isKnown(handshakeType)) {
|
||||
throw new SSLProtocolException(
|
||||
"Unknown handshake type size, Handshake.msg_type = " +
|
||||
(handshakeType & 0xFF));
|
||||
}
|
||||
|
||||
int handshakeBodyLen = Record.getInt24(handshakeFrag);
|
||||
if (handshakeBodyLen > SSLConfiguration.maxHandshakeMessageSize) {
|
||||
throw new SSLProtocolException(
|
||||
"The size of the handshake message ("
|
||||
+ handshakeBodyLen
|
||||
+ ") exceeds the maximum allowed size ("
|
||||
+ SSLConfiguration.maxHandshakeMessageSize
|
||||
+ ")");
|
||||
}
|
||||
|
||||
handshakeFrag.reset();
|
||||
int handshakeMessageLen =
|
||||
handshakeHeaderSize + handshakeBodyLen;
|
||||
if (remaining < handshakeMessageLen) {
|
||||
handshakeBuffer = ByteBuffer.wrap(new byte[remaining]);
|
||||
handshakeBuffer.put(handshakeFrag);
|
||||
handshakeBuffer.rewind();
|
||||
break;
|
||||
} else if (remaining == handshakeMessageLen) {
|
||||
if (handshakeHash.isHashable(handshakeType)) {
|
||||
handshakeHash.receive(handshakeFrag);
|
||||
}
|
||||
|
||||
plaintexts.add(
|
||||
new Plaintext(contentType,
|
||||
majorVersion, minorVersion, -1, -1L, handshakeFrag)
|
||||
);
|
||||
break;
|
||||
} else {
|
||||
int fragPos = handshakeFrag.position();
|
||||
int fragLim = handshakeFrag.limit();
|
||||
int nextPos = fragPos + handshakeMessageLen;
|
||||
handshakeFrag.limit(nextPos);
|
||||
|
||||
if (handshakeHash.isHashable(handshakeType)) {
|
||||
handshakeHash.receive(handshakeFrag);
|
||||
}
|
||||
|
||||
plaintexts.add(
|
||||
new Plaintext(contentType, majorVersion, minorVersion,
|
||||
-1, -1L, handshakeFrag.slice())
|
||||
);
|
||||
|
||||
handshakeFrag.position(nextPos);
|
||||
handshakeFrag.limit(fragLim);
|
||||
}
|
||||
}
|
||||
|
||||
return plaintexts.toArray(new Plaintext[0]);
|
||||
}
|
||||
|
||||
return new Plaintext[] {
|
||||
new Plaintext(contentType,
|
||||
majorVersion, minorVersion, -1, -1L, fragment)
|
||||
};
|
||||
}
|
||||
|
||||
private Plaintext[] handleUnknownRecord(ByteBuffer packet)
|
||||
throws IOException, BadPaddingException {
|
||||
//
|
||||
// The packet should be a complete record.
|
||||
//
|
||||
int srcPos = packet.position();
|
||||
int srcLim = packet.limit();
|
||||
|
||||
byte firstByte = packet.get(srcPos);
|
||||
byte thirdByte = packet.get(srcPos + 2);
|
||||
|
||||
// Does it look like a Version 2 client hello (V2ClientHello)?
|
||||
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
|
||||
/*
|
||||
* If SSLv2Hello is not enabled, throw an exception.
|
||||
*/
|
||||
if (helloVersion != ProtocolVersion.SSL20Hello) {
|
||||
throw new SSLHandshakeException("SSLv2Hello is not enabled");
|
||||
}
|
||||
|
||||
byte majorVersion = packet.get(srcPos + 3);
|
||||
byte minorVersion = packet.get(srcPos + 4);
|
||||
|
||||
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
|
||||
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
|
||||
|
||||
/*
|
||||
* Looks like a V2 client hello, but not one saying
|
||||
* "let's talk SSLv3". So we need to send an SSLv2
|
||||
* error message, one that's treated as fatal by
|
||||
* clients (Otherwise we'll hang.)
|
||||
*/
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
|
||||
SSLLogger.fine(
|
||||
"Requested to negotiate unsupported SSLv2!");
|
||||
}
|
||||
|
||||
// hack code, the exception is caught in SSLEngineImpl
|
||||
// so that SSLv2 error message can be delivered properly.
|
||||
throw new UnsupportedOperationException( // SSLv2Hello
|
||||
"Unsupported SSL v2.0 ClientHello");
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can map this into a V3 ClientHello, read and
|
||||
* hash the rest of the V2 handshake, turn it into a
|
||||
* V3 ClientHello message, and pass it up.
|
||||
*/
|
||||
packet.position(srcPos + 2); // exclude the header
|
||||
handshakeHash.receive(packet);
|
||||
packet.position(srcPos);
|
||||
|
||||
ByteBuffer converted = convertToClientHello(packet);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
|
||||
SSLLogger.fine(
|
||||
"[Converted] ClientHello", converted);
|
||||
}
|
||||
|
||||
return new Plaintext[] {
|
||||
new Plaintext(ContentType.HANDSHAKE.id,
|
||||
majorVersion, minorVersion, -1, -1L, converted)
|
||||
};
|
||||
} else {
|
||||
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
|
||||
throw new SSLException("SSL V2.0 servers are not supported.");
|
||||
}
|
||||
|
||||
throw new SSLException("Unsupported or unrecognized SSL message");
|
||||
}
|
||||
}
|
||||
}
|
||||
587
jdkSrc/jdk8/sun/security/ssl/SSLEngineOutputRecord.java
Normal file
587
jdkSrc/jdk8/sun/security/ssl/SSLEngineOutputRecord.java
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||
|
||||
/**
|
||||
* {@code OutputRecord} implementation for {@code SSLEngine}.
|
||||
*/
|
||||
final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
|
||||
|
||||
private HandshakeFragment fragmenter = null;
|
||||
private boolean isTalkingToV2 = false; // SSLv2Hello
|
||||
private ByteBuffer v2ClientHello = null; // SSLv2Hello
|
||||
|
||||
private volatile boolean isCloseWaiting = false;
|
||||
|
||||
SSLEngineOutputRecord(HandshakeHash handshakeHash) {
|
||||
super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher());
|
||||
|
||||
this.packetSize = SSLRecord.maxRecordSize;
|
||||
this.protocolVersion = ProtocolVersion.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!isClosed) {
|
||||
if (fragmenter != null && fragmenter.hasAlert()) {
|
||||
isCloseWaiting = true;
|
||||
} else {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isClosed() {
|
||||
return isClosed || isCloseWaiting;
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeAlert(byte level, byte description) throws IOException {
|
||||
if (isClosed()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"alert message: " + Alert.nameOf(description));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragmenter == null) {
|
||||
fragmenter = new HandshakeFragment();
|
||||
}
|
||||
|
||||
fragmenter.queueUpAlert(level, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeHandshake(byte[] source,
|
||||
int offset, int length) throws IOException {
|
||||
if (isClosed()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"handshake message",
|
||||
ByteBuffer.wrap(source, offset, length));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragmenter == null) {
|
||||
fragmenter = new HandshakeFragment();
|
||||
}
|
||||
|
||||
if (firstMessage) {
|
||||
firstMessage = false;
|
||||
|
||||
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
|
||||
(source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
|
||||
// 5: recode header size
|
||||
(source[offset + 4 + 2 + 32] == 0)) {
|
||||
// V3 session ID is empty
|
||||
// 4: handshake header size
|
||||
// 2: client_version in ClientHello
|
||||
// 32: random in ClientHello
|
||||
|
||||
// Double space should be big enough for the converted message.
|
||||
v2ClientHello = encodeV2ClientHello(
|
||||
source, (offset + 4), (length - 4));
|
||||
|
||||
v2ClientHello.position(2); // exclude the header
|
||||
handshakeHash.deliver(v2ClientHello);
|
||||
v2ClientHello.position(0);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte handshakeType = source[offset];
|
||||
if (handshakeHash.isHashable(handshakeType)) {
|
||||
handshakeHash.deliver(source, offset, length);
|
||||
}
|
||||
|
||||
fragmenter.queueUpFragment(source, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeChangeCipherSpec() throws IOException {
|
||||
if (isClosed()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"change_cipher_spec message");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragmenter == null) {
|
||||
fragmenter = new HandshakeFragment();
|
||||
}
|
||||
fragmenter.queueUpChangeCipherSpec();
|
||||
}
|
||||
|
||||
@Override
|
||||
void encodeV2NoCipher() throws IOException {
|
||||
isTalkingToV2 = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
Ciphertext encode(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
|
||||
|
||||
if (isClosed) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"application data or cached messages");
|
||||
}
|
||||
|
||||
return null;
|
||||
} else if (isCloseWaiting) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning("outbound has closed, ignore outbound " +
|
||||
"application data");
|
||||
}
|
||||
|
||||
srcs = null; // use no application data.
|
||||
}
|
||||
|
||||
return encode(srcs, srcsOffset, srcsLength, dsts[0]);
|
||||
}
|
||||
|
||||
private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
|
||||
ByteBuffer destination) throws IOException {
|
||||
|
||||
if (writeCipher.authenticator.seqNumOverflow()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.fine(
|
||||
"sequence number extremely close to overflow " +
|
||||
"(2^64-1 packets). Closing connection.");
|
||||
}
|
||||
|
||||
throw new SSLHandshakeException("sequence number overflow");
|
||||
}
|
||||
|
||||
// Don't process the incoming record until all of the
|
||||
// buffered records get handled.
|
||||
Ciphertext ct = acquireCiphertext(destination);
|
||||
if (ct != null) {
|
||||
return ct;
|
||||
}
|
||||
|
||||
if (sources == null || sources.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int srcsRemains = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
srcsRemains += sources[i].remaining();
|
||||
}
|
||||
|
||||
if (srcsRemains == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int dstLim = destination.limit();
|
||||
boolean isFirstRecordOfThePayload = true;
|
||||
int packetLeftSize = Math.min(maxRecordSize, packetSize);
|
||||
boolean needMorePayload = true;
|
||||
long recordSN = 0L;
|
||||
while (needMorePayload) {
|
||||
int fragLen;
|
||||
if (isFirstRecordOfThePayload && needToSplitPayload()) {
|
||||
needMorePayload = true;
|
||||
|
||||
fragLen = 1;
|
||||
isFirstRecordOfThePayload = false;
|
||||
} else {
|
||||
needMorePayload = false;
|
||||
|
||||
if (packetLeftSize > 0) {
|
||||
fragLen = writeCipher.calculateFragmentSize(
|
||||
packetLeftSize, headerSize);
|
||||
|
||||
fragLen = Math.min(fragLen, Record.maxDataSize);
|
||||
} else {
|
||||
fragLen = Record.maxDataSize;
|
||||
}
|
||||
|
||||
// Calculate more impact, for example TLS 1.3 padding.
|
||||
fragLen = calculateFragmentSize(fragLen);
|
||||
}
|
||||
|
||||
int dstPos = destination.position();
|
||||
int dstContent = dstPos + headerSize +
|
||||
writeCipher.getExplicitNonceSize();
|
||||
destination.position(dstContent);
|
||||
|
||||
int remains = Math.min(fragLen, destination.remaining());
|
||||
fragLen = 0;
|
||||
int srcsLen = offset + length;
|
||||
for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
|
||||
int amount = Math.min(sources[i].remaining(), remains);
|
||||
int srcLimit = sources[i].limit();
|
||||
sources[i].limit(sources[i].position() + amount);
|
||||
destination.put(sources[i]);
|
||||
sources[i].limit(srcLimit); // restore the limit
|
||||
remains -= amount;
|
||||
fragLen += amount;
|
||||
|
||||
if (remains > 0) {
|
||||
offset++;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
destination.limit(destination.position());
|
||||
destination.position(dstContent);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
|
||||
SSLLogger.fine(
|
||||
"WRITE: " + protocolVersion + " " +
|
||||
ContentType.APPLICATION_DATA.name +
|
||||
", length = " + destination.remaining());
|
||||
}
|
||||
|
||||
// Encrypt the fragment and wrap up a record.
|
||||
recordSN = encrypt(writeCipher,
|
||||
ContentType.APPLICATION_DATA.id, destination,
|
||||
dstPos, dstLim, headerSize,
|
||||
protocolVersion);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
|
||||
ByteBuffer temporary = destination.duplicate();
|
||||
temporary.limit(temporary.position());
|
||||
temporary.position(dstPos);
|
||||
SSLLogger.fine("Raw write", temporary);
|
||||
}
|
||||
|
||||
packetLeftSize -= destination.position() - dstPos;
|
||||
|
||||
// remain the limit unchanged
|
||||
destination.limit(dstLim);
|
||||
|
||||
if (isFirstAppOutputRecord) {
|
||||
isFirstAppOutputRecord = false;
|
||||
}
|
||||
}
|
||||
|
||||
return new Ciphertext(ContentType.APPLICATION_DATA.id,
|
||||
SSLHandshake.NOT_APPLICABLE.id, recordSN);
|
||||
}
|
||||
|
||||
private Ciphertext acquireCiphertext(
|
||||
ByteBuffer destination) throws IOException {
|
||||
if (isTalkingToV2) { // SSLv2Hello
|
||||
// We don't support SSLv2. Send an SSLv2 error message
|
||||
// so that the connection can be closed gracefully.
|
||||
//
|
||||
// Please don't change the limit of the destination buffer.
|
||||
destination.put(SSLRecord.v2NoCipher);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
|
||||
SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
|
||||
}
|
||||
|
||||
isTalkingToV2 = false;
|
||||
|
||||
return new Ciphertext(ContentType.ALERT.id,
|
||||
SSLHandshake.NOT_APPLICABLE.id, -1L);
|
||||
}
|
||||
|
||||
if (v2ClientHello != null) {
|
||||
// deliver the SSLv2 format ClientHello message
|
||||
//
|
||||
// Please don't change the limit of the destination buffer.
|
||||
if (SSLLogger.isOn) {
|
||||
if (SSLLogger.isOn("record")) {
|
||||
SSLLogger.fine(Thread.currentThread().getName() +
|
||||
", WRITE: SSLv2 ClientHello message" +
|
||||
", length = " + v2ClientHello.remaining());
|
||||
}
|
||||
|
||||
if (SSLLogger.isOn("packet")) {
|
||||
SSLLogger.fine("Raw write", v2ClientHello);
|
||||
}
|
||||
}
|
||||
|
||||
destination.put(v2ClientHello);
|
||||
v2ClientHello = null;
|
||||
|
||||
return new Ciphertext(ContentType.HANDSHAKE.id,
|
||||
SSLHandshake.CLIENT_HELLO.id, -1L);
|
||||
}
|
||||
|
||||
if (fragmenter != null) {
|
||||
return fragmenter.acquireCiphertext(destination);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEmpty() {
|
||||
return (!isTalkingToV2) && (v2ClientHello == null) &&
|
||||
((fragmenter == null) || fragmenter.isEmpty());
|
||||
}
|
||||
|
||||
// buffered record fragment
|
||||
private static class RecordMemo {
|
||||
byte contentType;
|
||||
byte majorVersion;
|
||||
byte minorVersion;
|
||||
SSLWriteCipher encodeCipher;
|
||||
|
||||
byte[] fragment;
|
||||
}
|
||||
|
||||
private static class HandshakeMemo extends RecordMemo {
|
||||
byte handshakeType;
|
||||
int acquireOffset;
|
||||
}
|
||||
|
||||
final class HandshakeFragment {
|
||||
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
|
||||
|
||||
void queueUpFragment(byte[] source,
|
||||
int offset, int length) throws IOException {
|
||||
HandshakeMemo memo = new HandshakeMemo();
|
||||
|
||||
memo.contentType = ContentType.HANDSHAKE.id;
|
||||
memo.majorVersion = protocolVersion.major; // kick start version?
|
||||
memo.minorVersion = protocolVersion.minor;
|
||||
memo.encodeCipher = writeCipher;
|
||||
|
||||
memo.handshakeType = source[offset];
|
||||
memo.acquireOffset = 0;
|
||||
memo.fragment = new byte[length - 4]; // 4: header size
|
||||
// 1: HandshakeType
|
||||
// 3: message length
|
||||
System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
|
||||
|
||||
handshakeMemos.add(memo);
|
||||
}
|
||||
|
||||
void queueUpChangeCipherSpec() {
|
||||
RecordMemo memo = new RecordMemo();
|
||||
|
||||
memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
|
||||
memo.majorVersion = protocolVersion.major;
|
||||
memo.minorVersion = protocolVersion.minor;
|
||||
memo.encodeCipher = writeCipher;
|
||||
|
||||
memo.fragment = new byte[1];
|
||||
memo.fragment[0] = 1;
|
||||
|
||||
handshakeMemos.add(memo);
|
||||
}
|
||||
|
||||
void queueUpAlert(byte level, byte description) {
|
||||
RecordMemo memo = new RecordMemo();
|
||||
|
||||
memo.contentType = ContentType.ALERT.id;
|
||||
memo.majorVersion = protocolVersion.major;
|
||||
memo.minorVersion = protocolVersion.minor;
|
||||
memo.encodeCipher = writeCipher;
|
||||
|
||||
memo.fragment = new byte[2];
|
||||
memo.fragment[0] = level;
|
||||
memo.fragment[1] = description;
|
||||
|
||||
handshakeMemos.add(memo);
|
||||
}
|
||||
|
||||
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RecordMemo memo = handshakeMemos.getFirst();
|
||||
HandshakeMemo hsMemo = null;
|
||||
if (memo.contentType == ContentType.HANDSHAKE.id) {
|
||||
hsMemo = (HandshakeMemo)memo;
|
||||
}
|
||||
|
||||
// ChangeCipherSpec message is pretty small. Don't worry about
|
||||
// the fragmentation of ChangeCipherSpec record.
|
||||
int fragLen;
|
||||
if (packetSize > 0) {
|
||||
fragLen = Math.min(maxRecordSize, packetSize);
|
||||
fragLen = memo.encodeCipher.calculateFragmentSize(
|
||||
fragLen, headerSize);
|
||||
} else {
|
||||
fragLen = Record.maxDataSize;
|
||||
}
|
||||
|
||||
// Calculate more impact, for example TLS 1.3 padding.
|
||||
fragLen = calculateFragmentSize(fragLen);
|
||||
|
||||
int dstPos = dstBuf.position();
|
||||
int dstLim = dstBuf.limit();
|
||||
int dstContent = dstPos + headerSize +
|
||||
memo.encodeCipher.getExplicitNonceSize();
|
||||
dstBuf.position(dstContent);
|
||||
|
||||
if (hsMemo != null) {
|
||||
int remainingFragLen = fragLen;
|
||||
while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
|
||||
int memoFragLen = hsMemo.fragment.length;
|
||||
if (hsMemo.acquireOffset == 0) {
|
||||
// Don't fragment handshake message header
|
||||
if (remainingFragLen <= 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
dstBuf.put(hsMemo.handshakeType);
|
||||
dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
|
||||
dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
|
||||
dstBuf.put((byte)(memoFragLen & 0xFF));
|
||||
|
||||
remainingFragLen -= 4;
|
||||
} // Otherwise, handshake message is fragmented.
|
||||
|
||||
int chipLen = Math.min(remainingFragLen,
|
||||
(memoFragLen - hsMemo.acquireOffset));
|
||||
dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
|
||||
|
||||
hsMemo.acquireOffset += chipLen;
|
||||
if (hsMemo.acquireOffset == memoFragLen) {
|
||||
handshakeMemos.removeFirst();
|
||||
|
||||
// still have space for more records?
|
||||
if ((remainingFragLen > chipLen) &&
|
||||
!handshakeMemos.isEmpty()) {
|
||||
|
||||
// look for the next buffered record fragment
|
||||
RecordMemo rm = handshakeMemos.getFirst();
|
||||
if (rm.contentType == ContentType.HANDSHAKE.id &&
|
||||
rm.encodeCipher == hsMemo.encodeCipher) {
|
||||
hsMemo = (HandshakeMemo)rm;
|
||||
} else {
|
||||
// not of the flight, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remainingFragLen -= chipLen;
|
||||
}
|
||||
} else {
|
||||
fragLen = Math.min(fragLen, memo.fragment.length);
|
||||
dstBuf.put(memo.fragment, 0, fragLen);
|
||||
|
||||
handshakeMemos.removeFirst();
|
||||
}
|
||||
|
||||
dstBuf.limit(dstBuf.position());
|
||||
dstBuf.position(dstContent);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("record")) {
|
||||
SSLLogger.fine(
|
||||
"WRITE: " + protocolVersion + " " +
|
||||
ContentType.nameOf(memo.contentType) +
|
||||
", length = " + dstBuf.remaining());
|
||||
}
|
||||
|
||||
// Encrypt the fragment and wrap up a record.
|
||||
long recordSN = encrypt(
|
||||
memo.encodeCipher,
|
||||
memo.contentType, dstBuf,
|
||||
dstPos, dstLim, headerSize,
|
||||
ProtocolVersion.valueOf(memo.majorVersion,
|
||||
memo.minorVersion));
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
|
||||
ByteBuffer temporary = dstBuf.duplicate();
|
||||
temporary.limit(temporary.position());
|
||||
temporary.position(dstPos);
|
||||
SSLLogger.fine("Raw write", temporary);
|
||||
}
|
||||
|
||||
// remain the limit unchanged
|
||||
dstBuf.limit(dstLim);
|
||||
|
||||
// Reset the fragmentation offset.
|
||||
if (hsMemo != null) {
|
||||
return new Ciphertext(hsMemo.contentType,
|
||||
hsMemo.handshakeType, recordSN);
|
||||
} else {
|
||||
if (isCloseWaiting &&
|
||||
memo.contentType == ContentType.ALERT.id) {
|
||||
close();
|
||||
}
|
||||
|
||||
return new Ciphertext(memo.contentType,
|
||||
SSLHandshake.NOT_APPLICABLE.id, recordSN);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return handshakeMemos.isEmpty();
|
||||
}
|
||||
|
||||
boolean hasAlert() {
|
||||
for (RecordMemo memo : handshakeMemos) {
|
||||
if (memo.contentType == ContentType.ALERT.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to split the payload except the following cases:
|
||||
*
|
||||
* 1. protocol version is TLS 1.1 or later;
|
||||
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
|
||||
* 3. the payload is the first application record of a freshly
|
||||
* negotiated TLS session.
|
||||
* 4. the CBC protection is disabled;
|
||||
*
|
||||
* By default, we counter chosen plaintext issues on CBC mode
|
||||
* ciphersuites in SSLv3/TLS1.0 by sending one byte of application
|
||||
* data in the first record of every payload, and the rest in
|
||||
* subsequent record(s). Note that the issues have been solved in
|
||||
* TLS 1.1 or later.
|
||||
*
|
||||
* It is not necessary to split the very first application record of
|
||||
* a freshly negotiated TLS session, as there is no previous
|
||||
* application data to guess. To improve compatibility, we will not
|
||||
* split such records.
|
||||
*
|
||||
* This avoids issues in the outbound direction. For a full fix,
|
||||
* the peer must have similar protections.
|
||||
*/
|
||||
boolean needToSplitPayload() {
|
||||
return (!protocolVersion.useTLS11PlusSpec()) &&
|
||||
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
|
||||
Record.enableCBCProtection;
|
||||
}
|
||||
}
|
||||
769
jdkSrc/jdk8/sun/security/ssl/SSLExtension.java
Normal file
769
jdkSrc/jdk8/sun/security/ssl/SSLExtension.java
Normal file
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Locale;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
enum SSLExtension implements SSLStringizer {
|
||||
// Extensions defined in RFC 6066
|
||||
CH_SERVER_NAME (0x0000, "server_name",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
ServerNameExtension.chNetworkProducer,
|
||||
ServerNameExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ServerNameExtension.chStringizer),
|
||||
SH_SERVER_NAME (0x0000, "server_name",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
ServerNameExtension.shNetworkProducer,
|
||||
ServerNameExtension.shOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ServerNameExtension.shStringizer),
|
||||
EE_SERVER_NAME (0x0000, "server_name",
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
ServerNameExtension.eeNetworkProducer,
|
||||
ServerNameExtension.eeOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ServerNameExtension.shStringizer),
|
||||
CH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
MaxFragExtension.chNetworkProducer,
|
||||
MaxFragExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
MaxFragExtension.maxFragLenStringizer),
|
||||
SH_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
MaxFragExtension.shNetworkProducer,
|
||||
MaxFragExtension.shOnLoadConsumer,
|
||||
null,
|
||||
MaxFragExtension.shOnTradeConsumer,
|
||||
null,
|
||||
MaxFragExtension.maxFragLenStringizer),
|
||||
EE_MAX_FRAGMENT_LENGTH (0x0001, "max_fragment_length",
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
MaxFragExtension.eeNetworkProducer,
|
||||
MaxFragExtension.eeOnLoadConsumer,
|
||||
null,
|
||||
MaxFragExtension.eeOnTradeConsumer,
|
||||
null,
|
||||
MaxFragExtension.maxFragLenStringizer),
|
||||
CLIENT_CERTIFICATE_URL (0x0002, "client_certificate_url"),
|
||||
TRUSTED_CA_KEYS (0x0003, "trusted_ca_keys"),
|
||||
TRUNCATED_HMAC (0x0004, "truncated_hmac"),
|
||||
|
||||
CH_STATUS_REQUEST (0x0005, "status_request",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
CertStatusExtension.chNetworkProducer,
|
||||
CertStatusExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertStatusExtension.certStatusReqStringizer),
|
||||
SH_STATUS_REQUEST (0x0005, "status_request",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
CertStatusExtension.shNetworkProducer,
|
||||
CertStatusExtension.shOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertStatusExtension.certStatusReqStringizer),
|
||||
CR_STATUS_REQUEST (0x0005, "status_request"),
|
||||
CT_STATUS_REQUEST (0x0005, "status_request",
|
||||
SSLHandshake.CERTIFICATE,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CertStatusExtension.ctNetworkProducer,
|
||||
CertStatusExtension.ctOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertStatusExtension.certStatusRespStringizer),
|
||||
|
||||
// extensions defined in RFC 4681
|
||||
USER_MAPPING (0x0006, "user_mapping"),
|
||||
|
||||
// extensions defined in RFC 5878
|
||||
CLIENT_AUTHZ (0x0007, "client_authz"),
|
||||
SERVER_AUTHZ (0x0008, "server_authz"),
|
||||
|
||||
// extensions defined in RFC 5081
|
||||
CERT_TYPE (0x0009, "cert_type"),
|
||||
|
||||
// extensions defined in RFC 4492 (ECC)
|
||||
CH_SUPPORTED_GROUPS (0x000A, "supported_groups",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
SupportedGroupsExtension.chNetworkProducer,
|
||||
SupportedGroupsExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
SupportedGroupsExtension.sgsStringizer),
|
||||
EE_SUPPORTED_GROUPS (0x000A, "supported_groups",
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
SupportedGroupsExtension.eeNetworkProducer,
|
||||
SupportedGroupsExtension.eeOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
SupportedGroupsExtension.sgsStringizer),
|
||||
|
||||
CH_EC_POINT_FORMATS (0x000B, "ec_point_formats",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
ECPointFormatsExtension.chNetworkProducer,
|
||||
ECPointFormatsExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ECPointFormatsExtension.epfStringizer),
|
||||
SH_EC_POINT_FORMATS (0x000B, "ec_point_formats",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
null, // not use of the producer
|
||||
ECPointFormatsExtension.shOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ECPointFormatsExtension.epfStringizer),
|
||||
|
||||
// extensions defined in RFC 5054
|
||||
SRP (0x000C, "srp"),
|
||||
|
||||
// extensions defined in RFC 5246
|
||||
CH_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_12_13,
|
||||
SignatureAlgorithmsExtension.chNetworkProducer,
|
||||
SignatureAlgorithmsExtension.chOnLoadConsumer,
|
||||
SignatureAlgorithmsExtension.chOnLoadAbsence,
|
||||
SignatureAlgorithmsExtension.chOnTradeConsumer,
|
||||
SignatureAlgorithmsExtension.chOnTradeAbsence,
|
||||
SignatureAlgorithmsExtension.ssStringizer),
|
||||
CR_SIGNATURE_ALGORITHMS (0x000D, "signature_algorithms",
|
||||
SSLHandshake.CERTIFICATE_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
SignatureAlgorithmsExtension.crNetworkProducer,
|
||||
SignatureAlgorithmsExtension.crOnLoadConsumer,
|
||||
SignatureAlgorithmsExtension.crOnLoadAbsence,
|
||||
SignatureAlgorithmsExtension.crOnTradeConsumer,
|
||||
null,
|
||||
SignatureAlgorithmsExtension.ssStringizer),
|
||||
|
||||
CH_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_12_13,
|
||||
CertSignAlgsExtension.chNetworkProducer,
|
||||
CertSignAlgsExtension.chOnLoadConsumer,
|
||||
null,
|
||||
CertSignAlgsExtension.chOnTradeConsumer,
|
||||
null,
|
||||
CertSignAlgsExtension.ssStringizer),
|
||||
CR_SIGNATURE_ALGORITHMS_CERT (0x0032, "signature_algorithms_cert",
|
||||
SSLHandshake.CERTIFICATE_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CertSignAlgsExtension.crNetworkProducer,
|
||||
CertSignAlgsExtension.crOnLoadConsumer,
|
||||
null,
|
||||
CertSignAlgsExtension.crOnTradeConsumer,
|
||||
null,
|
||||
CertSignAlgsExtension.ssStringizer),
|
||||
|
||||
// extensions defined in RFC 5764
|
||||
USE_SRTP (0x000E, "use_srtp"),
|
||||
|
||||
// extensions defined in RFC 6520
|
||||
HEARTBEAT (0x000E, "heartbeat"),
|
||||
|
||||
// extension defined in RFC 7301 (ALPN)
|
||||
CH_ALPN (0x0010, "application_layer_protocol_negotiation",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
AlpnExtension.chNetworkProducer,
|
||||
AlpnExtension.chOnLoadConsumer,
|
||||
AlpnExtension.chOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
AlpnExtension.alpnStringizer),
|
||||
SH_ALPN (0x0010, "application_layer_protocol_negotiation",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
AlpnExtension.shNetworkProducer,
|
||||
AlpnExtension.shOnLoadConsumer,
|
||||
AlpnExtension.shOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
AlpnExtension.alpnStringizer),
|
||||
EE_ALPN (0x0010, "application_layer_protocol_negotiation",
|
||||
SSLHandshake.ENCRYPTED_EXTENSIONS,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
AlpnExtension.shNetworkProducer,
|
||||
AlpnExtension.shOnLoadConsumer,
|
||||
AlpnExtension.shOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
AlpnExtension.alpnStringizer),
|
||||
|
||||
// extensions defined in RFC 6961
|
||||
CH_STATUS_REQUEST_V2 (0x0011, "status_request_v2",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
CertStatusExtension.chV2NetworkProducer,
|
||||
CertStatusExtension.chV2OnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertStatusExtension.certStatusReqV2Stringizer),
|
||||
SH_STATUS_REQUEST_V2 (0x0011, "status_request_v2",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
CertStatusExtension.shV2NetworkProducer,
|
||||
CertStatusExtension.shV2OnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertStatusExtension.certStatusReqV2Stringizer),
|
||||
|
||||
// extensions defined in RFC 6962
|
||||
SIGNED_CERT_TIMESTAMP (0x0012, "signed_certificate_timestamp"),
|
||||
|
||||
// extensions defined in RFC 7250
|
||||
CLIENT_CERT_TYPE (0x0013, "padding"),
|
||||
SERVER_CERT_TYPE (0x0014, "server_certificate_type"),
|
||||
|
||||
// extensions defined in RFC 7685
|
||||
PADDING (0x0015, "client_certificate_type"),
|
||||
|
||||
// extensions defined in RFC 7366
|
||||
ENCRYPT_THEN_MAC (0x0016, "encrypt_then_mac"),
|
||||
|
||||
// extensions defined in RFC 7627
|
||||
CH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
ExtendedMasterSecretExtension.chNetworkProducer,
|
||||
ExtendedMasterSecretExtension.chOnLoadConsumer,
|
||||
ExtendedMasterSecretExtension.chOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
ExtendedMasterSecretExtension.emsStringizer),
|
||||
SH_EXTENDED_MASTER_SECRET (0x0017, "extended_master_secret",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
ExtendedMasterSecretExtension.shNetworkProducer,
|
||||
ExtendedMasterSecretExtension.shOnLoadConsumer,
|
||||
ExtendedMasterSecretExtension.shOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
ExtendedMasterSecretExtension.emsStringizer),
|
||||
|
||||
// extensions defined in RFC draft-ietf-tokbind-negotiation
|
||||
TOKEN_BINDING (0x0018, "token_binding "),
|
||||
|
||||
// extensions defined in RFC 7924
|
||||
CACHED_INFO (0x0019, "cached_info"),
|
||||
|
||||
// extensions defined in RFC 4507/5077
|
||||
SESSION_TICKET (0x0023, "session_ticket"),
|
||||
|
||||
// extensions defined in TLS 1.3
|
||||
CH_EARLY_DATA (0x002A, "early_data"),
|
||||
EE_EARLY_DATA (0x002A, "early_data"),
|
||||
NST_EARLY_DATA (0x002A, "early_data"),
|
||||
|
||||
CH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_13,
|
||||
SupportedVersionsExtension.chNetworkProducer,
|
||||
SupportedVersionsExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
SupportedVersionsExtension.chStringizer),
|
||||
SH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
// and HelloRetryRequest
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
SupportedVersionsExtension.shNetworkProducer,
|
||||
SupportedVersionsExtension.shOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
SupportedVersionsExtension.shStringizer),
|
||||
HRR_SUPPORTED_VERSIONS (0x002B, "supported_versions",
|
||||
SSLHandshake.HELLO_RETRY_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
SupportedVersionsExtension.hrrNetworkProducer,
|
||||
SupportedVersionsExtension.hrrOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
SupportedVersionsExtension.hrrStringizer),
|
||||
MH_SUPPORTED_VERSIONS (0x002B, "supported_versions",
|
||||
SSLHandshake.MESSAGE_HASH,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
SupportedVersionsExtension.hrrReproducer,
|
||||
null, null, null,
|
||||
null,
|
||||
SupportedVersionsExtension.hrrStringizer),
|
||||
|
||||
CH_COOKIE (0x002C, "cookie",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CookieExtension.chNetworkProducer,
|
||||
CookieExtension.chOnLoadConsumer,
|
||||
null,
|
||||
CookieExtension.chOnTradeConsumer,
|
||||
null,
|
||||
CookieExtension.cookieStringizer),
|
||||
HRR_COOKIE (0x002C, "cookie",
|
||||
SSLHandshake.HELLO_RETRY_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CookieExtension.hrrNetworkProducer,
|
||||
CookieExtension.hrrOnLoadConsumer,
|
||||
null, null,
|
||||
null,
|
||||
CookieExtension.cookieStringizer),
|
||||
MH_COOKIE (0x002C, "cookie",
|
||||
SSLHandshake.MESSAGE_HASH,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CookieExtension.hrrNetworkReproducer,
|
||||
null, null, null,
|
||||
null,
|
||||
CookieExtension.cookieStringizer),
|
||||
|
||||
PSK_KEY_EXCHANGE_MODES (0x002D, "psk_key_exchange_modes",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
PskKeyExchangeModesExtension.chNetworkProducer,
|
||||
PskKeyExchangeModesExtension.chOnLoadConsumer,
|
||||
PskKeyExchangeModesExtension.chOnLoadAbsence,
|
||||
null,
|
||||
PskKeyExchangeModesExtension.chOnTradeAbsence,
|
||||
PskKeyExchangeModesExtension.pkemStringizer),
|
||||
|
||||
CH_CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CertificateAuthoritiesExtension.chNetworkProducer,
|
||||
CertificateAuthoritiesExtension.chOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertificateAuthoritiesExtension.ssStringizer),
|
||||
|
||||
CR_CERTIFICATE_AUTHORITIES (0x002F, "certificate_authorities",
|
||||
SSLHandshake.CERTIFICATE_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
CertificateAuthoritiesExtension.crNetworkProducer,
|
||||
CertificateAuthoritiesExtension.crOnLoadConsumer,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
CertificateAuthoritiesExtension.ssStringizer),
|
||||
|
||||
OID_FILTERS (0x0030, "oid_filters"),
|
||||
POST_HANDSHAKE_AUTH (0x0030, "post_handshake_auth"),
|
||||
|
||||
CH_KEY_SHARE (0x0033, "key_share",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
KeyShareExtension.chNetworkProducer,
|
||||
KeyShareExtension.chOnLoadConsumer,
|
||||
null, null, null,
|
||||
KeyShareExtension.chStringizer),
|
||||
SH_KEY_SHARE (0x0033, "key_share",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
KeyShareExtension.shNetworkProducer,
|
||||
KeyShareExtension.shOnLoadConsumer,
|
||||
KeyShareExtension.shOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
KeyShareExtension.shStringizer),
|
||||
HRR_KEY_SHARE (0x0033, "key_share",
|
||||
SSLHandshake.HELLO_RETRY_REQUEST,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
KeyShareExtension.hrrNetworkProducer,
|
||||
KeyShareExtension.hrrOnLoadConsumer,
|
||||
null, null, null,
|
||||
KeyShareExtension.hrrStringizer),
|
||||
MH_KEY_SHARE (0x0033, "key_share",
|
||||
SSLHandshake.MESSAGE_HASH,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
KeyShareExtension.hrrNetworkReproducer,
|
||||
null, null, null, null,
|
||||
KeyShareExtension.hrrStringizer),
|
||||
|
||||
// Extensions defined in RFC 5746
|
||||
CH_RENEGOTIATION_INFO (0xff01, "renegotiation_info",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
RenegoInfoExtension.chNetworkProducer,
|
||||
RenegoInfoExtension.chOnLoadConsumer,
|
||||
RenegoInfoExtension.chOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
RenegoInfoExtension.rniStringizer),
|
||||
SH_RENEGOTIATION_INFO (0xff01, "renegotiation_info",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_TO_12,
|
||||
RenegoInfoExtension.shNetworkProducer,
|
||||
RenegoInfoExtension.shOnLoadConsumer,
|
||||
RenegoInfoExtension.shOnLoadAbsence,
|
||||
null,
|
||||
null,
|
||||
RenegoInfoExtension.rniStringizer),
|
||||
|
||||
// TLS 1.3 PSK extension must be last
|
||||
CH_PRE_SHARED_KEY (0x0029, "pre_shared_key",
|
||||
SSLHandshake.CLIENT_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
PreSharedKeyExtension.chNetworkProducer,
|
||||
PreSharedKeyExtension.chOnLoadConsumer,
|
||||
PreSharedKeyExtension.chOnLoadAbsence,
|
||||
PreSharedKeyExtension.chOnTradeConsumer,
|
||||
null,
|
||||
PreSharedKeyExtension.chStringizer),
|
||||
SH_PRE_SHARED_KEY (0x0029, "pre_shared_key",
|
||||
SSLHandshake.SERVER_HELLO,
|
||||
ProtocolVersion.PROTOCOLS_OF_13,
|
||||
PreSharedKeyExtension.shNetworkProducer,
|
||||
PreSharedKeyExtension.shOnLoadConsumer,
|
||||
PreSharedKeyExtension.shOnLoadAbsence,
|
||||
null, null,
|
||||
PreSharedKeyExtension.shStringizer);
|
||||
|
||||
final int id;
|
||||
final SSLHandshake handshakeType;
|
||||
final String name;
|
||||
final ProtocolVersion[] supportedProtocols;
|
||||
final HandshakeProducer networkProducer;
|
||||
final ExtensionConsumer onLoadConsumer;
|
||||
final HandshakeAbsence onLoadAbsence;
|
||||
final HandshakeConsumer onTradeConsumer;
|
||||
final HandshakeAbsence onTradeAbsence;
|
||||
final SSLStringizer stringizer;
|
||||
|
||||
// known but unsupported extension
|
||||
private SSLExtension(int id, String name) {
|
||||
this.id = id;
|
||||
this.handshakeType = SSLHandshake.NOT_APPLICABLE;
|
||||
this.name = name;
|
||||
this.supportedProtocols = new ProtocolVersion[0];
|
||||
this.networkProducer = null;
|
||||
this.onLoadConsumer = null;
|
||||
this.onLoadAbsence = null;
|
||||
this.onTradeConsumer = null;
|
||||
this.onTradeAbsence = null;
|
||||
this.stringizer = null;
|
||||
}
|
||||
|
||||
// supported extension
|
||||
private SSLExtension(int id, String name, SSLHandshake handshakeType,
|
||||
ProtocolVersion[] supportedProtocols,
|
||||
HandshakeProducer producer,
|
||||
ExtensionConsumer onLoadConsumer, HandshakeAbsence onLoadAbsence,
|
||||
HandshakeConsumer onTradeConsumer, HandshakeAbsence onTradeAbsence,
|
||||
SSLStringizer stringize) {
|
||||
this.id = id;
|
||||
this.handshakeType = handshakeType;
|
||||
this.name = name;
|
||||
this.supportedProtocols = supportedProtocols;
|
||||
this.networkProducer = producer;
|
||||
this.onLoadConsumer = onLoadConsumer;
|
||||
this.onLoadAbsence = onLoadAbsence;
|
||||
this.onTradeConsumer = onTradeConsumer;
|
||||
this.onTradeAbsence = onTradeAbsence;
|
||||
this.stringizer = stringize;
|
||||
}
|
||||
|
||||
static SSLExtension valueOf(SSLHandshake handshakeType, int extensionType) {
|
||||
for (SSLExtension ext : SSLExtension.values()) {
|
||||
if (ext.id == extensionType &&
|
||||
ext.handshakeType == handshakeType) {
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String nameOf(int extensionType) {
|
||||
for (SSLExtension ext : SSLExtension.values()) {
|
||||
if (ext.id == extensionType) {
|
||||
return ext.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown extension";
|
||||
}
|
||||
|
||||
static boolean isConsumable(int extensionType) {
|
||||
for (SSLExtension ext : SSLExtension.values()) {
|
||||
if (ext.id == extensionType &&
|
||||
ext.onLoadConsumer != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
if (networkProducer != null) {
|
||||
return networkProducer.produce(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not yet supported extension producing.");
|
||||
}
|
||||
}
|
||||
|
||||
public void consumeOnLoad(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException {
|
||||
if (onLoadConsumer != null) {
|
||||
onLoadConsumer.consume(context, message, buffer);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not yet supported extension loading.");
|
||||
}
|
||||
}
|
||||
|
||||
public void consumeOnTrade(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
if (onTradeConsumer != null) {
|
||||
onTradeConsumer.consume(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not yet supported extension processing.");
|
||||
}
|
||||
}
|
||||
|
||||
void absentOnLoad(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
if (onLoadAbsence != null) {
|
||||
onLoadAbsence.absent(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not yet supported extension absence processing.");
|
||||
}
|
||||
}
|
||||
|
||||
void absentOnTrade(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
if (onTradeAbsence != null) {
|
||||
onTradeAbsence.absent(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Not yet supported extension absence processing.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAvailable(ProtocolVersion protocolVersion) {
|
||||
for (int i = 0; i < supportedProtocols.length; i++) {
|
||||
if (supportedProtocols[i] == protocolVersion) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(ByteBuffer byteBuffer) {
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"{0} ({1})\": '{'\n" +
|
||||
"{2}\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
String extData;
|
||||
if (stringizer == null) {
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
String encoded = hexEncoder.encode(byteBuffer.duplicate());
|
||||
extData = encoded;
|
||||
} else {
|
||||
extData = stringizer.toString(byteBuffer);
|
||||
}
|
||||
|
||||
Object[] messageFields = {
|
||||
this.name,
|
||||
this.id,
|
||||
Utilities.indent(extData)
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Nested extension, consumer and producer interfaces.
|
||||
|
||||
static interface ExtensionConsumer {
|
||||
void consume(ConnectionContext context,
|
||||
HandshakeMessage message, ByteBuffer buffer) throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* A (transparent) specification of extension data.
|
||||
*
|
||||
* This interface contains no methods or constants. Its only purpose is to
|
||||
* group all extension data. All extension data should implement this
|
||||
* interface if the data is expected to handle in the following handshake
|
||||
* processes.
|
||||
*/
|
||||
static interface SSLExtensionSpec {
|
||||
// blank
|
||||
}
|
||||
|
||||
// Default enabled client extensions.
|
||||
static final class ClientExtensions {
|
||||
static final Collection<SSLExtension> defaults;
|
||||
|
||||
static {
|
||||
Collection<SSLExtension> extensions = new LinkedList<>();
|
||||
for (SSLExtension extension : SSLExtension.values()) {
|
||||
if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
|
||||
extensions.add(extension);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch off SNI extention?
|
||||
boolean enableExtension =
|
||||
Utilities.getBooleanProperty("jsse.enableSNIExtension", true);
|
||||
if (!enableExtension) {
|
||||
extensions.remove(CH_SERVER_NAME);
|
||||
}
|
||||
|
||||
// To switch off the max_fragment_length extension.
|
||||
//
|
||||
// Note that "jsse.enableMFLNExtension" is the CSR approved
|
||||
// property name. However, "jsse.enableMFLExtension" was used
|
||||
// in the original implementation. Temporarily, if either of
|
||||
// the two properties set to true, the extension is switch on.
|
||||
// We may remove the "jsse.enableMFLExtension" property in the
|
||||
// future. Please don't continue to use the misspelling property.
|
||||
enableExtension =
|
||||
Utilities.getBooleanProperty(
|
||||
"jsse.enableMFLNExtension", false) ||
|
||||
Utilities.getBooleanProperty(
|
||||
"jsse.enableMFLExtension", false);
|
||||
if (!enableExtension) {
|
||||
extensions.remove(CH_MAX_FRAGMENT_LENGTH);
|
||||
}
|
||||
|
||||
// To switch on certificate_authorities extension in ClientHello.
|
||||
//
|
||||
// Note: Please be careful to enable this extension in ClientHello.
|
||||
//
|
||||
// In practice, if the server certificate cannot be validated by
|
||||
// the underlying programs, the user may manually check the
|
||||
// certificate in order to access the service. The certificate
|
||||
// could be accepted manually, and the handshake continues. For
|
||||
// example, the browsers provide the manual option to accept
|
||||
// untrusted server certificate. If this extension is enabled in
|
||||
// the ClientHello handshake message, and the server's certificate
|
||||
// does not chain back to any of the CAs in the extension, then the
|
||||
// server will terminate the handshake and close the connection.
|
||||
// There is no chance for the client to perform the manual check.
|
||||
// Therefore, enabling this extension in ClientHello may lead to
|
||||
// unexpected compatibility issues for such cases.
|
||||
//
|
||||
// According to TLS 1.3 specification [RFC 8446] the maximum size
|
||||
// of the certificate_authorities extension is 2^16 bytes. The
|
||||
// maximum TLS record size is 2^14 bytes. If the handshake
|
||||
// message is bigger than maximum TLS record size, it should be
|
||||
// splitted into several records. In fact, some server
|
||||
// implementations do not allow ClientHello messages bigger than
|
||||
// the maximum TLS record size and will immediately abort the
|
||||
// connection with a fatal alert. Therefore, if the client trusts
|
||||
// too many certificate authorities, there may be unexpected
|
||||
// interoperability issues.
|
||||
//
|
||||
// Furthermore, if the client trusts more CAs such that it exceeds
|
||||
// the size limit of the extension, enabling this extension in
|
||||
// client side does not really make sense any longer as there is
|
||||
// no way to indicate the server certificate selection accurately.
|
||||
//
|
||||
// In general, a server does not use multiple certificates issued
|
||||
// from different CAs. It is not expected to use this extension a
|
||||
// lot in practice. When there is a need to use this extension
|
||||
// in ClientHello handshake message, please take care of the
|
||||
// potential compatibility and interoperability issues above.
|
||||
enableExtension = Utilities.getBooleanProperty(
|
||||
"jdk.tls.client.enableCAExtension", false);
|
||||
if (!enableExtension) {
|
||||
extensions.remove(CH_CERTIFICATE_AUTHORITIES);
|
||||
}
|
||||
|
||||
defaults = Collections.unmodifiableCollection(extensions);
|
||||
}
|
||||
}
|
||||
|
||||
// Default enabled server extensions.
|
||||
static final class ServerExtensions {
|
||||
static final Collection<SSLExtension> defaults;
|
||||
|
||||
static {
|
||||
Collection<SSLExtension> extensions = new LinkedList<>();
|
||||
for (SSLExtension extension : SSLExtension.values()) {
|
||||
if (extension.handshakeType != SSLHandshake.NOT_APPLICABLE) {
|
||||
extensions.add(extension);
|
||||
}
|
||||
}
|
||||
|
||||
defaults = Collections.unmodifiableCollection(extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
389
jdkSrc/jdk8/sun/security/ssl/SSLExtensions.java
Normal file
389
jdkSrc/jdk8/sun/security/ssl/SSLExtensions.java
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
|
||||
/**
|
||||
* SSL/TLS extensions in a handshake message.
|
||||
*/
|
||||
final class SSLExtensions {
|
||||
private final HandshakeMessage handshakeMessage;
|
||||
private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>();
|
||||
private int encodedLength;
|
||||
|
||||
// Extension map for debug logging
|
||||
private final Map<Integer, byte[]> logMap =
|
||||
SSLLogger.isOn ? new LinkedHashMap<>() : null;
|
||||
|
||||
SSLExtensions(HandshakeMessage handshakeMessage) {
|
||||
this.handshakeMessage = handshakeMessage;
|
||||
this.encodedLength = 2; // 2: the length of the extensions.
|
||||
}
|
||||
|
||||
SSLExtensions(HandshakeMessage hm,
|
||||
ByteBuffer m, SSLExtension[] extensions) throws IOException {
|
||||
this.handshakeMessage = hm;
|
||||
|
||||
int len = Record.getInt16(m);
|
||||
encodedLength = len + 2; // 2: the length of the extensions.
|
||||
while (len > 0) {
|
||||
int extId = Record.getInt16(m);
|
||||
int extLen = Record.getInt16(m);
|
||||
if (extLen > m.remaining()) {
|
||||
throw hm.handshakeContext.conContext.fatal(
|
||||
Alert.ILLEGAL_PARAMETER,
|
||||
"Error parsing extension (" + extId +
|
||||
"): no sufficient data");
|
||||
}
|
||||
|
||||
boolean isSupported = true;
|
||||
SSLHandshake handshakeType = hm.handshakeType();
|
||||
if (SSLExtension.isConsumable(extId) &&
|
||||
SSLExtension.valueOf(handshakeType, extId) == null) {
|
||||
if (extId == SSLExtension.CH_SUPPORTED_GROUPS.id &&
|
||||
handshakeType == SSLHandshake.SERVER_HELLO) {
|
||||
// Note: It does not comply to the specification. However,
|
||||
// there are servers that send the supported_groups
|
||||
// extension in ServerHello handshake message.
|
||||
//
|
||||
// TLS 1.3 should not send this extension. We may want to
|
||||
// limit the workaround for TLS 1.2 and prior version only.
|
||||
// However, the implementation of the limit is complicated
|
||||
// and inefficient, and may not worthy the maintenance.
|
||||
isSupported = false;
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Received buggy supported_groups extension " +
|
||||
"in the ServerHello handshake message");
|
||||
}
|
||||
} else if (handshakeType == SSLHandshake.SERVER_HELLO) {
|
||||
throw hm.handshakeContext.conContext.fatal(
|
||||
Alert.UNSUPPORTED_EXTENSION, "extension (" +
|
||||
extId + ") should not be presented in " +
|
||||
handshakeType.name);
|
||||
} else {
|
||||
isSupported = false;
|
||||
// debug log to ignore unknown extension for handshakeType
|
||||
}
|
||||
}
|
||||
|
||||
if (isSupported) {
|
||||
isSupported = false;
|
||||
for (SSLExtension extension : extensions) {
|
||||
if ((extension.id != extId) ||
|
||||
(extension.onLoadConsumer == null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extension.handshakeType != handshakeType) {
|
||||
throw hm.handshakeContext.conContext.fatal(
|
||||
Alert.UNSUPPORTED_EXTENSION,
|
||||
"extension (" + extId + ") should not be " +
|
||||
"presented in " + handshakeType.name);
|
||||
}
|
||||
|
||||
byte[] extData = new byte[extLen];
|
||||
m.get(extData);
|
||||
extMap.put(extension, extData);
|
||||
if (logMap != null) {
|
||||
logMap.put(extId, extData);
|
||||
}
|
||||
|
||||
isSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSupported) {
|
||||
if (logMap != null) {
|
||||
// cache the extension for debug logging
|
||||
byte[] extData = new byte[extLen];
|
||||
m.get(extData);
|
||||
logMap.put(extId, extData);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unknown or unsupported extension",
|
||||
toString(extId, extData));
|
||||
}
|
||||
} else {
|
||||
// ignore the extension
|
||||
int pos = m.position() + extLen;
|
||||
m.position(pos);
|
||||
}
|
||||
}
|
||||
|
||||
len -= extLen + 4;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] get(SSLExtension ext) {
|
||||
return extMap.get(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the specified extensions.
|
||||
*/
|
||||
void consumeOnLoad(HandshakeContext context,
|
||||
SSLExtension[] extensions) throws IOException {
|
||||
for (SSLExtension extension : extensions) {
|
||||
if (context.negotiatedProtocol != null &&
|
||||
!extension.isAvailable(context.negotiatedProtocol)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unsupported extension: " + extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!extMap.containsKey(extension)) {
|
||||
if (extension.onLoadAbsence != null) {
|
||||
extension.absentOnLoad(context, handshakeMessage);
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable extension: " + extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (extension.onLoadConsumer == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore unsupported extension: " + extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ByteBuffer m = ByteBuffer.wrap(extMap.get(extension));
|
||||
extension.consumeOnLoad(context, handshakeMessage, m);
|
||||
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Consumed extension: " + extension.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider impact of the specified extensions.
|
||||
*/
|
||||
void consumeOnTrade(HandshakeContext context,
|
||||
SSLExtension[] extensions) throws IOException {
|
||||
for (SSLExtension extension : extensions) {
|
||||
if (!extMap.containsKey(extension)) {
|
||||
if (extension.onTradeAbsence != null) {
|
||||
extension.absentOnTrade(context, handshakeMessage);
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unavailable extension: " + extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extension.onTradeConsumer == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore impact of unsupported extension: " +
|
||||
extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
extension.consumeOnTrade(context, handshakeMessage);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Populated with extension: " + extension.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce extension values for the specified extensions.
|
||||
*/
|
||||
void produce(HandshakeContext context,
|
||||
SSLExtension[] extensions) throws IOException {
|
||||
for (SSLExtension extension : extensions) {
|
||||
if (extMap.containsKey(extension)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore, duplicated extension: " +
|
||||
extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extension.networkProducer == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore, no extension producer defined: " +
|
||||
extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] encoded = extension.produce(context, handshakeMessage);
|
||||
if (encoded != null) {
|
||||
extMap.put(extension, encoded);
|
||||
encodedLength += encoded.length + 4; // extension_type (2)
|
||||
// extension_data length(2)
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
// The extension is not available in the context.
|
||||
SSLLogger.fine(
|
||||
"Ignore, context unavailable extension: " +
|
||||
extension.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce extension values for the specified extensions, replacing if
|
||||
* there is an existing extension value for a specified extension.
|
||||
*/
|
||||
void reproduce(HandshakeContext context,
|
||||
SSLExtension[] extensions) throws IOException {
|
||||
for (SSLExtension extension : extensions) {
|
||||
if (extension.networkProducer == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.warning(
|
||||
"Ignore, no extension producer defined: " +
|
||||
extension.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] encoded = extension.produce(context, handshakeMessage);
|
||||
if (encoded != null) {
|
||||
if (extMap.containsKey(extension)) {
|
||||
byte[] old = extMap.replace(extension, encoded);
|
||||
if (old != null) {
|
||||
encodedLength -= old.length + 4;
|
||||
}
|
||||
encodedLength += encoded.length + 4;
|
||||
} else {
|
||||
extMap.put(extension, encoded);
|
||||
encodedLength += encoded.length + 4;
|
||||
// extension_type (2)
|
||||
// extension_data length(2)
|
||||
}
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
// The extension is not available in the context.
|
||||
SSLLogger.fine(
|
||||
"Ignore, context unavailable extension: " +
|
||||
extension.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that TLS 1.3 may use empty extensions. Please consider it while
|
||||
// using this method.
|
||||
int length() {
|
||||
if (extMap.isEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return encodedLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that TLS 1.3 may use empty extensions. Please consider it while
|
||||
// using this method.
|
||||
void send(HandshakeOutStream hos) throws IOException {
|
||||
int extsLen = length();
|
||||
if (extsLen == 0) {
|
||||
return;
|
||||
}
|
||||
hos.putInt16(extsLen - 2);
|
||||
// extensions must be sent in the order they appear in the enum
|
||||
for (SSLExtension ext : SSLExtension.values()) {
|
||||
byte[] extData = extMap.get(ext);
|
||||
if (extData != null) {
|
||||
hos.putInt16(ext.id);
|
||||
hos.putBytes16(extData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) {
|
||||
return "<no extension>";
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
if (logMap != null && !logMap.isEmpty()) {
|
||||
for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) {
|
||||
SSLExtension ext = SSLExtension.valueOf(
|
||||
handshakeMessage.handshakeType(), en.getKey());
|
||||
if (builder.length() != 0) {
|
||||
builder.append(",\n");
|
||||
}
|
||||
if (ext != null) {
|
||||
builder.append(
|
||||
ext.toString(ByteBuffer.wrap(en.getValue())));
|
||||
} else {
|
||||
builder.append(toString(en.getKey(), en.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
} else {
|
||||
for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append(",\n");
|
||||
}
|
||||
builder.append(
|
||||
en.getKey().toString(ByteBuffer.wrap(en.getValue())));
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String toString(int extId, byte[] extData) {
|
||||
String extName = SSLExtension.nameOf(extId);
|
||||
MessageFormat messageFormat = new MessageFormat(
|
||||
"\"{0} ({1})\": '{'\n" +
|
||||
"{2}\n" +
|
||||
"'}'",
|
||||
Locale.ENGLISH);
|
||||
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
String encoded = hexEncoder.encodeBuffer(extData);
|
||||
|
||||
Object[] messageFields = {
|
||||
extName,
|
||||
extId,
|
||||
Utilities.indent(encoded)
|
||||
};
|
||||
|
||||
return messageFormat.format(messageFields);
|
||||
}
|
||||
}
|
||||
557
jdkSrc/jdk8/sun/security/ssl/SSLHandshake.java
Normal file
557
jdkSrc/jdk8/sun/security/ssl/SSLHandshake.java
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Map;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
enum SSLHandshake implements SSLConsumer, HandshakeProducer {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
HELLO_REQUEST ((byte)0x00, "hello_request",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
HelloRequest.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
HelloRequest.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CLIENT_HELLO ((byte)0x01, "client_hello",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ClientHello.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ClientHello.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
SERVER_HELLO ((byte)0x02, "server_hello",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ServerHello.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ServerHello.t12HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ServerHello.t13HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
HELLO_RETRY_REQUEST ((byte)0x02, "hello_retry_request",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ServerHello.handshakeConsumer, // Use ServerHello consumer
|
||||
ProtocolVersion.PROTOCOLS_TO_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ServerHello.hrrHandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
NewSessionTicket.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
NewSessionTicket.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
ENCRYPTED_EXTENSIONS ((byte)0x08, "encrypted_extensions",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
EncryptedExtensions.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
EncryptedExtensions.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CERTIFICATE ((byte)0x0B, "certificate",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateMessage.t12HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateMessage.t13HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateMessage.t12HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateMessage.t13HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
SERVER_KEY_EXCHANGE ((byte)0x0C, "server_key_exchange",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ServerKeyExchange.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ServerKeyExchange.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CERTIFICATE_REQUEST ((byte)0x0D, "certificate_request",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateRequest.t10HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_11
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateRequest.t12HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateRequest.t13HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateRequest.t10HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_11
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateRequest.t12HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateRequest.t13HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
SERVER_HELLO_DONE ((byte)0x0E, "server_hello_done",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ServerHelloDone.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ServerHelloDone.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CERTIFICATE_VERIFY ((byte)0x0F, "certificate_verify",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateVerify.s30HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_30
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateVerify.t10HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_10_11
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateVerify.t12HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateVerify.t13HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateVerify.s30HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_30
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateVerify.t10HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_10_11
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateVerify.t12HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_12
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateVerify.t13HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CLIENT_KEY_EXCHANGE ((byte)0x10, "client_key_exchange",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
ClientKeyExchange.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
ClientKeyExchange.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
})),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
FINISHED ((byte)0x14, "finished",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
Finished.t12HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
),
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
Finished.t13HandshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
Finished.t12HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
),
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
Finished.t13HandshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
|
||||
CERTIFICATE_URL ((byte)0x15, "certificate_url"),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
CERTIFICATE_STATUS ((byte)0x16, "certificate_status",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
CertificateStatus.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
CertificateStatus.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeAbsence, ProtocolVersion[]>(
|
||||
CertificateStatus.handshakeAbsence,
|
||||
ProtocolVersion.PROTOCOLS_TO_12
|
||||
)
|
||||
})),
|
||||
|
||||
SUPPLEMENTAL_DATA ((byte)0x17, "supplemental_data"),
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
KEY_UPDATE ((byte)0x18, "key_update",
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||
KeyUpdate.handshakeConsumer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
}),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||
KeyUpdate.handshakeProducer,
|
||||
ProtocolVersion.PROTOCOLS_OF_13
|
||||
)
|
||||
})),
|
||||
MESSAGE_HASH ((byte)0xFE, "message_hash"),
|
||||
NOT_APPLICABLE ((byte)0xFF, "not_applicable");
|
||||
|
||||
final byte id;
|
||||
final String name;
|
||||
final Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers;
|
||||
final Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers;
|
||||
final Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsences;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
SSLHandshake(byte id, String name) {
|
||||
this(id, name,
|
||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(
|
||||
new Map.Entry[0]),
|
||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(
|
||||
new Map.Entry[0]),
|
||||
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
|
||||
new Map.Entry[0]));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
SSLHandshake(byte id, String name,
|
||||
Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
|
||||
Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers) {
|
||||
|
||||
this(id, name, handshakeConsumers, handshakeProducers,
|
||||
(Map.Entry<HandshakeAbsence, ProtocolVersion[]>[])(
|
||||
new Map.Entry[0]));
|
||||
}
|
||||
|
||||
SSLHandshake(byte id, String name,
|
||||
Map.Entry<SSLConsumer, ProtocolVersion[]>[] handshakeConsumers,
|
||||
Map.Entry<HandshakeProducer, ProtocolVersion[]>[] handshakeProducers,
|
||||
Map.Entry<HandshakeAbsence, ProtocolVersion[]>[] handshakeAbsence) {
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.handshakeConsumers = handshakeConsumers;
|
||||
this.handshakeProducers = handshakeProducers;
|
||||
this.handshakeAbsences = handshakeAbsence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ConnectionContext context,
|
||||
ByteBuffer message) throws IOException {
|
||||
SSLConsumer hc = getHandshakeConsumer(context);
|
||||
if (hc != null) {
|
||||
hc.consume(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported handshake consumer: " + this.name);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLConsumer getHandshakeConsumer(ConnectionContext context) {
|
||||
if (handshakeConsumers.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The consuming happens in handshake context only.
|
||||
HandshakeContext hc = (HandshakeContext)context;
|
||||
ProtocolVersion protocolVersion;
|
||||
if ((hc.negotiatedProtocol == null) ||
|
||||
(hc.negotiatedProtocol == ProtocolVersion.NONE)) {
|
||||
if (hc.conContext.isNegotiated &&
|
||||
hc.conContext.protocolVersion != ProtocolVersion.NONE) {
|
||||
protocolVersion = hc.conContext.protocolVersion;
|
||||
} else {
|
||||
protocolVersion = hc.maximumActiveProtocol;
|
||||
}
|
||||
} else {
|
||||
protocolVersion = hc.negotiatedProtocol;
|
||||
}
|
||||
|
||||
for (Map.Entry<SSLConsumer,
|
||||
ProtocolVersion[]> phe : handshakeConsumers) {
|
||||
for (ProtocolVersion pv : phe.getValue()) {
|
||||
if (protocolVersion == pv) {
|
||||
return phe.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context,
|
||||
HandshakeMessage message) throws IOException {
|
||||
HandshakeProducer hp = getHandshakeProducer(context);
|
||||
if (hp != null) {
|
||||
return hp.produce(context, message);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported handshake producer: " + this.name);
|
||||
}
|
||||
}
|
||||
|
||||
private HandshakeProducer getHandshakeProducer(
|
||||
ConnectionContext context) {
|
||||
if (handshakeConsumers.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The consuming happens in handshake context only.
|
||||
HandshakeContext hc = (HandshakeContext)context;
|
||||
ProtocolVersion protocolVersion;
|
||||
if ((hc.negotiatedProtocol == null) ||
|
||||
(hc.negotiatedProtocol == ProtocolVersion.NONE)) {
|
||||
if (hc.conContext.isNegotiated &&
|
||||
hc.conContext.protocolVersion != ProtocolVersion.NONE) {
|
||||
protocolVersion = hc.conContext.protocolVersion;
|
||||
} else {
|
||||
protocolVersion = hc.maximumActiveProtocol;
|
||||
}
|
||||
} else {
|
||||
protocolVersion = hc.negotiatedProtocol;
|
||||
}
|
||||
|
||||
for (Map.Entry<HandshakeProducer,
|
||||
ProtocolVersion[]> phe : handshakeProducers) {
|
||||
for (ProtocolVersion pv : phe.getValue()) {
|
||||
if (protocolVersion == pv) {
|
||||
return phe.getKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
static String nameOf(byte id) {
|
||||
// If two handshake message share the same handshake type, returns
|
||||
// the first handshake message name.
|
||||
//
|
||||
// It is not a big issue at present as only ServerHello and
|
||||
// HellRetryRequest share a handshake type.
|
||||
for (SSLHandshake hs : SSLHandshake.values()) {
|
||||
if (hs.id == id) {
|
||||
return hs.name;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN-HANDSHAKE-MESSAGE(" + id + ")";
|
||||
}
|
||||
|
||||
static boolean isKnown(byte id) {
|
||||
for (SSLHandshake hs : SSLHandshake.values()) {
|
||||
if (hs.id == id && id != NOT_APPLICABLE.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static final void kickstart(HandshakeContext context) throws IOException {
|
||||
if (context instanceof ClientHandshakeContext) {
|
||||
// For initial handshaking, including session resumption,
|
||||
// ClientHello message is used as the kickstart message.
|
||||
//
|
||||
// TLS 1.2 and older protocols support renegotiation on existing
|
||||
// connections. A ClientHello messages is used to kickstart the
|
||||
// renegotiation.
|
||||
//
|
||||
// TLS 1.3 forbids renegotiation. The post-handshake KeyUpdate
|
||||
// message is used to update the sending cryptographic keys.
|
||||
if (context.conContext.isNegotiated &&
|
||||
context.conContext.protocolVersion.useTLS13PlusSpec()) {
|
||||
// Use KeyUpdate message for renegotiation.
|
||||
KeyUpdate.kickstartProducer.produce(context);
|
||||
} else {
|
||||
// Using ClientHello message for the initial handshaking
|
||||
// (including session resumption) or renegotiation.
|
||||
// SSLHandshake.CLIENT_HELLO.produce(context);
|
||||
ClientHello.kickstartProducer.produce(context);
|
||||
}
|
||||
} else {
|
||||
// The server side can delivering kickstart message after the
|
||||
// connection has established.
|
||||
//
|
||||
// TLS 1.2 and older protocols use HelloRequest to begin a
|
||||
// negotiation process anew.
|
||||
//
|
||||
// While TLS 1.3 uses the post-handshake KeyUpdate message
|
||||
// to update the sending cryptographic keys.
|
||||
if (context.conContext.protocolVersion.useTLS13PlusSpec()) {
|
||||
// Use KeyUpdate message for renegotiation.
|
||||
KeyUpdate.kickstartProducer.produce(context);
|
||||
} else {
|
||||
// SSLHandshake.HELLO_REQUEST.produce(context);
|
||||
HelloRequest.kickstartProducer.produce(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A (transparent) specification of handshake message.
|
||||
*/
|
||||
static abstract class HandshakeMessage {
|
||||
final HandshakeContext handshakeContext;
|
||||
|
||||
HandshakeMessage(HandshakeContext handshakeContext) {
|
||||
this.handshakeContext = handshakeContext;
|
||||
}
|
||||
|
||||
abstract SSLHandshake handshakeType();
|
||||
abstract int messageLength();
|
||||
abstract void send(HandshakeOutStream hos) throws IOException;
|
||||
|
||||
void write(HandshakeOutStream hos) throws IOException {
|
||||
int len = messageLength();
|
||||
if (len >= Record.OVERFLOW_OF_INT24) {
|
||||
throw new SSLException("Handshake message is overflow"
|
||||
+ ", type = " + handshakeType() + ", len = " + len);
|
||||
}
|
||||
hos.write(handshakeType().id);
|
||||
hos.putInt24(len);
|
||||
send(hos);
|
||||
hos.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
47
jdkSrc/jdk8/sun/security/ssl/SSLHandshakeBinding.java
Normal file
47
jdkSrc/jdk8/sun/security/ssl/SSLHandshakeBinding.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
interface SSLHandshakeBinding {
|
||||
default SSLHandshake[] getRelatedHandshakers(
|
||||
HandshakeContext handshakeContext) {
|
||||
return new SSLHandshake[0];
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
default Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
|
||||
HandshakeContext handshakeContext) {
|
||||
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
default Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
|
||||
HandshakeContext handshakeContext) {
|
||||
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
|
||||
}
|
||||
}
|
||||
32
jdkSrc/jdk8/sun/security/ssl/SSLKeyAgreement.java
Normal file
32
jdkSrc/jdk8/sun/security/ssl/SSLKeyAgreement.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
interface SSLKeyAgreement
|
||||
extends SSLPossessionGenerator, SSLKeyAgreementGenerator,
|
||||
SSLHandshakeBinding {
|
||||
// blank
|
||||
}
|
||||
33
jdkSrc/jdk8/sun/security/ssl/SSLKeyAgreementGenerator.java
Normal file
33
jdkSrc/jdk8/sun/security/ssl/SSLKeyAgreementGenerator.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
interface SSLKeyAgreementGenerator {
|
||||
SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException;
|
||||
}
|
||||
35
jdkSrc/jdk8/sun/security/ssl/SSLKeyDerivation.java
Normal file
35
jdkSrc/jdk8/sun/security/ssl/SSLKeyDerivation.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
interface SSLKeyDerivation {
|
||||
SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException;
|
||||
}
|
||||
34
jdkSrc/jdk8/sun/security/ssl/SSLKeyDerivationGenerator.java
Normal file
34
jdkSrc/jdk8/sun/security/ssl/SSLKeyDerivationGenerator.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
interface SSLKeyDerivationGenerator {
|
||||
SSLKeyDerivation createKeyDerivation(HandshakeContext context,
|
||||
SecretKey secretKey) throws IOException;
|
||||
}
|
||||
633
jdkSrc/jdk8/sun/security/ssl/SSLKeyExchange.java
Normal file
633
jdkSrc/jdk8/sun/security/ssl/SSLKeyExchange.java
Normal file
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, Azul Systems, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import sun.security.ssl.DHKeyExchange.DHEPossession;
|
||||
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
|
||||
import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
|
||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||
import sun.security.ssl.X509Authentication.X509Possession;
|
||||
|
||||
final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||
SSLHandshakeBinding {
|
||||
private final SSLAuthentication authentication;
|
||||
private final SSLKeyAgreement keyAgreement;
|
||||
|
||||
SSLKeyExchange(X509Authentication authentication,
|
||||
SSLKeyAgreement keyAgreement) {
|
||||
this.authentication = authentication;
|
||||
this.keyAgreement = keyAgreement;
|
||||
}
|
||||
|
||||
SSLPossession[] createPossessions(HandshakeContext context) {
|
||||
// authentication
|
||||
SSLPossession authPossession = null;
|
||||
if (authentication != null) {
|
||||
authPossession = authentication.createPossession(context);
|
||||
if (authPossession == null) {
|
||||
return new SSLPossession[0];
|
||||
} else if (context instanceof ServerHandshakeContext) {
|
||||
// The authentication information may be used further for
|
||||
// key agreement parameters negotiation.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
shc.interimAuthn = authPossession;
|
||||
}
|
||||
}
|
||||
|
||||
// key agreement
|
||||
SSLPossession kaPossession;
|
||||
if (keyAgreement == T12KeyAgreement.RSA_EXPORT) {
|
||||
// a special case
|
||||
X509Possession x509Possession = (X509Possession)authPossession;
|
||||
if (JsseJce.getRSAKeyLength(
|
||||
x509Possession.popCerts[0].getPublicKey()) > 512) {
|
||||
kaPossession = keyAgreement.createPossession(context);
|
||||
|
||||
if (kaPossession == null) {
|
||||
return new SSLPossession[0];
|
||||
} else {
|
||||
return authentication != null ?
|
||||
new SSLPossession[] {authPossession, kaPossession} :
|
||||
new SSLPossession[] {kaPossession};
|
||||
}
|
||||
} else {
|
||||
return authentication != null ?
|
||||
new SSLPossession[] {authPossession} :
|
||||
new SSLPossession[0];
|
||||
}
|
||||
} else {
|
||||
kaPossession = keyAgreement.createPossession(context);
|
||||
if (kaPossession == null) {
|
||||
// special cases
|
||||
if (keyAgreement == T12KeyAgreement.RSA ||
|
||||
keyAgreement == T12KeyAgreement.ECDH) {
|
||||
return authentication != null ?
|
||||
new SSLPossession[] {authPossession} :
|
||||
new SSLPossession[0];
|
||||
} else {
|
||||
return new SSLPossession[0];
|
||||
}
|
||||
} else {
|
||||
return authentication != null ?
|
||||
new SSLPossession[] {authPossession, kaPossession} :
|
||||
new SSLPossession[] {kaPossession};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext handshakeContext) throws IOException {
|
||||
return keyAgreement.createKeyDerivation(handshakeContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake[] getRelatedHandshakers(
|
||||
HandshakeContext handshakeContext) {
|
||||
SSLHandshake[] auHandshakes;
|
||||
if (authentication != null) {
|
||||
auHandshakes =
|
||||
authentication.getRelatedHandshakers(handshakeContext);
|
||||
} else {
|
||||
auHandshakes = null;
|
||||
}
|
||||
|
||||
SSLHandshake[] kaHandshakes =
|
||||
keyAgreement.getRelatedHandshakers(handshakeContext);
|
||||
|
||||
if (auHandshakes == null || auHandshakes.length == 0) {
|
||||
return kaHandshakes;
|
||||
} else if (kaHandshakes == null || kaHandshakes.length == 0) {
|
||||
return auHandshakes;
|
||||
} else {
|
||||
SSLHandshake[] producers = Arrays.copyOf(
|
||||
auHandshakes, auHandshakes.length + kaHandshakes.length);
|
||||
System.arraycopy(kaHandshakes, 0,
|
||||
producers, auHandshakes.length, kaHandshakes.length);
|
||||
return producers;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
|
||||
HandshakeContext handshakeContext) {
|
||||
Map.Entry<Byte, HandshakeProducer>[] auProducers;
|
||||
if (authentication != null) {
|
||||
auProducers =
|
||||
authentication.getHandshakeProducers(handshakeContext);
|
||||
} else {
|
||||
auProducers = null;
|
||||
}
|
||||
|
||||
Map.Entry<Byte, HandshakeProducer>[] kaProducers =
|
||||
keyAgreement.getHandshakeProducers(handshakeContext);
|
||||
|
||||
if (auProducers == null || auProducers.length == 0) {
|
||||
return kaProducers;
|
||||
} else if (kaProducers == null || kaProducers.length == 0) {
|
||||
return auProducers;
|
||||
} else {
|
||||
Map.Entry<Byte, HandshakeProducer>[] producers = Arrays.copyOf(
|
||||
auProducers, auProducers.length + kaProducers.length);
|
||||
System.arraycopy(kaProducers, 0,
|
||||
producers, auProducers.length, kaProducers.length);
|
||||
return producers;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
|
||||
HandshakeContext handshakeContext) {
|
||||
Map.Entry<Byte, SSLConsumer>[] auConsumers;
|
||||
if (authentication != null) {
|
||||
auConsumers =
|
||||
authentication.getHandshakeConsumers(handshakeContext);
|
||||
} else {
|
||||
auConsumers = null;
|
||||
}
|
||||
|
||||
Map.Entry<Byte, SSLConsumer>[] kaConsumers =
|
||||
keyAgreement.getHandshakeConsumers(handshakeContext);
|
||||
|
||||
if (auConsumers == null || auConsumers.length == 0) {
|
||||
return kaConsumers;
|
||||
} else if (kaConsumers == null || kaConsumers.length == 0) {
|
||||
return auConsumers;
|
||||
} else {
|
||||
Map.Entry<Byte, SSLConsumer>[] producers = Arrays.copyOf(
|
||||
auConsumers, auConsumers.length + kaConsumers.length);
|
||||
System.arraycopy(kaConsumers, 0,
|
||||
producers, auConsumers.length, kaConsumers.length);
|
||||
return producers;
|
||||
}
|
||||
}
|
||||
|
||||
// SSL 3.0 - TLS 1.2
|
||||
static SSLKeyExchange valueOf(
|
||||
CipherSuite.KeyExchange keyExchange,
|
||||
ProtocolVersion protocolVersion) {
|
||||
if (keyExchange == null || protocolVersion == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (keyExchange) {
|
||||
case K_RSA:
|
||||
return SSLKeyExRSA.KE;
|
||||
case K_RSA_EXPORT:
|
||||
return SSLKeyExRSAExport.KE;
|
||||
case K_DHE_DSS:
|
||||
return SSLKeyExDHEDSS.KE;
|
||||
case K_DHE_DSS_EXPORT:
|
||||
return SSLKeyExDHEDSSExport.KE;
|
||||
case K_DHE_RSA:
|
||||
if (protocolVersion.useTLS12PlusSpec()) { // TLS 1.2
|
||||
return SSLKeyExDHERSAOrPSS.KE;
|
||||
} else { // SSL 3.0, TLS 1.0/1.1
|
||||
return SSLKeyExDHERSA.KE;
|
||||
}
|
||||
case K_DHE_RSA_EXPORT:
|
||||
return SSLKeyExDHERSAExport.KE;
|
||||
case K_DH_ANON:
|
||||
return SSLKeyExDHANON.KE;
|
||||
case K_DH_ANON_EXPORT:
|
||||
return SSLKeyExDHANONExport.KE;
|
||||
case K_ECDH_ECDSA:
|
||||
return SSLKeyExECDHECDSA.KE;
|
||||
case K_ECDH_RSA:
|
||||
return SSLKeyExECDHRSA.KE;
|
||||
case K_ECDHE_ECDSA:
|
||||
return SSLKeyExECDHEECDSA.KE;
|
||||
case K_ECDHE_RSA:
|
||||
if (protocolVersion.useTLS12PlusSpec()) { // TLS 1.2
|
||||
return SSLKeyExECDHERSAOrPSS.KE;
|
||||
} else { // SSL 3.0, TLS 1.0/1.1
|
||||
return SSLKeyExECDHERSA.KE;
|
||||
}
|
||||
case K_ECDH_ANON:
|
||||
return SSLKeyExECDHANON.KE;
|
||||
case K_KRB5:
|
||||
return SSLKeyExKRB5.KE;
|
||||
case K_KRB5_EXPORT:
|
||||
return SSLKeyExKRB5EXPORT.KE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TLS 1.3
|
||||
static SSLKeyExchange valueOf(NamedGroup namedGroup) {
|
||||
SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup);
|
||||
if (ka != null) {
|
||||
return new SSLKeyExchange(
|
||||
null, T13KeyAgreement.valueOf(namedGroup));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class SSLKeyExRSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA, T12KeyAgreement.RSA);
|
||||
}
|
||||
|
||||
private static class SSLKeyExRSAExport {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHEDSS {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.DSA, T12KeyAgreement.DHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHEDSSExport {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHERSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA, T12KeyAgreement.DHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHERSAOrPSS {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHERSAExport {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHANON {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
null, T12KeyAgreement.DHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExDHANONExport {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
null, T12KeyAgreement.DHE_EXPORT);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHECDSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.EC, T12KeyAgreement.ECDH);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHRSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.EC, T12KeyAgreement.ECDH);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHEECDSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.EC, T12KeyAgreement.ECDHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHERSA {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA, T12KeyAgreement.ECDHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHERSAOrPSS {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExECDHANON {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
null, T12KeyAgreement.ECDHE);
|
||||
}
|
||||
|
||||
private static class SSLKeyExKRB5 {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
null, T12KeyAgreement.KRB5);
|
||||
}
|
||||
|
||||
private static class SSLKeyExKRB5EXPORT {
|
||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||
null, T12KeyAgreement.KRB5_EXPORT);
|
||||
}
|
||||
|
||||
private enum T12KeyAgreement implements SSLKeyAgreement {
|
||||
RSA ("rsa", null,
|
||||
RSAKeyExchange.kaGenerator),
|
||||
RSA_EXPORT ("rsa_export", RSAKeyExchange.poGenerator,
|
||||
RSAKeyExchange.kaGenerator),
|
||||
DHE ("dhe", DHKeyExchange.poGenerator,
|
||||
DHKeyExchange.kaGenerator),
|
||||
DHE_EXPORT ("dhe_export", DHKeyExchange.poExportableGenerator,
|
||||
DHKeyExchange.kaGenerator),
|
||||
ECDH ("ecdh", null,
|
||||
ECDHKeyExchange.ecdhKAGenerator),
|
||||
ECDHE ("ecdhe", ECDHKeyExchange.poGenerator,
|
||||
ECDHKeyExchange.ecdheKAGenerator),
|
||||
KRB5 ("krb5", KrbKeyExchange.poGenerator,
|
||||
KrbKeyExchange.kaGenerator),
|
||||
KRB5_EXPORT ("krb5_export", KrbKeyExchange.poGenerator,
|
||||
KrbKeyExchange.kaGenerator);
|
||||
|
||||
final String name;
|
||||
final SSLPossessionGenerator possessionGenerator;
|
||||
final SSLKeyAgreementGenerator keyAgreementGenerator;
|
||||
|
||||
T12KeyAgreement(String name,
|
||||
SSLPossessionGenerator possessionGenerator,
|
||||
SSLKeyAgreementGenerator keyAgreementGenerator) {
|
||||
this.name = name;
|
||||
this.possessionGenerator = possessionGenerator;
|
||||
this.keyAgreementGenerator = keyAgreementGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext context) {
|
||||
if (possessionGenerator != null) {
|
||||
return possessionGenerator.createPossession(context);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext context) throws IOException {
|
||||
return keyAgreementGenerator.createKeyDerivation(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLHandshake[] getRelatedHandshakers(
|
||||
HandshakeContext handshakeContext) {
|
||||
if (!handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
if (this.possessionGenerator != null) {
|
||||
return new SSLHandshake[] {
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new SSLHandshake[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
|
||||
HandshakeContext handshakeContext) {
|
||||
if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
|
||||
}
|
||||
|
||||
if (handshakeContext.sslConfig.isClientMode) {
|
||||
switch (this) {
|
||||
case RSA:
|
||||
case RSA_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
RSAClientKeyExchange.rsaHandshakeProducer
|
||||
)
|
||||
});
|
||||
|
||||
case DHE:
|
||||
case DHE_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<Byte, HandshakeProducer>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
DHClientKeyExchange.dhHandshakeProducer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDH:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
ECDHClientKeyExchange.ecdhHandshakeProducer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDHE:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
ECDHClientKeyExchange.ecdheHandshakeProducer
|
||||
)
|
||||
});
|
||||
case KRB5:
|
||||
case KRB5_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
KrbClientKeyExchange.krbHandshakeProducer
|
||||
)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
switch (this) {
|
||||
case RSA_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
RSAServerKeyExchange.rsaHandshakeProducer
|
||||
)
|
||||
});
|
||||
|
||||
case DHE:
|
||||
case DHE_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
DHServerKeyExchange.dhHandshakeProducer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDHE:
|
||||
return (Map.Entry<Byte,
|
||||
HandshakeProducer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
ECDHServerKeyExchange.ecdheHandshakeProducer
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (Map.Entry<Byte, HandshakeProducer>[])(new Map.Entry[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
|
||||
HandshakeContext handshakeContext) {
|
||||
if (handshakeContext.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
|
||||
}
|
||||
|
||||
if (handshakeContext.sslConfig.isClientMode) {
|
||||
switch (this) {
|
||||
case RSA_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
RSAServerKeyExchange.rsaHandshakeConsumer
|
||||
)
|
||||
});
|
||||
|
||||
case DHE:
|
||||
case DHE_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
DHServerKeyExchange.dhHandshakeConsumer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDHE:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.SERVER_KEY_EXCHANGE.id,
|
||||
ECDHServerKeyExchange.ecdheHandshakeConsumer
|
||||
)
|
||||
});
|
||||
}
|
||||
} else {
|
||||
switch (this) {
|
||||
case RSA:
|
||||
case RSA_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
RSAClientKeyExchange.rsaHandshakeConsumer
|
||||
)
|
||||
});
|
||||
|
||||
case DHE:
|
||||
case DHE_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
DHClientKeyExchange.dhHandshakeConsumer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDH:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
ECDHClientKeyExchange.ecdhHandshakeConsumer
|
||||
)
|
||||
});
|
||||
|
||||
case ECDHE:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
ECDHClientKeyExchange.ecdheHandshakeConsumer
|
||||
)
|
||||
});
|
||||
case KRB5:
|
||||
case KRB5_EXPORT:
|
||||
return (Map.Entry<Byte,
|
||||
SSLConsumer>[])(new Map.Entry[] {
|
||||
new SimpleImmutableEntry<>(
|
||||
SSLHandshake.CLIENT_KEY_EXCHANGE.id,
|
||||
KrbClientKeyExchange.krbHandshakeConsumer
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (Map.Entry<Byte, SSLConsumer>[])(new Map.Entry[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class T13KeyAgreement implements SSLKeyAgreement {
|
||||
private final NamedGroup namedGroup;
|
||||
static final Map<NamedGroup, T13KeyAgreement>
|
||||
supportedKeyShares = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (NamedGroup namedGroup :
|
||||
SupportedGroups.supportedNamedGroups) {
|
||||
supportedKeyShares.put(
|
||||
namedGroup, new T13KeyAgreement(namedGroup));
|
||||
}
|
||||
}
|
||||
|
||||
private T13KeyAgreement(NamedGroup namedGroup) {
|
||||
this.namedGroup = namedGroup;
|
||||
}
|
||||
|
||||
static T13KeyAgreement valueOf(NamedGroup namedGroup) {
|
||||
return supportedKeyShares.get(namedGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLPossession createPossession(HandshakeContext hc) {
|
||||
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||
return new ECDHEPossession(
|
||||
namedGroup, hc.sslContext.getSecureRandom());
|
||||
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||
return new DHEPossession(
|
||||
namedGroup, hc.sslContext.getSecureRandom());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(
|
||||
HandshakeContext hc) throws IOException {
|
||||
if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
|
||||
return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc);
|
||||
} else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) {
|
||||
return DHKeyExchange.kaGenerator.createKeyDerivation(hc);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
594
jdkSrc/jdk8/sun/security/ssl/SSLLogger.java
Normal file
594
jdkSrc/jdk8/sun/security/ssl/SSLLogger.java
Normal file
@@ -0,0 +1,594 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.Extension;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.misc.HexDumpEncoder;
|
||||
import sun.security.x509.*;
|
||||
|
||||
/**
|
||||
* Implementation of SSL logger.
|
||||
*
|
||||
* If the system property "javax.net.debug" is not defined, the debug logging
|
||||
* is turned off. If the system property "javax.net.debug" is defined as
|
||||
* empty, the debug logger is specified by System.getLogger("javax.net.ssl"),
|
||||
* and applications can customize and configure the logger or use external
|
||||
* logging mechanisms. If the system property "javax.net.debug" is defined
|
||||
* and non-empty, a private debug logger implemented in this class is used.
|
||||
*/
|
||||
public final class SSLLogger {
|
||||
private static final Logger logger;
|
||||
private static final String property;
|
||||
public static final boolean isOn;
|
||||
|
||||
static {
|
||||
String p = GetPropertyAction.privilegedGetProperty("javax.net.debug");
|
||||
if (p != null) {
|
||||
if (p.isEmpty()) {
|
||||
property = "";
|
||||
logger = Logger.getLogger("javax.net.ssl");
|
||||
} else {
|
||||
property = p.toLowerCase(Locale.ENGLISH);
|
||||
if (property.equals("help")) {
|
||||
help();
|
||||
}
|
||||
|
||||
logger = new SSLConsoleLogger("javax.net.ssl", p);
|
||||
}
|
||||
isOn = true;
|
||||
} else {
|
||||
property = null;
|
||||
logger = null;
|
||||
isOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void help() {
|
||||
System.err.println();
|
||||
System.err.println("help print the help messages");
|
||||
System.err.println("expand expand debugging information");
|
||||
System.err.println();
|
||||
System.err.println("all turn on all debugging");
|
||||
System.err.println("ssl turn on ssl debugging");
|
||||
System.err.println();
|
||||
System.err.println("The following can be used with ssl:");
|
||||
System.err.println("\trecord enable per-record tracing");
|
||||
System.err.println("\thandshake print each handshake message");
|
||||
System.err.println("\tkeygen print key generation data");
|
||||
System.err.println("\tsession print session activity");
|
||||
System.err.println("\tdefaultctx print default SSL initialization");
|
||||
System.err.println("\tsslctx print SSLContext tracing");
|
||||
System.err.println("\tsessioncache print session cache tracing");
|
||||
System.err.println("\tkeymanager print key manager tracing");
|
||||
System.err.println("\ttrustmanager print trust manager tracing");
|
||||
System.err.println("\tpluggability print pluggability tracing");
|
||||
System.err.println();
|
||||
System.err.println("\thandshake debugging can be widened with:");
|
||||
System.err.println("\tdata hex dump of each handshake message");
|
||||
System.err.println("\tverbose verbose handshake message printing");
|
||||
System.err.println();
|
||||
System.err.println("\trecord debugging can be widened with:");
|
||||
System.err.println("\tplaintext hex dump of record plaintext");
|
||||
System.err.println("\tpacket print raw SSL/TLS packets");
|
||||
System.err.println();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the "javax.net.debug" property contains the
|
||||
* debug check points, or System.Logger is used.
|
||||
*/
|
||||
public static boolean isOn(String checkPoints) {
|
||||
if (property == null) { // debugging is turned off
|
||||
return false;
|
||||
} else if (property.isEmpty()) { // use System.Logger
|
||||
return true;
|
||||
} // use provider logger
|
||||
|
||||
String[] options = checkPoints.split(",");
|
||||
for (String option : options) {
|
||||
option = option.trim();
|
||||
if (!SSLLogger.hasOption(option)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasOption(String option) {
|
||||
option = option.toLowerCase(Locale.ENGLISH);
|
||||
if (property.contains("all")) {
|
||||
return true;
|
||||
} else {
|
||||
int offset = property.indexOf("ssl");
|
||||
if (offset != -1 && property.indexOf("sslctx", offset) != -1) {
|
||||
// don't enable data and plaintext options by default
|
||||
if (!(option.equals("data")
|
||||
|| option.equals("packet")
|
||||
|| option.equals("plaintext"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return property.contains(option);
|
||||
}
|
||||
|
||||
public static void severe(String msg, Object... params) {
|
||||
SSLLogger.log(Level.SEVERE, msg, params);
|
||||
}
|
||||
|
||||
public static void warning(String msg, Object... params) {
|
||||
SSLLogger.log(Level.WARNING, msg, params);
|
||||
}
|
||||
|
||||
public static void info(String msg, Object... params) {
|
||||
SSLLogger.log(Level.INFO, msg, params);
|
||||
}
|
||||
|
||||
public static void fine(String msg, Object... params) {
|
||||
SSLLogger.log(Level.FINE, msg, params);
|
||||
}
|
||||
|
||||
public static void finer(String msg, Object... params) {
|
||||
SSLLogger.log(Level.FINER, msg, params);
|
||||
}
|
||||
|
||||
public static void finest(String msg, Object... params) {
|
||||
SSLLogger.log(Level.ALL, msg, params);
|
||||
}
|
||||
|
||||
private static void log(Level level, String msg, Object... params) {
|
||||
if (logger != null && logger.isLoggable(level)) {
|
||||
if (params == null || params.length == 0) {
|
||||
logger.log(level, msg);
|
||||
} else {
|
||||
try {
|
||||
String formatted =
|
||||
SSLSimpleFormatter.formatParameters(params);
|
||||
logger.log(level, msg, formatted);
|
||||
} catch (Exception exp) {
|
||||
// ignore it, just for debugging.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String toString(Object... params) {
|
||||
try {
|
||||
return SSLSimpleFormatter.formatParameters(params);
|
||||
} catch (Exception exp) {
|
||||
return "unexpected exception thrown: " + exp.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SSLConsoleLogger extends Logger {
|
||||
private final String loggerName;
|
||||
private final boolean useCompactFormat;
|
||||
|
||||
SSLConsoleLogger(String loggerName, String options) {
|
||||
super(loggerName, null);
|
||||
this.loggerName = loggerName;
|
||||
options = options.toLowerCase(Locale.ENGLISH);
|
||||
this.useCompactFormat = !options.contains("expand");
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return loggerName;
|
||||
}
|
||||
|
||||
public boolean isLoggable(Level level) {
|
||||
return (level != Level.OFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(LogRecord record) {
|
||||
if (isLoggable(record.getLevel())) {
|
||||
try {
|
||||
String formatted = null;
|
||||
if (record.getThrown() != null) {
|
||||
formatted =
|
||||
SSLSimpleFormatter.format(this, record.getLevel(),
|
||||
record.getMessage(),
|
||||
record.getThrown());
|
||||
} else {
|
||||
formatted =
|
||||
SSLSimpleFormatter.format(this, record.getLevel(),
|
||||
record.getMessage(),
|
||||
record.getParameters());
|
||||
}
|
||||
System.err.write(formatted.getBytes("UTF-8"));
|
||||
} catch (Exception exp) {
|
||||
// ignore it, just for debugging.
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class SSLSimpleFormatter {
|
||||
private static final ThreadLocal<SimpleDateFormat> dateFormat =
|
||||
new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override protected SimpleDateFormat initialValue() {
|
||||
return new SimpleDateFormat(
|
||||
"yyyy-MM-dd kk:mm:ss.SSS z", Locale.ENGLISH);
|
||||
}
|
||||
};
|
||||
|
||||
private static final MessageFormat basicCertFormat = new MessageFormat(
|
||||
"\"version\" : \"v{0}\",\n" +
|
||||
"\"serial number\" : \"{1}\",\n" +
|
||||
"\"signature algorithm\": \"{2}\",\n" +
|
||||
"\"issuer\" : \"{3}\",\n" +
|
||||
"\"not before\" : \"{4}\",\n" +
|
||||
"\"not after\" : \"{5}\",\n" +
|
||||
"\"subject\" : \"{6}\",\n" +
|
||||
"\"subject public key\" : \"{7}\"\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
private static final MessageFormat extendedCertFormart =
|
||||
new MessageFormat(
|
||||
"\"version\" : \"v{0}\",\n" +
|
||||
"\"serial number\" : \"{1}\",\n" +
|
||||
"\"signature algorithm\": \"{2}\",\n" +
|
||||
"\"issuer\" : \"{3}\",\n" +
|
||||
"\"not before\" : \"{4}\",\n" +
|
||||
"\"not after\" : \"{5}\",\n" +
|
||||
"\"subject\" : \"{6}\",\n" +
|
||||
"\"subject public key\" : \"{7}\",\n" +
|
||||
"\"extensions\" : [\n" +
|
||||
"{8}\n" +
|
||||
"]\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
//
|
||||
// private static MessageFormat certExtFormat = new MessageFormat(
|
||||
// "{0} [{1}] '{'\n" +
|
||||
// " critical: {2}\n" +
|
||||
// " value: {3}\n" +
|
||||
// "'}'",
|
||||
// Locale.ENGLISH);
|
||||
//
|
||||
|
||||
private static final MessageFormat messageFormatNoParas =
|
||||
new MessageFormat(
|
||||
"'{'\n" +
|
||||
" \"logger\" : \"{0}\",\n" +
|
||||
" \"level\" : \"{1}\",\n" +
|
||||
" \"thread id\" : \"{2}\",\n" +
|
||||
" \"thread name\" : \"{3}\",\n" +
|
||||
" \"time\" : \"{4}\",\n" +
|
||||
" \"caller\" : \"{5}\",\n" +
|
||||
" \"message\" : \"{6}\"\n" +
|
||||
"'}'\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
private static final MessageFormat messageCompactFormatNoParas =
|
||||
new MessageFormat(
|
||||
"{0}|{1}|{2}|{3}|{4}|{5}|{6}\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
private static final MessageFormat messageFormatWithParas =
|
||||
new MessageFormat(
|
||||
"'{'\n" +
|
||||
" \"logger\" : \"{0}\",\n" +
|
||||
" \"level\" : \"{1}\",\n" +
|
||||
" \"thread id\" : \"{2}\",\n" +
|
||||
" \"thread name\" : \"{3}\",\n" +
|
||||
" \"time\" : \"{4}\",\n" +
|
||||
" \"caller\" : \"{5}\",\n" +
|
||||
" \"message\" : \"{6}\",\n" +
|
||||
" \"specifics\" : [\n" +
|
||||
"{7}\n" +
|
||||
" ]\n" +
|
||||
"'}'\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
private static final MessageFormat messageCompactFormatWithParas =
|
||||
new MessageFormat(
|
||||
"{0}|{1}|{2}|{3}|{4}|{5}|{6} (\n" +
|
||||
"{7}\n" +
|
||||
")\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
private static final MessageFormat keyObjectFormat = new MessageFormat(
|
||||
"\"{0}\" : '{'\n" +
|
||||
"{1}" +
|
||||
"'}'\n",
|
||||
Locale.ENGLISH);
|
||||
|
||||
// INFO: [TH: 123450] 2011-08-20 23:12:32.3225 PDT
|
||||
// log message
|
||||
// log message
|
||||
// ...
|
||||
private static String format(SSLConsoleLogger logger, Level level,
|
||||
String message, Object ... parameters) {
|
||||
|
||||
if (parameters == null || parameters.length == 0) {
|
||||
Object[] messageFields = {
|
||||
logger.loggerName,
|
||||
level.getName(),
|
||||
Utilities.toHexString(Thread.currentThread().getId()),
|
||||
Thread.currentThread().getName(),
|
||||
dateFormat.get().format(new Date(System.currentTimeMillis())),
|
||||
formatCaller(),
|
||||
message
|
||||
};
|
||||
|
||||
if (logger.useCompactFormat) {
|
||||
return messageCompactFormatNoParas.format(messageFields);
|
||||
} else {
|
||||
return messageFormatNoParas.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
Object[] messageFields = {
|
||||
logger.loggerName,
|
||||
level.getName(),
|
||||
Utilities.toHexString(Thread.currentThread().getId()),
|
||||
Thread.currentThread().getName(),
|
||||
dateFormat.get().format(new Date(System.currentTimeMillis())),
|
||||
formatCaller(),
|
||||
message,
|
||||
(logger.useCompactFormat ?
|
||||
formatParameters(parameters) :
|
||||
Utilities.indent(formatParameters(parameters)))
|
||||
};
|
||||
|
||||
if (logger.useCompactFormat) {
|
||||
return messageCompactFormatWithParas.format(messageFields);
|
||||
} else {
|
||||
return messageFormatWithParas.format(messageFields);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatCaller() {
|
||||
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
|
||||
for (int i=1; i<stElements.length; i++) {
|
||||
StackTraceElement ste = stElements[i];
|
||||
if (!ste.getClassName().startsWith(SSLLogger.class.getName()) &&
|
||||
!ste.getClassName().startsWith(Logger.class.getName())) {
|
||||
return ste.getFileName() + ":" + ste.getLineNumber();
|
||||
}
|
||||
}
|
||||
return "unknown caller";
|
||||
}
|
||||
|
||||
private static String formatParameters(Object ... parameters) {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
boolean isFirst = true;
|
||||
for (Object parameter : parameters) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
builder.append(",\n");
|
||||
}
|
||||
|
||||
if (parameter instanceof Throwable) {
|
||||
builder.append(formatThrowable((Throwable)parameter));
|
||||
} else if (parameter instanceof Certificate) {
|
||||
builder.append(formatCertificate((Certificate)parameter));
|
||||
} else if (parameter instanceof ByteArrayInputStream) {
|
||||
builder.append(formatByteArrayInputStream(
|
||||
(ByteArrayInputStream)parameter));
|
||||
} else if (parameter instanceof ByteBuffer) {
|
||||
builder.append(formatByteBuffer((ByteBuffer)parameter));
|
||||
} else if (parameter instanceof byte[]) {
|
||||
builder.append(formatByteArrayInputStream(
|
||||
new ByteArrayInputStream((byte[])parameter)));
|
||||
} else if (parameter instanceof Map.Entry) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map.Entry<String, ?> mapParameter =
|
||||
(Map.Entry<String, ?>)parameter;
|
||||
builder.append(formatMapEntry(mapParameter));
|
||||
} else {
|
||||
builder.append(formatObject(parameter));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
// "throwable": {
|
||||
// ...
|
||||
// }
|
||||
private static String formatThrowable(Throwable throwable) {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
try (PrintStream out = new PrintStream(bytesOut)) {
|
||||
throwable.printStackTrace(out);
|
||||
builder.append(Utilities.indent(bytesOut.toString()));
|
||||
}
|
||||
Object[] fields = {
|
||||
"throwable",
|
||||
builder.toString()
|
||||
};
|
||||
|
||||
return keyObjectFormat.format(fields);
|
||||
}
|
||||
|
||||
// "certificate": {
|
||||
// ...
|
||||
// }
|
||||
private static String formatCertificate(Certificate certificate) {
|
||||
|
||||
if (!(certificate instanceof X509Certificate)) {
|
||||
return Utilities.indent(certificate.toString());
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
try {
|
||||
X509CertImpl x509 =
|
||||
X509CertImpl.toImpl((X509Certificate)certificate);
|
||||
X509CertInfo certInfo =
|
||||
(X509CertInfo)x509.get(X509CertImpl.NAME + "." +
|
||||
X509CertImpl.INFO);
|
||||
CertificateExtensions certExts = (CertificateExtensions)
|
||||
certInfo.get(X509CertInfo.EXTENSIONS);
|
||||
if (certExts == null) {
|
||||
Object[] certFields = {
|
||||
x509.getVersion(),
|
||||
Utilities.toHexString(
|
||||
x509.getSerialNumber().toByteArray()),
|
||||
x509.getSigAlgName(),
|
||||
x509.getIssuerX500Principal().toString(),
|
||||
dateFormat.get().format(x509.getNotBefore()),
|
||||
dateFormat.get().format(x509.getNotAfter()),
|
||||
x509.getSubjectX500Principal().toString(),
|
||||
x509.getPublicKey().getAlgorithm()
|
||||
};
|
||||
builder.append(Utilities.indent(
|
||||
basicCertFormat.format(certFields)));
|
||||
} else {
|
||||
StringBuilder extBuilder = new StringBuilder(512);
|
||||
boolean isFirst = true;
|
||||
for (Extension certExt : certExts.getAllExtensions()) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
extBuilder.append(",\n");
|
||||
}
|
||||
extBuilder.append("{\n" +
|
||||
Utilities.indent(certExt.toString()) + "\n}");
|
||||
}
|
||||
Object[] certFields = {
|
||||
x509.getVersion(),
|
||||
Utilities.toHexString(
|
||||
x509.getSerialNumber().toByteArray()),
|
||||
x509.getSigAlgName(),
|
||||
x509.getIssuerX500Principal().toString(),
|
||||
dateFormat.get().format(x509.getNotBefore()),
|
||||
dateFormat.get().format(x509.getNotAfter()),
|
||||
x509.getSubjectX500Principal().toString(),
|
||||
x509.getPublicKey().getAlgorithm(),
|
||||
Utilities.indent(extBuilder.toString())
|
||||
};
|
||||
builder.append(Utilities.indent(
|
||||
extendedCertFormart.format(certFields)));
|
||||
}
|
||||
} catch (Exception ce) {
|
||||
// ignore the exception
|
||||
}
|
||||
|
||||
Object[] fields = {
|
||||
"certificate",
|
||||
builder.toString()
|
||||
};
|
||||
|
||||
return Utilities.indent(keyObjectFormat.format(fields));
|
||||
}
|
||||
|
||||
private static String formatByteArrayInputStream(
|
||||
ByteArrayInputStream bytes) {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
|
||||
try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
hexEncoder.encodeBuffer(bytes, bytesOut);
|
||||
|
||||
builder.append(Utilities.indent(bytesOut.toString()));
|
||||
} catch (IOException ioe) {
|
||||
// ignore it, just for debugging.
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String formatByteBuffer(ByteBuffer byteBuffer) {
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
try (ByteArrayOutputStream bytesOut = new ByteArrayOutputStream()) {
|
||||
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||
hexEncoder.encodeBuffer(byteBuffer.duplicate(), bytesOut);
|
||||
builder.append(Utilities.indent(bytesOut.toString()));
|
||||
} catch (IOException ioe) {
|
||||
// ignore it, just for debugging.
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String formatMapEntry(Map.Entry<String, ?> entry) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
String formatted;
|
||||
if (value instanceof String) {
|
||||
// "key": "value"
|
||||
formatted = "\"" + key + "\": \"" + (String)value + "\"";
|
||||
} else if (value instanceof String[]) {
|
||||
// "key": [ "string a",
|
||||
// "string b",
|
||||
// "string c"
|
||||
// ]
|
||||
StringBuilder builder = new StringBuilder(512);
|
||||
String[] strings = (String[])value;
|
||||
builder.append("\"" + key + "\": [\n");
|
||||
for (String string : strings) {
|
||||
builder.append(" \"" + string + "\"");
|
||||
if (string != strings[strings.length - 1]) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.append(" ]");
|
||||
|
||||
formatted = builder.toString();
|
||||
} else if (value instanceof byte[]) {
|
||||
formatted = "\"" + key + "\": \"" +
|
||||
Utilities.toHexString((byte[])value) + "\"";
|
||||
} else if (value instanceof Byte) {
|
||||
formatted = "\"" + key + "\": \"" +
|
||||
Utilities.toHexString((byte)value) + "\"";
|
||||
} else {
|
||||
formatted = "\"" + key + "\": " +
|
||||
"\"" + value.toString() + "\"";
|
||||
}
|
||||
|
||||
return Utilities.indent(formatted);
|
||||
}
|
||||
|
||||
private static String formatObject(Object obj) {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
145
jdkSrc/jdk8/sun/security/ssl/SSLMasterKeyDerivation.java
Normal file
145
jdkSrc/jdk8/sun/security/ssl/SSLMasterKeyDerivation.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import sun.security.internal.spec.TlsMasterSecretParameterSpec;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
|
||||
|
||||
enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
SSL30 ("kdf_ssl30"),
|
||||
TLS10 ("kdf_tls10"),
|
||||
TLS12 ("kdf_tls12");
|
||||
|
||||
final String name;
|
||||
|
||||
private SSLMasterKeyDerivation(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static SSLMasterKeyDerivation valueOf(ProtocolVersion protocolVersion) {
|
||||
switch (protocolVersion) {
|
||||
case SSL30:
|
||||
return SSLMasterKeyDerivation.SSL30;
|
||||
case TLS10:
|
||||
case TLS11:
|
||||
return SSLMasterKeyDerivation.TLS10;
|
||||
case TLS12:
|
||||
return SSLMasterKeyDerivation.TLS12;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SSLKeyDerivation createKeyDerivation(HandshakeContext context,
|
||||
SecretKey secretKey) throws IOException {
|
||||
return new LegacyMasterKeyDerivation(context, secretKey);
|
||||
}
|
||||
|
||||
// Note, we may use different key derivation implementation in the future.
|
||||
private static final
|
||||
class LegacyMasterKeyDerivation implements SSLKeyDerivation {
|
||||
|
||||
final HandshakeContext context;
|
||||
final SecretKey preMasterSecret;
|
||||
|
||||
LegacyMasterKeyDerivation(
|
||||
HandshakeContext context, SecretKey preMasterSecret) {
|
||||
this.context = context;
|
||||
this.preMasterSecret = preMasterSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
|
||||
CipherSuite cipherSuite = context.negotiatedCipherSuite;
|
||||
ProtocolVersion protocolVersion = context.negotiatedProtocol;
|
||||
|
||||
// What algs/params do we need to use?
|
||||
String masterAlg;
|
||||
HashAlg hashAlg;
|
||||
|
||||
byte majorVersion = protocolVersion.major;
|
||||
byte minorVersion = protocolVersion.minor;
|
||||
if (protocolVersion.id >= ProtocolVersion.TLS12.id) {
|
||||
masterAlg = "SunTls12MasterSecret";
|
||||
hashAlg = cipherSuite.hashAlg;
|
||||
} else {
|
||||
masterAlg = "SunTlsMasterSecret";
|
||||
hashAlg = H_NONE;
|
||||
}
|
||||
|
||||
TlsMasterSecretParameterSpec spec;
|
||||
if (context.handshakeSession.useExtendedMasterSecret) {
|
||||
// reset to use the extended master secret algorithm
|
||||
masterAlg = "SunTlsExtendedMasterSecret";
|
||||
|
||||
// For the session hash, use the handshake messages up to and
|
||||
// including the ClientKeyExchange message.
|
||||
context.handshakeHash.utilize();
|
||||
byte[] sessionHash = context.handshakeHash.digest();
|
||||
spec = new TlsMasterSecretParameterSpec(
|
||||
preMasterSecret,
|
||||
(majorVersion & 0xFF), (minorVersion & 0xFF),
|
||||
sessionHash,
|
||||
hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
|
||||
} else {
|
||||
spec = new TlsMasterSecretParameterSpec(
|
||||
preMasterSecret,
|
||||
(majorVersion & 0xFF), (minorVersion & 0xFF),
|
||||
context.clientHelloRandom.randomBytes,
|
||||
context.serverHelloRandom.randomBytes,
|
||||
hashAlg.name, hashAlg.hashLength, hashAlg.blockSize);
|
||||
}
|
||||
|
||||
try {
|
||||
KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
|
||||
kg.init(spec);
|
||||
return kg.generateKey();
|
||||
} catch (InvalidAlgorithmParameterException |
|
||||
NoSuchAlgorithmException iae) {
|
||||
// unlikely to happen, otherwise, must be a provider exception
|
||||
//
|
||||
// For RSA premaster secrets, do not signal a protocol error
|
||||
// due to the Bleichenbacher attack. See comments further down.
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
|
||||
SSLLogger.fine("RSA master secret generation error.", iae);
|
||||
}
|
||||
throw new ProviderException(iae);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
jdkSrc/jdk8/sun/security/ssl/SSLPossession.java
Normal file
32
jdkSrc/jdk8/sun/security/ssl/SSLPossession.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
interface SSLPossession {
|
||||
default byte[] encode() {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
30
jdkSrc/jdk8/sun/security/ssl/SSLPossessionGenerator.java
Normal file
30
jdkSrc/jdk8/sun/security/ssl/SSLPossessionGenerator.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
interface SSLPossessionGenerator {
|
||||
SSLPossession createPossession(HandshakeContext handshakeContext);
|
||||
}
|
||||
33
jdkSrc/jdk8/sun/security/ssl/SSLProducer.java
Normal file
33
jdkSrc/jdk8/sun/security/ssl/SSLProducer.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
interface SSLProducer {
|
||||
// return the encoded producing if it has not been dumped to the context
|
||||
byte[] produce(ConnectionContext context) throws IOException;
|
||||
}
|
||||
93
jdkSrc/jdk8/sun/security/ssl/SSLRecord.java
Normal file
93
jdkSrc/jdk8/sun/security/ssl/SSLRecord.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
/**
|
||||
* SSL/TLS record
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
interface SSLRecord extends Record {
|
||||
|
||||
static final int headerSize = 5; // SSLv3 record header
|
||||
static final int handshakeHeaderSize = 4; // SSLv3 handshake header
|
||||
|
||||
/*
|
||||
* The size of the header plus the max IV length
|
||||
*/
|
||||
static final int headerPlusMaxIVSize =
|
||||
headerSize // header
|
||||
+ maxIVLength; // iv
|
||||
|
||||
/*
|
||||
* The maximum size that may be increased when translating plaintext to
|
||||
* ciphertext fragment.
|
||||
*/
|
||||
static final int maxPlaintextPlusSize =
|
||||
headerSize // header
|
||||
+ maxIVLength // iv
|
||||
+ maxMacSize // MAC or AEAD tag
|
||||
+ maxPadding; // block cipher padding
|
||||
|
||||
/*
|
||||
* SSL has a maximum record size. It's header, (compressed) data,
|
||||
* padding, and a trailer for the message authentication information (MAC
|
||||
* for block and stream ciphers, and message authentication tag for AEAD
|
||||
* ciphers).
|
||||
*
|
||||
* Some compression algorithms have rare cases where they expand the data.
|
||||
* As we don't support compression at this time, leave that out.
|
||||
*/
|
||||
static final int maxRecordSize =
|
||||
headerPlusMaxIVSize // header + iv
|
||||
+ maxDataSize // data
|
||||
+ maxPadding // padding
|
||||
+ maxMacSize; // MAC or AEAD tag
|
||||
|
||||
/*
|
||||
* The maximum large record size.
|
||||
*
|
||||
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
|
||||
* such as Microsoft. We support large incoming fragments.
|
||||
*
|
||||
* The maximum large record size is defined as maxRecordSize plus 2^14,
|
||||
* this is the amount OpenSSL is using.
|
||||
*/
|
||||
static final int maxLargeRecordSize =
|
||||
maxRecordSize // Max size with a conforming implementation
|
||||
+ maxDataSize; // extra 2^14 bytes for large data packets.
|
||||
|
||||
/*
|
||||
* We may need to send this SSL v2 "No Cipher" message back, if we
|
||||
* are faced with an SSLv2 "hello" that's not saying "I talk v3".
|
||||
* It's the only one documented in the V2 spec as a fatal error.
|
||||
*/
|
||||
static final byte[] v2NoCipher = {
|
||||
(byte)0x80, (byte)0x03, // unpadded 3 byte record
|
||||
(byte)0x00, // ... error message
|
||||
(byte)0x00, (byte)0x01 // ... NO_CIPHER error
|
||||
};
|
||||
}
|
||||
152
jdkSrc/jdk8/sun/security/ssl/SSLSecretDerivation.java
Normal file
152
jdkSrc/jdk8/sun/security/ssl/SSLSecretDerivation.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
|
||||
final class SSLSecretDerivation implements SSLKeyDerivation {
|
||||
private static final byte[] sha256EmptyDigest = new byte[] {
|
||||
(byte)0xE3, (byte)0xB0, (byte)0xC4, (byte)0x42,
|
||||
(byte)0x98, (byte)0xFC, (byte)0x1C, (byte)0x14,
|
||||
(byte)0x9A, (byte)0xFB, (byte)0xF4, (byte)0xC8,
|
||||
(byte)0x99, (byte)0x6F, (byte)0xB9, (byte)0x24,
|
||||
(byte)0x27, (byte)0xAE, (byte)0x41, (byte)0xE4,
|
||||
(byte)0x64, (byte)0x9B, (byte)0x93, (byte)0x4C,
|
||||
(byte)0xA4, (byte)0x95, (byte)0x99, (byte)0x1B,
|
||||
(byte)0x78, (byte)0x52, (byte)0xB8, (byte)0x55
|
||||
};
|
||||
|
||||
private static final byte[] sha384EmptyDigest = new byte[] {
|
||||
(byte)0x38, (byte)0xB0, (byte)0x60, (byte)0xA7,
|
||||
(byte)0x51, (byte)0xAC, (byte)0x96, (byte)0x38,
|
||||
(byte)0x4C, (byte)0xD9, (byte)0x32, (byte)0x7E,
|
||||
(byte)0xB1, (byte)0xB1, (byte)0xE3, (byte)0x6A,
|
||||
(byte)0x21, (byte)0xFD, (byte)0xB7, (byte)0x11,
|
||||
(byte)0x14, (byte)0xBE, (byte)0x07, (byte)0x43,
|
||||
(byte)0x4C, (byte)0x0C, (byte)0xC7, (byte)0xBF,
|
||||
(byte)0x63, (byte)0xF6, (byte)0xE1, (byte)0xDA,
|
||||
(byte)0x27, (byte)0x4E, (byte)0xDE, (byte)0xBF,
|
||||
(byte)0xE7, (byte)0x6F, (byte)0x65, (byte)0xFB,
|
||||
(byte)0xD5, (byte)0x1A, (byte)0xD2, (byte)0xF1,
|
||||
(byte)0x48, (byte)0x98, (byte)0xB9, (byte)0x5B
|
||||
};
|
||||
|
||||
private final HandshakeContext context;
|
||||
private final String hkdfAlg;
|
||||
private final HashAlg hashAlg;
|
||||
private final SecretKey secret;
|
||||
private final byte[] transcriptHash; // handshake messages transcript hash
|
||||
|
||||
SSLSecretDerivation(
|
||||
HandshakeContext context, SecretKey secret) {
|
||||
this.context = context;
|
||||
this.secret = secret;
|
||||
this.hashAlg = context.negotiatedCipherSuite.hashAlg;
|
||||
this.hkdfAlg =
|
||||
"HKDF-Expand/Hmac" + hashAlg.name.replace("-", "");
|
||||
context.handshakeHash.update();
|
||||
this.transcriptHash = context.handshakeHash.digest();
|
||||
}
|
||||
|
||||
SSLSecretDerivation forContext(HandshakeContext context) {
|
||||
return new SSLSecretDerivation(context, secret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
SecretSchedule ks = SecretSchedule.valueOf(algorithm);
|
||||
try {
|
||||
byte[] expandContext;
|
||||
if (ks == SecretSchedule.TlsSaltSecret) {
|
||||
if (hashAlg == HashAlg.H_SHA256) {
|
||||
expandContext = sha256EmptyDigest;
|
||||
} else if (hashAlg == HashAlg.H_SHA384) {
|
||||
expandContext = sha384EmptyDigest;
|
||||
} else {
|
||||
// unlikely, but please update if more hash algorithm
|
||||
// get supported in the future.
|
||||
throw new SSLHandshakeException(
|
||||
"Unexpected unsupported hash algorithm: " +
|
||||
algorithm);
|
||||
}
|
||||
} else {
|
||||
expandContext = transcriptHash;
|
||||
}
|
||||
byte[] hkdfInfo = createHkdfInfo(ks.label,
|
||||
expandContext, hashAlg.hashLength);
|
||||
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw (SSLHandshakeException) new SSLHandshakeException(
|
||||
"Could not generate secret").initCause(gse);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] createHkdfInfo(
|
||||
byte[] label, byte[] context, int length) {
|
||||
byte[] info = new byte[4 + label.length + context.length];
|
||||
ByteBuffer m = ByteBuffer.wrap(info);
|
||||
try {
|
||||
Record.putInt16(m, length);
|
||||
Record.putBytes8(m, label);
|
||||
Record.putBytes8(m, context);
|
||||
} catch (IOException ioe) {
|
||||
// unlikely
|
||||
throw new RuntimeException("Unexpected exception", ioe);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private enum SecretSchedule {
|
||||
// Note that we use enum name as the key/secret name.
|
||||
TlsSaltSecret ("derived"),
|
||||
TlsExtBinderKey ("ext binder"),
|
||||
TlsResBinderKey ("res binder"),
|
||||
TlsClientEarlyTrafficSecret ("c e traffic"),
|
||||
TlsEarlyExporterMasterSecret ("e exp master"),
|
||||
TlsClientHandshakeTrafficSecret ("c hs traffic"),
|
||||
TlsServerHandshakeTrafficSecret ("s hs traffic"),
|
||||
TlsClientAppTrafficSecret ("c ap traffic"),
|
||||
TlsServerAppTrafficSecret ("s ap traffic"),
|
||||
TlsExporterMasterSecret ("exp master"),
|
||||
TlsResumptionMasterSecret ("res master");
|
||||
|
||||
private final byte[] label;
|
||||
|
||||
private SecretSchedule(String label) {
|
||||
this.label = ("tls13 " + label).getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
116
jdkSrc/jdk8/sun/security/ssl/SSLServerSocketFactoryImpl.java
Normal file
116
jdkSrc/jdk8/sun/security/ssl/SSLServerSocketFactoryImpl.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
|
||||
/**
|
||||
* This class creates SSL server sockets.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public final class SSLServerSocketFactoryImpl extends SSLServerSocketFactory {
|
||||
private static final int DEFAULT_BACKLOG = 50;
|
||||
private final SSLContextImpl context;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor used to instantiate the default factory. This method is
|
||||
* only called if the old "ssl.ServerSocketFactory.provider" property in the
|
||||
* java.security file is set.
|
||||
*/
|
||||
public SSLServerSocketFactoryImpl() throws Exception {
|
||||
this.context = SSLContextImpl.DefaultSSLContext.getDefaultImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from SSLContextImpl's getSSLServerSocketFactory().
|
||||
*/
|
||||
SSLServerSocketFactoryImpl(SSLContextImpl context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unbound server socket.
|
||||
*
|
||||
* @return the unbound socket
|
||||
* @throws IOException if the socket cannot be created
|
||||
* @see java.net.Socket#bind(java.net.SocketAddress)
|
||||
*/
|
||||
@Override
|
||||
public ServerSocket createServerSocket() throws IOException {
|
||||
return new SSLServerSocketImpl(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerSocket createServerSocket(int port) throws IOException {
|
||||
return new SSLServerSocketImpl(context, port, DEFAULT_BACKLOG);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServerSocket createServerSocket (
|
||||
int port, int backlog) throws IOException {
|
||||
return new SSLServerSocketImpl(context, port, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerSocket
|
||||
createServerSocket (int port,
|
||||
int backlog, InetAddress ifAddress) throws IOException {
|
||||
return new SSLServerSocketImpl(context, port, backlog, ifAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of the supported cipher suites which are
|
||||
* enabled by default. These cipher suites all provide a minimum
|
||||
* quality of service whereby the server authenticates itself
|
||||
* (preventing person-in-the-middle attacks) and where traffic
|
||||
* is encrypted to provide confidentiality.
|
||||
*/
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return CipherSuite.namesOf(context.getDefaultCipherSuites(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the cipher suites which could be enabled for use
|
||||
* on an SSL connection. Normally, only a subset of these will actually
|
||||
* be enabled by default, since this list may include cipher suites which
|
||||
* do not support the mutual authentication of servers and clients, or
|
||||
* which do not protect data confidentiality. Servers may also need
|
||||
* certain kinds of certificates to use certain cipher suites.
|
||||
*
|
||||
* @return an array of cipher suite names
|
||||
*/
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return CipherSuite.namesOf(context.getSupportedCipherSuites());
|
||||
}
|
||||
}
|
||||
208
jdkSrc/jdk8/sun/security/ssl/SSLServerSocketImpl.java
Normal file
208
jdkSrc/jdk8/sun/security/ssl/SSLServerSocketImpl.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
|
||||
/**
|
||||
* This class provides a simple way for servers to support conventional
|
||||
* use of the Secure Sockets Layer (SSL). Application code uses an
|
||||
* SSLServerSocketImpl exactly like it uses a regular TCP ServerSocket; the
|
||||
* difference is that the connections established are secured using SSL.
|
||||
*
|
||||
* <P> Also, the constructors take an explicit authentication context
|
||||
* parameter, giving flexibility with respect to how the server socket
|
||||
* authenticates itself. That policy flexibility is not exposed through
|
||||
* the standard SSLServerSocketFactory API.
|
||||
*
|
||||
* <P> System security defaults prevent server sockets from accepting
|
||||
* connections if they the authentication context has not been given
|
||||
* a certificate chain and its matching private key. If the clients
|
||||
* of your application support "anonymous" cipher suites, you may be
|
||||
* able to configure a server socket to accept those suites.
|
||||
*
|
||||
* @see SSLSocketImpl
|
||||
* @see SSLServerSocketFactoryImpl
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
final class SSLServerSocketImpl extends SSLServerSocket {
|
||||
private final SSLContextImpl sslContext;
|
||||
private final SSLConfiguration sslConfig;
|
||||
|
||||
SSLServerSocketImpl(SSLContextImpl sslContext) throws IOException {
|
||||
|
||||
super();
|
||||
this.sslContext = sslContext;
|
||||
this.sslConfig = new SSLConfiguration(sslContext, false);
|
||||
}
|
||||
|
||||
SSLServerSocketImpl(SSLContextImpl sslContext,
|
||||
int port, int backlog) throws IOException {
|
||||
|
||||
super(port, backlog);
|
||||
this.sslContext = sslContext;
|
||||
this.sslConfig = new SSLConfiguration(sslContext, false);
|
||||
}
|
||||
|
||||
SSLServerSocketImpl(SSLContextImpl sslContext,
|
||||
int port, int backlog, InetAddress address) throws IOException {
|
||||
|
||||
super(port, backlog, address);
|
||||
this.sslContext = sslContext;
|
||||
this.sslConfig = new SSLConfiguration(sslContext, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String[] getEnabledCipherSuites() {
|
||||
return CipherSuite.namesOf(sslConfig.enabledCipherSuites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnabledCipherSuites(String[] suites) {
|
||||
sslConfig.enabledCipherSuites =
|
||||
CipherSuite.validValuesOf(suites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return CipherSuite.namesOf(sslContext.getSupportedCipherSuites());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedProtocols() {
|
||||
return ProtocolVersion.toStringArray(
|
||||
sslContext.getSupportedProtocolVersions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String[] getEnabledProtocols() {
|
||||
return ProtocolVersion.toStringArray(sslConfig.enabledProtocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnabledProtocols(String[] protocols) {
|
||||
if (protocols == null) {
|
||||
throw new IllegalArgumentException("Protocols cannot be null");
|
||||
}
|
||||
|
||||
sslConfig.enabledProtocols = ProtocolVersion.namesOf(protocols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setNeedClientAuth(boolean need) {
|
||||
sslConfig.clientAuthType =
|
||||
(need ? ClientAuthType.CLIENT_AUTH_REQUIRED :
|
||||
ClientAuthType.CLIENT_AUTH_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getNeedClientAuth() {
|
||||
return (sslConfig.clientAuthType ==
|
||||
ClientAuthType.CLIENT_AUTH_REQUIRED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setWantClientAuth(boolean want) {
|
||||
sslConfig.clientAuthType =
|
||||
(want ? ClientAuthType.CLIENT_AUTH_REQUESTED :
|
||||
ClientAuthType.CLIENT_AUTH_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getWantClientAuth() {
|
||||
return (sslConfig.clientAuthType ==
|
||||
ClientAuthType.CLIENT_AUTH_REQUESTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setUseClientMode(boolean useClientMode) {
|
||||
/*
|
||||
* If we need to change the client mode and the enabled
|
||||
* protocols and cipher suites haven't specifically been
|
||||
* set by the user, change them to the corresponding
|
||||
* default ones.
|
||||
*/
|
||||
if (sslConfig.isClientMode != useClientMode) {
|
||||
if (sslContext.isDefaultProtocolVesions(
|
||||
sslConfig.enabledProtocols)) {
|
||||
sslConfig.enabledProtocols =
|
||||
sslContext.getDefaultProtocolVersions(!useClientMode);
|
||||
}
|
||||
|
||||
if (sslContext.isDefaultCipherSuiteList(
|
||||
sslConfig.enabledCipherSuites)) {
|
||||
sslConfig.enabledCipherSuites =
|
||||
sslContext.getDefaultCipherSuites(!useClientMode);
|
||||
}
|
||||
|
||||
sslConfig.toggleClientMode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getUseClientMode() {
|
||||
return sslConfig.isClientMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setEnableSessionCreation(boolean flag) {
|
||||
sslConfig.enableSessionCreation = flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getEnableSessionCreation() {
|
||||
return sslConfig.enableSessionCreation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SSLParameters getSSLParameters() {
|
||||
return sslConfig.getSSLParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSSLParameters(SSLParameters params) {
|
||||
sslConfig.setSSLParameters(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket accept() throws IOException {
|
||||
SSLSocketImpl s = new SSLSocketImpl(sslContext, sslConfig);
|
||||
|
||||
implAccept(s);
|
||||
s.doneConnect();
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[SSL: "+ super.toString() + "]";
|
||||
}
|
||||
}
|
||||
272
jdkSrc/jdk8/sun/security/ssl/SSLSessionContextImpl.java
Normal file
272
jdkSrc/jdk8/sun/security/ssl/SSLSessionContextImpl.java
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
|
||||
import sun.security.action.GetIntegerAction;
|
||||
import sun.security.util.Cache;
|
||||
|
||||
|
||||
final class SSLSessionContextImpl implements SSLSessionContext {
|
||||
private final static int DEFAULT_MAX_CACHE_SIZE = 20480;
|
||||
|
||||
private final Cache<SessionId, SSLSessionImpl> sessionCache;
|
||||
// session cache, session id as key
|
||||
private final Cache<String, SSLSessionImpl> sessionHostPortCache;
|
||||
// session cache, "host:port" as key
|
||||
private int cacheLimit; // the max cache size
|
||||
private int timeout; // timeout in seconds
|
||||
|
||||
// package private
|
||||
SSLSessionContextImpl() {
|
||||
cacheLimit = getDefaultCacheLimit(); // default cache size
|
||||
timeout = 86400; // default, 24 hours
|
||||
|
||||
// use soft reference
|
||||
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
|
||||
sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>SSLSession</code> bound to the specified session id.
|
||||
*/
|
||||
@Override
|
||||
public SSLSession getSession(byte[] sessionId) {
|
||||
if (sessionId == null) {
|
||||
throw new NullPointerException("session id cannot be null");
|
||||
}
|
||||
|
||||
SSLSessionImpl sess = sessionCache.get(new SessionId(sessionId));
|
||||
if (!isTimedout(sess)) {
|
||||
return sess;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of the active SSL sessions.
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<byte[]> getIds() {
|
||||
SessionCacheVisitor scVisitor = new SessionCacheVisitor();
|
||||
sessionCache.accept(scVisitor);
|
||||
|
||||
return scVisitor.getSessionIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeout limit for cached <code>SSLSession</code> objects
|
||||
*
|
||||
* Note that after reset the timeout, the cached session before
|
||||
* should be timed within the shorter one of the old timeout and the
|
||||
* new timeout.
|
||||
*/
|
||||
@Override
|
||||
public void setSessionTimeout(int seconds)
|
||||
throws IllegalArgumentException {
|
||||
if (seconds < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (timeout != seconds) {
|
||||
sessionCache.setTimeout(seconds);
|
||||
sessionHostPortCache.setTimeout(seconds);
|
||||
timeout = seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeout limit for cached <code>SSLSession</code> objects
|
||||
*/
|
||||
@Override
|
||||
public int getSessionTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the cache used for storing
|
||||
* <code>SSLSession</code> objects.
|
||||
*/
|
||||
@Override
|
||||
public void setSessionCacheSize(int size)
|
||||
throws IllegalArgumentException {
|
||||
if (size < 0)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
if (cacheLimit != size) {
|
||||
sessionCache.setCapacity(size);
|
||||
sessionHostPortCache.setCapacity(size);
|
||||
cacheLimit = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the cache used for storing
|
||||
* <code>SSLSession</code> objects.
|
||||
*/
|
||||
@Override
|
||||
public int getSessionCacheSize() {
|
||||
return cacheLimit;
|
||||
}
|
||||
|
||||
// package-private method, used ONLY by ServerHandshaker
|
||||
SSLSessionImpl get(byte[] id) {
|
||||
return (SSLSessionImpl)getSession(id);
|
||||
}
|
||||
|
||||
// package-private method, find and remove session from cache
|
||||
// return found session
|
||||
SSLSessionImpl pull(byte[] id) {
|
||||
if (id != null) {
|
||||
return sessionCache.pull(new SessionId(id));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// package-private method, used ONLY by ClientHandshaker
|
||||
SSLSessionImpl get(String hostname, int port) {
|
||||
/*
|
||||
* If no session caching info is available, we won't
|
||||
* get one, so exit before doing a lookup.
|
||||
*/
|
||||
if (hostname == null && port == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SSLSessionImpl sess = sessionHostPortCache.get(getKey(hostname, port));
|
||||
if (!isTimedout(sess)) {
|
||||
return sess;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getKey(String hostname, int port) {
|
||||
return (hostname + ":" +
|
||||
String.valueOf(port)).toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
// cache a SSLSession
|
||||
//
|
||||
// In SunJSSE implementation, a session is created while getting a
|
||||
// client hello or a server hello message, and cached while the
|
||||
// handshaking finished.
|
||||
// Here we time the session from the time it cached instead of the
|
||||
// time it created, which is a little longer than the expected. So
|
||||
// please do check isTimedout() while getting entry from the cache.
|
||||
void put(SSLSessionImpl s) {
|
||||
sessionCache.put(s.getSessionId(), s);
|
||||
|
||||
// If no hostname/port info is available, don't add this one.
|
||||
if ((s.getPeerHost() != null) && (s.getPeerPort() != -1)) {
|
||||
sessionHostPortCache.put(
|
||||
getKey(s.getPeerHost(), s.getPeerPort()), s);
|
||||
}
|
||||
|
||||
s.setContext(this);
|
||||
}
|
||||
|
||||
// package-private method, remove a cached SSLSession
|
||||
void remove(SessionId key) {
|
||||
SSLSessionImpl s = sessionCache.get(key);
|
||||
if (s != null) {
|
||||
sessionCache.remove(key);
|
||||
sessionHostPortCache.remove(
|
||||
getKey(s.getPeerHost(), s.getPeerPort()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int getDefaultCacheLimit() {
|
||||
try {
|
||||
int defaultCacheLimit = AccessController.doPrivileged(
|
||||
new GetIntegerAction("javax.net.ssl.sessionCacheSize",
|
||||
DEFAULT_MAX_CACHE_SIZE));
|
||||
if (defaultCacheLimit >= 0) {
|
||||
return defaultCacheLimit;
|
||||
} else if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning(
|
||||
"invalid System Property javax.net.ssl.sessionCacheSize, " +
|
||||
"use the default session cache size (" +
|
||||
DEFAULT_MAX_CACHE_SIZE + ") instead");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// unlikely, log it for safe
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.warning(
|
||||
"the System Property javax.net.ssl.sessionCacheSize is " +
|
||||
"not available, use the default value (" +
|
||||
DEFAULT_MAX_CACHE_SIZE + ") instead");
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_MAX_CACHE_SIZE;
|
||||
}
|
||||
|
||||
private boolean isTimedout(SSLSession sess) {
|
||||
if (timeout == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((sess != null) && ((sess.getCreationTime() + timeout * 1000L)
|
||||
<= (System.currentTimeMillis()))) {
|
||||
sess.invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private final class SessionCacheVisitor
|
||||
implements Cache.CacheVisitor<SessionId, SSLSessionImpl> {
|
||||
ArrayList<byte[]> ids = null;
|
||||
|
||||
// public void visit(java.util.Map<K,V> map) {}
|
||||
@Override
|
||||
public void visit(java.util.Map<SessionId, SSLSessionImpl> map) {
|
||||
ids = new ArrayList<>(map.size());
|
||||
|
||||
for (SessionId key : map.keySet()) {
|
||||
SSLSessionImpl value = map.get(key);
|
||||
if (!isTimedout(value)) {
|
||||
ids.add(key.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<byte[]> getSessionIds() {
|
||||
return ids != null ? Collections.enumeration(ids) :
|
||||
Collections.emptyEnumeration();
|
||||
}
|
||||
}
|
||||
}
|
||||
1120
jdkSrc/jdk8/sun/security/ssl/SSLSessionImpl.java
Normal file
1120
jdkSrc/jdk8/sun/security/ssl/SSLSessionImpl.java
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user