This commit is contained in:
Mikael Vidstedt 2019-07-23 11:17:59 -07:00
commit f73a94a772
83 changed files with 6892 additions and 1047 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -69,8 +69,8 @@ final class SSLSessionContextImpl implements SSLSessionContext {
private int cacheLimit; // the max cache size
private int timeout; // timeout in seconds
// Does this context support stateless session (RFC 5077)
private boolean statelessSession = true;
// Default setting for stateless session resumption support (RFC 5077)
private boolean statelessSession = false;
// package private
SSLSessionContextImpl(boolean server) {
@ -234,15 +234,14 @@ final class SSLSessionContextImpl implements SSLSessionContext {
// Property for Session Cache state
if (server) {
st = GetPropertyAction.privilegedGetProperty(
"jdk.tls.server.enableSessionTicketExtension", "true");
"jdk.tls.server.enableSessionTicketExtension", "false");
} else {
st = GetPropertyAction.privilegedGetProperty(
"jdk.tls.client.enableSessionTicketExtension", "true");
}
if (st.compareToIgnoreCase("false") == 0) {
statelessSession = false;
"jdk.tls.client.enableSessionTicketExtension", "false");
}
statelessSession = Boolean.parseBoolean(st);
// Property for Session Ticket Timeout. The value can be changed
// by SSLSessionContext.setSessionTimeout(int)
String s = GetPropertyAction.privilegedGetProperty(

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,10 +25,12 @@
package sun.security.util;
import java.security.*;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
@ -40,13 +42,27 @@ import static java.nio.charset.StandardCharsets.UTF_8;
*/
public class ManifestDigester {
/**
* The part "{@code Manifest-Main-Attributes}" of the main attributes
* digest header name in a signature file as described in the jar
* specification:
* <blockquote>{@code x-Digest-Manifest-Main-Attributes}
* (where x is the standard name of a {@link MessageDigest} algorithm):
* The value of this attribute is the digest value of the main attributes
* of the manifest.</blockquote>
* @see <a href="{@docRoot}/../specs/jar/jar.html#signature-file">
* JAR File Specification, section Signature File</a>
* @see #getMainAttsEntry
*/
public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes";
/** the raw bytes of the manifest */
private byte[] rawBytes;
private final byte[] rawBytes;
/** the entries grouped by names */
private HashMap<String, Entry> entries; // key is a UTF-8 string
private final Entry mainAttsEntry;
/** individual sections by their names */
private final HashMap<String, Entry> entries = new HashMap<>();
/** state returned by findSection */
static class Position {
@ -72,29 +88,31 @@ public class ManifestDigester {
private boolean findSection(int offset, Position pos)
{
int i = offset, len = rawBytes.length;
int last = offset;
int last = offset - 1;
int next;
boolean allBlank = true;
pos.endOfFirstLine = -1;
/* denotes that a position is not yet assigned.
* As a primitive type int it cannot be null
* and -1 would be confused with (i - 1) when i == 0 */
final int UNASSIGNED = Integer.MIN_VALUE;
pos.endOfFirstLine = UNASSIGNED;
while (i < len) {
byte b = rawBytes[i];
switch(b) {
case '\r':
if (pos.endOfFirstLine == -1)
if (pos.endOfFirstLine == UNASSIGNED)
pos.endOfFirstLine = i-1;
if ((i < len) && (rawBytes[i+1] == '\n'))
if (i < len - 1 && rawBytes[i + 1] == '\n')
i++;
/* fall through */
case '\n':
if (pos.endOfFirstLine == -1)
if (pos.endOfFirstLine == UNASSIGNED)
pos.endOfFirstLine = i-1;
if (allBlank || (i == len-1)) {
if (i == len-1)
pos.endOfSection = i;
else
pos.endOfSection = last;
pos.endOfSection = allBlank ? last : i;
pos.startOfNext = i+1;
return true;
}
@ -116,16 +134,17 @@ public class ManifestDigester {
public ManifestDigester(byte[] bytes)
{
rawBytes = bytes;
entries = new HashMap<>();
Position pos = new Position();
if (!findSection(0, pos))
if (!findSection(0, pos)) {
mainAttsEntry = null;
return; // XXX: exception?
}
// create an entry for main attributes
entries.put(MF_MAIN_ATTRS, new Entry().addSection(
new Section(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)));
mainAttsEntry = new Entry().addSection(new Section(
0, pos.endOfSection + 1, pos.startOfNext, rawBytes));
int start = pos.startOfNext;
while(findSection(start, pos)) {
@ -133,14 +152,16 @@ public class ManifestDigester {
int sectionLen = pos.endOfSection-start+1;
int sectionLenWithBlank = pos.startOfNext-start;
if (len > 6) {
if (len >= 6) { // 6 == "Name: ".length()
if (isNameAttr(bytes, start)) {
ByteArrayOutputStream nameBuf = new ByteArrayOutputStream();
nameBuf.write(bytes, start+6, len-6);
int i = start + len;
if ((i-start) < sectionLen) {
if (bytes[i] == '\r') {
if (bytes[i] == '\r'
&& i + 1 - start < sectionLen
&& bytes[i + 1] == '\n') {
i += 2;
} else {
i += 1;
@ -152,14 +173,16 @@ public class ManifestDigester {
// name is wrapped
int wrapStart = i;
while (((i-start) < sectionLen)
&& (bytes[i++] != '\n'));
if (bytes[i-1] != '\n')
return; // XXX: exception?
int wrapLen;
if (bytes[i-2] == '\r')
wrapLen = i-wrapStart-2;
else
wrapLen = i-wrapStart-1;
&& (bytes[i] != '\r')
&& (bytes[i] != '\n')) i++;
int wrapLen = i - wrapStart;
if (i - start < sectionLen) {
i++;
if (bytes[i - 1] == '\r'
&& i - start < sectionLen
&& bytes[i] == '\n')
i++;
}
nameBuf.write(bytes, wrapStart, wrapLen);
} else {
@ -167,7 +190,7 @@ public class ManifestDigester {
}
}
entries.computeIfAbsent(new String(nameBuf.toByteArray(), UTF_8),
entries.computeIfAbsent(nameBuf.toString(UTF_8),
dummy -> new Entry())
.addSection(new Section(start, sectionLen,
sectionLenWithBlank, rawBytes));
@ -202,6 +225,26 @@ public class ManifestDigester {
return this;
}
/**
* Check if the sections (particularly the last one of usually only one)
* are properly delimited with a trailing blank line so that another
* section can be correctly appended and return {@code true} or return
* {@code false} to indicate that reproduction is not advised and should
* be carried out with a clean "normalized" newly-written manifest.
*
* @see #reproduceRaw
*/
public boolean isProperlyDelimited() {
return sections.stream().allMatch(
Section::isProperlySectionDelimited);
}
public void reproduceRaw(OutputStream out) throws IOException {
for (Section sec : sections) {
out.write(sec.rawBytes, sec.offset, sec.lengthWithBlankLine);
}
}
public byte[] digest(MessageDigest md)
{
md.reset();
@ -242,6 +285,15 @@ public class ManifestDigester {
this.rawBytes = rawBytes;
}
/**
* Returns {@code true} if the raw section is terminated with a blank
* line so that another section can possibly be appended resulting in a
* valid manifest and {@code false} otherwise.
*/
private boolean isProperlySectionDelimited() {
return lengthWithBlankLine > length;
}
private static void doOldStyle(MessageDigest md,
byte[] bytes,
int offset,
@ -268,10 +320,33 @@ public class ManifestDigester {
}
}
/**
* @see #MF_MAIN_ATTRS
*/
public Entry getMainAttsEntry() {
return mainAttsEntry;
}
/**
* @see #MF_MAIN_ATTRS
*/
public Entry getMainAttsEntry(boolean oldStyle) {
mainAttsEntry.oldStyle = oldStyle;
return mainAttsEntry;
}
public Entry get(String name) {
return entries.get(name);
}
public Entry get(String name, boolean oldStyle) {
Entry e = entries.get(name);
if (e != null)
Entry e = get(name);
if (e == null && MF_MAIN_ATTRS.equals(name)) {
e = getMainAttsEntry();
}
if (e != null) {
e.oldStyle = oldStyle;
}
return e;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -72,8 +72,7 @@ public class SignatureFileVerifier {
private ArrayList<CodeSigner[]> signerCache;
private static final String ATTR_DIGEST =
("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase
(Locale.ENGLISH);
"-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS.toUpperCase(Locale.ENGLISH);
/** the PKCS7 block for this .DSA/.RSA/.EC file */
private PKCS7 block;
@ -537,8 +536,7 @@ public class SignatureFileVerifier {
MessageDigest digest = getDigest(algorithm);
if (digest != null) {
ManifestDigester.Entry mde =
md.get(ManifestDigester.MF_MAIN_ATTRS, false);
ManifestDigester.Entry mde = md.getMainAttsEntry(false);
byte[] computedHash = mde.digest(digest);
byte[] expectedHash =
Base64.getMimeDecoder().decode((String)se.getValue());

View file

@ -1,6 +1,7 @@
## International Components for Unicode (ICU4J) v64.2
### ICU4J License
```
COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
@ -387,3 +388,6 @@ Database section 7.
# by ICANN or the IETF Trust on the database or the code. Any person
# making a contribution to the database or code waives all rights to
# future claims in that contribution or in the TZ Database.
```

View file

@ -1,6 +1,7 @@
## The Unicode Standard, Unicode Character Database, Version 12.1.0
### Unicode Character Database
```
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
@ -48,3 +49,6 @@ Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in these Data Files or Software without prior
written authorization of the copyright holder.
```