8211018: Session Resumption without Server-Side State

Reviewed-by: xuelei, jnimeh, jjiang
This commit is contained in:
Anthony Scarpino 2019-06-11 16:31:37 -07:00
parent 9597144fb2
commit 94e1d7530f
21 changed files with 1620 additions and 150 deletions

View file

@ -35,7 +35,6 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
@ -969,11 +968,24 @@ final class ClientHello {
}
}
// Is it an abbreviated handshake?
if (clientHello.sessionId.length() != 0) {
SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext())
.get(clientHello.sessionId.getId());
// Consume a Session Ticket Extension if it exists
SSLExtension[] ext = new SSLExtension[]{
SSLExtension.CH_SESSION_TICKET
};
clientHello.extensions.consumeOnLoad(shc, ext);
// Does the client want to resume a session?
if (clientHello.sessionId.length() != 0 || shc.statelessResumption) {
SSLSessionContextImpl cache = (SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext();
SSLSessionImpl previous;
// Use the stateless session ticket if provided
if (shc.statelessResumption) {
previous = shc.resumingSession;
} else {
previous = cache.get(clientHello.sessionId.getId());
}
boolean resumingSession =
(previous != null) && previous.isRejoinable();
@ -1051,14 +1063,20 @@ final class ClientHello {
// the resuming options later.
shc.isResumption = resumingSession;
shc.resumingSession = resumingSession ? previous : null;
if (!resumingSession && SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Session not resumed.");
}
}
// cache the client random number for further using
shc.clientHelloRandom = clientHello.clientRandom;
// Check and launch ClientHello extensions.
SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
SSLHandshake.CLIENT_HELLO);
SSLExtension[] extTypes = shc.sslConfig.getExclusiveExtensions(
SSLHandshake.CLIENT_HELLO,
Arrays.asList(SSLExtension.CH_SESSION_TICKET));
clientHello.extensions.consumeOnLoad(shc, extTypes);
//
@ -1276,11 +1294,25 @@ final class ClientHello {
}
}
// Is it an abbreviated handshake?
// Does the client want to resume a session?
if (clientHello.sessionId.length() != 0) {
SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext())
.get(clientHello.sessionId.getId());
SSLSessionContextImpl cache = (SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext();
// Consume a Session Ticket Extension if it exists
SSLExtension[] ext = new SSLExtension[]{
SSLExtension.CH_SESSION_TICKET
};
clientHello.extensions.consumeOnLoad(shc, ext);
SSLSessionImpl previous;
// Use stateless session ticket if provided.
if (shc.statelessResumption) {
previous = shc.resumingSession;
} else {
previous = cache.get(clientHello.sessionId.getId());
}
boolean resumingSession =
(previous != null) && previous.isRejoinable();

View file

@ -410,6 +410,10 @@ final class Finished {
chc.conContext.clientVerifyData = fm.verifyData;
}
if (chc.statelessResumption) {
chc.handshakeConsumers.put(
SSLHandshake.NEW_SESSION_TICKET.id, SSLHandshake.NEW_SESSION_TICKET);
}
// update the consumers and producers
if (!chc.isResumption) {
chc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
@ -441,6 +445,10 @@ final class Finished {
private byte[] onProduceFinished(ServerHandshakeContext shc,
HandshakeMessage message) throws IOException {
if (shc.statelessResumption) {
NewSessionTicket.handshake12Producer.produce(shc, message);
}
// Refresh handshake hash
shc.handshakeHash.update();
@ -473,7 +481,8 @@ final class Finished {
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
shc.conContext.inputRecord.expectingFinishFlight();
} else {
if (shc.handshakeSession.isRejoinable()) {
if (shc.handshakeSession.isRejoinable() &&
!shc.statelessResumption) {
((SSLSessionContextImpl)shc.sslContext.
engineGetServerSessionContext()).put(
shc.handshakeSession);
@ -591,7 +600,8 @@ final class Finished {
}
if (shc.isResumption) {
if (shc.handshakeSession.isRejoinable()) {
if (shc.handshakeSession.isRejoinable() &&
!shc.statelessResumption) {
((SSLSessionContextImpl)shc.sslContext.
engineGetServerSessionContext()).put(
shc.handshakeSession);
@ -915,9 +925,9 @@ final class Finished {
// save the session
if (!chc.isResumption && chc.handshakeSession.isRejoinable()) {
SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
chc.sslContext.engineGetClientSessionContext();
sessionContext.put(chc.handshakeSession);
((SSLSessionContextImpl)chc.sslContext.
engineGetClientSessionContext()).
put(chc.handshakeSession);
}
// derive salt secret
@ -1028,8 +1038,9 @@ final class Finished {
shc.negotiatedProtocol);
}
// save the session
if (!shc.isResumption && shc.handshakeSession.isRejoinable()) {
// Save the session if possible and not stateless
if (!shc.statelessResumption && !shc.isResumption &&
shc.handshakeSession.isRejoinable()) {
SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
sessionContext.put(shc.handshakeSession);

View file

@ -102,6 +102,8 @@ abstract class HandshakeContext implements ConnectionContext {
// Resumption
boolean isResumption;
SSLSessionImpl resumingSession;
// Session is using stateless resumption
boolean statelessResumption = false;
final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
volatile boolean taskDelegated = false;
@ -551,7 +553,7 @@ abstract class HandshakeContext implements ConnectionContext {
List<SNIServerName> getRequestedServerNames() {
if (requestedServerNames == null) {
return Collections.<SNIServerName>emptyList();
return Collections.emptyList();
}
return requestedServerNames;
}

View file

@ -28,40 +28,139 @@ import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.ProviderException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Locale;
import javax.crypto.SecretKey;
import javax.net.ssl.SSLHandshakeException;
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.util.HexDumpEncoder;
import static sun.security.ssl.SSLHandshake.NEW_SESSION_TICKET;
/**
* Pack of the NewSessionTicket handshake message.
*/
final class NewSessionTicket {
private static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
static final SSLConsumer handshakeConsumer =
new NewSessionTicketConsumer();
new T13NewSessionTicketConsumer();
static final SSLConsumer handshake12Consumer =
new T12NewSessionTicketConsumer();
static final SSLProducer kickstartProducer =
new NewSessionTicketKickstartProducer();
static final HandshakeProducer handshakeProducer =
new NewSessionTicketProducer();
static final HandshakeProducer handshake12Producer =
new T12NewSessionTicketProducer();
/**
* The NewSessionTicketMessage handshake message.
* The NewSessionTicketMessage handshake messages.
*/
static final class NewSessionTicketMessage extends HandshakeMessage {
final int ticketLifetime;
final int ticketAgeAdd;
final byte[] ticketNonce;
final byte[] ticket;
final SSLExtensions extensions;
abstract static class NewSessionTicketMessage extends HandshakeMessage {
int ticketLifetime;
byte[] ticket;
NewSessionTicketMessage(HandshakeContext context,
NewSessionTicketMessage(HandshakeContext context) {
super(context);
}
@Override
public SSLHandshake handshakeType() {
return NEW_SESSION_TICKET;
}
// For TLS 1.3 only
int getTicketAgeAdd() throws IOException {
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"TicketAgeAdd not part of RFC 5077.");
}
// For TLS 1.3 only
byte[] getTicketNonce() throws IOException {
throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"TicketNonce not part of RFC 5077.");
}
}
/**
* NewSessionTicket for TLS 1.2 and below (RFC 5077)
*/
static final class T12NewSessionTicketMessage extends NewSessionTicketMessage {
T12NewSessionTicketMessage(HandshakeContext context,
int ticketLifetime, byte[] ticket) {
super(context);
this.ticketLifetime = ticketLifetime;
this.ticket = ticket;
}
T12NewSessionTicketMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
// RFC5077 struct {
// uint32 ticket_lifetime;
// opaque ticket<1..2^16-1>;
// } NewSessionTicket;
super(context);
if (m.remaining() < 14) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
}
this.ticketLifetime = Record.getInt32(m);
this.ticket = Record.getBytes16(m);
}
@Override
public SSLHandshake handshakeType() {
return NEW_SESSION_TICKET;
}
@Override
public int messageLength() {
return 4 + // ticketLifetime
2 + ticket.length; // len of ticket + ticket
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
hos.putInt32(ticketLifetime);
hos.putBytes16(ticket);
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"NewSessionTicket\": '{'\n" +
" \"ticket_lifetime\" : \"{0}\",\n" +
" \"ticket\" : '{'\n" +
"{1}\n" +
" '}'" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
ticketLifetime,
Utilities.indent(hexEncoder.encode(ticket), " "),
};
return messageFormat.format(messageFields);
}
}
/**
* NewSessionTicket defined by the TLS 1.3
*/
static final class T13NewSessionTicketMessage extends NewSessionTicketMessage {
int ticketAgeAdd;
byte[] ticketNonce;
SSLExtensions extensions;
T13NewSessionTicketMessage(HandshakeContext context,
int ticketLifetime, SecureRandom generator,
byte[] ticketNonce, byte[] ticket) {
super(context);
@ -73,7 +172,7 @@ final class NewSessionTicket {
this.extensions = new SSLExtensions(this);
}
NewSessionTicketMessage(HandshakeContext context,
T13NewSessionTicketMessage(HandshakeContext context,
ByteBuffer m) throws IOException {
super(context);
@ -84,6 +183,7 @@ final class NewSessionTicket {
// opaque ticket<1..2^16-1>;
// Extension extensions<0..2^16-2>;
// } NewSessionTicket;
if (m.remaining() < 14) {
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
"Invalid NewSessionTicket message: no sufficient data");
@ -111,24 +211,36 @@ final class NewSessionTicket {
SSLExtension[] supportedExtensions =
context.sslConfig.getEnabledExtensions(
SSLHandshake.NEW_SESSION_TICKET);
NEW_SESSION_TICKET);
this.extensions = new SSLExtensions(this, m, supportedExtensions);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.NEW_SESSION_TICKET;
return NEW_SESSION_TICKET;
}
int getTicketAgeAdd() {
return ticketAgeAdd;
}
byte[] getTicketNonce() {
return ticketNonce;
}
@Override
public int messageLength() {
int extLen = extensions.length();
if (extLen == 0) {
extLen = 2; // empty extensions
}
return 8 + ticketNonce.length + 1 +
ticket.length + 2 + extLen;
return 4 +// ticketLifetime
4 + // ticketAgeAdd
1 + ticketNonce.length + // len of nonce + nonce
2 + ticket.length + // len of ticket + ticket
extLen;
}
@Override
@ -153,18 +265,21 @@ final class NewSessionTicket {
" \"ticket_lifetime\" : \"{0}\",\n" +
" \"ticket_age_add\" : \"{1}\",\n" +
" \"ticket_nonce\" : \"{2}\",\n" +
" \"ticket\" : \"{3}\",\n" +
" \"ticket\" : '{'\n" +
"{3}\n" +
" '}'" +
" \"extensions\" : [\n" +
"{4}\n" +
" ]\n" +
"'}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
ticketLifetime,
"<omitted>", //ticketAgeAdd should not be logged
Utilities.toHexString(ticketNonce),
Utilities.toHexString(ticket),
Utilities.indent(hexEncoder.encode(ticket), " "),
Utilities.indent(extensions.toString(), " ")
};
@ -248,25 +363,46 @@ final class NewSessionTicket {
}
return null;
}
NewSessionTicketMessage nstm = new NewSessionTicketMessage(shc,
NewSessionTicketMessage nstm;
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setPskIdentity(newId.getId());
if (shc.statelessResumption) {
try {
nstm = new T13NewSessionTicketMessage(shc,
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
nonceArr, newId.getId());
nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy));
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket handshake message", nstm);
"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,
newId.getId());
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced NewSessionTicket handshake message",
nstm);
}
// create and cache the new session
// The new session must be a child of the existing session so
// they will be invalidated together, etc.
SSLSessionImpl sessionCopy =
new SSLSessionImpl(shc.handshakeSession, newId);
shc.handshakeSession.addChild(sessionCopy);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setPskIdentity(newId.getId());
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
sessionCache.put(sessionCopy);
}
// Output the handshake message.
nstm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
@ -277,13 +413,13 @@ final class NewSessionTicket {
}
/**
* The "NewSessionTicket" handshake message producer.
* The "NewSessionTicket" handshake message producer for RFC 5077
*/
private static final class NewSessionTicketProducer
private static final class T12NewSessionTicketProducer
implements HandshakeProducer {
// Prevent instantiation of this class.
private NewSessionTicketProducer() {
private T12NewSessionTicketProducer() {
// blank
}
@ -291,18 +427,59 @@ final class NewSessionTicket {
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
// NSTM may be sent in response to handshake messages.
// For example: key update
ServerHandshakeContext shc = (ServerHandshakeContext)context;
throw new ProviderException(
"NewSessionTicket handshake producer not implemented");
// Is this session resumable?
if (!shc.handshakeSession.isRejoinable()) {
return null;
}
// get a new session ID
SessionId newId = shc.handshakeSession.getSessionId();
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session timeout is too long. No ticket sent.");
}
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;
}
// Output the handshake message.
nstm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
// The message has been delivered.
return null;
}
}
private static final
class NewSessionTicketConsumer implements SSLConsumer {
class T13NewSessionTicketConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private NewSessionTicketConsumer() {
private T13NewSessionTicketConsumer() {
// blank
}
@ -317,13 +494,12 @@ final class NewSessionTicket {
// upon sending its Finished rather than waiting for the client
// Finished.
//
// The consuming happens in client side only. As the server
// may send the NewSessionTicket before handshake complete, the
// context may be a PostHandshakeContext or HandshakeContext
// instance.
// The consuming happens in client side only and is received after
// the server's Finished message with PostHandshakeContext.
HandshakeContext hc = (HandshakeContext)context;
NewSessionTicketMessage nstm =
new NewSessionTicketMessage(hc, message);
new T13NewSessionTicketMessage(hc, message);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming NewSessionTicket message", nstm);
@ -352,21 +528,24 @@ final class NewSessionTicket {
}
SSLSessionImpl sessionToSave = hc.conContext.conSession;
SecretKey psk = null;
if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
SecretKey resumptionMasterSecret =
sessionToSave.getResumptionMasterSecret();
if (resumptionMasterSecret == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session has no resumption master secret. Ignoring ticket.");
"Session has no resumption master secret." +
" Ignoring ticket.");
}
return;
}
// derive the PSK
SecretKey psk = derivePreSharedKey(
sessionToSave.getSuite().hashAlg, resumptionMasterSecret,
nstm.ticketNonce);
psk = derivePreSharedKey(
sessionToSave.getSuite().hashAlg,
resumptionMasterSecret, nstm.getTicketNonce());
}
// create and cache the new session
// The new session must be a child of the existing session so
@ -377,13 +556,68 @@ final class NewSessionTicket {
newId);
sessionToSave.addChild(sessionCopy);
sessionCopy.setPreSharedKey(psk);
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
sessionCopy.setPskIdentity(nstm.ticket);
sessionCache.put(sessionCopy);
// clean handshake context
if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
hc.conContext.finishPostHandshake();
}
}
}
private static final
class T12NewSessionTicketConsumer implements SSLConsumer {
// Prevent instantiation of this class.
private T12NewSessionTicketConsumer() {
// blank
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
hc.handshakeConsumers.remove(NEW_SESSION_TICKET.id);
NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(hc,
message);
if (nstm.ticket.length == 0) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("NewSessionTicket ticket was empty");
}
return;
}
// discard tickets with timeout 0
if (nstm.ticketLifetime <= 0 ||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Discarding NewSessionTicket with lifetime "
+ nstm.ticketLifetime, nstm);
}
return;
}
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
hc.sslContext.engineGetClientSessionContext();
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Session cache lifetime is too long. Discarding ticket.");
}
return;
}
hc.handshakeSession.setPskIdentity(nstm.ticket);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Consuming NewSessionTicket\n" +
nstm.toString());
}
}
}
}

