8357033: Reduce stateless session ticket size
Reviewed-by: wetmore, djelinski, ascarpino
This commit is contained in:
parent
26275a10b2
commit
99048c3d4a
@ -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 &&
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user