mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
Merge
This commit is contained in:
commit
f73a94a772
83 changed files with 6892 additions and 1047 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -35,7 +35,11 @@ import java.util.Enumeration;
|
|||
* it could be associated with a server or client who participates in many
|
||||
* sessions concurrently.
|
||||
* <p>
|
||||
* Not all environments will contain session contexts.
|
||||
* Not all environments will contain session contexts. For example, stateless
|
||||
* session resumption.
|
||||
* <p>
|
||||
* Session contexts may not contain all sessions. For example, stateless
|
||||
* sessions are not stored in the session context.
|
||||
* <p>
|
||||
* There are <code>SSLSessionContext</code> parameters that affect how
|
||||
* sessions are stored:
|
||||
|
@ -68,8 +72,11 @@ public interface SSLSessionContext {
|
|||
public SSLSession getSession(byte[] sessionId);
|
||||
|
||||
/**
|
||||
* Returns an Enumeration of all session id's grouped under this
|
||||
* Returns an Enumeration of all known session id's grouped under this
|
||||
* <code>SSLSessionContext</code>.
|
||||
* <p>Session contexts may not contain all sessions. For example,
|
||||
* stateless sessions are not stored in the session context.
|
||||
* <p>
|
||||
*
|
||||
* @return an enumeration of all the Session id's
|
||||
*/
|
||||
|
|
|
@ -482,7 +482,7 @@ final class Finished {
|
|||
shc.conContext.inputRecord.expectingFinishFlight();
|
||||
} else {
|
||||
if (shc.handshakeSession.isRejoinable() &&
|
||||
!shc.statelessResumption) {
|
||||
!shc.handshakeSession.isStatelessable(shc)) {
|
||||
((SSLSessionContextImpl)shc.sslContext.
|
||||
engineGetServerSessionContext()).put(
|
||||
shc.handshakeSession);
|
||||
|
@ -847,6 +847,8 @@ final class Finished {
|
|||
shc.conContext.serverVerifyData = fm.verifyData;
|
||||
}
|
||||
|
||||
shc.conContext.conSession = shc.handshakeSession.finish();
|
||||
|
||||
// update the context
|
||||
shc.handshakeConsumers.put(
|
||||
SSLHandshake.FINISHED.id, SSLHandshake.FINISHED);
|
||||
|
|
|
@ -45,7 +45,6 @@ import static sun.security.ssl.SSLHandshake.NEW_SESSION_TICKET;
|
|||
*/
|
||||
final class NewSessionTicket {
|
||||
static final int MAX_TICKET_LIFETIME = 604800; // seconds, 7 days
|
||||
|
||||
static final SSLConsumer handshakeConsumer =
|
||||
new T13NewSessionTicketConsumer();
|
||||
static final SSLConsumer handshake12Consumer =
|
||||
|
@ -60,7 +59,7 @@ final class NewSessionTicket {
|
|||
*/
|
||||
abstract static class NewSessionTicketMessage extends HandshakeMessage {
|
||||
int ticketLifetime;
|
||||
byte[] ticket;
|
||||
byte[] ticket = new byte[0];
|
||||
|
||||
NewSessionTicketMessage(HandshakeContext context) {
|
||||
super(context);
|
||||
|
@ -83,6 +82,9 @@ final class NewSessionTicket {
|
|||
"TicketNonce not part of RFC 5077.");
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
return (ticket.length > 0);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* NewSessionTicket for TLS 1.2 and below (RFC 5077)
|
||||
|
@ -102,13 +104,13 @@ final class NewSessionTicket {
|
|||
|
||||
// RFC5077 struct {
|
||||
// uint32 ticket_lifetime;
|
||||
// opaque ticket<1..2^16-1>;
|
||||
// opaque ticket<0..2^16-1>;
|
||||
// } NewSessionTicket;
|
||||
|
||||
super(context);
|
||||
if (m.remaining() < 14) {
|
||||
if (m.remaining() < 6) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
"Invalid NewSessionTicket message: insufficient data");
|
||||
}
|
||||
|
||||
this.ticketLifetime = Record.getInt32(m);
|
||||
|
@ -186,7 +188,7 @@ final class NewSessionTicket {
|
|||
|
||||
if (m.remaining() < 14) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
"Invalid NewSessionTicket message: insufficient data");
|
||||
}
|
||||
|
||||
this.ticketLifetime = Record.getInt32(m);
|
||||
|
@ -195,18 +197,21 @@ final class NewSessionTicket {
|
|||
|
||||
if (m.remaining() < 5) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
"Invalid NewSessionTicket message: insufficient ticket" +
|
||||
" data");
|
||||
}
|
||||
|
||||
this.ticket = Record.getBytes16(m);
|
||||
if (ticket.length == 0) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"No ticket in the NewSessionTicket handshake message");
|
||||
}
|
||||
}
|
||||
|
||||
if (m.remaining() < 2) {
|
||||
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Invalid NewSessionTicket message: no sufficient data");
|
||||
"Invalid NewSessionTicket message: extra data");
|
||||
}
|
||||
|
||||
SSLExtension[] supportedExtensions =
|
||||
|
@ -310,36 +315,45 @@ final class NewSessionTicket {
|
|||
|
||||
@Override
|
||||
public byte[] produce(ConnectionContext context) throws IOException {
|
||||
HandshakeContext hc = (HandshakeContext)context;
|
||||
|
||||
// The producing happens in server side only.
|
||||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
if (hc instanceof ServerHandshakeContext) {
|
||||
// Is this session resumable?
|
||||
if (!hc.handshakeSession.isRejoinable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is this session resumable?
|
||||
if (!shc.handshakeSession.isRejoinable()) {
|
||||
return null;
|
||||
}
|
||||
// What's the requested PSK key exchange modes?
|
||||
//
|
||||
// Note that currently, the NewSessionTicket post-handshake is
|
||||
// produced and delivered only in the current handshake context
|
||||
// if required.
|
||||
PskKeyExchangeModesSpec pkemSpec =
|
||||
(PskKeyExchangeModesSpec) hc.handshakeExtensions.get(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES);
|
||||
if (pkemSpec == null || !pkemSpec.contains(
|
||||
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
|
||||
// Client doesn't support PSK with (EC)DHE key establishment.
|
||||
return null;
|
||||
}
|
||||
} else { // PostHandshakeContext
|
||||
|
||||
// What's the requested PSK key exchange modes?
|
||||
//
|
||||
// Note that currently, the NewSessionTicket post-handshake is
|
||||
// produced and delivered only in the current handshake context
|
||||
// if required.
|
||||
PskKeyExchangeModesSpec pkemSpec =
|
||||
(PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
|
||||
SSLExtension.PSK_KEY_EXCHANGE_MODES);
|
||||
if (pkemSpec == null || !pkemSpec.contains(
|
||||
PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
|
||||
// Client doesn't support PSK with (EC)DHE key establishment.
|
||||
return null;
|
||||
// Check if we have sent a PSK already, then we know it is using a
|
||||
// allowable PSK exchange key mode
|
||||
if (!hc.handshakeSession.isPSKable()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// get a new session ID
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
shc.sslContext.engineGetServerSessionContext();
|
||||
hc.sslContext.engineGetServerSessionContext();
|
||||
SessionId newId = new SessionId(true,
|
||||
shc.sslContext.getSecureRandom());
|
||||
hc.sslContext.getSecureRandom());
|
||||
|
||||
SecretKey resumptionMasterSecret =
|
||||
shc.handshakeSession.getResumptionMasterSecret();
|
||||
hc.handshakeSession.getResumptionMasterSecret();
|
||||
if (resumptionMasterSecret == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
|
@ -349,10 +363,10 @@ final class NewSessionTicket {
|
|||
}
|
||||
|
||||
// construct the PSK and handshake message
|
||||
BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
|
||||
BigInteger nonce = hc.handshakeSession.incrTicketNonceCounter();
|
||||
byte[] nonceArr = nonce.toByteArray();
|
||||
SecretKey psk = derivePreSharedKey(
|
||||
shc.negotiatedCipherSuite.hashAlg,
|
||||
hc.negotiatedCipherSuite.hashAlg,
|
||||
resumptionMasterSecret, nonceArr);
|
||||
|
||||
int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
|
||||
|
@ -364,31 +378,35 @@ final class NewSessionTicket {
|
|||
return null;
|
||||
}
|
||||
|
||||
NewSessionTicketMessage nstm;
|
||||
NewSessionTicketMessage nstm = null;
|
||||
|
||||
SSLSessionImpl sessionCopy =
|
||||
new SSLSessionImpl(shc.handshakeSession, newId);
|
||||
new SSLSessionImpl(hc.handshakeSession, newId);
|
||||
sessionCopy.setPreSharedKey(psk);
|
||||
sessionCopy.setPskIdentity(newId.getId());
|
||||
|
||||
if (shc.statelessResumption) {
|
||||
try {
|
||||
nstm = new T13NewSessionTicketMessage(shc,
|
||||
sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
|
||||
nonceArr, new SessionTicketSpec().encrypt(shc, sessionCopy));
|
||||
// If a stateless ticket is allowed, attempt to make one
|
||||
if (hc.handshakeSession.isStatelessable(hc)) {
|
||||
nstm = new T13NewSessionTicketMessage(hc,
|
||||
sessionTimeoutSeconds,
|
||||
hc.sslContext.getSecureRandom(),
|
||||
nonceArr,
|
||||
new SessionTicketSpec().encrypt(hc, sessionCopy));
|
||||
// If ticket construction failed, switch to session cache
|
||||
if (!nstm.isValid()) {
|
||||
hc.statelessResumption = false;
|
||||
} else {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced NewSessionTicket stateless " +
|
||||
"handshake message", nstm);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Error with NST ticket, abort NST
|
||||
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
nstm = new T13NewSessionTicketMessage(shc, sessionTimeoutSeconds,
|
||||
shc.sslContext.getSecureRandom(), nonceArr,
|
||||
}
|
||||
// If a session cache ticket is being used, make one
|
||||
if (!hc.handshakeSession.isStatelessable(hc)) {
|
||||
nstm = new T13NewSessionTicketMessage(hc, sessionTimeoutSeconds,
|
||||
hc.sslContext.getSecureRandom(), nonceArr,
|
||||
newId.getId());
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
|
@ -399,13 +417,21 @@ final class NewSessionTicket {
|
|||
// create and cache the new session
|
||||
// The new session must be a child of the existing session so
|
||||
// they will be invalidated together, etc.
|
||||
shc.handshakeSession.addChild(sessionCopy);
|
||||
hc.handshakeSession.addChild(sessionCopy);
|
||||
sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
|
||||
sessionCache.put(sessionCopy);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
nstm.write(shc.handshakeOutput);
|
||||
shc.handshakeOutput.flush();
|
||||
if (nstm != null) {
|
||||
// should never be null
|
||||
nstm.write(hc.handshakeOutput);
|
||||
hc.handshakeOutput.flush();
|
||||
}
|
||||
|
||||
if (hc instanceof PostHandshakeContext) {
|
||||
((PostHandshakeContext) hc).finish();
|
||||
}
|
||||
|
||||
// The message has been delivered.
|
||||
return null;
|
||||
|
@ -448,23 +474,16 @@ final class NewSessionTicket {
|
|||
return null;
|
||||
}
|
||||
|
||||
NewSessionTicketMessage nstm;
|
||||
|
||||
SSLSessionImpl sessionCopy =
|
||||
new SSLSessionImpl(shc.handshakeSession, newId);
|
||||
sessionCopy.setPskIdentity(newId.getId());
|
||||
|
||||
try {
|
||||
nstm = new T12NewSessionTicketMessage(shc, sessionTimeoutSeconds,
|
||||
new SessionTicketSpec().encrypt(shc, sessionCopy));
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced NewSessionTicket stateless handshake message", nstm);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Abort on error with NST ticket
|
||||
shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
|
||||
return null;
|
||||
NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc,
|
||||
sessionTimeoutSeconds,
|
||||
new SessionTicketSpec().encrypt(shc, sessionCopy));
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Produced NewSessionTicket stateless handshake message", nstm);
|
||||
}
|
||||
|
||||
// Output the handshake message.
|
||||
|
@ -505,6 +524,9 @@ final class NewSessionTicket {
|
|||
"Consuming NewSessionTicket message", nstm);
|
||||
}
|
||||
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
hc.sslContext.engineGetClientSessionContext();
|
||||
|
||||
// discard tickets with timeout 0
|
||||
if (nstm.ticketLifetime <= 0 ||
|
||||
nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
|
||||
|
@ -513,12 +535,10 @@ final class NewSessionTicket {
|
|||
"Discarding NewSessionTicket with lifetime "
|
||||
+ nstm.ticketLifetime, nstm);
|
||||
}
|
||||
sessionCache.remove(hc.handshakeSession.getSessionId());
|
||||
return;
|
||||
}
|
||||
|
||||
SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
|
||||
hc.sslContext.engineGetClientSessionContext();
|
||||
|
||||
if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
|
|
|
@ -54,6 +54,7 @@ final class PostHandshakeContext extends HandshakeContext {
|
|||
|
||||
handshakeConsumers = new LinkedHashMap<>(consumers);
|
||||
handshakeFinished = true;
|
||||
handshakeSession = context.conSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -82,4 +83,9 @@ final class PostHandshakeContext extends HandshakeContext {
|
|||
SSLHandshake.nameOf(handshakeType), be);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish this PostHandshake event
|
||||
void finish() {
|
||||
handshakeSession = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -344,6 +344,12 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
|
|||
hsStatus = tryKeyUpdate(hsStatus);
|
||||
}
|
||||
|
||||
// Check if NewSessionTicket PostHandshake message needs to be sent
|
||||
if (conContext.conSession.updateNST &&
|
||||
!conContext.sslConfig.isClientMode) {
|
||||
hsStatus = tryNewSessionTicket(hsStatus);
|
||||
}
|
||||
|
||||
// update context status
|
||||
ciphertext.handshakeStatus = hsStatus;
|
||||
|
||||
|
@ -397,6 +403,29 @@ final class SSLEngineImpl extends SSLEngine implements SSLTransport {
|
|||
return currentHandshakeStatus;
|
||||
}
|
||||
|
||||
// Try to generate a PostHandshake NewSessionTicket message. This is
|
||||
// TLS 1.3 only.
|
||||
private HandshakeStatus tryNewSessionTicket(
|
||||
HandshakeStatus currentHandshakeStatus) throws IOException {
|
||||
// Don't bother to kickstart if handshaking is in progress, or if the
|
||||
// connection is not duplex-open.
|
||||
if ((conContext.handshakeContext == null) &&
|
||||
conContext.protocolVersion.useTLS13PlusSpec() &&
|
||||
!conContext.isOutboundClosed() &&
|
||||
!conContext.isInboundClosed() &&
|
||||
!conContext.isBroken) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.finest("trigger NST");
|
||||
}
|
||||
conContext.conSession.updateNST = false;
|
||||
NewSessionTicket.kickstartProducer.produce(
|
||||
new PostHandshakeContext(conContext));
|
||||
return conContext.getHandshakeStatus();
|
||||
}
|
||||
|
||||
return currentHandshakeStatus;
|
||||
}
|
||||
|
||||
private static void checkParams(
|
||||
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
|
||||
ByteBuffer[] dsts, int dstsOffset, int dstsLength) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -27,6 +27,7 @@ package sun.security.ssl;
|
|||
import sun.security.x509.X509CertImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -35,6 +36,7 @@ import java.security.PrivateKey;
|
|||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -104,7 +106,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
private X509Certificate[] localCerts;
|
||||
private PrivateKey localPrivateKey;
|
||||
private final Collection<SignatureScheme> localSupportedSignAlgs;
|
||||
private String[] peerSupportedSignAlgs; // for certificate
|
||||
private Collection<SignatureScheme> peerSupportedSignAlgs; //for certificate
|
||||
private boolean useDefaultPeerSignAlgs = false;
|
||||
private List<byte[]> statusResponses;
|
||||
private SecretKey resumptionMasterSecret;
|
||||
|
@ -236,7 +238,8 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
baseSession.localSupportedSignAlgs == null ?
|
||||
Collections.emptySet() : baseSession.localSupportedSignAlgs;
|
||||
this.peerSupportedSignAlgs =
|
||||
baseSession.getPeerSupportedSignatureAlgorithms();
|
||||
baseSession.peerSupportedSignAlgs == null ?
|
||||
Collections.emptySet() : baseSession.peerSupportedSignAlgs;
|
||||
this.serverNameIndication = baseSession.serverNameIndication;
|
||||
this.requestedServerNames = baseSession.getRequestedServerNames();
|
||||
this.masterSecret = baseSession.getMasterSecret();
|
||||
|
@ -261,8 +264,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
/**
|
||||
* < 2 bytes > protocolVersion
|
||||
* < 2 bytes > cipherSuite
|
||||
* < 2 bytes > localSupportedSignAlgs entries
|
||||
* < 1 byte > localSupportedSignAlgs entries
|
||||
* < 2 bytes per entries > localSupportedSignAlgs
|
||||
* < 1 bytes > peerSupportedSignAlgs entries
|
||||
* < 2 bytes per entries > peerSupportedSignAlgs
|
||||
* < 2 bytes > preSharedKey length
|
||||
* < length in bytes > preSharedKey
|
||||
* < 1 byte > pskIdentity length
|
||||
|
@ -281,6 +286,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
* < 1 byte > ServerName length
|
||||
* < length in bytes > ServerName
|
||||
* < 4 bytes > creationTime
|
||||
* < 2 byte > status response length
|
||||
* < 2 byte > status response entry length
|
||||
* < length in byte > status response entry
|
||||
* < 1 byte > Length of peer host
|
||||
* < length in bytes > peer host
|
||||
* < 2 bytes> peer port
|
||||
|
@ -302,17 +310,17 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
* < length in bytes> PSK identity
|
||||
* Anonymous
|
||||
* < 1 byte >
|
||||
* < 4 bytes > maximumPacketSize
|
||||
* < 4 bytes > negotiatedMaxFragSize
|
||||
*/
|
||||
|
||||
SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException {
|
||||
int i = 0;
|
||||
byte[] b;
|
||||
|
||||
this.localSupportedSignAlgs = new ArrayList<>();
|
||||
|
||||
boundValues = null;
|
||||
|
||||
this.protocolVersion = ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
|
||||
boundValues = new ConcurrentHashMap<>();
|
||||
this.protocolVersion =
|
||||
ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort()));
|
||||
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
this.sessionId = new SessionId(false, null);
|
||||
|
@ -322,14 +330,26 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
hc.sslContext.getSecureRandom());
|
||||
}
|
||||
|
||||
this.cipherSuite = CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
|
||||
this.cipherSuite =
|
||||
CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort()));
|
||||
|
||||
// Local Supported signature algorithms
|
||||
i = Short.toUnsignedInt(buf.getShort());
|
||||
ArrayList<SignatureScheme> list = new ArrayList<>();
|
||||
i = Byte.toUnsignedInt(buf.get());
|
||||
while (i-- > 0) {
|
||||
this.localSupportedSignAlgs.add(SignatureScheme.valueOf(
|
||||
list.add(SignatureScheme.valueOf(
|
||||
Short.toUnsignedInt(buf.getShort())));
|
||||
}
|
||||
this.localSupportedSignAlgs = Collections.unmodifiableCollection(list);
|
||||
|
||||
// Peer Supported signature algorithms
|
||||
i = Byte.toUnsignedInt(buf.get());
|
||||
list.clear();
|
||||
while (i-- > 0) {
|
||||
list.add(SignatureScheme.valueOf(
|
||||
Short.toUnsignedInt(buf.getShort())));
|
||||
}
|
||||
this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list);
|
||||
|
||||
// PSK
|
||||
i = Short.toUnsignedInt(buf.getShort());
|
||||
|
@ -410,9 +430,27 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
}
|
||||
}
|
||||
|
||||
maximumPacketSize = buf.getInt();
|
||||
negotiatedMaxFragLen = buf.getInt();
|
||||
|
||||
// Get creation time
|
||||
this.creationTime = buf.getLong();
|
||||
|
||||
// Get Buffer sizes
|
||||
|
||||
// Status Response
|
||||
len = Short.toUnsignedInt(buf.getShort());
|
||||
if (len == 0) {
|
||||
statusResponses = Collections.emptyList();
|
||||
} else {
|
||||
statusResponses = new ArrayList<>();
|
||||
}
|
||||
while (len-- > 0) {
|
||||
b = new byte[Short.toUnsignedInt(buf.getShort())];
|
||||
buf.get(b);
|
||||
statusResponses.add(b);
|
||||
}
|
||||
|
||||
// Get Peer host & port
|
||||
i = Byte.toUnsignedInt(buf.get());
|
||||
if (i == 0) {
|
||||
|
@ -484,6 +522,33 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
|
||||
context = (SSLSessionContextImpl)
|
||||
hc.sslContext.engineGetServerSessionContext();
|
||||
this.lastUsedTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// Some situations we cannot provide a stateless ticket, but after it
|
||||
// has been negotiated
|
||||
boolean isStatelessable(HandshakeContext hc) {
|
||||
if (!hc.statelessResumption) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no getMasterSecret with TLS1.2 or under, do not resume.
|
||||
if (!protocolVersion.useTLS13PlusSpec() &&
|
||||
getMasterSecret().getEncoded() == null) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest("No MasterSecret, cannot make stateless" +
|
||||
" ticket");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (boundValues != null && boundValues.size() > 0) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.finest("There are boundValues, cannot make" +
|
||||
" stateless ticket");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -497,11 +562,14 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
hos.putInt16(cipherSuite.id);
|
||||
|
||||
// Local Supported signature algorithms
|
||||
int l = localSupportedSignAlgs.size();
|
||||
hos.putInt16(l);
|
||||
SignatureScheme[] sig = new SignatureScheme[l];
|
||||
localSupportedSignAlgs.toArray(sig);
|
||||
for (SignatureScheme s : sig) {
|
||||
hos.putInt8(localSupportedSignAlgs.size());
|
||||
for (SignatureScheme s : localSupportedSignAlgs) {
|
||||
hos.putInt16(s.id);
|
||||
}
|
||||
|
||||
// Peer Supported signature algorithms
|
||||
hos.putInt8(peerSupportedSignAlgs.size());
|
||||
for (SignatureScheme s : peerSupportedSignAlgs) {
|
||||
hos.putInt16(s.id);
|
||||
}
|
||||
|
||||
|
@ -564,16 +632,30 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
// List of SNIServerName
|
||||
hos.putInt16(requestedServerNames.size());
|
||||
if (requestedServerNames.size() > 0) {
|
||||
for (SNIServerName host: requestedServerNames) {
|
||||
for (SNIServerName host : requestedServerNames) {
|
||||
b = host.getEncoded();
|
||||
hos.putInt8(b.length);
|
||||
hos.write(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer sizes
|
||||
hos.putInt32(maximumPacketSize);
|
||||
hos.putInt32(negotiatedMaxFragLen);
|
||||
|
||||
// creation time
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||
hos.writeBytes(buffer.putLong(creationTime).array());
|
||||
|
||||
// Status Responses
|
||||
List<byte[]> list = getStatusResponses();
|
||||
int l = list.size();
|
||||
hos.putInt16(l);
|
||||
for (byte[] e : list) {
|
||||
hos.putInt16(e.length);
|
||||
hos.write(e);
|
||||
}
|
||||
|
||||
// peer Host & Port
|
||||
if (host == null || host.length() == 0) {
|
||||
hos.putInt8(0);
|
||||
|
@ -649,10 +731,14 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
|
||||
BigInteger incrTicketNonceCounter() {
|
||||
BigInteger result = ticketNonceCounter;
|
||||
ticketNonceCounter = ticketNonceCounter.add(BigInteger.valueOf(1));
|
||||
ticketNonceCounter = ticketNonceCounter.add(BigInteger.ONE);
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean isPSKable() {
|
||||
return (ticketNonceCounter.compareTo(BigInteger.ZERO) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the master secret ... treat with extreme caution!
|
||||
*/
|
||||
|
@ -725,8 +811,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
|
||||
void setPeerSupportedSignatureAlgorithms(
|
||||
Collection<SignatureScheme> signatureSchemes) {
|
||||
peerSupportedSignAlgs =
|
||||
SignatureScheme.getAlgorithmNames(signatureSchemes);
|
||||
peerSupportedSignAlgs = signatureSchemes;
|
||||
}
|
||||
|
||||
// TLS 1.2 only
|
||||
|
@ -740,16 +825,20 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
// certificates and server key exchange), it MUST send the
|
||||
// signature_algorithms extension, listing the algorithms it
|
||||
// is willing to accept.
|
||||
private static final ArrayList<SignatureScheme> defaultPeerSupportedSignAlgs =
|
||||
new ArrayList<>(Arrays.asList(SignatureScheme.RSA_PKCS1_SHA1,
|
||||
SignatureScheme.DSA_SHA1,
|
||||
SignatureScheme.ECDSA_SHA1));
|
||||
|
||||
void setUseDefaultPeerSignAlgs() {
|
||||
useDefaultPeerSignAlgs = true;
|
||||
peerSupportedSignAlgs = new String[] {
|
||||
"SHA1withRSA", "SHA1withDSA", "SHA1withECDSA"};
|
||||
peerSupportedSignAlgs = defaultPeerSupportedSignAlgs;
|
||||
}
|
||||
|
||||
// Returns the connection session.
|
||||
SSLSessionImpl finish() {
|
||||
if (useDefaultPeerSignAlgs) {
|
||||
this.peerSupportedSignAlgs = new String[0];
|
||||
peerSupportedSignAlgs = Collections.emptySet();
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -1212,6 +1301,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
* sessions can be shared across different protection domains.
|
||||
*/
|
||||
private final ConcurrentHashMap<SecureKey, Object> boundValues;
|
||||
boolean updateNST;
|
||||
|
||||
/**
|
||||
* Assigns a session value. Session change events are given if
|
||||
|
@ -1238,6 +1328,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
e = new SSLSessionBindingEvent(this, key);
|
||||
((SSLSessionBindingListener)value).valueBound(e);
|
||||
}
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
updateNST = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1273,6 +1366,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
e = new SSLSessionBindingEvent(this, key);
|
||||
((SSLSessionBindingListener)value).valueUnbound(e);
|
||||
}
|
||||
if (protocolVersion.useTLS13PlusSpec()) {
|
||||
updateNST = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1474,11 +1570,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
*/
|
||||
@Override
|
||||
public String[] getPeerSupportedSignatureAlgorithms() {
|
||||
if (peerSupportedSignAlgs != null) {
|
||||
return peerSupportedSignAlgs.clone();
|
||||
}
|
||||
|
||||
return new String[0];
|
||||
return SignatureScheme.getAlgorithmNames(peerSupportedSignAlgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1264,6 +1264,11 @@ public final class SSLSocketImpl
|
|||
conContext.outputRecord.writeCipher.atKeyLimit()) {
|
||||
tryKeyUpdate();
|
||||
}
|
||||
// Check if NewSessionTicket PostHandshake message needs to be sent
|
||||
if (conContext.conSession.updateNST) {
|
||||
conContext.conSession.updateNST = false;
|
||||
tryNewSessionTicket();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1499,6 +1504,25 @@ public final class SSLSocketImpl
|
|||
}
|
||||
}
|
||||
|
||||
// Try to generate a PostHandshake NewSessionTicket message. This is
|
||||
// TLS 1.3 only.
|
||||
private void tryNewSessionTicket() throws IOException {
|
||||
// Don't bother to kickstart if handshaking is in progress, or if the
|
||||
// connection is not duplex-open.
|
||||
if (!conContext.sslConfig.isClientMode &&
|
||||
conContext.protocolVersion.useTLS13PlusSpec() &&
|
||||
conContext.handshakeContext == null &&
|
||||
!conContext.isOutboundClosed() &&
|
||||
!conContext.isInboundClosed() &&
|
||||
!conContext.isBroken) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
|
||||
SSLLogger.finest("trigger new session ticket");
|
||||
}
|
||||
NewSessionTicket.kickstartProducer.produce(
|
||||
new PostHandshakeContext(conContext));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the handshaker and socket streams.
|
||||
*
|
||||
|
|
|
@ -1155,14 +1155,14 @@ final class ServerHello {
|
|||
chc, chc.resumingSession.getMasterSecret());
|
||||
}
|
||||
|
||||
chc.conContext.consumers.putIfAbsent(
|
||||
ContentType.CHANGE_CIPHER_SPEC.id,
|
||||
ChangeCipherSpec.t10Consumer);
|
||||
if (chc.statelessResumption) {
|
||||
chc.handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.NEW_SESSION_TICKET.id,
|
||||
SSLHandshake.NEW_SESSION_TICKET);
|
||||
}
|
||||
chc.conContext.consumers.putIfAbsent(
|
||||
ContentType.CHANGE_CIPHER_SPEC.id,
|
||||
ChangeCipherSpec.t10Consumer);
|
||||
chc.handshakeConsumers.put(
|
||||
SSLHandshake.FINISHED.id,
|
||||
SSLHandshake.FINISHED);
|
||||
|
|
|
@ -46,7 +46,6 @@ import java.nio.ByteBuffer;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
@ -255,13 +254,17 @@ final class SessionTicketExtension {
|
|||
data = buf;
|
||||
}
|
||||
|
||||
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session)
|
||||
throws IOException {
|
||||
public byte[] encrypt(HandshakeContext hc, SSLSessionImpl session) {
|
||||
byte[] encrypted;
|
||||
StatelessKey key = KeyState.getCurrentKey(hc);
|
||||
byte[] iv = new byte[16];
|
||||
|
||||
if (!hc.handshakeSession.isStatelessable(hc)) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
try {
|
||||
StatelessKey key = KeyState.getCurrentKey(hc);
|
||||
byte[] iv = new byte[16];
|
||||
|
||||
SecureRandom random = hc.sslContext.getSecureRandom();
|
||||
random.nextBytes(iv);
|
||||
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
|
@ -273,8 +276,11 @@ final class SessionTicketExtension {
|
|||
(byte)(key.num >>> 8),
|
||||
(byte)(key.num)}
|
||||
);
|
||||
encrypted = c.doFinal(session.write());
|
||||
|
||||
byte[] data = session.write();
|
||||
if (data.length == 0) {
|
||||
return data;
|
||||
}
|
||||
encrypted = c.doFinal(data);
|
||||
byte[] result = new byte[encrypted.length + Integer.BYTES +
|
||||
iv.length];
|
||||
result[0] = (byte)(key.num >>> 24);
|
||||
|
@ -286,7 +292,10 @@ final class SessionTicketExtension {
|
|||
Integer.BYTES + iv.length, encrypted.length);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw hc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, e);
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine("Encryption failed." + e);
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,11 +320,7 @@ final class SessionTicketExtension {
|
|||
(byte)(keyID >>> 8),
|
||||
(byte)(keyID)}
|
||||
);
|
||||
/*
|
||||
return ByteBuffer.wrap(c.doFinal(data,
|
||||
Integer.BYTES + iv.length,
|
||||
data.length - (Integer.BYTES + iv.length)));
|
||||
*/
|
||||
|
||||
ByteBuffer out;
|
||||
out = ByteBuffer.allocate(data.remaining() - GCM_TAG_LEN / 8);
|
||||
c.doFinal(data, out);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
```
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue