8206929: Check session context for TLS 1.3 session resumption
Additional checks to prevent TLS 1.3 sessions from being resumed when they shouldn't Reviewed-by: xuelei
This commit is contained in:
parent
2c82c9e1bd
commit
108461949f
@ -27,6 +27,7 @@ package sun.security.ssl;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -46,6 +47,9 @@ final class PostHandshakeContext extends HandshakeContext {
|
|||||||
"Post-handshake not supported in " + negotiatedProtocol.name);
|
"Post-handshake not supported in " + negotiatedProtocol.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.localSupportedSignAlgs = new ArrayList<SignatureScheme>(
|
||||||
|
context.conSession.getLocalSupportedSignatureSchemes());
|
||||||
|
|
||||||
handshakeConsumers = new LinkedHashMap<>(consumers);
|
handshakeConsumers = new LinkedHashMap<>(consumers);
|
||||||
handshakeFinished = true;
|
handshakeFinished = true;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Collection;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.SecretKey;
|
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.ClientHello.ClientHelloMessage;
|
||||||
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
import sun.security.ssl.SSLExtension.ExtensionConsumer;
|
||||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||||
@ -167,7 +170,7 @@ final class PreSharedKeyExtension {
|
|||||||
|
|
||||||
int getIdsEncodedLength() {
|
int getIdsEncodedLength() {
|
||||||
int idEncodedLength = 0;
|
int idEncodedLength = 0;
|
||||||
for(PskIdentity curId : identities) {
|
for (PskIdentity curId : identities) {
|
||||||
idEncodedLength += curId.getEncodedLength();
|
idEncodedLength += curId.getEncodedLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +193,7 @@ final class PreSharedKeyExtension {
|
|||||||
byte[] buffer = new byte[encodedLength];
|
byte[] buffer = new byte[encodedLength];
|
||||||
ByteBuffer m = ByteBuffer.wrap(buffer);
|
ByteBuffer m = ByteBuffer.wrap(buffer);
|
||||||
Record.putInt16(m, idsEncodedLength);
|
Record.putInt16(m, idsEncodedLength);
|
||||||
for(PskIdentity curId : identities) {
|
for (PskIdentity curId : identities) {
|
||||||
curId.writeEncoded(m);
|
curId.writeEncoded(m);
|
||||||
}
|
}
|
||||||
Record.putInt16(m, bindersEncodedLength);
|
Record.putInt16(m, bindersEncodedLength);
|
||||||
@ -220,7 +223,7 @@ final class PreSharedKeyExtension {
|
|||||||
|
|
||||||
String identitiesString() {
|
String identitiesString() {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
for(PskIdentity curId : identities) {
|
for (PskIdentity curId : identities) {
|
||||||
result.append(curId.toString() + "\n");
|
result.append(curId.toString() + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +232,7 @@ final class PreSharedKeyExtension {
|
|||||||
|
|
||||||
String bindersString() {
|
String bindersString() {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
for(byte[] curBinder : binders) {
|
for (byte[] curBinder : binders) {
|
||||||
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
|
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,6 +331,7 @@ final class PreSharedKeyExtension {
|
|||||||
public void consume(ConnectionContext context,
|
public void consume(ConnectionContext context,
|
||||||
HandshakeMessage message,
|
HandshakeMessage message,
|
||||||
ByteBuffer buffer) throws IOException {
|
ByteBuffer buffer) throws IOException {
|
||||||
|
ClientHelloMessage clientHello = (ClientHelloMessage) message;
|
||||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||||
// Is it a supported and enabled extension?
|
// Is it a supported and enabled extension?
|
||||||
if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
|
if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
|
||||||
@ -367,8 +371,7 @@ final class PreSharedKeyExtension {
|
|||||||
int idIndex = 0;
|
int idIndex = 0;
|
||||||
for (PskIdentity requestedId : pskSpec.identities) {
|
for (PskIdentity requestedId : pskSpec.identities) {
|
||||||
SSLSessionImpl s = sessionCache.get(requestedId.identity);
|
SSLSessionImpl s = sessionCache.get(requestedId.identity);
|
||||||
if (s != null && s.isRejoinable() &&
|
if (s != null && canRejoin(clientHello, shc, s)) {
|
||||||
s.getPreSharedKey().isPresent()) {
|
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine("Resuming session: ", s);
|
SSLLogger.fine("Resuming session: ", s);
|
||||||
}
|
}
|
||||||
@ -392,10 +395,68 @@ final class PreSharedKeyExtension {
|
|||||||
|
|
||||||
// update the context
|
// update the context
|
||||||
shc.handshakeExtensions.put(
|
shc.handshakeExtensions.put(
|
||||||
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
|
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean canRejoin(ClientHelloMessage clientHello,
|
||||||
|
ServerHandshakeContext shc, SSLSessionImpl s) {
|
||||||
|
|
||||||
|
boolean result = s.isRejoinable() && s.getPreSharedKey().isPresent();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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
|
private static final
|
||||||
class CHPreSharedKeyUpdate implements HandshakeConsumer {
|
class CHPreSharedKeyUpdate implements HandshakeConsumer {
|
||||||
// Prevent instantiation of this class.
|
// Prevent instantiation of this class.
|
||||||
@ -547,6 +608,18 @@ final class PreSharedKeyExtension {
|
|||||||
return null;
|
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
|
||||||
Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey();
|
Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey();
|
||||||
if (!pskOpt.isPresent()) {
|
if (!pskOpt.isPresent()) {
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
@ -658,7 +731,7 @@ final class PreSharedKeyExtension {
|
|||||||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
||||||
throw new IOException(ex);
|
throw new IOException(ex);
|
||||||
}
|
}
|
||||||
} catch(GeneralSecurityException ex) {
|
} catch (GeneralSecurityException ex) {
|
||||||
throw new IOException(ex);
|
throw new IOException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
private boolean invalidated;
|
private boolean invalidated;
|
||||||
private X509Certificate[] localCerts;
|
private X509Certificate[] localCerts;
|
||||||
private PrivateKey localPrivateKey;
|
private PrivateKey localPrivateKey;
|
||||||
private final String[] localSupportedSignAlgs;
|
private final Collection<SignatureScheme> localSupportedSignAlgs;
|
||||||
private String[] peerSupportedSignAlgs; // for certificate
|
private String[] peerSupportedSignAlgs; // for certificate
|
||||||
private boolean useDefaultPeerSignAlgs = false;
|
private boolean useDefaultPeerSignAlgs = false;
|
||||||
private List<byte[]> statusResponses;
|
private List<byte[]> statusResponses;
|
||||||
@ -144,7 +144,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
this.sessionId = new SessionId(false, null);
|
this.sessionId = new SessionId(false, null);
|
||||||
this.host = null;
|
this.host = null;
|
||||||
this.port = -1;
|
this.port = -1;
|
||||||
this.localSupportedSignAlgs = new String[0];
|
this.localSupportedSignAlgs = Collections.emptySet();
|
||||||
this.serverNameIndication = null;
|
this.serverNameIndication = null;
|
||||||
this.requestedServerNames = Collections.<SNIServerName>emptyList();
|
this.requestedServerNames = Collections.<SNIServerName>emptyList();
|
||||||
this.useExtendedMasterSecret = false;
|
this.useExtendedMasterSecret = false;
|
||||||
@ -179,8 +179,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
this.sessionId = id;
|
this.sessionId = id;
|
||||||
this.host = hc.conContext.transport.getPeerHost();
|
this.host = hc.conContext.transport.getPeerHost();
|
||||||
this.port = hc.conContext.transport.getPeerPort();
|
this.port = hc.conContext.transport.getPeerPort();
|
||||||
this.localSupportedSignAlgs =
|
this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ?
|
||||||
SignatureScheme.getAlgorithmNames(hc.localSupportedSignAlgs);
|
Collections.emptySet() :
|
||||||
|
Collections.unmodifiableCollection(hc.localSupportedSignAlgs);
|
||||||
this.serverNameIndication = hc.negotiatedServerName;
|
this.serverNameIndication = hc.negotiatedServerName;
|
||||||
this.requestedServerNames = Collections.<SNIServerName>unmodifiableList(
|
this.requestedServerNames = Collections.<SNIServerName>unmodifiableList(
|
||||||
hc.getRequestedServerNames());
|
hc.getRequestedServerNames());
|
||||||
@ -969,16 +970,20 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an array of supported signature algorithms that the local side is
|
* Gets an array of supported signature algorithm names that the local
|
||||||
* willing to verify.
|
* side is willing to verify.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] getLocalSupportedSignatureAlgorithms() {
|
public String[] getLocalSupportedSignatureAlgorithms() {
|
||||||
if (localSupportedSignAlgs != null) {
|
return SignatureScheme.getAlgorithmNames(localSupportedSignAlgs);
|
||||||
return localSupportedSignAlgs.clone();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return new String[0];
|
/**
|
||||||
|
* Gets an array of supported signature schemes that the local side is
|
||||||
|
* willing to verify.
|
||||||
|
*/
|
||||||
|
public Collection<SignatureScheme> getLocalSupportedSignatureSchemes() {
|
||||||
|
return localSupportedSignAlgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
269
test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksClient.java
Normal file
269
test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksClient.java
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8206929
|
||||||
|
* @summary ensure that client only resumes a session if certain properties
|
||||||
|
* of the session are compatible with the new connection
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksClient BASIC
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient BASIC
|
||||||
|
* @run main/othervm ResumeChecksClient BASIC
|
||||||
|
* @run main/othervm ResumeChecksClient VERSION_2_TO_3
|
||||||
|
* @run main/othervm ResumeChecksClient VERSION_3_TO_2
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient CIPHER_SUITE
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient SIGNATURE_SCHEME
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.net.*;
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.security.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ResumeChecksClient {
|
||||||
|
|
||||||
|
static String pathToStores = "../../../../javax/net/ssl/etc";
|
||||||
|
static String keyStoreFile = "keystore";
|
||||||
|
static String trustStoreFile = "truststore";
|
||||||
|
static String passwd = "passphrase";
|
||||||
|
|
||||||
|
enum TestMode {
|
||||||
|
BASIC,
|
||||||
|
VERSION_2_TO_3,
|
||||||
|
VERSION_3_TO_2,
|
||||||
|
CIPHER_SUITE,
|
||||||
|
SIGNATURE_SCHEME
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
TestMode mode = TestMode.valueOf(args[0]);
|
||||||
|
|
||||||
|
String keyFilename =
|
||||||
|
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||||
|
"/" + keyStoreFile;
|
||||||
|
String trustFilename =
|
||||||
|
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||||
|
"/" + trustStoreFile;
|
||||||
|
|
||||||
|
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||||
|
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||||
|
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||||
|
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||||
|
|
||||||
|
Server server = startServer();
|
||||||
|
server.signal();
|
||||||
|
SSLContext sslContext = SSLContext.getDefault();
|
||||||
|
while (!server.started) {
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
connect(sslContext, server.port, mode, false);
|
||||||
|
|
||||||
|
server.signal();
|
||||||
|
long secondStartTime = System.currentTimeMillis();
|
||||||
|
Thread.sleep(10);
|
||||||
|
SSLSession secondSession = connect(sslContext, server.port, mode, true);
|
||||||
|
|
||||||
|
server.go = false;
|
||||||
|
server.signal();
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BASIC:
|
||||||
|
// fail if session is not resumed
|
||||||
|
if (secondSession.getCreationTime() > secondStartTime) {
|
||||||
|
throw new RuntimeException("Session was not reused");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VERSION_2_TO_3:
|
||||||
|
case VERSION_3_TO_2:
|
||||||
|
case CIPHER_SUITE:
|
||||||
|
case SIGNATURE_SCHEME:
|
||||||
|
// fail if a new session is not created
|
||||||
|
if (secondSession.getCreationTime() <= secondStartTime) {
|
||||||
|
throw new RuntimeException("Existing session was used");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("unknown mode: " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NoSig implements AlgorithmConstraints {
|
||||||
|
|
||||||
|
private final String alg;
|
||||||
|
|
||||||
|
NoSig(String alg) {
|
||||||
|
this.alg = alg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean test(String a) {
|
||||||
|
return !a.toLowerCase().contains(alg.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||||
|
String algorithm, AlgorithmParameters parameters) {
|
||||||
|
|
||||||
|
return test(algorithm);
|
||||||
|
}
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||||
|
String algorithm, Key key, AlgorithmParameters parameters) {
|
||||||
|
|
||||||
|
return test(algorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLSession connect(SSLContext sslContext, int port,
|
||||||
|
TestMode mode, boolean second) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
SSLSocket sock = (SSLSocket)
|
||||||
|
sslContext.getSocketFactory().createSocket();
|
||||||
|
SSLParameters params = sock.getSSLParameters();
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BASIC:
|
||||||
|
// do nothing to ensure resumption works
|
||||||
|
break;
|
||||||
|
case VERSION_2_TO_3:
|
||||||
|
if (second) {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.3"});
|
||||||
|
} else {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.2"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VERSION_3_TO_2:
|
||||||
|
if (second) {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.2"});
|
||||||
|
} else {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.3"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CIPHER_SUITE:
|
||||||
|
if (second) {
|
||||||
|
params.setCipherSuites(
|
||||||
|
new String[] {"TLS_AES_256_GCM_SHA384"});
|
||||||
|
} else {
|
||||||
|
params.setCipherSuites(
|
||||||
|
new String[] {"TLS_AES_128_GCM_SHA256"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGNATURE_SCHEME:
|
||||||
|
AlgorithmConstraints constraints =
|
||||||
|
params.getAlgorithmConstraints();
|
||||||
|
if (second) {
|
||||||
|
params.setAlgorithmConstraints(new NoSig("ecdsa"));
|
||||||
|
} else {
|
||||||
|
params.setAlgorithmConstraints(new NoSig("rsa"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("unknown mode: " + mode);
|
||||||
|
}
|
||||||
|
sock.setSSLParameters(params);
|
||||||
|
sock.connect(new InetSocketAddress("localhost", port));
|
||||||
|
PrintWriter out = new PrintWriter(
|
||||||
|
new OutputStreamWriter(sock.getOutputStream()));
|
||||||
|
out.println("message");
|
||||||
|
out.flush();
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(sock.getInputStream()));
|
||||||
|
String inMsg = reader.readLine();
|
||||||
|
System.out.println("Client received: " + inMsg);
|
||||||
|
SSLSession result = sock.getSession();
|
||||||
|
sock.close();
|
||||||
|
return result;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// unexpected exception
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Server startServer() {
|
||||||
|
Server server = new Server();
|
||||||
|
new Thread(server).start();
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Server implements Runnable {
|
||||||
|
|
||||||
|
public volatile boolean go = true;
|
||||||
|
private boolean signal = false;
|
||||||
|
public volatile int port = 0;
|
||||||
|
public volatile boolean started = false;
|
||||||
|
|
||||||
|
private synchronized void waitForSignal() {
|
||||||
|
while (!signal) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal = false;
|
||||||
|
}
|
||||||
|
public synchronized void signal() {
|
||||||
|
signal = true;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
SSLContext sc = SSLContext.getDefault();
|
||||||
|
ServerSocketFactory fac = sc.getServerSocketFactory();
|
||||||
|
SSLServerSocket ssock = (SSLServerSocket)
|
||||||
|
fac.createServerSocket(0);
|
||||||
|
this.port = ssock.getLocalPort();
|
||||||
|
|
||||||
|
waitForSignal();
|
||||||
|
started = true;
|
||||||
|
while (go) {
|
||||||
|
try {
|
||||||
|
System.out.println("Waiting for connection");
|
||||||
|
Socket sock = ssock.accept();
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(sock.getInputStream()));
|
||||||
|
String line = reader.readLine();
|
||||||
|
System.out.println("server read: " + line);
|
||||||
|
PrintWriter out = new PrintWriter(
|
||||||
|
new OutputStreamWriter(sock.getOutputStream()));
|
||||||
|
out.println(line);
|
||||||
|
out.flush();
|
||||||
|
waitForSignal();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
307
test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java
Normal file
307
test/jdk/sun/security/ssl/SSLSessionImpl/ResumeChecksServer.java
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8206929
|
||||||
|
* @summary ensure that server only resumes a session if certain properties
|
||||||
|
* of the session are compatible with the new connection
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksServer BASIC
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer BASIC
|
||||||
|
* @run main/othervm ResumeChecksServer BASIC
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksServer CLIENT_AUTH
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CLIENT_AUTH
|
||||||
|
* @run main/othervm ResumeChecksServer CLIENT_AUTH
|
||||||
|
* @run main/othervm ResumeChecksServer VERSION_2_TO_3
|
||||||
|
* @run main/othervm ResumeChecksServer VERSION_3_TO_2
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CIPHER_SUITE
|
||||||
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer SIGNATURE_SCHEME
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.net.*;
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.security.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ResumeChecksServer {
|
||||||
|
|
||||||
|
static String pathToStores = "../../../../javax/net/ssl/etc";
|
||||||
|
static String keyStoreFile = "keystore";
|
||||||
|
static String trustStoreFile = "truststore";
|
||||||
|
static String passwd = "passphrase";
|
||||||
|
|
||||||
|
enum TestMode {
|
||||||
|
BASIC,
|
||||||
|
CLIENT_AUTH,
|
||||||
|
VERSION_2_TO_3,
|
||||||
|
VERSION_3_TO_2,
|
||||||
|
CIPHER_SUITE,
|
||||||
|
SIGNATURE_SCHEME
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
TestMode mode = TestMode.valueOf(args[0]);
|
||||||
|
|
||||||
|
String keyFilename =
|
||||||
|
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||||
|
"/" + keyStoreFile;
|
||||||
|
String trustFilename =
|
||||||
|
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||||
|
"/" + trustStoreFile;
|
||||||
|
|
||||||
|
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||||
|
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||||
|
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||||
|
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||||
|
|
||||||
|
SSLSession secondSession = null;
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getDefault();
|
||||||
|
ServerSocketFactory fac = sslContext.getServerSocketFactory();
|
||||||
|
SSLServerSocket ssock = (SSLServerSocket)
|
||||||
|
fac.createServerSocket(0);
|
||||||
|
|
||||||
|
Client client = startClient(ssock.getLocalPort());
|
||||||
|
|
||||||
|
try {
|
||||||
|
connect(client, ssock, mode, false);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
long secondStartTime = System.currentTimeMillis();
|
||||||
|
Thread.sleep(10);
|
||||||
|
try {
|
||||||
|
secondSession = connect(client, ssock, mode, true);
|
||||||
|
} catch (SSLHandshakeException ex) {
|
||||||
|
// this is expected
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.go = false;
|
||||||
|
client.signal();
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BASIC:
|
||||||
|
// fail if session is not resumed
|
||||||
|
if (secondSession.getCreationTime() > secondStartTime) {
|
||||||
|
throw new RuntimeException("Session was not reused");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CLIENT_AUTH:
|
||||||
|
// throws an exception if the client is not authenticated
|
||||||
|
secondSession.getPeerCertificates();
|
||||||
|
break;
|
||||||
|
case VERSION_2_TO_3:
|
||||||
|
case VERSION_3_TO_2:
|
||||||
|
case CIPHER_SUITE:
|
||||||
|
case SIGNATURE_SCHEME:
|
||||||
|
// fail if a new session is not created
|
||||||
|
if (secondSession.getCreationTime() <= secondStartTime) {
|
||||||
|
throw new RuntimeException("Existing session was used");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("unknown mode: " + mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NoSig implements AlgorithmConstraints {
|
||||||
|
|
||||||
|
private final String alg;
|
||||||
|
|
||||||
|
NoSig(String alg) {
|
||||||
|
this.alg = alg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean test(String a) {
|
||||||
|
return !a.toLowerCase().contains(alg.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives, Key key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||||
|
String algorithm, AlgorithmParameters parameters) {
|
||||||
|
|
||||||
|
return test(algorithm);
|
||||||
|
}
|
||||||
|
public boolean permits(Set<CryptoPrimitive> primitives,
|
||||||
|
String algorithm, Key key, AlgorithmParameters parameters) {
|
||||||
|
|
||||||
|
return test(algorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SSLSession connect(Client client, SSLServerSocket ssock,
|
||||||
|
TestMode mode, boolean second) throws Exception {
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.signal();
|
||||||
|
System.out.println("Waiting for connection");
|
||||||
|
SSLSocket sock = (SSLSocket) ssock.accept();
|
||||||
|
SSLParameters params = sock.getSSLParameters();
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BASIC:
|
||||||
|
// do nothing to ensure resumption works
|
||||||
|
break;
|
||||||
|
case CLIENT_AUTH:
|
||||||
|
if (second) {
|
||||||
|
params.setNeedClientAuth(true);
|
||||||
|
} else {
|
||||||
|
params.setNeedClientAuth(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VERSION_2_TO_3:
|
||||||
|
if (second) {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.3"});
|
||||||
|
} else {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.2"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VERSION_3_TO_2:
|
||||||
|
if (second) {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.2"});
|
||||||
|
} else {
|
||||||
|
params.setProtocols(new String[] {"TLSv1.3"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CIPHER_SUITE:
|
||||||
|
if (second) {
|
||||||
|
params.setCipherSuites(
|
||||||
|
new String[] {"TLS_AES_128_GCM_SHA256"});
|
||||||
|
} else {
|
||||||
|
params.setCipherSuites(
|
||||||
|
new String[] {"TLS_AES_256_GCM_SHA384"});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SIGNATURE_SCHEME:
|
||||||
|
params.setNeedClientAuth(true);
|
||||||
|
AlgorithmConstraints constraints =
|
||||||
|
params.getAlgorithmConstraints();
|
||||||
|
if (second) {
|
||||||
|
params.setAlgorithmConstraints(new NoSig("ecdsa"));
|
||||||
|
} else {
|
||||||
|
params.setAlgorithmConstraints(new NoSig("rsa"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("unknown mode: " + mode);
|
||||||
|
}
|
||||||
|
sock.setSSLParameters(params);
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(sock.getInputStream()));
|
||||||
|
String line = reader.readLine();
|
||||||
|
System.out.println("server read: " + line);
|
||||||
|
PrintWriter out = new PrintWriter(
|
||||||
|
new OutputStreamWriter(sock.getOutputStream()));
|
||||||
|
out.println(line);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
SSLSession result = sock.getSession();
|
||||||
|
sock.close();
|
||||||
|
return result;
|
||||||
|
} catch (SSLHandshakeException ex) {
|
||||||
|
if (!second) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Client startClient(int port) {
|
||||||
|
Client client = new Client(port);
|
||||||
|
new Thread(client).start();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Client implements Runnable {
|
||||||
|
|
||||||
|
public volatile boolean go = true;
|
||||||
|
private boolean signal = false;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
Client(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void waitForSignal() {
|
||||||
|
while (!signal) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public synchronized void signal() {
|
||||||
|
signal = true;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
SSLContext sc = SSLContext.getDefault();
|
||||||
|
|
||||||
|
waitForSignal();
|
||||||
|
while (go) {
|
||||||
|
try {
|
||||||
|
SSLSocket sock = (SSLSocket)
|
||||||
|
sc.getSocketFactory().createSocket();
|
||||||
|
sock.connect(new InetSocketAddress("localhost", port));
|
||||||
|
PrintWriter out = new PrintWriter(
|
||||||
|
new OutputStreamWriter(sock.getOutputStream()));
|
||||||
|
out.println("message");
|
||||||
|
out.flush();
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(sock.getInputStream()));
|
||||||
|
String inMsg = reader.readLine();
|
||||||
|
System.out.println("Client received: " + inMsg);
|
||||||
|
out.close();
|
||||||
|
sock.close();
|
||||||
|
waitForSignal();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user