View file

@ -32,7 +32,6 @@ import java.util.List;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Arrays;
import java.util.Objects;
import java.util.Collection;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
@ -42,6 +41,9 @@ import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
import sun.security.util.HexDumpEncoder;
import static sun.security.ssl.SSLExtension.*;
/**
@ -88,7 +90,7 @@ final class PreSharedKeyExtension {
@Override
public String toString() {
return "{" + Utilities.toHexString(identity) + "," +
return "{" + Utilities.toHexString(identity) + ", " +
obfuscatedAge + "}";
}
}
@ -208,8 +210,10 @@ final class PreSharedKeyExtension {
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"PreSharedKey\": '{'\n" +
" \"identities\" : \"{0}\",\n" +
" \"binders\" : \"{1}\",\n" +
" \"identities\": '{'\n" +
"{0}\n" +
" '}'" +
" \"binders\": \"{1}\",\n" +
"'}'",
Locale.ENGLISH);
@ -222,9 +226,13 @@ final class PreSharedKeyExtension {
}
String identitiesString() {
HexDumpEncoder hexEncoder = new HexDumpEncoder();
StringBuilder result = new StringBuilder();
for (PskIdentity curId : identities) {
result.append(curId.toString() + "\n");
result.append(" {\n"+ Utilities.indent(
hexEncoder.encode(curId.identity), " ") +
"\n }\n");
}
return result.toString();
@ -278,7 +286,7 @@ final class PreSharedKeyExtension {
this.selectedIdentity = Record.getInt16(m);
}
byte[] getEncoded() throws IOException {
byte[] getEncoded() {
return new byte[] {
(byte)((selectedIdentity >> 8) & 0xFF),
(byte)(selectedIdentity & 0xFF)
@ -368,8 +376,36 @@ final class PreSharedKeyExtension {
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
int idIndex = 0;
SSLSessionImpl s = null;
for (PskIdentity requestedId : pskSpec.identities) {
SSLSessionImpl s = sessionCache.get(requestedId.identity);
// If we are keeping state, see if the identity is in the cache
if (requestedId.identity.length == SessionId.MAX_LENGTH) {
s = sessionCache.get(requestedId.identity);
}
// See if the identity is a stateless ticket
if (s == null &&
requestedId.identity.length > SessionId.MAX_LENGTH &&
sessionCache.statelessEnabled()) {
ByteBuffer b =
new SessionTicketSpec(requestedId.identity).
decrypt(shc);
if (b != null) {
try {
s = new SSLSessionImpl(shc, b);
} catch (IOException | RuntimeException e) {
s = null;
}
}
if (b == null || s == null) {
if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Stateless session ticket invalid");
}
}
}
if (s != null && canRejoin(clientHello, shc, s)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Resuming session: ", s);
@ -391,7 +427,6 @@ final class PreSharedKeyExtension {
shc.resumingSession = null;
}
}
// update the context
shc.handshakeExtensions.put(
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
@ -708,7 +743,8 @@ final class PreSharedKeyExtension {
int hashLength, List<PskIdentity> identities) {
List<byte[]> binders = new ArrayList<>();
byte[] binderProto = new byte[hashLength];
for (PskIdentity curId : identities) {
int i = identities.size();
while (i-- > 0) {
binders.add(binderProto);
}

View file

@ -71,11 +71,13 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private volatile StatusResponseManager statusResponseManager;
private final ReentrantLock contextLock = new ReentrantLock();
final HashMap<Integer, SessionTicketExtension.StatelessKey> keyHashMap = new HashMap<>();
SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl();
serverCache = new SSLSessionContextImpl();
clientCache = new SSLSessionContextImpl(false);
serverCache = new SSLSessionContextImpl(true);
}
@Override

View file

@ -309,8 +309,28 @@ enum SSLExtension implements SSLStringizer {
// extensions defined in RFC 7924
CACHED_INFO (0x0019, "cached_info"),
// extensions defined in RFC 4507/5077
SESSION_TICKET (0x0023, "session_ticket"),
// extensions defined in RFC 5077
CH_SESSION_TICKET (0x0023, "session_ticket",
SSLHandshake.CLIENT_HELLO,
ProtocolVersion.PROTOCOLS_10_12,
SessionTicketExtension.chNetworkProducer,
SessionTicketExtension.chOnLoadConsumer,
null,
null,
null,
SessionTicketExtension.steStringizer),
//null),
SH_SESSION_TICKET (0x0023, "session_ticket",
SSLHandshake.SERVER_HELLO,
ProtocolVersion.PROTOCOLS_10_12,
SessionTicketExtension.shNetworkProducer,
SessionTicketExtension.shOnLoadConsumer,
null,
null,
null,
SessionTicketExtension.steStringizer),
//null),
// extensions defined in TLS 1.3
CH_EARLY_DATA (0x002A, "early_data"),

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 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
@ -114,6 +114,10 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer {
@SuppressWarnings({"unchecked", "rawtypes"})
NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket",
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
NewSessionTicket.handshake12Consumer,
ProtocolVersion.PROTOCOLS_TO_12
),
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
NewSessionTicket.handshakeConsumer,
ProtocolVersion.PROTOCOLS_OF_13
@ -121,8 +125,8 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer {
}),
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
NewSessionTicket.handshakeProducer,
ProtocolVersion.PROTOCOLS_OF_13
NewSessionTicket.handshake12Producer,
ProtocolVersion.PROTOCOLS_TO_12
)
})),
END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"),

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
@ -33,11 +33,34 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import sun.security.action.GetIntegerAction;
import sun.security.action.GetPropertyAction;
import sun.security.util.Cache;
/**
* @systemProperty jdk.tls.server.enableSessionTicketExtension} determines if the
* server will provide stateless session tickets, if the client supports it,
* as described in RFC 5077 and RFC 8446. a stateless session ticket
* contains the encrypted server's state which saves server resources.
*
* {@systemProperty jdk.tls.client.enableSessionTicketExtension} determines if the
* client will send an extension in the ClientHello in the pre-TLS 1.3.
* This extension allows the client to accept the server's session state for
* Server Side stateless resumption (RFC 5077). Setting the property to
* "true" turns this on, by default it is false. For TLS 1.3, the system
* property is not needed as this support is part of the spec.
*
* {@systemProperty jdk.tls.server.sessionTicketTimeout} determines how long
* a session in the server cache or the stateless resumption tickets are
* available for use. The value set by the property can be modified by
* {@code SSLSessionContext.setSessionTimeout()} during runtime.
*
*/
final class SSLSessionContextImpl implements SSLSessionContext {
private final static int DEFAULT_MAX_CACHE_SIZE = 20480;
// Default lifetime of a session. 24 hours
final static int DEFAULT_SESSION_TIMEOUT = 86400;
private final Cache<SessionId, SSLSessionImpl> sessionCache;
// session cache, session id as key
@ -46,16 +69,24 @@ final class SSLSessionContextImpl implements SSLSessionContext {
private int cacheLimit; // the max cache size
private int timeout; // timeout in seconds
// Does this context support stateless session (RFC 5077)
private boolean statelessSession = true;
// package private
SSLSessionContextImpl() {
cacheLimit = getDefaultCacheLimit(); // default cache size
timeout = 86400; // default, 24 hours
SSLSessionContextImpl(boolean server) {
timeout = DEFAULT_SESSION_TIMEOUT;
cacheLimit = getDefaults(server); // default cache size
// use soft reference
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
}
// Stateless sessions when available, but there is a cache
boolean statelessEnabled() {
return statelessSession;
}
/**
* Returns the <code>SSLSession</code> bound to the specified session id.
*/
@ -163,8 +194,7 @@ final class SSLSessionContextImpl implements SSLSessionContext {
}
private static String getKey(String hostname, int port) {
return (hostname + ":" +
String.valueOf(port)).toLowerCase(Locale.ENGLISH);
return (hostname + ":" + port).toLowerCase(Locale.ENGLISH);
}
// cache a SSLSession
@ -197,8 +227,51 @@ final class SSLSessionContextImpl implements SSLSessionContext {
}
}
private static int getDefaultCacheLimit() {
private int getDefaults(boolean server) {
try {
String st;
// Property for Session Cache state
if (server) {
st = GetPropertyAction.privilegedGetProperty(
"jdk.tls.server.enableSessionTicketExtension", "true");
} else {
st = GetPropertyAction.privilegedGetProperty(
"jdk.tls.client.enableSessionTicketExtension", "true");
}
if (st.compareToIgnoreCase("false") == 0) {
statelessSession = false;
}
// Property for Session Ticket Timeout. The value can be changed
// by SSLSessionContext.setSessionTimeout(int)
String s = GetPropertyAction.privilegedGetProperty(
"jdk.tls.server.sessionTicketTimeout");
if (s != null) {
try {
int t = Integer.parseInt(s);
if (t < 0 ||
t > NewSessionTicket.MAX_TICKET_LIFETIME) {
timeout = DEFAULT_SESSION_TIMEOUT;
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("Invalid timeout given " +
"jdk.tls.server.sessionTicketTimeout: " + t +
". Set to default value " + timeout);
}
} else {
timeout = t;
}
} catch (NumberFormatException e) {
setSessionTimeout(DEFAULT_SESSION_TIMEOUT);
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("Invalid timeout for " +
"jdk.tls.server.sessionTicketTimeout: " + s +
". Set to default value " + timeout);
}
}
}
int defaultCacheLimit = GetIntegerAction.privilegedGetProperty(
"javax.net.ssl.sessionCacheSize", DEFAULT_MAX_CACHE_SIZE);

View file

@ -24,8 +24,12 @@
*/
package sun.security.ssl;
import sun.security.x509.X509CertImpl;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
@ -40,8 +44,11 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
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.SSLPermission;
import javax.net.ssl.SSLSessionBindingEvent;
@ -251,6 +258,371 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
}
/**
* < 2 bytes > protocolVersion
* < 2 bytes > cipherSuite
* < 2 bytes > localSupportedSignAlgs entries
* < 2 bytes per entries > localSupportedSignAlgs
* < 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
* < 1 byte > identificationProtocol length
* < length in bytes > identificationProtocol
* < 1 byte > serverNameIndication length
* < length in bytes > serverNameIndication
* < 1 byte > Number of requestedServerNames entries
* < 1 byte > ServerName length
* < length in bytes > ServerName
* < 4 bytes > creationTime
* < 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 >
*/
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()));
if (protocolVersion.useTLS13PlusSpec()) {
this.sessionId = new SessionId(false, null);
} else {
// The CH session id may reset this if it's provided
this.sessionId = new SessionId(true,
hc.sslContext.getSecureRandom());
}
this.cipherSuite = CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
// Local Supported signature algorithms
i = Short.toUnsignedInt(buf.getShort());
while (i-- > 0) {
this.localSupportedSignAlgs.add(SignatureScheme.valueOf(
Short.toUnsignedInt(buf.getShort())));
}
// PSK
i = Short.toUnsignedInt(buf.getShort());
if (i > 0) {
b = new byte[i];
// Get algorithm string
buf.get(b, 0, i);
// Encoded length
i = Short.toUnsignedInt(buf.getShort());
// Encoded SecretKey
b = new byte[i];
buf.get(b);
this.preSharedKey = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.preSharedKey = null;
}
// PSK identity
i = buf.get();
if (i > 0) {
b = new byte[i];
buf.get(b);
this.pskIdentity = b;
} else {
this.pskIdentity = null;
}
// Master secret length of secret key algorithm (one byte)
i = buf.get();
if (i > 0) {
b = new byte[i];
// Get algorithm string
buf.get(b, 0, i);
// Encoded length
i = Short.toUnsignedInt(buf.getShort());
// Encoded SecretKey
b = new byte[i];
buf.get(b);
this.masterSecret = new SecretKeySpec(b, "TlsMasterSecret");
} else {
this.masterSecret = null;
}
// Use extended master secret
this.useExtendedMasterSecret = (buf.get() != 0);
// Identification Protocol
i = buf.get();
if (i == 0) {
identificationProtocol = null;
} else {
b = new byte[i];
identificationProtocol =
buf.get(b, 0, i).asCharBuffer().toString();
}
// SNI
i = buf.get(); // length
if (i == 0) {
serverNameIndication = null;
} else {
b = new byte[i];
buf.get(b, 0, b.length);
serverNameIndication = new SNIHostName(b);
}
// List of SNIServerName
int len = Short.toUnsignedInt(buf.getShort());
if (len == 0) {
this.requestedServerNames = Collections.<SNIServerName>emptyList();
} else {
requestedServerNames = new ArrayList<>();
while (len > 0) {
int l = buf.get();
b = new byte[l];
buf.get(b, 0, l);
requestedServerNames.add(new SNIHostName(new String(b)));
len--;
}
}
// Get creation time
this.creationTime = buf.getLong();
// Get Peer host & port
i = Byte.toUnsignedInt(buf.get());
if (i == 0) {
this.host = new String();
} else {
b = new byte[i];
this.host = buf.get(b).toString();
}
this.port = Short.toUnsignedInt(buf.getShort());
// Peer certs
i = buf.get();
if (i == 0) {
this.peerCerts = null;
} else {
this.peerCerts = new X509Certificate[i];
int j = 0;
while (i > j) {
b = new byte[buf.getInt()];
buf.get(b);
try {
this.peerCerts[j] = new X509CertImpl(b);
} catch (Exception e) {
throw new IOException(e);
}
j++;
}
}
// Get local certs of PSK
switch (buf.get()) {
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);
try {
this.localCerts[i] = new X509CertImpl(b);
} catch (Exception e) {
throw new IOException(e);
}
i++;
}
break;
case 2:
// pre-shared key
// Length of pre-shared key algorithm (one byte)
i = buf.get();
b = new byte[i];
String alg = buf.get(b, 0, i).asCharBuffer().toString();
// Get length of encoding
i = Short.toUnsignedInt(buf.getShort());
// Get encoding
b = new byte[i];
buf.get(b);
this.preSharedKey = new SecretKeySpec(b, alg);
// Get identity len
this.pskIdentity = new byte[buf.get()];
buf.get(pskIdentity);
break;
default:
throw new SSLException("Failed local certs of session.");
}
context = (SSLSessionContextImpl)
hc.sslContext.engineGetServerSessionContext();
}
/**
* Write out a SSLSessionImpl in a byte array for a stateless session ticket
*/
byte[] write() throws Exception {
byte[] b;
HandshakeOutStream hos = new HandshakeOutStream(null);
hos.putInt16(protocolVersion.id);
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.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());
}
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());
}
b = getMasterSecret().getEncoded();
hos.putInt16(b.length);
hos.write(b, 0, b.length);
}
hos.putInt8(useExtendedMasterSecret ? 1 : 0);
// Identification Protocol
if (identificationProtocol == null) {
hos.putInt8(0);
} else {
hos.putInt8(identificationProtocol.length());
hos.write(identificationProtocol.getBytes(), 0,
identificationProtocol.length());
}
// SNI
if (serverNameIndication == null) {
hos.putInt8(0);
} else {
b = serverNameIndication.getEncoded();
hos.putInt8(b.length);
hos.write(b, 0, b.length);
}
// List of SNIServerName
hos.putInt16(requestedServerNames.size());
if (requestedServerNames.size() > 0) {
for (SNIServerName host: requestedServerNames) {
b = host.getEncoded();
hos.putInt8(b.length);
hos.write(b, 0, b.length);
}
}
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
hos.writeBytes(buffer.putLong(creationTime).array());
// peer Host & Port
if (host == null || host.length() == 0) {
hos.putInt8(0);
} else {
hos.putInt8(host.length());
hos.writeBytes(host.getBytes());
}
hos.putInt16(port);
// Peer cert
if (peerCerts == null || peerCerts.length == 0) {
hos.putInt8(0);
} else {
hos.putInt8(peerCerts.length);
for (X509Certificate c : peerCerts) {
b = c.getEncoded();
hos.putInt32(b.length);
hos.writeBytes(b);
}
}
// Client identity
if (localCerts != null && localCerts.length > 0) {
// certificate based
hos.putInt8(1);
hos.putInt8(localCerts.length);
for (X509Certificate c : localCerts) {
b = c.getEncoded();
hos.putInt32(b.length);
hos.writeBytes(b);
}
} 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();
}
void setMasterSecret(SecretKey secret) {
masterSecret = secret;
}
@ -333,6 +705,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
}
}
byte[] getPskIdentity() {
return pskIdentity;
}
void setPeerCertificates(X509Certificate[] peer) {
if (peerCerts == null) {
peerCerts = peer;
@ -400,6 +776,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
* maximum lifetime in any case.
*/
boolean isRejoinable() {
// TLS 1.3 can have no session id
if (protocolVersion.useTLS13PlusSpec()) {
return (!invalidated && isLocalAuthenticationValid());
}
return sessionId != null && sessionId.length() != 0 &&
!invalidated && isLocalAuthenticationValid();
}

View file

@ -47,6 +47,8 @@ import sun.security.ssl.SSLCipher.SSLWriteCipher;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec;
import static sun.security.ssl.SSLExtension.SH_SESSION_TICKET;
/**
* Pack of the ServerHello/HelloRetryRequest handshake message.
*/
@ -337,6 +339,15 @@ final class ServerHello {
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id,
SSLHandshake.SERVER_HELLO_DONE);
} else {
// stateless and use the client session id (RFC 5077 3.4)
if (shc.statelessResumption) {
shc.resumingSession = new SSLSessionImpl(shc.resumingSession,
(clientHello.sessionId.length() == 0) ?
new SessionId(true,
shc.sslContext.getSecureRandom()) :
new SessionId(clientHello.sessionId.getId())
);
}
shc.handshakeSession = shc.resumingSession;
shc.negotiatedProtocol =
shc.resumingSession.getProtocolVersion();
@ -491,6 +502,9 @@ final class ServerHello {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
ClientHelloMessage clientHello = (ClientHelloMessage)message;
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
// If client hasn't specified a session we can resume, start a
// new one and choose its cipher suite and compression options,
// unless new session creation is disabled for this connection!
@ -546,8 +560,6 @@ final class ServerHello {
shc.resumingSession.consumePreSharedKey());
// The session can't be resumed again---remove it from cache
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
shc.sslContext.engineGetServerSessionContext();
sessionCache.remove(shc.resumingSession.getSessionId());
}
@ -679,6 +691,11 @@ final class ServerHello {
// Update the context for master key derivation.
shc.handshakeKeyDerivation = kd;
// Check if the server supports stateless resumption
if (sessionCache.statelessEnabled()) {
shc.statelessResumption = true;
}
// The handshake message has been delivered.
return null;
}
@ -1098,9 +1115,23 @@ final class ServerHello {
throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,
"New session creation is disabled");
}
if (serverHello.sessionId.length() == 0 &&
chc.statelessResumption) {
SessionId newId = new SessionId(true,
chc.sslContext.getSecureRandom());
chc.handshakeSession = new SSLSessionImpl(chc,
chc.negotiatedCipherSuite, newId);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Locally assigned Session Id: " +
newId.toString());
}
} else {
chc.handshakeSession = new SSLSessionImpl(chc,
chc.negotiatedCipherSuite,
serverHello.sessionId);
}
chc.handshakeSession.setMaximumPacketSize(
chc.sslConfig.maximumPacketSize);
}
@ -1127,6 +1158,11 @@ final class ServerHello {
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.handshakeConsumers.put(
SSLHandshake.FINISHED.id,
SSLHandshake.FINISHED);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 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
@ -36,7 +36,7 @@ import javax.net.ssl.SSLProtocolException;
* @author David Brownell
*/
final class SessionId {
private static final int MAX_LENGTH = 32;
static final int MAX_LENGTH = 32;
private final byte[] sessionId; // max 32 bytes
// Constructs a new session ID ... perhaps for a rejoinable session

View file

@ -0,0 +1,539 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import sun.security.action.GetPropertyAction;
import sun.security.ssl.SSLExtension.ExtensionConsumer;
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
import sun.security.util.HexDumpEncoder;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.net.ssl.SSLProtocolException;
import static sun.security.ssl.SSLExtension.CH_SESSION_TICKET;
import static sun.security.ssl.SSLExtension.SH_SESSION_TICKET;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Locale;
/**
* SessionTicketExtension is an implementation of RFC 5077 with some internals
* that are used for stateless operation in TLS 1.3.
*
* {@systemProperty jdk.tls.server.statelessKeyTimeout} can override the default
* amount of time, in seconds, for how long a randomly-generated key and
* parameters can be used before being regenerated. The key material is used
* to encrypt the stateless session ticket that is sent to the client that will
* be used during resumption. Default is 3600 seconds (1 hour)
*
*/
final class SessionTicketExtension {
static final HandshakeProducer chNetworkProducer =
new T12CHSessionTicketProducer();
static final ExtensionConsumer chOnLoadConsumer =
new T12CHSessionTicketConsumer();
static final HandshakeProducer shNetworkProducer =
new T12SHSessionTicketProducer();
static final ExtensionConsumer shOnLoadConsumer =
new T12SHSessionTicketConsumer();
static final SSLStringizer steStringizer = new SessionTicketStringizer();
// Time in milliseconds until key is changed for encrypting session state
private static final int TIMEOUT_DEFAULT = 3600 * 1000;
private static final int keyTimeout;
private static int currentKeyID = new SecureRandom().nextInt();
private static final int KEYLEN = 256;
static {
String s = GetPropertyAction.privilegedGetProperty(
"jdk.tls.server.statelessKeyTimeout");
if (s != null) {
int kt;
try {
kt = Integer.parseInt(s) * 1000; // change to ms
if (kt < 0 ||
kt > NewSessionTicket.MAX_TICKET_LIFETIME) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("Invalid timeout for " +
"jdk.tls.server.statelessKeyTimeout: " +
kt + ". Set to default value " +
TIMEOUT_DEFAULT + "sec");
}
kt = TIMEOUT_DEFAULT;
}
} catch (NumberFormatException e) {
kt = TIMEOUT_DEFAULT;
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("Invalid timeout for " +
"jdk.tls.server.statelessKeyTimeout: " + s +
". Set to default value " + TIMEOUT_DEFAULT +
"sec");
}
}
keyTimeout = kt;
} else {
keyTimeout = TIMEOUT_DEFAULT;
}
}
// Crypto key context for session state. Used with stateless operation.
final static class StatelessKey {
final long timeout;
final SecretKey key;
final int num;
StatelessKey(HandshakeContext hc, int newNum) {
SecretKey k = null;
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(KEYLEN, hc.sslContext.getSecureRandom());
k = kg.generateKey();
} catch (NoSuchAlgorithmException e) {
// should not happen;
}
key = k;
timeout = System.currentTimeMillis() + keyTimeout;
num = newNum;
hc.sslContext.keyHashMap.put(Integer.valueOf(num), this);
}
// Check if key needs to be changed
boolean isExpired() {
return ((System.currentTimeMillis()) > timeout);
}
// Check if this key is ready for deletion.
boolean isInvalid(long sessionTimeout) {
return ((System.currentTimeMillis()) > (timeout + sessionTimeout));
}
}
private static final class KeyState {
// Get a key with a specific key number
static StatelessKey getKey(HandshakeContext hc, int num) {
StatelessKey ssk = hc.sslContext.keyHashMap.get(num);
if (ssk == null || ssk.isInvalid(getSessionTimeout(hc))) {
return null;
}
return ssk;
}
// Get the current valid key, this will generate a new key if needed
static StatelessKey getCurrentKey(HandshakeContext hc) {
StatelessKey ssk = hc.sslContext.keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
return nextKey(hc);
}
// This method locks when the first getCurrentKey() finds it to be too
// old and create a new key to replace the current key. After the new
// key established, the lock can be released so following
// operations will start using the new key.
// The first operation will take a longer code path by generating the
// next key and cleaning up old keys.
private static StatelessKey nextKey(HandshakeContext hc) {
StatelessKey ssk;
synchronized (hc.sslContext.keyHashMap) {
// If the current key is no longer expired, it was already
// updated by a previous operation and we can return.
ssk = hc.sslContext.keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
int newNum;
if (currentKeyID == Integer.MAX_VALUE) {
newNum = 0;
} else {
newNum = currentKeyID + 1;
}
// Get new key
ssk = new StatelessKey(hc, newNum);
currentKeyID = newNum;
// Release lock since the new key is ready to be used.
}
// Clean up any old keys, then return the current key
cleanup(hc);
return ssk;
}
// Deletes any invalid SessionStateKeys.
static void cleanup(HandshakeContext hc) {
int sessionTimeout = getSessionTimeout(hc);
StatelessKey ks;
for (Object o : hc.sslContext.keyHashMap.keySet().toArray()) {
Integer i = (Integer)o;
ks = hc.sslContext.keyHashMap.get(i);
if (ks.isInvalid(sessionTimeout)) {
try {
ks.key.destroy();
} catch (Exception e) {
// Suppress
}
hc.sslContext.keyHashMap.remove(i);
}
}
}
static int getSessionTimeout(HandshakeContext hc) {
return hc.sslContext.engineGetServerSessionContext().
getSessionTimeout() * 1000;
}
}
/**
* This class contains the session state that is in the session ticket.
* Using the key associated with the ticket, the class encrypts and
* decrypts the data, but does not interpret the data.
*/
static final class SessionTicketSpec implements SSLExtensionSpec {
private static final int GCM_TAG_LEN = 128;
ByteBuffer data;
static final ByteBuffer zero = ByteBuffer.wrap(new byte[0]);
SessionTicketSpec() {
data = zero;
}
SessionTicketSpec(byte[] b) throws IOException {
this(ByteBuffer.wrap(b));
}
SessionTicketSpec(ByteBuffer buf) throws IOException {
if (buf == null) {
throw new SSLProtocolException(
"SessionTicket buffer too small");
}
if (buf.remaining() > 65536) {
throw new SSLProtocolException(
"SessionTicket buffer too large. " + buf.remaining());
}
data = buf;
}
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session)
throws IOException {
byte[] encrypted;
StatelessKey key = KeyState.getCurrentKey(hc);
byte[] iv = new byte[16];
try {
SecureRandom random = hc.sslContext.getSecureRandom();
random.nextBytes(iv);
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)}
);
encrypted = c.doFinal(session.write());
byte[] result = new byte[encrypted.length + Integer.BYTES +
iv.length];
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);
System.arraycopy(encrypted, 0, result,
Integer.BYTES + iv.length, encrypted.length);
return result;
} catch (Exception e) {
throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
}
}
ByteBuffer decrypt(HandshakeContext hc) {
int keyID;
byte[] iv;
try {
keyID = data.getInt();
StatelessKey key = KeyState.getKey(hc, keyID);
if (key == null) {
return null;
}
iv = new byte[16];
data.get(iv);
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)}
);
/*
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);
out.flip();
return out;
} catch (Exception e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Decryption failed." + e.getMessage());
}
}
return null;
}
byte[] getEncoded() {
byte[] out = new byte[data.capacity()];
data.duplicate().get(out);
return out;
}
@Override
public String toString() {
if (data == null) {
return "<null>";
}
if (data.capacity() == 0) {
return "<empty>";
}
MessageFormat messageFormat = new MessageFormat(
" \"ticket\" : '{'\n" +
"{0}\n" +
" '}'",
Locale.ENGLISH);
HexDumpEncoder hexEncoder = new HexDumpEncoder();
Object[] messageFields = {
Utilities.indent(hexEncoder.encode(data.duplicate()),
" "),
};
return messageFormat.format(messageFields);
}
}
static final class SessionTicketStringizer implements SSLStringizer {
SessionTicketStringizer() {}
@Override
public String toString(ByteBuffer buffer) {
try {
return new SessionTicketSpec(buffer).toString();
} catch (IOException e) {
return e.getMessage();
}
}
}
private static final class T12CHSessionTicketProducer
extends SupportedGroups implements HandshakeProducer {
T12CHSessionTicketProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext)context;
// If the context does not allow stateless tickets, exit
if (!((SSLSessionContextImpl)chc.sslContext.
engineGetClientSessionContext()).statelessEnabled()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Stateless resumption not supported");
}
return null;
}
chc.statelessResumption = true;
// If resumption is not in progress, return an empty value
if (!chc.isResumption || chc.resumingSession == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Stateless resumption supported");
}
return new SessionTicketSpec().getEncoded();
}
if (chc.localSupportedSignAlgs == null) {
chc.localSupportedSignAlgs =
SignatureScheme.getSupportedAlgorithms(
chc.algorithmConstraints, chc.activeProtocols);
}
return chc.resumingSession.getPskIdentity();
}
}
private static final class T12CHSessionTicketConsumer
implements ExtensionConsumer {
T12CHSessionTicketConsumer() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer)
throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext) context;
// Skip if extension is not provided
if (!shc.sslConfig.isAvailable(CH_SESSION_TICKET)) {
return;
}
// Skip consumption if we are already in stateless resumption
if (shc.statelessResumption) {
return;
}
// If the context does not allow stateless tickets, exit
SSLSessionContextImpl cache = (SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext();
if (!cache.statelessEnabled()) {
return;
}
if (buffer.remaining() == 0) {
shc.statelessResumption = true;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Client accepts session tickets.");
}
return;
}
// Parse the extension.
SessionTicketSpec spec;
try {
spec = new SessionTicketSpec(buffer);
} catch (IOException | RuntimeException e) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("SessionTicket data invalid. Doing full " +
"handshake.");
}
return;
}
ByteBuffer b = spec.decrypt(shc);
if (b != null) {
shc.resumingSession = new SSLSessionImpl(shc, b);
shc.isResumption = true;
shc.statelessResumption = true;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Valid stateless session ticket found");
}
}
}
}
private static final class T12SHSessionTicketProducer
extends SupportedGroups implements HandshakeProducer {
T12SHSessionTicketProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) {
ServerHandshakeContext shc = (ServerHandshakeContext)context;
// If boolean is false, the CH did not have this extension
if (!shc.statelessResumption) {
return null;
}
// If the client has sent a SessionTicketExtension and stateless
// is enabled on the server, return an empty message.
// If the context does not allow stateless tickets, exit
SSLSessionContextImpl cache = (SSLSessionContextImpl)shc.sslContext
.engineGetServerSessionContext();
if (cache.statelessEnabled()) {
return new byte[0];
}
shc.statelessResumption = false;
return null;
}
}
private static final class T12SHSessionTicketConsumer
implements ExtensionConsumer {
T12SHSessionTicketConsumer() {
}
@Override
public void consume(ConnectionContext context,
HandshakeMessage message, ByteBuffer buffer)
throws IOException {
ClientHandshakeContext chc = (ClientHandshakeContext) context;
// Skip if extension is not provided
if (!chc.sslConfig.isAvailable(SH_SESSION_TICKET)) {
chc.statelessResumption = false;
return;
}
// If the context does not allow stateless tickets, exit
if (!((SSLSessionContextImpl)chc.sslContext.
engineGetClientSessionContext()).statelessEnabled()) {
chc.statelessResumption = false;
return;
}
try {
if (new SessionTicketSpec(buffer) == null) {
return;
}
chc.statelessResumption = true;
} catch (IOException e) {
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
}
}
}
}

View file

@ -159,14 +159,19 @@ class TransportContext implements ConnectionContext {
if (handshakeContext == null) {
if (type == SSLHandshake.KEY_UPDATE.id ||
type == SSLHandshake.NEW_SESSION_TICKET.id) {
if (isNegotiated &&
protocolVersion.useTLS13PlusSpec()) {
handshakeContext = new PostHandshakeContext(this);
} else {
if (!isNegotiated) {
throw fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected unnegotiated post-handshake" +
" message: " +
SSLHandshake.nameOf(type));
}
if (type == SSLHandshake.KEY_UPDATE.id &&
!protocolVersion.useTLS13PlusSpec()) {
throw fatal(Alert.UNEXPECTED_MESSAGE,
"Unexpected post-handshake message: " +
SSLHandshake.nameOf(type));
}
handshakeContext = new PostHandshakeContext(this);
} else {
handshakeContext = sslConfig.isClientMode ?
new ClientHandshakeContext(sslContext, this) :

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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,7 @@
* @run main/othervm PacketLossRetransmission client 1 client_hello
* @run main/othervm PacketLossRetransmission client 2 server_hello
* @run main/othervm PacketLossRetransmission client 3 hello_verify_request
* @run main/othervm PacketLossRetransmission client 4 new_session_ticket
* @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false PacketLossRetransmission client 4 new_session_ticket
* @run main/othervm PacketLossRetransmission client 11 certificate
* @run main/othervm PacketLossRetransmission client 12 server_key_exchange
* @run main/othervm PacketLossRetransmission client 13 certificate_request
@ -51,7 +51,7 @@
* @run main/othervm PacketLossRetransmission server 1 client_hello
* @run main/othervm PacketLossRetransmission server 2 server_hello
* @run main/othervm PacketLossRetransmission server 3 hello_verify_request
* @run main/othervm PacketLossRetransmission server 4 new_session_ticket
* @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false PacketLossRetransmission server 4 new_session_ticket
* @run main/othervm PacketLossRetransmission server 11 certificate
* @run main/othervm PacketLossRetransmission server 12 server_key_exchange
* @run main/othervm PacketLossRetransmission server 13 certificate_request

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 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
@ -25,7 +25,9 @@
* @test
* @bug 4473210
* @summary SSLSessionContext should be accessible from SSLContext
* @run main/othervm SSLCtxAccessToSessCtx
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false
* SSLCtxAccessToSessCtx
*
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.

View file

@ -30,44 +30,57 @@
* @test
* @bug 6956398
* @summary make ephemeral DH key match the length of the certificate key
* @run main/othervm
* @run main/othervm -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=matched
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=legacy
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=1024
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
*
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 233 75
*
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=legacy
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1323 107
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=matched
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.ephemeralDHKeySize=1024
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
*
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* -Djdk.tls.ephemeralDHKeySize=legacy
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 297 107
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* -Djdk.tls.ephemeralDHKeySize=matched
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
* @run main/othervm -Djsse.enableFFDHE=false
* -Djdk.tls.client.enableSessionTicketExtension=false
* -Djdk.tls.ephemeralDHKeySize=1024
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -26,13 +26,17 @@
* @bug 8206929 8212885
* @summary ensure that client only resumes a session if certain properties
* of the session are compatible with the new connection
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient BASIC
* @run main/othervm ResumeChecksClient BASIC
* @run main/othervm ResumeChecksClient VERSION_2_TO_3
* @run main/othervm ResumeChecksClient VERSION_3_TO_2
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient CIPHER_SUITE
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksClient SIGNATURE_SCHEME
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient VERSION_2_TO_3
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient VERSION_3_TO_2
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient CIPHER_SUITE
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient SIGNATURE_SCHEME
*
*/

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8211018
* @summary ensure that client only resumes a session if certain properties
* of the session are compatible with the new connection
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient VERSION_2_TO_3
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient VERSION_3_TO_2
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient CIPHER_SUITE
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient SIGNATURE_SCHEME
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -26,16 +26,18 @@
* @bug 8206929
* @summary ensure that server only resumes a session if certain properties
* of the session are compatible with the new connection
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer BASIC
* @run main/othervm ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 ResumeChecksServer CLIENT_AUTH
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CLIENT_AUTH
* @run main/othervm ResumeChecksServer CLIENT_AUTH
* @run main/othervm ResumeChecksServer VERSION_2_TO_3
* @run main/othervm ResumeChecksServer VERSION_3_TO_2
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CIPHER_SUITE
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer SIGNATURE_SCHEME
* @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
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer CLIENT_AUTH
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer CLIENT_AUTH
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.2 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksServer CLIENT_AUTH
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_2_TO_3
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksServer VERSION_2_TO_3
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_2_TO_3
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_3_TO_2
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksServer VERSION_3_TO_2
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_3_TO_2
*
*/

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8211018
* @summary ensure that server only resumes a session if certain properties
* of the session are compatible with the new connection
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer BASIC
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CLIENT_AUTH
* @run main/othervm ResumeChecksServer VERSION_2_TO_3
* @run main/othervm ResumeChecksServer VERSION_3_TO_2
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CIPHER_SUITE
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer SIGNATURE_SCHEME
*/