mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8211018: Session Resumption without Server-Side State
Reviewed-by: xuelei, jnimeh, jjiang
This commit is contained in:
parent
9597144fb2
commit
94e1d7530f
21 changed files with 1620 additions and 150 deletions
|
@ -35,7 +35,6 @@ import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
@ -969,11 +968,24 @@ final class ClientHello {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it an abbreviated handshake?
|
// Consume a Session Ticket Extension if it exists
|
||||||
if (clientHello.sessionId.length() != 0) {
|
SSLExtension[] ext = new SSLExtension[]{
|
||||||
SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
|
SSLExtension.CH_SESSION_TICKET
|
||||||
.engineGetServerSessionContext())
|
};
|
||||||
.get(clientHello.sessionId.getId());
|
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 =
|
boolean resumingSession =
|
||||||
(previous != null) && previous.isRejoinable();
|
(previous != null) && previous.isRejoinable();
|
||||||
|
@ -1051,14 +1063,20 @@ final class ClientHello {
|
||||||
// the resuming options later.
|
// the resuming options later.
|
||||||
shc.isResumption = resumingSession;
|
shc.isResumption = resumingSession;
|
||||||
shc.resumingSession = resumingSession ? previous : null;
|
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
|
// cache the client random number for further using
|
||||||
shc.clientHelloRandom = clientHello.clientRandom;
|
shc.clientHelloRandom = clientHello.clientRandom;
|
||||||
|
|
||||||
// Check and launch ClientHello extensions.
|
// Check and launch ClientHello extensions.
|
||||||
SSLExtension[] extTypes = shc.sslConfig.getEnabledExtensions(
|
SSLExtension[] extTypes = shc.sslConfig.getExclusiveExtensions(
|
||||||
SSLHandshake.CLIENT_HELLO);
|
SSLHandshake.CLIENT_HELLO,
|
||||||
|
Arrays.asList(SSLExtension.CH_SESSION_TICKET));
|
||||||
clientHello.extensions.consumeOnLoad(shc, extTypes);
|
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) {
|
if (clientHello.sessionId.length() != 0) {
|
||||||
SSLSessionImpl previous = ((SSLSessionContextImpl)shc.sslContext
|
SSLSessionContextImpl cache = (SSLSessionContextImpl)shc.sslContext
|
||||||
.engineGetServerSessionContext())
|
.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);
|
||||||
|
|
||||||
|
SSLSessionImpl previous;
|
||||||
|
// Use stateless session ticket if provided.
|
||||||
|
if (shc.statelessResumption) {
|
||||||
|
previous = shc.resumingSession;
|
||||||
|
} else {
|
||||||
|
previous = cache.get(clientHello.sessionId.getId());
|
||||||
|
}
|
||||||
|
|
||||||
boolean resumingSession =
|
boolean resumingSession =
|
||||||
(previous != null) && previous.isRejoinable();
|
(previous != null) && previous.isRejoinable();
|
||||||
|
|
|
@ -410,6 +410,10 @@ final class Finished {
|
||||||
chc.conContext.clientVerifyData = fm.verifyData;
|
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
|
// update the consumers and producers
|
||||||
if (!chc.isResumption) {
|
if (!chc.isResumption) {
|
||||||
chc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
|
chc.conContext.consumers.put(ContentType.CHANGE_CIPHER_SPEC.id,
|
||||||
|
@ -441,6 +445,10 @@ final class Finished {
|
||||||
|
|
||||||
private byte[] onProduceFinished(ServerHandshakeContext shc,
|
private byte[] onProduceFinished(ServerHandshakeContext shc,
|
||||||
HandshakeMessage message) throws IOException {
|
HandshakeMessage message) throws IOException {
|
||||||
|
if (shc.statelessResumption) {
|
||||||
|
NewSessionTicket.handshake12Producer.produce(shc, message);
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh handshake hash
|
// Refresh handshake hash
|
||||||
shc.handshakeHash.update();
|
shc.handshakeHash.update();
|
||||||
|
|
||||||
|
@ -473,7 +481,8 @@ final class Finished {
|
||||||
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
|
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
|
||||||
shc.conContext.inputRecord.expectingFinishFlight();
|
shc.conContext.inputRecord.expectingFinishFlight();
|
||||||
} else {
|
} else {
|
||||||
if (shc.handshakeSession.isRejoinable()) {
|
if (shc.handshakeSession.isRejoinable() &&
|
||||||
|
!shc.statelessResumption) {
|
||||||
((SSLSessionContextImpl)shc.sslContext.
|
((SSLSessionContextImpl)shc.sslContext.
|
||||||
engineGetServerSessionContext()).put(
|
engineGetServerSessionContext()).put(
|
||||||
shc.handshakeSession);
|
shc.handshakeSession);
|
||||||
|
@ -591,7 +600,8 @@ final class Finished {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shc.isResumption) {
|
if (shc.isResumption) {
|
||||||
if (shc.handshakeSession.isRejoinable()) {
|
if (shc.handshakeSession.isRejoinable() &&
|
||||||
|
!shc.statelessResumption) {
|
||||||
((SSLSessionContextImpl)shc.sslContext.
|
((SSLSessionContextImpl)shc.sslContext.
|
||||||
engineGetServerSessionContext()).put(
|
engineGetServerSessionContext()).put(
|
||||||
shc.handshakeSession);
|
shc.handshakeSession);
|
||||||
|
@ -915,9 +925,9 @@ final class Finished {
|
||||||
|
|
||||||
// save the session
|
// save the session
|
||||||
if (!chc.isResumption && chc.handshakeSession.isRejoinable()) {
|
if (!chc.isResumption && chc.handshakeSession.isRejoinable()) {
|
||||||
SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
|
((SSLSessionContextImpl)chc.sslContext.
|
||||||
chc.sslContext.engineGetClientSessionContext();
|
engineGetClientSessionContext()).
|
||||||
sessionContext.put(chc.handshakeSession);
|
put(chc.handshakeSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
// derive salt secret
|
// derive salt secret
|
||||||
|
@ -1028,8 +1038,9 @@ final class Finished {
|
||||||
shc.negotiatedProtocol);
|
shc.negotiatedProtocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the session
|
// Save the session if possible and not stateless
|
||||||
if (!shc.isResumption && shc.handshakeSession.isRejoinable()) {
|
if (!shc.statelessResumption && !shc.isResumption &&
|
||||||
|
shc.handshakeSession.isRejoinable()) {
|
||||||
SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
|
SSLSessionContextImpl sessionContext = (SSLSessionContextImpl)
|
||||||
shc.sslContext.engineGetServerSessionContext();
|
shc.sslContext.engineGetServerSessionContext();
|
||||||
sessionContext.put(shc.handshakeSession);
|
sessionContext.put(shc.handshakeSession);
|
||||||
|
|
|
@ -102,6 +102,8 @@ abstract class HandshakeContext implements ConnectionContext {
|
||||||
// Resumption
|
// Resumption
|
||||||
boolean isResumption;
|
boolean isResumption;
|
||||||
SSLSessionImpl resumingSession;
|
SSLSessionImpl resumingSession;
|
||||||
|
// Session is using stateless resumption
|
||||||
|
boolean statelessResumption = false;
|
||||||
|
|
||||||
final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
|
final Queue<Map.Entry<Byte, ByteBuffer>> delegatedActions;
|
||||||
volatile boolean taskDelegated = false;
|
volatile boolean taskDelegated = false;
|
||||||
|
@ -551,7 +553,7 @@ abstract class HandshakeContext implements ConnectionContext {
|
||||||
|
|
||||||
List<SNIServerName> getRequestedServerNames() {
|
List<SNIServerName> getRequestedServerNames() {
|
||||||
if (requestedServerNames == null) {
|
if (requestedServerNames == null) {
|
||||||
return Collections.<SNIServerName>emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return requestedServerNames;
|
return requestedServerNames;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,40 +28,139 @@ import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.ProviderException;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
|
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
|
||||||
|
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
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.
|
* Pack of the NewSessionTicket handshake message.
|
||||||
*/
|
*/
|
||||||
final class NewSessionTicket {
|
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 =
|
static final SSLConsumer handshakeConsumer =
|
||||||
new NewSessionTicketConsumer();
|
new T13NewSessionTicketConsumer();
|
||||||
|
static final SSLConsumer handshake12Consumer =
|
||||||
|
new T12NewSessionTicketConsumer();
|
||||||
static final SSLProducer kickstartProducer =
|
static final SSLProducer kickstartProducer =
|
||||||
new NewSessionTicketKickstartProducer();
|
new NewSessionTicketKickstartProducer();
|
||||||
static final HandshakeProducer handshakeProducer =
|
static final HandshakeProducer handshake12Producer =
|
||||||
new NewSessionTicketProducer();
|
new T12NewSessionTicketProducer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The NewSessionTicketMessage handshake message.
|
* The NewSessionTicketMessage handshake messages.
|
||||||
*/
|
*/
|
||||||
static final class NewSessionTicketMessage extends HandshakeMessage {
|
abstract static class NewSessionTicketMessage extends HandshakeMessage {
|
||||||
final int ticketLifetime;
|
int ticketLifetime;
|
||||||
final int ticketAgeAdd;
|
byte[] ticket;
|
||||||
final byte[] ticketNonce;
|
|
||||||
final byte[] ticket;
|
|
||||||
final SSLExtensions extensions;
|
|
||||||
|
|
||||||
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,
|
int ticketLifetime, SecureRandom generator,
|
||||||
byte[] ticketNonce, byte[] ticket) {
|
byte[] ticketNonce, byte[] ticket) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -73,7 +172,7 @@ final class NewSessionTicket {
|
||||||
this.extensions = new SSLExtensions(this);
|
this.extensions = new SSLExtensions(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NewSessionTicketMessage(HandshakeContext context,
|
T13NewSessionTicketMessage(HandshakeContext context,
|
||||||
ByteBuffer m) throws IOException {
|
ByteBuffer m) throws IOException {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
|
@ -84,6 +183,7 @@ final class NewSessionTicket {
|
||||||
// opaque ticket<1..2^16-1>;
|
// opaque ticket<1..2^16-1>;
|
||||||
// Extension extensions<0..2^16-2>;
|
// Extension extensions<0..2^16-2>;
|
||||||
// } NewSessionTicket;
|
// } NewSessionTicket;
|
||||||
|
|
||||||
if (m.remaining() < 14) {
|
if (m.remaining() < 14) {
|
||||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||||
"Invalid NewSessionTicket message: no sufficient data");
|
"Invalid NewSessionTicket message: no sufficient data");
|
||||||
|
@ -111,24 +211,36 @@ final class NewSessionTicket {
|
||||||
|
|
||||||
SSLExtension[] supportedExtensions =
|
SSLExtension[] supportedExtensions =
|
||||||
context.sslConfig.getEnabledExtensions(
|
context.sslConfig.getEnabledExtensions(
|
||||||
SSLHandshake.NEW_SESSION_TICKET);
|
NEW_SESSION_TICKET);
|
||||||
this.extensions = new SSLExtensions(this, m, supportedExtensions);
|
this.extensions = new SSLExtensions(this, m, supportedExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLHandshake handshakeType() {
|
public SSLHandshake handshakeType() {
|
||||||
return SSLHandshake.NEW_SESSION_TICKET;
|
return NEW_SESSION_TICKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTicketAgeAdd() {
|
||||||
|
return ticketAgeAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getTicketNonce() {
|
||||||
|
return ticketNonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int messageLength() {
|
public int messageLength() {
|
||||||
|
|
||||||
int extLen = extensions.length();
|
int extLen = extensions.length();
|
||||||
if (extLen == 0) {
|
if (extLen == 0) {
|
||||||
extLen = 2; // empty extensions
|
extLen = 2; // empty extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
return 8 + ticketNonce.length + 1 +
|
return 4 +// ticketLifetime
|
||||||
ticket.length + 2 + extLen;
|
4 + // ticketAgeAdd
|
||||||
|
1 + ticketNonce.length + // len of nonce + nonce
|
||||||
|
2 + ticket.length + // len of ticket + ticket
|
||||||
|
extLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -153,18 +265,21 @@ final class NewSessionTicket {
|
||||||
" \"ticket_lifetime\" : \"{0}\",\n" +
|
" \"ticket_lifetime\" : \"{0}\",\n" +
|
||||||
" \"ticket_age_add\" : \"{1}\",\n" +
|
" \"ticket_age_add\" : \"{1}\",\n" +
|
||||||
" \"ticket_nonce\" : \"{2}\",\n" +
|
" \"ticket_nonce\" : \"{2}\",\n" +
|
||||||
" \"ticket\" : \"{3}\",\n" +
|
" \"ticket\" : '{'\n" +
|
||||||
|
"{3}\n" +
|
||||||
|
" '}'" +
|
||||||
" \"extensions\" : [\n" +
|
" \"extensions\" : [\n" +
|
||||||
"{4}\n" +
|
"{4}\n" +
|
||||||
" ]\n" +
|
" ]\n" +
|
||||||
"'}'",
|
"'}'",
|
||||||
Locale.ENGLISH);
|
Locale.ENGLISH);
|
||||||
|
|
||||||
|
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||||
Object[] messageFields = {
|
Object[] messageFields = {
|
||||||
ticketLifetime,
|
ticketLifetime,
|
||||||
"<omitted>", //ticketAgeAdd should not be logged
|
"<omitted>", //ticketAgeAdd should not be logged
|
||||||
Utilities.toHexString(ticketNonce),
|
Utilities.toHexString(ticketNonce),
|
||||||
Utilities.toHexString(ticket),
|
Utilities.indent(hexEncoder.encode(ticket), " "),
|
||||||
Utilities.indent(extensions.toString(), " ")
|
Utilities.indent(extensions.toString(), " ")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -248,25 +363,46 @@ final class NewSessionTicket {
|
||||||
}
|
}
|
||||||
return null;
|
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(),
|
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
|
||||||
nonceArr, newId.getId());
|
nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy));
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
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
|
// create and cache the new session
|
||||||
// The new session must be a child of the existing session so
|
// The new session must be a child of the existing session so
|
||||||
// they will be invalidated together, etc.
|
// they will be invalidated together, etc.
|
||||||
SSLSessionImpl sessionCopy =
|
|
||||||
new SSLSessionImpl(shc.handshakeSession, newId);
|
|
||||||
shc.handshakeSession.addChild(sessionCopy);
|
shc.handshakeSession.addChild(sessionCopy);
|
||||||
sessionCopy.setPreSharedKey(psk);
|
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
|
||||||
sessionCopy.setPskIdentity(newId.getId());
|
|
||||||
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
|
|
||||||
sessionCache.put(sessionCopy);
|
sessionCache.put(sessionCopy);
|
||||||
|
}
|
||||||
// Output the handshake message.
|
// Output the handshake message.
|
||||||
nstm.write(shc.handshakeOutput);
|
nstm.write(shc.handshakeOutput);
|
||||||
shc.handshakeOutput.flush();
|
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 {
|
implements HandshakeProducer {
|
||||||
|
|
||||||
// Prevent instantiation of this class.
|
// Prevent instantiation of this class.
|
||||||
private NewSessionTicketProducer() {
|
private T12NewSessionTicketProducer() {
|
||||||
// blank
|
// blank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,18 +427,59 @@ final class NewSessionTicket {
|
||||||
public byte[] produce(ConnectionContext context,
|
public byte[] produce(ConnectionContext context,
|
||||||
HandshakeMessage message) throws IOException {
|
HandshakeMessage message) throws IOException {
|
||||||
|
|
||||||
// NSTM may be sent in response to handshake messages.
|
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||||
// For example: key update
|
|
||||||
|
|
||||||
throw new ProviderException(
|
// Is this session resumable?
|
||||||
"NewSessionTicket handshake producer not implemented");
|
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
|
private static final
|
||||||
class NewSessionTicketConsumer implements SSLConsumer {
|
class T13NewSessionTicketConsumer implements SSLConsumer {
|
||||||
// Prevent instantiation of this class.
|
// Prevent instantiation of this class.
|
||||||
private NewSessionTicketConsumer() {
|
private T13NewSessionTicketConsumer() {
|
||||||
// blank
|
// blank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,13 +494,12 @@ final class NewSessionTicket {
|
||||||
// upon sending its Finished rather than waiting for the client
|
// upon sending its Finished rather than waiting for the client
|
||||||
// Finished.
|
// Finished.
|
||||||
//
|
//
|
||||||
// The consuming happens in client side only. As the server
|
// The consuming happens in client side only and is received after
|
||||||
// may send the NewSessionTicket before handshake complete, the
|
// the server's Finished message with PostHandshakeContext.
|
||||||
// context may be a PostHandshakeContext or HandshakeContext
|
|
||||||
// instance.
|
|
||||||
HandshakeContext hc = (HandshakeContext)context;
|
HandshakeContext hc = (HandshakeContext)context;
|
||||||
NewSessionTicketMessage nstm =
|
NewSessionTicketMessage nstm =
|
||||||
new NewSessionTicketMessage(hc, message);
|
new T13NewSessionTicketMessage(hc, message);
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
"Consuming NewSessionTicket message", nstm);
|
"Consuming NewSessionTicket message", nstm);
|
||||||
|
@ -352,21 +528,24 @@ final class NewSessionTicket {
|
||||||
}
|
}
|
||||||
|
|
||||||
SSLSessionImpl sessionToSave = hc.conContext.conSession;
|
SSLSessionImpl sessionToSave = hc.conContext.conSession;
|
||||||
|
SecretKey psk = null;
|
||||||
|
if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||||
SecretKey resumptionMasterSecret =
|
SecretKey resumptionMasterSecret =
|
||||||
sessionToSave.getResumptionMasterSecret();
|
sessionToSave.getResumptionMasterSecret();
|
||||||
if (resumptionMasterSecret == null) {
|
if (resumptionMasterSecret == null) {
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine(
|
SSLLogger.fine(
|
||||||
"Session has no resumption master secret. Ignoring ticket.");
|
"Session has no resumption master secret." +
|
||||||
|
" Ignoring ticket.");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// derive the PSK
|
// derive the PSK
|
||||||
SecretKey psk = derivePreSharedKey(
|
psk = derivePreSharedKey(
|
||||||
sessionToSave.getSuite().hashAlg, resumptionMasterSecret,
|
sessionToSave.getSuite().hashAlg,
|
||||||
nstm.ticketNonce);
|
resumptionMasterSecret, nstm.getTicketNonce());
|
||||||
|
}
|
||||||
|
|
||||||
// create and cache the new session
|
// create and cache the new session
|
||||||
// The new session must be a child of the existing session so
|
// The new session must be a child of the existing session so
|
||||||
|
@ -377,13 +556,68 @@ final class NewSessionTicket {
|
||||||
newId);
|
newId);
|
||||||
sessionToSave.addChild(sessionCopy);
|
sessionToSave.addChild(sessionCopy);
|
||||||
sessionCopy.setPreSharedKey(psk);
|
sessionCopy.setPreSharedKey(psk);
|
||||||
sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
|
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
|
||||||
sessionCopy.setPskIdentity(nstm.ticket);
|
sessionCopy.setPskIdentity(nstm.ticket);
|
||||||
sessionCache.put(sessionCopy);
|
sessionCache.put(sessionCopy);
|
||||||
|
|
||||||
// clean handshake context
|
// clean handshake context
|
||||||
|
if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||||
hc.conContext.finishPostHandshake();
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.SecretKey;
|
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.ExtensionConsumer;
|
||||||
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
|
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
|
||||||
|
import sun.security.util.HexDumpEncoder;
|
||||||
|
|
||||||
import static sun.security.ssl.SSLExtension.*;
|
import static sun.security.ssl.SSLExtension.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,7 +210,9 @@ final class PreSharedKeyExtension {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
MessageFormat messageFormat = new MessageFormat(
|
MessageFormat messageFormat = new MessageFormat(
|
||||||
"\"PreSharedKey\": '{'\n" +
|
"\"PreSharedKey\": '{'\n" +
|
||||||
" \"identities\" : \"{0}\",\n" +
|
" \"identities\": '{'\n" +
|
||||||
|
"{0}\n" +
|
||||||
|
" '}'" +
|
||||||
" \"binders\": \"{1}\",\n" +
|
" \"binders\": \"{1}\",\n" +
|
||||||
"'}'",
|
"'}'",
|
||||||
Locale.ENGLISH);
|
Locale.ENGLISH);
|
||||||
|
@ -222,9 +226,13 @@ final class PreSharedKeyExtension {
|
||||||
}
|
}
|
||||||
|
|
||||||
String identitiesString() {
|
String identitiesString() {
|
||||||
|
HexDumpEncoder hexEncoder = new HexDumpEncoder();
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
for (PskIdentity curId : identities) {
|
for (PskIdentity curId : identities) {
|
||||||
result.append(curId.toString() + "\n");
|
result.append(" {\n"+ Utilities.indent(
|
||||||
|
hexEncoder.encode(curId.identity), " ") +
|
||||||
|
"\n }\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
|
@ -278,7 +286,7 @@ final class PreSharedKeyExtension {
|
||||||
this.selectedIdentity = Record.getInt16(m);
|
this.selectedIdentity = Record.getInt16(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] getEncoded() throws IOException {
|
byte[] getEncoded() {
|
||||||
return new byte[] {
|
return new byte[] {
|
||||||
(byte)((selectedIdentity >> 8) & 0xFF),
|
(byte)((selectedIdentity >> 8) & 0xFF),
|
||||||
(byte)(selectedIdentity & 0xFF)
|
(byte)(selectedIdentity & 0xFF)
|
||||||
|
@ -368,8 +376,36 @@ final class PreSharedKeyExtension {
|
||||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||||
shc.sslContext.engineGetServerSessionContext();
|
shc.sslContext.engineGetServerSessionContext();
|
||||||
int idIndex = 0;
|
int idIndex = 0;
|
||||||
|
SSLSessionImpl s = null;
|
||||||
|
|
||||||
for (PskIdentity requestedId : pskSpec.identities) {
|
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 (s != null && canRejoin(clientHello, shc, s)) {
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||||
SSLLogger.fine("Resuming session: ", s);
|
SSLLogger.fine("Resuming session: ", s);
|
||||||
|
@ -391,7 +427,6 @@ final class PreSharedKeyExtension {
|
||||||
shc.resumingSession = null;
|
shc.resumingSession = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the context
|
// update the context
|
||||||
shc.handshakeExtensions.put(
|
shc.handshakeExtensions.put(
|
||||||
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
|
SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
|
||||||
|
@ -708,7 +743,8 @@ final class PreSharedKeyExtension {
|
||||||
int hashLength, List<PskIdentity> identities) {
|
int hashLength, List<PskIdentity> identities) {
|
||||||
List<byte[]> binders = new ArrayList<>();
|
List<byte[]> binders = new ArrayList<>();
|
||||||
byte[] binderProto = new byte[hashLength];
|
byte[] binderProto = new byte[hashLength];
|
||||||
for (PskIdentity curId : identities) {
|
int i = identities.size();
|
||||||
|
while (i-- > 0) {
|
||||||
binders.add(binderProto);
|
binders.add(binderProto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,13 @@ public abstract class SSLContextImpl extends SSLContextSpi {
|
||||||
private volatile StatusResponseManager statusResponseManager;
|
private volatile StatusResponseManager statusResponseManager;
|
||||||
|
|
||||||
private final ReentrantLock contextLock = new ReentrantLock();
|
private final ReentrantLock contextLock = new ReentrantLock();
|
||||||
|
final HashMap<Integer, SessionTicketExtension.StatelessKey> keyHashMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
SSLContextImpl() {
|
SSLContextImpl() {
|
||||||
ephemeralKeyManager = new EphemeralKeyManager();
|
ephemeralKeyManager = new EphemeralKeyManager();
|
||||||
clientCache = new SSLSessionContextImpl();
|
clientCache = new SSLSessionContextImpl(false);
|
||||||
serverCache = new SSLSessionContextImpl();
|
serverCache = new SSLSessionContextImpl(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -309,8 +309,28 @@ enum SSLExtension implements SSLStringizer {
|
||||||
// extensions defined in RFC 7924
|
// extensions defined in RFC 7924
|
||||||
CACHED_INFO (0x0019, "cached_info"),
|
CACHED_INFO (0x0019, "cached_info"),
|
||||||
|
|
||||||
// extensions defined in RFC 4507/5077
|
// extensions defined in RFC 5077
|
||||||
SESSION_TICKET (0x0023, "session_ticket"),
|
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
|
// extensions defined in TLS 1.3
|
||||||
CH_EARLY_DATA (0x002A, "early_data"),
|
CH_EARLY_DATA (0x002A, "early_data"),
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket",
|
NEW_SESSION_TICKET ((byte)0x04, "new_session_ticket",
|
||||||
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
(Map.Entry<SSLConsumer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||||
|
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||||
|
NewSessionTicket.handshake12Consumer,
|
||||||
|
ProtocolVersion.PROTOCOLS_TO_12
|
||||||
|
),
|
||||||
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
new SimpleImmutableEntry<SSLConsumer, ProtocolVersion[]>(
|
||||||
NewSessionTicket.handshakeConsumer,
|
NewSessionTicket.handshakeConsumer,
|
||||||
ProtocolVersion.PROTOCOLS_OF_13
|
ProtocolVersion.PROTOCOLS_OF_13
|
||||||
|
@ -121,8 +125,8 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer {
|
||||||
}),
|
}),
|
||||||
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
(Map.Entry<HandshakeProducer, ProtocolVersion[]>[])(new Map.Entry[] {
|
||||||
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
new SimpleImmutableEntry<HandshakeProducer, ProtocolVersion[]>(
|
||||||
NewSessionTicket.handshakeProducer,
|
NewSessionTicket.handshake12Producer,
|
||||||
ProtocolVersion.PROTOCOLS_OF_13
|
ProtocolVersion.PROTOCOLS_TO_12
|
||||||
)
|
)
|
||||||
})),
|
})),
|
||||||
END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"),
|
END_OF_EARLY_DATA ((byte)0x05, "end_of_early_data"),
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 javax.net.ssl.SSLSessionContext;
|
||||||
|
|
||||||
import sun.security.action.GetIntegerAction;
|
import sun.security.action.GetIntegerAction;
|
||||||
|
import sun.security.action.GetPropertyAction;
|
||||||
import sun.security.util.Cache;
|
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 {
|
final class SSLSessionContextImpl implements SSLSessionContext {
|
||||||
private final static int DEFAULT_MAX_CACHE_SIZE = 20480;
|
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;
|
private final Cache<SessionId, SSLSessionImpl> sessionCache;
|
||||||
// session cache, session id as key
|
// session cache, session id as key
|
||||||
|
@ -46,16 +69,24 @@ final class SSLSessionContextImpl implements SSLSessionContext {
|
||||||
private int cacheLimit; // the max cache size
|
private int cacheLimit; // the max cache size
|
||||||
private int timeout; // timeout in seconds
|
private int timeout; // timeout in seconds
|
||||||
|
|
||||||
|
// Does this context support stateless session (RFC 5077)
|
||||||
|
private boolean statelessSession = true;
|
||||||
|
|
||||||
// package private
|
// package private
|
||||||
SSLSessionContextImpl() {
|
SSLSessionContextImpl(boolean server) {
|
||||||
cacheLimit = getDefaultCacheLimit(); // default cache size
|
timeout = DEFAULT_SESSION_TIMEOUT;
|
||||||
timeout = 86400; // default, 24 hours
|
cacheLimit = getDefaults(server); // default cache size
|
||||||
|
|
||||||
// use soft reference
|
// use soft reference
|
||||||
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
|
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
|
||||||
sessionHostPortCache = 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.
|
* 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) {
|
private static String getKey(String hostname, int port) {
|
||||||
return (hostname + ":" +
|
return (hostname + ":" + port).toLowerCase(Locale.ENGLISH);
|
||||||
String.valueOf(port)).toLowerCase(Locale.ENGLISH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache a SSLSession
|
// cache a SSLSession
|
||||||
|
@ -197,8 +227,51 @@ final class SSLSessionContextImpl implements SSLSessionContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getDefaultCacheLimit() {
|
private int getDefaults(boolean server) {
|
||||||
try {
|
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(
|
int defaultCacheLimit = GetIntegerAction.privilegedGetProperty(
|
||||||
"javax.net.ssl.sessionCacheSize", DEFAULT_MAX_CACHE_SIZE);
|
"javax.net.ssl.sessionCacheSize", DEFAULT_MAX_CACHE_SIZE);
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,12 @@
|
||||||
*/
|
*/
|
||||||
package sun.security.ssl;
|
package sun.security.ssl;
|
||||||
|
|
||||||
|
import sun.security.x509.X509CertImpl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
@ -40,8 +44,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import javax.net.ssl.ExtendedSSLSession;
|
import javax.net.ssl.ExtendedSSLSession;
|
||||||
|
import javax.net.ssl.SNIHostName;
|
||||||
import javax.net.ssl.SNIServerName;
|
import javax.net.ssl.SNIServerName;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
import javax.net.ssl.SSLPermission;
|
import javax.net.ssl.SSLPermission;
|
||||||
import javax.net.ssl.SSLSessionBindingEvent;
|
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) {
|
void setMasterSecret(SecretKey secret) {
|
||||||
masterSecret = secret;
|
masterSecret = secret;
|
||||||
}
|
}
|
||||||
|
@ -333,6 +705,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] getPskIdentity() {
|
||||||
|
return pskIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
void setPeerCertificates(X509Certificate[] peer) {
|
void setPeerCertificates(X509Certificate[] peer) {
|
||||||
if (peerCerts == null) {
|
if (peerCerts == null) {
|
||||||
peerCerts = peer;
|
peerCerts = peer;
|
||||||
|
@ -400,6 +776,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
||||||
* maximum lifetime in any case.
|
* maximum lifetime in any case.
|
||||||
*/
|
*/
|
||||||
boolean isRejoinable() {
|
boolean isRejoinable() {
|
||||||
|
// TLS 1.3 can have no session id
|
||||||
|
if (protocolVersion.useTLS13PlusSpec()) {
|
||||||
|
return (!invalidated && isLocalAuthenticationValid());
|
||||||
|
}
|
||||||
return sessionId != null && sessionId.length() != 0 &&
|
return sessionId != null && sessionId.length() != 0 &&
|
||||||
!invalidated && isLocalAuthenticationValid();
|
!invalidated && isLocalAuthenticationValid();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||||
import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec;
|
import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec;
|
||||||
|
|
||||||
|
import static sun.security.ssl.SSLExtension.SH_SESSION_TICKET;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack of the ServerHello/HelloRetryRequest handshake message.
|
* Pack of the ServerHello/HelloRetryRequest handshake message.
|
||||||
*/
|
*/
|
||||||
|
@ -337,6 +339,15 @@ final class ServerHello {
|
||||||
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id,
|
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id,
|
||||||
SSLHandshake.SERVER_HELLO_DONE);
|
SSLHandshake.SERVER_HELLO_DONE);
|
||||||
} else {
|
} 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.handshakeSession = shc.resumingSession;
|
||||||
shc.negotiatedProtocol =
|
shc.negotiatedProtocol =
|
||||||
shc.resumingSession.getProtocolVersion();
|
shc.resumingSession.getProtocolVersion();
|
||||||
|
@ -491,6 +502,9 @@ final class ServerHello {
|
||||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||||
|
|
||||||
|
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||||
|
shc.sslContext.engineGetServerSessionContext();
|
||||||
|
|
||||||
// If client hasn't specified a session we can resume, start a
|
// If client hasn't specified a session we can resume, start a
|
||||||
// new one and choose its cipher suite and compression options,
|
// new one and choose its cipher suite and compression options,
|
||||||
// unless new session creation is disabled for this connection!
|
// unless new session creation is disabled for this connection!
|
||||||
|
@ -546,8 +560,6 @@ final class ServerHello {
|
||||||
shc.resumingSession.consumePreSharedKey());
|
shc.resumingSession.consumePreSharedKey());
|
||||||
|
|
||||||
// The session can't be resumed again---remove it from cache
|
// The session can't be resumed again---remove it from cache
|
||||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
|
||||||
shc.sslContext.engineGetServerSessionContext();
|
|
||||||
sessionCache.remove(shc.resumingSession.getSessionId());
|
sessionCache.remove(shc.resumingSession.getSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,6 +691,11 @@ final class ServerHello {
|
||||||
// Update the context for master key derivation.
|
// Update the context for master key derivation.
|
||||||
shc.handshakeKeyDerivation = kd;
|
shc.handshakeKeyDerivation = kd;
|
||||||
|
|
||||||
|
// Check if the server supports stateless resumption
|
||||||
|
if (sessionCache.statelessEnabled()) {
|
||||||
|
shc.statelessResumption = true;
|
||||||
|
}
|
||||||
|
|
||||||
// The handshake message has been delivered.
|
// The handshake message has been delivered.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1098,9 +1115,23 @@ final class ServerHello {
|
||||||
throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,
|
throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,
|
||||||
"New session creation is disabled");
|
"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.handshakeSession = new SSLSessionImpl(chc,
|
||||||
chc.negotiatedCipherSuite,
|
chc.negotiatedCipherSuite,
|
||||||
serverHello.sessionId);
|
serverHello.sessionId);
|
||||||
|
}
|
||||||
chc.handshakeSession.setMaximumPacketSize(
|
chc.handshakeSession.setMaximumPacketSize(
|
||||||
chc.sslConfig.maximumPacketSize);
|
chc.sslConfig.maximumPacketSize);
|
||||||
}
|
}
|
||||||
|
@ -1127,6 +1158,11 @@ final class ServerHello {
|
||||||
chc.conContext.consumers.putIfAbsent(
|
chc.conContext.consumers.putIfAbsent(
|
||||||
ContentType.CHANGE_CIPHER_SPEC.id,
|
ContentType.CHANGE_CIPHER_SPEC.id,
|
||||||
ChangeCipherSpec.t10Consumer);
|
ChangeCipherSpec.t10Consumer);
|
||||||
|
if (chc.statelessResumption) {
|
||||||
|
chc.handshakeConsumers.putIfAbsent(
|
||||||
|
SSLHandshake.NEW_SESSION_TICKET.id,
|
||||||
|
SSLHandshake.NEW_SESSION_TICKET);
|
||||||
|
}
|
||||||
chc.handshakeConsumers.put(
|
chc.handshakeConsumers.put(
|
||||||
SSLHandshake.FINISHED.id,
|
SSLHandshake.FINISHED.id,
|
||||||
SSLHandshake.FINISHED);
|
SSLHandshake.FINISHED);
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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
|
* @author David Brownell
|
||||||
*/
|
*/
|
||||||
final class SessionId {
|
final class SessionId {
|
||||||
private static final int MAX_LENGTH = 32;
|
static final int MAX_LENGTH = 32;
|
||||||
private final byte[] sessionId; // max 32 bytes
|
private final byte[] sessionId; // max 32 bytes
|
||||||
|
|
||||||
// Constructs a new session ID ... perhaps for a rejoinable session
|
// Constructs a new session ID ... perhaps for a rejoinable session
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -159,14 +159,19 @@ class TransportContext implements ConnectionContext {
|
||||||
if (handshakeContext == null) {
|
if (handshakeContext == null) {
|
||||||
if (type == SSLHandshake.KEY_UPDATE.id ||
|
if (type == SSLHandshake.KEY_UPDATE.id ||
|
||||||
type == SSLHandshake.NEW_SESSION_TICKET.id) {
|
type == SSLHandshake.NEW_SESSION_TICKET.id) {
|
||||||
if (isNegotiated &&
|
if (!isNegotiated) {
|
||||||
protocolVersion.useTLS13PlusSpec()) {
|
throw fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
handshakeContext = new PostHandshakeContext(this);
|
"Unexpected unnegotiated post-handshake" +
|
||||||
} else {
|
" message: " +
|
||||||
|
SSLHandshake.nameOf(type));
|
||||||
|
}
|
||||||
|
if (type == SSLHandshake.KEY_UPDATE.id &&
|
||||||
|
!protocolVersion.useTLS13PlusSpec()) {
|
||||||
throw fatal(Alert.UNEXPECTED_MESSAGE,
|
throw fatal(Alert.UNEXPECTED_MESSAGE,
|
||||||
"Unexpected post-handshake message: " +
|
"Unexpected post-handshake message: " +
|
||||||
SSLHandshake.nameOf(type));
|
SSLHandshake.nameOf(type));
|
||||||
}
|
}
|
||||||
|
handshakeContext = new PostHandshakeContext(this);
|
||||||
} else {
|
} else {
|
||||||
handshakeContext = sslConfig.isClientMode ?
|
handshakeContext = sslConfig.isClientMode ?
|
||||||
new ClientHandshakeContext(sslContext, this) :
|
new ClientHandshakeContext(sslContext, this) :
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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 1 client_hello
|
||||||
* @run main/othervm PacketLossRetransmission client 2 server_hello
|
* @run main/othervm PacketLossRetransmission client 2 server_hello
|
||||||
* @run main/othervm PacketLossRetransmission client 3 hello_verify_request
|
* @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 11 certificate
|
||||||
* @run main/othervm PacketLossRetransmission client 12 server_key_exchange
|
* @run main/othervm PacketLossRetransmission client 12 server_key_exchange
|
||||||
* @run main/othervm PacketLossRetransmission client 13 certificate_request
|
* @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 1 client_hello
|
||||||
* @run main/othervm PacketLossRetransmission server 2 server_hello
|
* @run main/othervm PacketLossRetransmission server 2 server_hello
|
||||||
* @run main/othervm PacketLossRetransmission server 3 hello_verify_request
|
* @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 11 certificate
|
||||||
* @run main/othervm PacketLossRetransmission server 12 server_key_exchange
|
* @run main/othervm PacketLossRetransmission server 12 server_key_exchange
|
||||||
* @run main/othervm PacketLossRetransmission server 13 certificate_request
|
* @run main/othervm PacketLossRetransmission server 13 certificate_request
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,7 +25,9 @@
|
||||||
* @test
|
* @test
|
||||||
* @bug 4473210
|
* @bug 4473210
|
||||||
* @summary SSLSessionContext should be accessible from SSLContext
|
* @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
|
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||||
* system properties in samevm/agentvm mode.
|
* system properties in samevm/agentvm mode.
|
||||||
|
|
|
@ -30,44 +30,57 @@
|
||||||
* @test
|
* @test
|
||||||
* @bug 6956398
|
* @bug 6956398
|
||||||
* @summary make ephemeral DH key match the length of the certificate key
|
* @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
|
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=matched
|
* -Djdk.tls.ephemeralDHKeySize=matched
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=legacy
|
* -Djdk.tls.ephemeralDHKeySize=legacy
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=1024
|
* -Djdk.tls.ephemeralDHKeySize=1024
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
* DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
|
||||||
*
|
*
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 233 75
|
* DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 233 75
|
||||||
*
|
*
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
|
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=legacy
|
* -Djdk.tls.ephemeralDHKeySize=legacy
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1323 107
|
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1323 107
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=matched
|
* -Djdk.tls.ephemeralDHKeySize=matched
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267
|
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1643 267
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=1024
|
* -Djdk.tls.ephemeralDHKeySize=1024
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
|
* DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA false 1387 139
|
||||||
*
|
*
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=legacy
|
* -Djdk.tls.ephemeralDHKeySize=legacy
|
||||||
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 297 107
|
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 297 107
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=matched
|
* -Djdk.tls.ephemeralDHKeySize=matched
|
||||||
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
||||||
* @run main/othervm -Djsse.enableFFDHE=false
|
* @run main/othervm -Djsse.enableFFDHE=false
|
||||||
|
* -Djdk.tls.client.enableSessionTicketExtension=false
|
||||||
* -Djdk.tls.ephemeralDHKeySize=1024
|
* -Djdk.tls.ephemeralDHKeySize=1024
|
||||||
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
* DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5 false 361 139
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,13 +26,17 @@
|
||||||
* @bug 8206929 8212885
|
* @bug 8206929 8212885
|
||||||
* @summary ensure that client only resumes a session if certain properties
|
* @summary ensure that client only resumes a session if certain properties
|
||||||
* of the session are compatible with the new connection
|
* 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.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksClient BASIC
|
||||||
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 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 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 ResumeChecksClient VERSION_2_TO_3
|
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksClient BASIC
|
||||||
* @run main/othervm ResumeChecksClient VERSION_3_TO_2
|
* @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 ResumeChecksClient CIPHER_SUITE
|
* @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.client.protocols=TLSv1.3 ResumeChecksClient SIGNATURE_SCHEME
|
* @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
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
*/
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,16 +26,18 @@
|
||||||
* @bug 8206929
|
* @bug 8206929
|
||||||
* @summary ensure that server only resumes a session if certain properties
|
* @summary ensure that server only resumes a session if certain properties
|
||||||
* of the session are compatible with the new connection
|
* 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.2 -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer BASIC
|
||||||
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 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 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 ResumeChecksServer CLIENT_AUTH
|
* @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.3 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 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 ResumeChecksServer VERSION_2_TO_3
|
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_2_TO_3
|
||||||
* @run main/othervm ResumeChecksServer VERSION_3_TO_2
|
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=false -Djdk.tls.client.enableSessionTicketExtension=true ResumeChecksServer VERSION_2_TO_3
|
||||||
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer CIPHER_SUITE
|
* @run main/othervm -Djdk.tls.server.enableSessionTicketExtension=true -Djdk.tls.client.enableSessionTicketExtension=false ResumeChecksServer VERSION_2_TO_3
|
||||||
* @run main/othervm -Djdk.tls.client.protocols=TLSv1.3 ResumeChecksServer SIGNATURE_SCHEME
|
* @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
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
*/
|
Loading…
Add table
Add a link
Reference in a new issue