mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
8226338: Updates to Stateless Resumption
Reviewed-by: xuelei, jnimeh
This commit is contained in:
parent
e352e9327c
commit
200254b479
11 changed files with 677 additions and 113 deletions
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue