8357033: Reduce stateless session ticket size

Reviewed-by: wetmore, djelinski, ascarpino
This commit is contained in:
Artur Barashev 2025-05-30 16:03:13 +00:00
parent 26275a10b2
commit 99048c3d4a
5 changed files with 261 additions and 238 deletions

View File

@ -443,12 +443,6 @@ final class PreSharedKeyExtension {
result = false;
}
// Make sure that the server handshake context's
// localSupportedCertSignAlgs field is populated. This is particularly
// important when client authentication was used in an initial session,
// and it is now being resumed.
SignatureScheme.updateHandshakeLocalSupportedAlgs(shc);
// Validate the required client authentication.
if (result &&
(shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
@ -464,7 +458,9 @@ final class PreSharedKeyExtension {
result = false;
}
// Make sure the list of supported signature algorithms matches
// Make sure the list of supported signature algorithms matches.
// HandshakeContext's localSupportedCertSignAlgs has been already
// updated when we set the negotiated protocol.
Collection<SignatureScheme> sessionSigAlgs =
s.getLocalSupportedSignatureSchemes();
if (result &&

View File

@ -24,34 +24,33 @@
*/
package sun.security.ssl;
import sun.security.provider.X509Factory;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Queue;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.Adler32;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.ExtendedSSLSession;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSessionBindingEvent;
import javax.net.ssl.SSLSessionBindingListener;
import javax.net.ssl.SSLSessionContext;
import sun.security.provider.X509Factory;
import sun.security.ssl.X509Authentication.X509Possession;
/**
* Implements the SSL session interface, and exposes the session context
@ -256,59 +255,51 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
/**
* Reassemble new session ticket.
* <p>
* < 2 bytes > protocolVersion
* < 2 bytes > cipherSuite
* < 1 byte > localSupportedSignAlgs entries
* < 2 bytes per entries > localSupportedSignAlgs
* < 1 bytes > peerSupportedSignAlgs entries
* < 2 bytes per entries > peerSupportedSignAlgs
* < 2 bytes > preSharedKey length
* < length in bytes > preSharedKey
* < 1 byte > pskIdentity length
* < length in bytes > pskIdentity
* < 1 byte > masterSecret length
* < 1 byte > masterSecret algorithm length
* < length in bytes > masterSecret algorithm
* < 2 bytes > masterSecretKey length
* < length in bytes> masterSecretKey
* < 1 byte > useExtendedMasterSecret
* select (protocolVersion)
* case TLS13Plus:
* < 2 bytes > preSharedKey length
* < length in bytes > preSharedKey
* case non-TLS13Plus:
* < 2 bytes > masterSecretKey length
* < length in bytes> masterSecretKey
* < 1 byte > useExtendedMasterSecret
* < 1 byte > identificationProtocol length
* < length in bytes > identificationProtocol
* < length in bytes > identificationProtocol
* < 1 byte > serverNameIndication length
* < length in bytes > serverNameIndication
* < length in bytes > serverNameIndication
* < 1 byte > Number of requestedServerNames entries
* < 1 byte > ServerName length
* < length in bytes > ServerName
* For each entry {
* < 1 byte > ServerName length
* < length in bytes > ServerName
* }
* < 4 bytes > maximumPacketSize
* < 4 bytes > negotiatedMaxFragSize
* < 4 bytes > creationTime
* < 2 byte > status response length
* < 2 byte > status response entry length
* < length in byte > status response entry
* < 1 byte > Length of peer host
* < length in bytes > peer host
* < 2 bytes> peer port
* < 1 byte > Number of peerCerts entries
* < 4 byte > peerCert length
* < length in bytes > peerCert
* < 1 byte > localCerts type (Cert, PSK, Anonymous)
* Certificate
* < 1 byte > Number of Certificate entries
* < 4 byte> Certificate length
* < length in bytes> Certificate
* PSK
* < 1 byte > Number of PSK entries
* < 1 bytes > PSK algorithm length
* < length in bytes > PSK algorithm string
* < 4 bytes > PSK key length
* < length in bytes> PSK key
* < 4 bytes > PSK identity length
* < length in bytes> PSK identity
* Anonymous
* < 1 byte >
* < 1 byte > Number of Peer Certificate entries
* For each entry {
* < 4 bytes > Peer certificate length
* < length in bytes> Peer certificate
* }
* < 1 byte > Number of Local Certificate entries
* For each entry {
* < 1 byte > Local Certificate algorithm length
* < length in bytes> Local Certificate algorithm
* < 4 bytes > Certificate checksum
* }
*/
SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException {
int len;
byte[] b;
boundValues = new ConcurrentHashMap<>();
this.protocolVersion =
ProtocolVersion.valueOf(Record.getInt16(buf));
@ -321,51 +312,36 @@ final class SSLSessionImpl extends ExtendedSSLSession {
CipherSuite.valueOf(Record.getInt16(buf));
// Local Supported signature algorithms
ArrayList<SignatureScheme> list = new ArrayList<>();
int i = Record.getInt8(buf);
while (i-- > 0) {
List<SignatureScheme> list = new ArrayList<>();
len = Record.getInt8(buf);
while (len-- > 0) {
list.add(SignatureScheme.valueOf(
Record.getInt16(buf)));
}
this.localSupportedSignAlgs = Collections.unmodifiableCollection(list);
// Peer Supported signature algorithms
i = Record.getInt8(buf);
list.clear();
while (i-- > 0) {
list.add(SignatureScheme.valueOf(
Record.getInt16(buf)));
}
this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list);
// PSK
byte[] b = Record.getBytes16(buf);
if (b.length > 0) {
if (protocolVersion.useTLS13PlusSpec()) {
// PSK
b = Record.getBytes16(buf);
this.preSharedKey = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.preSharedKey = null;
}
if (b.length > 0) {
this.preSharedKey = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.preSharedKey = null;
}
// PSK identity
b = Record.getBytes8(buf);
if (b.length > 0) {
this.pskIdentity = b;
this.useExtendedMasterSecret = false;
} else {
this.pskIdentity = null;
}
// Master secret length of secret key algorithm (one byte)
b = Record.getBytes8(buf);
if (b.length > 0) {
// Master secret
b = Record.getBytes16(buf);
this.masterSecret = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.masterSecret = null;
}
if (b.length > 0) {
this.masterSecret = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.masterSecret = null;
}
// Use extended master secret
this.useExtendedMasterSecret = (Record.getInt8(buf) != 0);
// Extended master secret usage.
this.useExtendedMasterSecret = (Record.getInt8(buf) != 0);
}
// Identification Protocol
b = Record.getBytes8(buf);
@ -384,7 +360,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
// List of SNIServerName
int len = Record.getInt16(buf);
len = Record.getInt16(buf);
if (len == 0) {
this.requestedServerNames = Collections.emptyList();
} else {
@ -401,20 +377,6 @@ final class SSLSessionImpl extends ExtendedSSLSession {
// Get creation time
this.creationTime = buf.getLong();
// Get Buffer sizes
// Status Response
len = Record.getInt16(buf);
if (len == 0) {
statusResponses = Collections.emptyList();
} else {
statusResponses = new ArrayList<>();
}
while (len-- > 0) {
b = Record.getBytes16(buf);
statusResponses.add(b);
}
// Get Peer host & port
b = Record.getBytes8(buf);
if (b.length == 0) {
@ -424,71 +386,89 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
this.port = Record.getInt16(buf);
// Peer certs
i = Record.getInt8(buf);
if (i == 0) {
// Peer certs.
len = Record.getInt8(buf);
if (len == 0) {
this.peerCerts = null;
} else {
this.peerCerts = new X509Certificate[i];
int j = 0;
while (i > j) {
this.peerCerts = new X509Certificate[len];
for (int i = 0; len > i; i++) {
b = new byte[buf.getInt()];
buf.get(b);
try {
this.peerCerts[j] = X509Factory.cachedGetX509Cert(b);
this.peerCerts[i] = X509Factory.cachedGetX509Cert(b);
} catch (Exception e) {
throw new IOException(e);
}
j++;
}
}
// Get local certs of PSK
switch (Record.getInt8(buf)) {
case 0:
break;
case 1:
// number of certs
len = buf.get();
this.localCerts = new X509Certificate[len];
i = 0;
while (len > i) {
b = new byte[buf.getInt()];
buf.get(b);
// Restore local certificates if cert algorithm(s) present.
len = Record.getInt8(buf);
if (len == 0) {
this.localCerts = null;
} else {
String[] certAlgs = new String[len];
int[] certCheckSums = new int[len];
for (int i = 0; len > i; i++) {
certAlgs[i] = new String(Record.getBytes8(buf));
certCheckSums[i] = Record.getInt32(buf);
}
SSLPossession pos = X509Authentication.createPossession(
hc, certAlgs);
boolean same = false;
if (pos instanceof X509Possession x509Pos
&& x509Pos.popCerts != null
&& x509Pos.popCerts.length == len) {
// Make sure we got the exact same cert chain.
for (int i = 0; i < x509Pos.popCerts.length; i++) {
try {
this.localCerts[i] = X509Factory.cachedGetX509Cert(b);
byte[] encoded = x509Pos.popCerts[i].getEncoded();
String popAlg = x509Pos.popCerts[i]
.getPublicKey().getAlgorithm();
if (certCheckSums[i] == getChecksum(encoded)
&& certAlgs[i].equals(popAlg)) {
// Use certs from cache.
x509Pos.popCerts[i] =
X509Factory.cachedGetX509Cert(encoded);
same = true;
} else {
same = false;
break;
}
} catch (Exception e) {
throw new IOException(e);
}
i++;
}
break;
case 2:
// pre-shared key
// Length of pre-shared key algorithm (one byte)
b = Record.getBytes8(buf);
String alg = new String(b);
// Get encoding
b = Record.getBytes16(buf);
this.preSharedKey = new SecretKeySpec(b, alg);
// Get identity len
i = Record.getInt8(buf);
if (i > 0) {
this.pskIdentity = Record.getBytes8(buf);
} else {
this.pskIdentity = null;
}
if (same) {
this.localCerts = ((X509Possession) pos).popCerts;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) {
SSLLogger.fine("Restored " + len
+ " local certificates from session ticket"
+ " for algorithms " + Arrays.toString(certAlgs));
}
break;
default:
throw new SSLException("Failed local certs of session.");
} else {
this.localCerts = null;
this.invalidated = true;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,session")) {
SSLLogger.warning("Local certificates can not be restored "
+ "from session ticket "
+ "for algorithms " + Arrays.toString(certAlgs));
}
}
}
context = (SSLSessionContextImpl)
this.context = (SSLSessionContextImpl)
hc.sslContext.engineGetServerSessionContext();
this.lastUsedTime = System.currentTimeMillis();
}
// Some situations we cannot provide a stateless ticket, but after it
// has been negotiated
boolean isStatelessable() {
@ -529,49 +509,25 @@ final class SSLSessionImpl extends ExtendedSSLSession {
hos.putInt16(s.id);
}
// Peer Supported signature algorithms
hos.putInt8(peerSupportedSignAlgs.size());
for (SignatureScheme s : peerSupportedSignAlgs) {
hos.putInt16(s.id);
}
// PSK
if (preSharedKey == null ||
preSharedKey.getAlgorithm() == null) {
hos.putInt16(0);
} else {
hos.putInt16(preSharedKey.getAlgorithm().length());
if (preSharedKey.getAlgorithm().length() != 0) {
hos.write(preSharedKey.getAlgorithm().getBytes());
// PreSharedKey is only needed by TLSv1.3,
// masterSecret is only needed by pre-TLSv1.3.
if (protocolVersion.useTLS13PlusSpec()) {
// PSK
if (preSharedKey == null) {
hos.putInt16(0);
} else {
hos.putBytes16(preSharedKey.getEncoded());
}
b = preSharedKey.getEncoded();
hos.putInt16(b.length);
hos.write(b, 0, b.length);
}
// PSK Identity
if (pskIdentity == null) {
hos.putInt8(0);
} else {
hos.putInt8(pskIdentity.length);
hos.write(pskIdentity, 0, pskIdentity.length);
}
// Master Secret
if (getMasterSecret() == null ||
getMasterSecret().getAlgorithm() == null) {
hos.putInt8(0);
} else {
hos.putInt8(getMasterSecret().getAlgorithm().length());
if (getMasterSecret().getAlgorithm().length() != 0) {
hos.write(getMasterSecret().getAlgorithm().getBytes());
// Master Secret
if (getMasterSecret() == null) {
hos.putInt16(0);
} else {
hos.putBytes16(masterSecret.getEncoded());
}
b = getMasterSecret().getEncoded();
hos.putInt16(b.length);
hos.write(b, 0, b.length);
}
hos.putInt8(useExtendedMasterSecret ? 1 : 0);
hos.putInt8(useExtendedMasterSecret ? 1 : 0);
}
// Identification Protocol
if (identificationProtocol == null) {
@ -605,20 +561,11 @@ final class SSLSessionImpl extends ExtendedSSLSession {
hos.putInt32(maximumPacketSize);
hos.putInt32(negotiatedMaxFragLen);
// creation time
// Creation time
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
hos.writeBytes(buffer.putLong(creationTime).array());
// Status Responses
List<byte[]> list = getStatusResponses();
int l = list.size();
hos.putInt16(l);
for (byte[] e : list) {
hos.putInt16(e.length);
hos.write(e);
}
// peer Host & Port
// Peer Host & Port
if (host == null || host.length() == 0) {
hos.putInt8(0);
} else {
@ -627,7 +574,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
hos.putInt16(port);
// Peer cert
// Peer certs.
if (peerCerts == null || peerCerts.length == 0) {
hos.putInt8(0);
} else {
@ -639,34 +586,28 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
}
// Client identity
if (localCerts != null && localCerts.length > 0) {
// certificate based
hos.putInt8(1);
// Local certificates' algorithms and checksums.
// We don't include the complete local certificates in a session ticket
// to decrease the size of ClientHello message.
if (localCerts == null || localCerts.length == 0) {
hos.putInt8(0);
} else {
hos.putInt8(localCerts.length);
for (X509Certificate c : localCerts) {
b = c.getEncoded();
hos.putInt32(b.length);
hos.writeBytes(b);
hos.putBytes8(c.getPublicKey().getAlgorithm().getBytes());
hos.putInt32(getChecksum(c.getEncoded()));
}
} else if (preSharedKey != null) {
// pre-shared key
hos.putInt8(2);
hos.putInt8(preSharedKey.getAlgorithm().length());
hos.write(preSharedKey.getAlgorithm().getBytes());
b = preSharedKey.getEncoded();
hos.putInt32(b.length);
hos.writeBytes(b);
hos.putInt32(pskIdentity.length);
hos.writeBytes(pskIdentity);
} else {
// anonymous
hos.putInt8(0);
}
return hos.toByteArray();
}
private static int getChecksum(byte[] input) {
Adler32 adler32 = new Adler32();
adler32.update(input);
return (int) adler32.getValue();
}
void setMasterSecret(SecretKey secret) {
masterSecret = secret;
}

View File

@ -25,12 +25,16 @@
package sun.security.ssl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@ -70,6 +74,9 @@ final class SessionTicketExtension {
new T12SHSessionTicketConsumer();
static final SSLStringizer steStringizer = new SessionTicketStringizer();
// No need to compress a ticket if it can fit in a single packet.
// Besides, small buffers often end up to be larger when compressed.
static final int MIN_COMPRESS_SIZE = 600;
// Time in milliseconds until key is changed for encrypting session state
private static final int TIMEOUT_DEFAULT = 3600 * 1000;
@ -196,7 +203,7 @@ final class SessionTicketExtension {
data = buf;
}
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) {
byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) {
byte[] encrypted;
if (!hc.statelessResumption ||
@ -213,26 +220,34 @@ final class SessionTicketExtension {
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, key.key,
new GCMParameterSpec(GCM_TAG_LEN, iv));
c.updateAAD(new byte[] {
(byte)(key.num >>> 24),
(byte)(key.num >>> 16),
(byte)(key.num >>> 8),
(byte)(key.num)}
);
byte[] data = session.write();
if (data.length == 0) {
return data;
}
// Compress the session before encryption if needed.
byte compressed = 0;
if (data.length >= MIN_COMPRESS_SIZE) {
data = compress(data);
compressed = 1;
}
ByteBuffer aad = ByteBuffer.allocate(Integer.BYTES + 1);
aad.putInt(key.num).put(compressed);
c.updateAAD(aad);
encrypted = c.doFinal(data);
byte[] result = new byte[encrypted.length + Integer.BYTES +
iv.length];
iv.length + 1];
result[0] = (byte)(key.num >>> 24);
result[1] = (byte)(key.num >>> 16);
result[2] = (byte)(key.num >>> 8);
result[3] = (byte)(key.num);
System.arraycopy(iv, 0, result, Integer.BYTES, iv.length);
result[Integer.BYTES + iv.length] = compressed;
System.arraycopy(encrypted, 0, result,
Integer.BYTES + iv.length, encrypted.length);
Integer.BYTES + iv.length + 1, encrypted.length);
return result;
} catch (Exception e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
@ -257,26 +272,67 @@ final class SessionTicketExtension {
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.DECRYPT_MODE, key.key,
new GCMParameterSpec(GCM_TAG_LEN, iv));
c.updateAAD(new byte[] {
(byte)(keyID >>> 24),
(byte)(keyID >>> 16),
(byte)(keyID >>> 8),
(byte)(keyID)}
);
ByteBuffer out;
out = ByteBuffer.allocate(data.remaining() - GCM_TAG_LEN / 8);
byte compressed = data.get();
ByteBuffer aad = ByteBuffer.allocate(Integer.BYTES + 1);
aad.putInt(keyID).put(compressed);
c.updateAAD(aad);
ByteBuffer out = ByteBuffer.allocate(
data.remaining() - GCM_TAG_LEN / 8);
c.doFinal(data, out);
out.flip();
// Decompress the session after decryption if needed.
if (compressed == 1) {
out = decompress(out);
}
return out;
} catch (Exception e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Decryption failed." + e.getMessage());
}
}
return null;
}
private static byte[] compress(byte[] input) throws IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gos = new GZIPOutputStream(baos)) {
final int decompressedLen = input.length;
gos.write(input, 0, decompressedLen);
gos.finish();
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("decompressed bytes: " + decompressedLen
+ "; compressed bytes: " + baos.size());
}
return baos.toByteArray();
}
}
private static ByteBuffer decompress(ByteBuffer input)
throws IOException {
final int compressedLen = input.remaining();
byte[] bytes = new byte[compressedLen];
input.get(bytes);
try (GZIPInputStream gis = new GZIPInputStream(
new ByteArrayInputStream(bytes))) {
byte[] out = gis.readAllBytes();
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("compressed bytes: " + compressedLen
+ "; decompressed bytes: " + out.length);
}
return ByteBuffer.wrap(out);
}
}
byte[] getEncoded() {
byte[] out = new byte[data.capacity()];
data.duplicate().get(out);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,6 +26,7 @@
* @bug 8206929
* @summary ensure that server only resumes a session if certain properties
* of the session are compatible with the new connection
* @modules java.base/sun.security.x509
* @library /javax/net/ssl/templates
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer BASIC
@ -48,6 +49,7 @@ import java.io.*;
import java.security.*;
import java.net.*;
import java.util.*;
import sun.security.x509.X509CertImpl;
public class ResumeChecksServer extends SSLContextTemplate {
@ -57,7 +59,8 @@ public class ResumeChecksServer extends SSLContextTemplate {
VERSION_2_TO_3,
VERSION_3_TO_2,
CIPHER_SUITE,
SIGNATURE_SCHEME
SIGNATURE_SCHEME,
LOCAL_CERTS
}
public static void main(String[] args) throws Exception {
@ -71,6 +74,7 @@ public class ResumeChecksServer extends SSLContextTemplate {
}
private void run() throws Exception {
SSLSession firstSession;
SSLSession secondSession = null;
SSLContext sslContext = createServerSSLContext();
@ -81,7 +85,7 @@ public class ResumeChecksServer extends SSLContextTemplate {
Client client = startClient(ssock.getLocalPort());
try {
connect(client, ssock, testMode, false);
firstSession = connect(client, ssock, testMode, null);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
@ -89,7 +93,7 @@ public class ResumeChecksServer extends SSLContextTemplate {
long secondStartTime = System.currentTimeMillis();
Thread.sleep(10);
try {
secondSession = connect(client, ssock, testMode, true);
secondSession = connect(client, ssock, testMode, firstSession);
} catch (SSLHandshakeException ex) {
// this is expected
} catch (Exception ex) {
@ -105,6 +109,14 @@ public class ResumeChecksServer extends SSLContextTemplate {
if (secondSession.getCreationTime() > secondStartTime) {
throw new RuntimeException("Session was not reused");
}
// Fail if session's certificates are not restored correctly.
if (!java.util.Arrays.equals(
firstSession.getLocalCertificates(),
secondSession.getLocalCertificates())) {
throw new RuntimeException("Certificates do not match");
}
break;
case CLIENT_AUTH:
// throws an exception if the client is not authenticated
@ -114,6 +126,7 @@ public class ResumeChecksServer extends SSLContextTemplate {
case VERSION_3_TO_2:
case CIPHER_SUITE:
case SIGNATURE_SCHEME:
case LOCAL_CERTS:
// fail if a new session is not created
if (secondSession.getCreationTime() <= secondStartTime) {
throw new RuntimeException("Existing session was used");
@ -153,7 +166,9 @@ public class ResumeChecksServer extends SSLContextTemplate {
}
private static SSLSession connect(Client client, SSLServerSocket ssock,
TestMode mode, boolean second) throws Exception {
TestMode mode, SSLSession firstSession) throws Exception {
boolean second = firstSession != null;
try {
client.signal();
@ -200,9 +215,22 @@ public class ResumeChecksServer extends SSLContextTemplate {
AlgorithmConstraints constraints =
params.getAlgorithmConstraints();
if (second) {
params.setAlgorithmConstraints(new NoSig("ecdsa"));
params.setAlgorithmConstraints(
new NoSig("ecdsa_secp384r1_sha384"));
} else {
params.setAlgorithmConstraints(new NoSig("rsa"));
params.setAlgorithmConstraints(
new NoSig("ecdsa_secp521r1_sha512"));
}
break;
case LOCAL_CERTS:
if (second) {
// Add first session's certificate signature
// algorithm to constraints so local certificates
// can't be restored from the session ticket.
params.setAlgorithmConstraints(
new NoSig(X509CertImpl.toImpl((X509CertImpl)
firstSession.getLocalCertificates()[0])
.getSigAlgName()));
}
break;
default:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,9 +23,10 @@
/*
* @test
* @bug 8211018
* @bug 8211018 8357033
* @summary ensure that server only resumes a session if certain properties
* of the session are compatible with the new connection
* @modules java.base/sun.security.x509
* @library /javax/net/ssl/templates
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CLIENT_AUTH
@ -33,4 +34,5 @@
* @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
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer LOCAL_CERTS
*/