8226338: Updates to Stateless Resumption

Reviewed-by: xuelei, jnimeh
This commit is contained in:
Anthony Scarpino 2019-07-17 14:37:50 -07:00
parent e352e9327c
commit 200254b479
11 changed files with 677 additions and 113 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -35,7 +35,11 @@ import java.util.Enumeration;
* it could be associated with a server or client who participates in many
* sessions concurrently.
* <p>
* Not all environments will contain session contexts.
* Not all environments will contain session contexts. For example, stateless
* session resumption.
* <p>
* Session contexts may not contain all sessions. For example, stateless
* sessions are not stored in the session context.
* <p>
* There are <code>SSLSessionContext</code> parameters that affect how
* sessions are stored:
@ -68,8 +72,11 @@ public interface SSLSessionContext {
public SSLSession getSession(byte[] sessionId);
/**
* Returns an Enumeration of all session id's grouped under this
* Returns an Enumeration of all known session id's grouped under this
* <code>SSLSessionContext</code>.
* <p>Session contexts may not contain all sessions. For example,
* stateless sessions are not stored in the session context.
* <p>
*
* @return an enumeration of all the Session id's
*/

View file

@ -482,7 +482,7 @@ final class Finished {
shc.conContext.inputRecord.expectingFinishFlight();
} else {
if (shc.handshakeSession.isRejoinable() &&
!shc.statelessResumption) {
!shc.handshakeSession.isStatelessable(shc)) {
((SSLSessionContextImpl)shc.sslContext.
engineGetServerSessionContext()).put(
shc.handshakeSession);
@ -847,6 +847,8 @@ final class Finished {
shc.conContext.serverVerifyData = fm.verifyData;
}
shc.conContext.conSession = shc.handshakeSession.finish();
// update the context
shc.handshakeConsumers.put(
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);

View file

@ -45,7 +45,6 @@ import static sun.security.ssl.SSLHandshake.NEW_SESSION_TICKET;
*/
final class NewSessionTicket {
static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
static final SSLConsumer handshakeConsumer =
new T13NewSessionTicketConsumer();
static final SSLConsumer handshake12Consumer =
@ -60,7 +59,7 @@ final class NewSessionTicket {
*/
abstract static class NewSessionTicketMessage extends HandshakeMessage {
int ticketLifetime;
byte[] ticket;
byte[] ticket = new byte[0];
NewSessionTicketMessage(HandshakeContext context) {
super(context);
@ -83,6 +82,9 @@ final class NewSessionTicket {
"TicketNonce not part of RFC 5077.");
}
boolean isValid() {
return (ticket.length > 0);
}
}
/**
* NewSessionTicket for TLS 1.2 and below (RFC 5077)
@ -102,13 +104,13 @@ final class NewSessionTicket {
// RFC5077 struct {
// uint32 ticket_lifetime;
// opaque ticket<1..2^16-1>;
// opaque ticket<0..2^16-1>;
// } NewSessionTicket;
super(context);
if (m.remaining() < 14) {
if (m.remaining() < 6) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
"Invalid NewSessionTicket message: insufficient data");
}
this.ticketLifetime = Record.getInt32(m);
@ -186,7 +188,7 @@ final class NewSessionTicket {
if (m.remaining() < 14) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
"Invalid NewSessionTicket message: insufficient data");
}
this.ticketLifetime = Record.getInt32(m);
@ -195,18 +197,21 @@ final class NewSessionTicket {
if (m.remaining() < 5) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
"Invalid NewSessionTicket message: insufficient ticket" +
" data");
}
this.ticket = Record.getBytes16(m);
if (ticket.length == 0) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No ticket in the NewSessionTicket handshake message");
}
}
if (m.remaining() < 2) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
"Invalid NewSessionTicket message: extra data");
}
SSLExtension[] supportedExtensions =
@ -310,36 +315,45 @@ final class NewSessionTicket {
@Override
public byte[] produce(ConnectionContext context) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
// The producing happens in server side only.
ServerHandshakeContext shc = (ServerHandshakeContext)context;
if (hc instanceof ServerHandshakeContext) {
// Is this session resumable?
if (!hc.handshakeSession.isRejoinable()) {
return null;
}
// Is this session resumable?
if (!shc.handshakeSession.isRejoinable()) {
return null;
}
// What's the requested PSK key exchange modes?
//
// Note that currently, the NewSessionTicket post-handshake is
// produced and delivered only in the current handshake context
// if required.
PskKeyExchangeModesSpec pkemSpec =
(PskKeyExchangeModesSpec) hc.handshakeExtensions.get(
SSLExtension.PSK_KEY_EXCHANGE_MODES);
if (pkemSpec == null || !pkemSpec.contains(
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
// Client doesn't support PSK with (EC)DHE key establishment.
return null;
}
} else { // PostHandshakeContext
// What's the requested PSK key exchange modes?
//
// Note that currently, the NewSessionTicket post-handshake is
// produced and delivered only in the current handshake context
// if required.
PskKeyExchangeModesSpec pkemSpec =
(PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
SSLExtension.PSK_KEY_EXCHANGE_MODES);
if (pkemSpec == null || !pkemSpec.contains(
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
// Client doesn't support PSK with (EC)DHE key establishment.
return null;
// Check if we have sent a PSK already, then we know it is using a
// allowable PSK exchange key mode
if (!hc.handshakeSession.isPSKable()) {
return null;
}
}
// get a new session ID
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
hc.sslContext.engineGetServerSessionContext();
SessionId newId = new SessionId(true,
shc.sslContext.getSecureRandom());
hc.sslContext.getSecureRandom());
SecretKey resumptionMasterSecret =
shc.handshakeSession.getResumptionMasterSecret();
hc.handshakeSession.getResumptionMasterSecret();
if (resumptionMasterSecret == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
@ -349,10 +363,10 @@ final class NewSessionTicket {
}
// construct the PSK and handshake message
BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
BigInteger nonce = hc.handshakeSession.incrTicketNonceCounter();
byte[] nonceArr = nonce.toByteArray();
SecretKey psk = derivePreSharedKey(
shc.negotiatedCipherSuite.hashAlg,
hc.negotiatedCipherSuite.hashAlg,
resumptionMasterSecret, nonceArr);
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
@ -364,31 +378,35 @@ final class NewSessionTicket {
return null;
}
NewSessionTicketMessage nstm;
NewSessionTicketMessage nstm = null;
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
new SSLSessionImpl(hc.handshakeSession, newId);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setPskIdentity(newId.getId());
if (shc.statelessResumption) {
try {
nstm = new T13NewSessionTicketMessage(shc,
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy));
// If a stateless ticket is allowed, attempt to make one
if (hc.handshakeSession.isStatelessable(hc)) {
nstm = new T13NewSessionTicketMessage(hc,
sessionTimeoutSeconds,
hc.sslContext.getSecureRandom(),
nonceArr,
new SessionTicketSpec().encrypt(hc, sessionCopy));
// If ticket construction failed, switch to session cache
if (!nstm.isValid()) {
hc.statelessResumption = false;
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket stateless " +
"handshake message", nstm);
}
} catch (Exception e) {
// Error with NST ticket, abort NST
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
return null;
}
} else {
nstm = new T13NewSessionTicketMessage(shc, sessionTimeoutSeconds,
shc.sslContext.getSecureRandom(), nonceArr,
}
// If a session cache ticket is being used, make one
if (!hc.handshakeSession.isStatelessable(hc)) {
nstm = new T13NewSessionTicketMessage(hc, sessionTimeoutSeconds,
hc.sslContext.getSecureRandom(), nonceArr,
newId.getId());
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
@ -399,13 +417,21 @@ final class NewSessionTicket {
// create and cache the new session
// The new session must be a child of the existing session so
// they will be invalidated together, etc.
shc.handshakeSession.addChild(sessionCopy);
hc.handshakeSession.addChild(sessionCopy);
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
sessionCache.put(sessionCopy);
}
// Output the handshake message.
nstm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
if (nstm != null) {
// should never be null
nstm.write(hc.handshakeOutput);
hc.handshakeOutput.flush();
}
if (hc instanceof PostHandshakeContext) {
((PostHandshakeContext) hc).finish();
}
// The message has been delivered.
return null;
@ -448,23 +474,16 @@ final class NewSessionTicket {
return null;
}
NewSessionTicketMessage nstm;
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
sessionCopy.setPskIdentity(newId.getId());
try {
nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds,
new SessionTicketSpec().encrypt(shc, sessionCopy));
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket stateless handshake message", nstm);
}
} catch (Exception e) {
// Abort on error with NST ticket
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
return null;
NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc,
sessionTimeoutSeconds,
new SessionTicketSpec().encrypt(shc, sessionCopy));
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket stateless handshake message", nstm);
}
// Output the handshake message.
@ -505,6 +524,9 @@ final class NewSessionTicket {
"Consuming NewSessionTicket message", nstm);
}
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
hc.sslContext.engineGetClientSessionContext();
// discard tickets with timeout 0
if (nstm.ticketLifetime <= 0 ||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
@ -513,12 +535,10 @@ final class NewSessionTicket {
"Discarding NewSessionTicket with lifetime "
+ nstm.ticketLifetime, nstm);
}
sessionCache.remove(hc.handshakeSession.getSessionId());
return;
}
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
hc.sslContext.engineGetClientSessionContext();
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(

View file

@ -54,6 +54,7 @@ final class PostHandshakeContext extends HandshakeContext {
handshakeConsumers = new LinkedHashMap<>(consumers);
handshakeFinished = true;
handshakeSession = context.conSession;
}
@Override
@ -82,4 +83,9 @@ final class PostHandshakeContext extends HandshakeContext {
SSLHandshake.nameOf(handshakeType), be);
}
}
// Finish this PostHandshake event
void finish() {
handshakeSession = null;
}
}

View file

@ -344,6 +344,12 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
hsStatus = tryKeyUpdate(hsStatus);
}
// Check if NewSessionTicket PostHandshake message needs to be sent
if (conContext.conSession.updateNST &&
!conContext.sslConfig.isClientMode) {
hsStatus = tryNewSessionTicket(hsStatus);
}
// update context status
ciphertext.handshakeStatus = hsStatus;
@ -397,6 +403,29 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
return currentHandshakeStatus;
}
// Try to generate a PostHandshake NewSessionTicket message. This is
// TLS 1.3 only.
private HandshakeStatus tryNewSessionTicket(
HandshakeStatus currentHandshakeStatus) throws IOException {
// Don't bother to kickstart if handshaking is in progress, or if the
// connection is not duplex-open.
if ((conContext.handshakeContext == null) &&
conContext.protocolVersion.useTLS13PlusSpec() &&
!conContext.isOutboundClosed() &&
!conContext.isInboundClosed() &&
!conContext.isBroken) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("trigger NST");
}
conContext.conSession.updateNST = false;
NewSessionTicket.kickstartProducer.produce(
new PostHandshakeContext(conContext));
return conContext.getHandshakeStatus();
}
return currentHandshakeStatus;
}
private static void checkParams(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) {

View file

@ -27,6 +27,7 @@ package sun.security.ssl;
import sun.security.x509.X509CertImpl;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.ByteBuffer;
@ -35,6 +36,7 @@ import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
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;
@ -104,7 +106,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
private X509Certificate[] localCerts;
private PrivateKey localPrivateKey;
private final Collection<SignatureScheme> localSupportedSignAlgs;
private String[] peerSupportedSignAlgs; // for certificate
private Collection<SignatureScheme> peerSupportedSignAlgs; //for certificate
private boolean useDefaultPeerSignAlgs = false;
private List<byte[]> statusResponses;
private SecretKey resumptionMasterSecret;
@ -236,7 +238,8 @@ final class SSLSessionImpl extends ExtendedSSLSession {
baseSession.localSupportedSignAlgs == null ?
Collections.emptySet() : baseSession.localSupportedSignAlgs;
this.peerSupportedSignAlgs =
baseSession.getPeerSupportedSignatureAlgorithms();
baseSession.peerSupportedSignAlgs == null ?
Collections.emptySet() : baseSession.peerSupportedSignAlgs;
this.serverNameIndication = baseSession.serverNameIndication;
this.requestedServerNames = baseSession.getRequestedServerNames();
this.masterSecret = baseSession.getMasterSecret();
@ -261,8 +264,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
/**
* < 2 bytes > protocolVersion
* < 2 bytes > cipherSuite
* < 2 bytes > localSupportedSignAlgs entries
* < 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
@ -281,6 +286,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* < 1 byte > ServerName length
* < length in bytes > ServerName
* < 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
@ -302,17 +310,17 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* < length in bytes> PSK identity
* Anonymous
* < 1 byte >
* < 4 bytes > maximumPacketSize
* < 4 bytes > negotiatedMaxFragSize
*/
SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException {
int i = 0;
byte[] b;
this.localSupportedSignAlgs = new ArrayList<>();
boundValues = null;
this.protocolVersion = ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
boundValues = new ConcurrentHashMap<>();
this.protocolVersion =
ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
if (protocolVersion.useTLS13PlusSpec()) {
this.sessionId = new SessionId(false, null);
@ -322,14 +330,26 @@ final class SSLSessionImpl extends ExtendedSSLSession {
hc.sslContext.getSecureRandom());
}
this.cipherSuite = CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
this.cipherSuite =
CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
// Local Supported signature algorithms
i = Short.toUnsignedInt(buf.getShort());
ArrayList<SignatureScheme> list = new ArrayList<>();
i = Byte.toUnsignedInt(buf.get());
while (i-- > 0) {
this.localSupportedSignAlgs.add(SignatureScheme.valueOf(
list.add(SignatureScheme.valueOf(
Short.toUnsignedInt(buf.getShort())));
}
this.localSupportedSignAlgs = Collections.unmodifiableCollection(list);
// Peer Supported signature algorithms
i = Byte.toUnsignedInt(buf.get());
list.clear();
while (i-- > 0) {
list.add(SignatureScheme.valueOf(
Short.toUnsignedInt(buf.getShort())));
}
this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list);
// PSK
i = Short.toUnsignedInt(buf.getShort());
@ -410,9 +430,27 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
}
maximumPacketSize = buf.getInt();
negotiatedMaxFragLen = buf.getInt();
// Get creation time
this.creationTime = buf.getLong();
// Get Buffer sizes
// Status Response
len = Short.toUnsignedInt(buf.getShort());
if (len == 0) {
statusResponses = Collections.emptyList();
} else {
statusResponses = new ArrayList<>();
}
while (len-- > 0) {
b = new byte[Short.toUnsignedInt(buf.getShort())];
buf.get(b);
statusResponses.add(b);
}
// Get Peer host & port
i = Byte.toUnsignedInt(buf.get());
if (i == 0) {
@ -484,6 +522,33 @@ final class SSLSessionImpl extends ExtendedSSLSession {
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(HandshakeContext hc) {
if (!hc.statelessResumption) {
return false;
}
// If there is no getMasterSecret with TLS1.2 or under, do not resume.
if (!protocolVersion.useTLS13PlusSpec() &&
getMasterSecret().getEncoded() == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest("No MasterSecret, cannot make stateless" +
" ticket");
}
return false;
}
if (boundValues != null && boundValues.size() > 0) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.finest("There are boundValues, cannot make" +
" stateless ticket");
}
return false;
}
return true;
}
/**
@ -497,11 +562,14 @@ final class SSLSessionImpl extends ExtendedSSLSession {
hos.putInt16(cipherSuite.id);
// Local Supported signature algorithms
int l = localSupportedSignAlgs.size();
hos.putInt16(l);
SignatureScheme[] sig = new SignatureScheme[l];
localSupportedSignAlgs.toArray(sig);
for (SignatureScheme s : sig) {
hos.putInt8(localSupportedSignAlgs.size());
for (SignatureScheme s : localSupportedSignAlgs) {
hos.putInt16(s.id);
}
// Peer Supported signature algorithms
hos.putInt8(peerSupportedSignAlgs.size());
for (SignatureScheme s : peerSupportedSignAlgs) {
hos.putInt16(s.id);
}
@ -564,16 +632,30 @@ final class SSLSessionImpl extends ExtendedSSLSession {
// List of SNIServerName
hos.putInt16(requestedServerNames.size());
if (requestedServerNames.size() > 0) {
for (SNIServerName host: requestedServerNames) {
for (SNIServerName host : requestedServerNames) {
b = host.getEncoded();
hos.putInt8(b.length);
hos.write(b, 0, b.length);
}
}
// Buffer sizes
hos.putInt32(maximumPacketSize);
hos.putInt32(negotiatedMaxFragLen);
// 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
if (host == null || host.length() == 0) {
hos.putInt8(0);
@ -649,10 +731,14 @@ final class SSLSessionImpl extends ExtendedSSLSession {
BigInteger incrTicketNonceCounter() {
BigInteger result = ticketNonceCounter;
ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1));
ticketNonceCounter = ticketNonceCounter.add(BigInteger.ONE);
return result;
}
boolean isPSKable() {
return (ticketNonceCounter.compareTo(BigInteger.ZERO) > 0);
}
/**
* Returns the master secret ... treat with extreme caution!
*/
@ -725,8 +811,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
void setPeerSupportedSignatureAlgorithms(
Collection<SignatureScheme> signatureSchemes) {
peerSupportedSignAlgs =
SignatureScheme.getAlgorithmNames(signatureSchemes);
peerSupportedSignAlgs = signatureSchemes;
}
// TLS 1.2 only
@ -740,16 +825,20 @@ final class SSLSessionImpl extends ExtendedSSLSession {
// certificates and server key exchange), it MUST send the
// signature_algorithms extension, listing the algorithms it
// is willing to accept.
private static final ArrayList<SignatureScheme> defaultPeerSupportedSignAlgs =
new ArrayList<>(Arrays.asList(SignatureScheme.RSA_PKCS1_SHA1,
SignatureScheme.DSA_SHA1,
SignatureScheme.ECDSA_SHA1));
void setUseDefaultPeerSignAlgs() {
useDefaultPeerSignAlgs = true;
peerSupportedSignAlgs = new String[] {
"SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"};
peerSupportedSignAlgs = defaultPeerSupportedSignAlgs;
}
// Returns the connection session.
SSLSessionImpl finish() {
if (useDefaultPeerSignAlgs) {
this.peerSupportedSignAlgs = new String[0];
peerSupportedSignAlgs = Collections.emptySet();
}
return this;
@ -1212,6 +1301,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* sessions can be shared across different protection domains.
*/
private final ConcurrentHashMap<SecureKey, Object> boundValues;
boolean updateNST;
/**
* Assigns a session value. Session change events are given if
@ -1238,6 +1328,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
e = new SSLSessionBindingEvent(this, key);
((SSLSessionBindingListener)value).valueBound(e);
}
if (protocolVersion.useTLS13PlusSpec()) {
updateNST = true;
}
}
/**
@ -1273,6 +1366,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
e = new SSLSessionBindingEvent(this, key);
((SSLSessionBindingListener)value).valueUnbound(e);
}
if (protocolVersion.useTLS13PlusSpec()) {
updateNST = true;
}
}
@ -1474,11 +1570,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
*/
@Override
public String[] getPeerSupportedSignatureAlgorithms() {
if (peerSupportedSignAlgs != null) {
return peerSupportedSignAlgs.clone();
}
return new String[0];
return SignatureScheme.getAlgorithmNames(peerSupportedSignAlgs);
}
/**

View file

@ -1264,6 +1264,11 @@ public final class SSLSocketImpl
conContext.outputRecord.writeCipher.atKeyLimit()) {
tryKeyUpdate();
}
// Check if NewSessionTicket PostHandshake message needs to be sent
if (conContext.conSession.updateNST) {
conContext.conSession.updateNST = false;
tryNewSessionTicket();
}
}
@Override
@ -1499,6 +1504,25 @@ public final class SSLSocketImpl
}
}
// Try to generate a PostHandshake NewSessionTicket message. This is
// TLS 1.3 only.
private void tryNewSessionTicket() throws IOException {
// Don't bother to kickstart if handshaking is in progress, or if the
// connection is not duplex-open.
if (!conContext.sslConfig.isClientMode &&
conContext.protocolVersion.useTLS13PlusSpec() &&
conContext.handshakeContext == null &&
!conContext.isOutboundClosed() &&
!conContext.isInboundClosed() &&
!conContext.isBroken) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.finest("trigger new session ticket");
}
NewSessionTicket.kickstartProducer.produce(
new PostHandshakeContext(conContext));
}
}
/**
* Initialize the handshaker and socket streams.
*

View file

@ -1155,14 +1155,14 @@ final class ServerHello {
chc, chc.resumingSession.getMasterSecret());
}
chc.conContext.consumers.putIfAbsent(
ContentType.CHANGE_CIPHER_SPEC.id,
ChangeCipherSpec.t10Consumer);
if (chc.statelessResumption) {
chc.handshakeConsumers.putIfAbsent(
SSLHandshake.NEW_SESSION_TICKET.id,
SSLHandshake.NEW_SESSION_TICKET);
}
chc.conContext.consumers.putIfAbsent(
ContentType.CHANGE_CIPHER_SPEC.id,
ChangeCipherSpec.t10Consumer);
chc.handshakeConsumers.put(
SSLHandshake.FINISHED.id,
SSLHandshake.FINISHED);

View file

@ -46,7 +46,6 @@ import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Locale;
/**
@ -255,13 +254,17 @@ final class SessionTicketExtension {
data = buf;
}
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session)
throws IOException {
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) {
byte[] encrypted;
StatelessKey key = KeyState.getCurrentKey(hc);
byte[] iv = new byte[16];
if (!hc.handshakeSession.isStatelessable(hc)) {
return new byte[0];
}
try {
StatelessKey key = KeyState.getCurrentKey(hc);
byte[] iv = new byte[16];
SecureRandom random = hc.sslContext.getSecureRandom();
random.nextBytes(iv);
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
@ -273,8 +276,11 @@ final class SessionTicketExtension {
(byte)(key.num >>> 8),
(byte)(key.num)}
);
encrypted = c.doFinal(session.write());
byte[] data = session.write();
if (data.length == 0) {
return data;
}
encrypted = c.doFinal(data);
byte[] result = new byte[encrypted.length + Integer.BYTES +
iv.length];
result[0] = (byte)(key.num >>> 24);
@ -286,7 +292,10 @@ final class SessionTicketExtension {
Integer.BYTES + iv.length, encrypted.length);
return result;
} catch (Exception e) {
throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Encryption failed." + e);
}
return new byte[0];
}
}
@ -311,11 +320,7 @@ final class SessionTicketExtension {
(byte)(keyID >>> 8),
(byte)(keyID)}
);
/*
return ByteBuffer.wrap(c.doFinal(data,
Integer.BYTES + iv.length,
data.length - (Integer.BYTES + iv.length)));
*/
ByteBuffer out;
out = ByteBuffer.allocate(data.remaining() - GCM_TAG_LEN / 8);
c.doFinal(data, out);