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.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -46,6 +47,9 @@ final class PostHandshakeContext extends HandshakeContext {
|
||||
"Post-handshake not supported in " + negotiatedProtocol.name);
|
||||
}
|
||||
|
||||
this.localSupportedSignAlgs = new ArrayList<SignatureScheme>(
|
||||
context.conSession.getLocalSupportedSignatureSchemes());
|
||||
|
||||
handshakeConsumers = new LinkedHashMap<>(consumers);
|
||||
handshakeFinished = true;
|
||||
}
|
||||
|
@ -33,8 +33,11 @@ import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
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;
|
||||
@ -167,7 +170,7 @@ final class PreSharedKeyExtension {
|
||||
|
||||
int getIdsEncodedLength() {
|
||||
int idEncodedLength = 0;
|
||||
for(PskIdentity curId : identities) {
|
||||
for (PskIdentity curId : identities) {
|
||||
idEncodedLength += curId.getEncodedLength();
|
||||
}
|
||||
|
||||
@ -190,7 +193,7 @@ final class PreSharedKeyExtension {
|
||||
byte[] buffer = new byte[encodedLength];
|
||||
ByteBuffer m = ByteBuffer.wrap(buffer);
|
||||
Record.putInt16(m, idsEncodedLength);
|
||||
for(PskIdentity curId : identities) {
|
||||
for (PskIdentity curId : identities) {
|
||||
curId.writeEncoded(m);
|
||||
}
|
||||
Record.putInt16(m, bindersEncodedLength);
|
||||
@ -220,7 +223,7 @@ final class PreSharedKeyExtension {
|
||||
|
||||
String identitiesString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for(PskIdentity curId : identities) {
|
||||
for (PskIdentity curId : identities) {
|
||||
result.append(curId.toString() + "\n");
|
||||
}
|
||||
|
||||
@ -229,7 +232,7 @@ final class PreSharedKeyExtension {
|
||||
|
||||
String bindersString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for(byte[] curBinder : binders) {
|
||||
for (byte[] curBinder : binders) {
|
||||
result.append("{" + Utilities.toHexString(curBinder) + "}\n");
|
||||
}
|
||||
|
||||
@ -328,6 +331,7 @@ final class PreSharedKeyExtension {
|
||||
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)) {
|
||||
@ -367,8 +371,7 @@ final class PreSharedKeyExtension {
|
||||
int idIndex = 0;
|
||||
for (PskIdentity requestedId : pskSpec.identities) {
|
||||
SSLSessionImpl s = sessionCache.get(requestedId.identity);
|
||||
if (s != null && s.isRejoinable() &&
|
||||
s.getPreSharedKey().isPresent()) {
|
||||
if (s != null && canRejoin(clientHello, shc, s)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Resuming session: ", s);
|
||||
}
|
||||
@ -392,10 +395,68 @@ final class PreSharedKeyExtension {
|
||||
|
||||
// update the context
|
||||
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
|
||||
class CHPreSharedKeyUpdate implements HandshakeConsumer {
|
||||
// Prevent instantiation of this class.
|
||||
@ -547,6 +608,18 @@ final class PreSharedKeyExtension {
|
||||
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();
|
||||
if (!pskOpt.isPresent()) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
@ -658,7 +731,7 @@ final class PreSharedKeyExtension {
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
} catch(GeneralSecurityException ex) {
|
||||
} catch (GeneralSecurityException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||
private boolean invalidated;
|
||||
private X509Certificate[] localCerts;
|
||||
private PrivateKey localPrivateKey;
|
||||
private final String[] localSupportedSignAlgs;
|
||||
private final Collection<SignatureScheme> localSupportedSignAlgs;
|
||||
private String[] peerSupportedSignAlgs; // for certificate
|
||||
private boolean useDefaultPeerSignAlgs = false;
|
||||
private List<byte[]> statusResponses;
|
||||
@ -144,7 +144,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||
this.sessionId = new SessionId(false, null);
|
||||
this.host = null;
|
||||
this.port = -1;
|
||||
this.localSupportedSignAlgs = new String[0];
|
||||
this.localSupportedSignAlgs = Collections.emptySet();
|
||||
this.serverNameIndication = null;
|
||||
this.requestedServerNames = Collections.<SNIServerName>emptyList();
|
||||
this.useExtendedMasterSecret = false;
|
||||
@ -179,8 +179,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||
this.sessionId = id;
|
||||
this.host = hc.conContext.transport.getPeerHost();
|
||||
this.port = hc.conContext.transport.getPeerPort();
|
||||
this.localSupportedSignAlgs =
|
||||
SignatureScheme.getAlgorithmNames(hc.localSupportedSignAlgs);
|
||||
this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ?
|
||||
Collections.emptySet() :
|
||||
Collections.unmodifiableCollection(hc.localSupportedSignAlgs);
|
||||
this.serverNameIndication = hc.negotiatedServerName;
|
||||
this.requestedServerNames = Collections.<SNIServerName>unmodifiableList(
|
||||
hc.getRequestedServerNames());
|
||||
@ -969,16 +970,20 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of supported signature algorithms that the local side is
|
||||
* willing to verify.
|
||||
* Gets an array of supported signature algorithm names that the local
|
||||
* side is willing to verify.
|
||||
*/
|
||||
@Override
|
||||
public String[] getLocalSupportedSignatureAlgorithms() {
|
||||
if (localSupportedSignAlgs != null) {
|
||||
return localSupportedSignAlgs.clone();
|
||||
}
|
||||
return SignatureScheme.getAlgorithmNames(localSupportedSignAlgs);
|
||||
}
|
||||
|
||||
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