|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package sun.security.ssl; |
|
|
|
import sun.security.x509.X509CertImpl; |
|
|
|
import java.io.IOException; |
|
import java.math.BigInteger; |
|
import java.net.InetAddress; |
|
import java.nio.ByteBuffer; |
|
import java.security.Principal; |
|
import java.security.PrivateKey; |
|
import java.security.cert.X509Certificate; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Queue; |
|
import java.util.Collection; |
|
import java.util.Collections; |
|
import java.util.Enumeration; |
|
import java.util.List; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.concurrent.ConcurrentLinkedQueue; |
|
import java.util.concurrent.locks.ReentrantLock; |
|
import javax.crypto.SecretKey; |
|
import javax.crypto.spec.SecretKeySpec; |
|
import javax.net.ssl.ExtendedSSLSession; |
|
import javax.net.ssl.SNIHostName; |
|
import javax.net.ssl.SNIServerName; |
|
import javax.net.ssl.SSLException; |
|
import javax.net.ssl.SSLPeerUnverifiedException; |
|
import javax.net.ssl.SSLPermission; |
|
import javax.net.ssl.SSLSessionBindingEvent; |
|
import javax.net.ssl.SSLSessionBindingListener; |
|
import javax.net.ssl.SSLSessionContext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class SSLSessionImpl extends ExtendedSSLSession { |
|
|
|
|
|
|
|
|
|
*/ |
|
private final ProtocolVersion protocolVersion; |
|
private final SessionId sessionId; |
|
private X509Certificate[] peerCerts; |
|
private CipherSuite cipherSuite; |
|
private SecretKey masterSecret; |
|
final boolean useExtendedMasterSecret; |
|
|
|
|
|
|
|
|
|
*/ |
|
private final long creationTime; |
|
private long lastUsedTime = 0; |
|
private final String host; |
|
private final int port; |
|
private SSLSessionContextImpl context; |
|
private boolean invalidated; |
|
private X509Certificate[] localCerts; |
|
private PrivateKey localPrivateKey; |
|
private final Collection<SignatureScheme> localSupportedSignAlgs; |
|
private Collection<SignatureScheme> peerSupportedSignAlgs; |
|
private boolean useDefaultPeerSignAlgs = false; |
|
private List<byte[]> statusResponses; |
|
private SecretKey resumptionMasterSecret; |
|
private SecretKey preSharedKey; |
|
private byte[] pskIdentity; |
|
private final long ticketCreationTime = System.currentTimeMillis(); |
|
private int ticketAgeAdd; |
|
|
|
private int negotiatedMaxFragLen = -1; |
|
private int maximumPacketSize; |
|
|
|
private final Queue<SSLSessionImpl> childSessions = |
|
new ConcurrentLinkedQueue<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean isSessionResumption = false; |
|
|
|
|
|
|
|
*/ |
|
private static final boolean defaultRejoinable = true; |
|
|
|
|
|
final SNIServerName serverNameIndication; |
|
private final List<SNIServerName> requestedServerNames; |
|
|
|
|
|
private BigInteger ticketNonceCounter = BigInteger.ONE; |
|
|
|
// The endpoint identification algorithm used to check certificates |
|
|
|
private final String identificationProtocol; |
|
|
|
private final ReentrantLock sessionLock = new ReentrantLock(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SSLSessionImpl() { |
|
this.protocolVersion = ProtocolVersion.NONE; |
|
this.cipherSuite = CipherSuite.C_NULL; |
|
this.sessionId = new SessionId(false, null); |
|
this.host = null; |
|
this.port = -1; |
|
this.localSupportedSignAlgs = Collections.emptySet(); |
|
this.serverNameIndication = null; |
|
this.requestedServerNames = Collections.<SNIServerName>emptyList(); |
|
this.useExtendedMasterSecret = false; |
|
this.creationTime = System.currentTimeMillis(); |
|
this.identificationProtocol = null; |
|
this.boundValues = new ConcurrentHashMap<>(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite) { |
|
this(hc, cipherSuite, |
|
new SessionId(defaultRejoinable, hc.sslContext.getSecureRandom())); |
|
} |
|
|
|
|
|
|
|
*/ |
|
SSLSessionImpl(HandshakeContext hc, CipherSuite cipherSuite, SessionId id) { |
|
this(hc, cipherSuite, id, System.currentTimeMillis()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SSLSessionImpl(HandshakeContext hc, |
|
CipherSuite cipherSuite, SessionId id, long creationTime) { |
|
this.protocolVersion = hc.negotiatedProtocol; |
|
this.cipherSuite = cipherSuite; |
|
this.sessionId = id; |
|
this.host = hc.conContext.transport.getPeerHost(); |
|
this.port = hc.conContext.transport.getPeerPort(); |
|
this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ? |
|
Collections.emptySet() : |
|
Collections.unmodifiableCollection( |
|
new ArrayList<>(hc.localSupportedSignAlgs)); |
|
this.serverNameIndication = hc.negotiatedServerName; |
|
this.requestedServerNames = List.copyOf(hc.getRequestedServerNames()); |
|
if (hc.sslConfig.isClientMode) { |
|
this.useExtendedMasterSecret = |
|
(hc.handshakeExtensions.get( |
|
SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) && |
|
(hc.handshakeExtensions.get( |
|
SSLExtension.SH_EXTENDED_MASTER_SECRET) != null); |
|
} else { |
|
this.useExtendedMasterSecret = |
|
(hc.handshakeExtensions.get( |
|
SSLExtension.CH_EXTENDED_MASTER_SECRET) != null) && |
|
(!hc.negotiatedProtocol.useTLS13PlusSpec()); |
|
} |
|
this.creationTime = creationTime; |
|
this.identificationProtocol = hc.sslConfig.identificationProtocol; |
|
this.boundValues = new ConcurrentHashMap<>(); |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("session")) { |
|
SSLLogger.finest("Session initialized: " + this); |
|
} |
|
} |
|
|
|
SSLSessionImpl(SSLSessionImpl baseSession, SessionId newId) { |
|
this.protocolVersion = baseSession.getProtocolVersion(); |
|
this.cipherSuite = baseSession.cipherSuite; |
|
this.sessionId = newId; |
|
this.host = baseSession.getPeerHost(); |
|
this.port = baseSession.getPeerPort(); |
|
this.localSupportedSignAlgs = |
|
baseSession.localSupportedSignAlgs == null ? |
|
Collections.emptySet() : baseSession.localSupportedSignAlgs; |
|
this.peerSupportedSignAlgs = |
|
baseSession.peerSupportedSignAlgs == null ? |
|
Collections.emptySet() : baseSession.peerSupportedSignAlgs; |
|
this.serverNameIndication = baseSession.serverNameIndication; |
|
this.requestedServerNames = baseSession.getRequestedServerNames(); |
|
this.masterSecret = baseSession.getMasterSecret(); |
|
this.useExtendedMasterSecret = baseSession.useExtendedMasterSecret; |
|
this.creationTime = baseSession.getCreationTime(); |
|
this.lastUsedTime = System.currentTimeMillis(); |
|
this.identificationProtocol = baseSession.getIdentificationProtocol(); |
|
this.localCerts = baseSession.localCerts; |
|
this.peerCerts = baseSession.peerCerts; |
|
this.statusResponses = baseSession.statusResponses; |
|
this.resumptionMasterSecret = baseSession.resumptionMasterSecret; |
|
this.context = baseSession.context; |
|
this.negotiatedMaxFragLen = baseSession.negotiatedMaxFragLen; |
|
this.maximumPacketSize = baseSession.maximumPacketSize; |
|
this.boundValues = baseSession.boundValues; |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("session")) { |
|
SSLLogger.finest("Session initialized: " + this); |
|
} |
|
} |
|
|
|
/** |
|
* < 2 bytes > protocolVersion |
|
* < 2 bytes > cipherSuite |
|
* < 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 |
|
* < 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 |
|
* < 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 |
|
* < 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 > |
|
* < 4 bytes > maximumPacketSize |
|
* < 4 bytes > negotiatedMaxFragSize |
|
*/ |
|
|
|
SSLSessionImpl(HandshakeContext hc, ByteBuffer buf) throws IOException { |
|
boundValues = new ConcurrentHashMap<>(); |
|
this.protocolVersion = |
|
ProtocolVersion.valueOf(Short.toUnsignedInt(buf.getShort())); |
|
|
|
|
|
this.sessionId = new SessionId(true, |
|
hc.sslContext.getSecureRandom()); |
|
|
|
this.cipherSuite = |
|
CipherSuite.valueOf(Short.toUnsignedInt(buf.getShort())); |
|
|
|
|
|
ArrayList<SignatureScheme> list = new ArrayList<>(); |
|
int i = Byte.toUnsignedInt(buf.get()); |
|
while (i-- > 0) { |
|
list.add(SignatureScheme.valueOf( |
|
Short.toUnsignedInt(buf.getShort()))); |
|
} |
|
this.localSupportedSignAlgs = Collections.unmodifiableCollection(list); |
|
|
|
|
|
i = Byte.toUnsignedInt(buf.get()); |
|
list.clear(); |
|
while (i-- > 0) { |
|
list.add(SignatureScheme.valueOf( |
|
Short.toUnsignedInt(buf.getShort()))); |
|
} |
|
this.peerSupportedSignAlgs = Collections.unmodifiableCollection(list); |
|
|
|
|
|
byte[] b; |
|
i = Short.toUnsignedInt(buf.getShort()); |
|
if (i > 0) { |
|
b = new byte[i]; |
|
|
|
buf.get(b, 0, i); |
|
|
|
i = Short.toUnsignedInt(buf.getShort()); |
|
|
|
b = new byte[i]; |
|
buf.get(b); |
|
this.preSharedKey = new SecretKeySpec(b, "TlsMasterSecret"); |
|
} else { |
|
this.preSharedKey = null; |
|
} |
|
|
|
|
|
i = buf.get(); |
|
if (i > 0) { |
|
b = new byte[i]; |
|
buf.get(b); |
|
this.pskIdentity = b; |
|
} else { |
|
this.pskIdentity = null; |
|
} |
|
|
|
|
|
i = buf.get(); |
|
if (i > 0) { |
|
b = new byte[i]; |
|
|
|
buf.get(b, 0, i); |
|
|
|
i = Short.toUnsignedInt(buf.getShort()); |
|
|
|
b = new byte[i]; |
|
buf.get(b); |
|
this.masterSecret = new SecretKeySpec(b, "TlsMasterSecret"); |
|
} else { |
|
this.masterSecret = null; |
|
} |
|
|
|
this.useExtendedMasterSecret = (buf.get() != 0); |
|
|
|
|
|
i = buf.get(); |
|
if (i == 0) { |
|
identificationProtocol = null; |
|
} else { |
|
b = new byte[i]; |
|
buf.get(b); |
|
identificationProtocol = new String(b); |
|
} |
|
|
|
// SNI |
|
i = buf.get(); |
|
if (i == 0) { |
|
serverNameIndication = null; |
|
} else { |
|
b = new byte[i]; |
|
buf.get(b, 0, b.length); |
|
serverNameIndication = new SNIHostName(b); |
|
} |
|
|
|
|
|
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--; |
|
} |
|
} |
|
|
|
maximumPacketSize = buf.getInt(); |
|
negotiatedMaxFragLen = buf.getInt(); |
|
|
|
|
|
this.creationTime = buf.getLong(); |
|
|
|
// Get Buffer sizes |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
i = Byte.toUnsignedInt(buf.get()); |
|
if (i == 0) { |
|
this.host = new String(); |
|
} else { |
|
b = new byte[i]; |
|
buf.get(b, 0, i); |
|
this.host = new String(b); |
|
} |
|
this.port = Short.toUnsignedInt(buf.getShort()); |
|
|
|
|
|
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++; |
|
} |
|
} |
|
|
|
|
|
switch (buf.get()) { |
|
case 0: |
|
break; |
|
case 1: |
|
|
|
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 |
|
|
|
i = buf.get(); |
|
b = new byte[i]; |
|
buf.get(b, 0 , i); |
|
String alg = new String(b); |
|
|
|
i = Short.toUnsignedInt(buf.getShort()); |
|
|
|
b = new byte[i]; |
|
buf.get(b); |
|
this.preSharedKey = new SecretKeySpec(b, alg); |
|
|
|
this.pskIdentity = new byte[buf.get()]; |
|
buf.get(pskIdentity); |
|
break; |
|
default: |
|
throw new SSLException("Failed local certs of session."); |
|
} |
|
|
|
context = (SSLSessionContextImpl) |
|
hc.sslContext.engineGetServerSessionContext(); |
|
this.lastUsedTime = System.currentTimeMillis(); |
|
} |
|
|
|
// Some situations we cannot provide a stateless ticket, but after it |
|
|
|
boolean isStatelessable() { |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
byte[] write() throws Exception { |
|
byte[] b; |
|
HandshakeOutStream hos = new HandshakeOutStream(null); |
|
|
|
hos.putInt16(protocolVersion.id); |
|
hos.putInt16(cipherSuite.id); |
|
|
|
|
|
hos.putInt8(localSupportedSignAlgs.size()); |
|
for (SignatureScheme s : localSupportedSignAlgs) { |
|
hos.putInt16(s.id); |
|
} |
|
|
|
|
|
hos.putInt8(peerSupportedSignAlgs.size()); |
|
for (SignatureScheme s : peerSupportedSignAlgs) { |
|
hos.putInt16(s.id); |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
if (pskIdentity == null) { |
|
hos.putInt8(0); |
|
} else { |
|
hos.putInt8(pskIdentity.length); |
|
hos.write(pskIdentity, 0, pskIdentity.length); |
|
} |
|
|
|
|
|
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); |
|
|
|
|
|
if (identificationProtocol == null) { |
|
hos.putInt8(0); |
|
} else { |
|
hos.putInt8(identificationProtocol.length()); |
|
hos.write(identificationProtocol.getBytes(), 0, |
|
identificationProtocol.length()); |
|
} |
|
|
|
|
|
if (serverNameIndication == null) { |
|
hos.putInt8(0); |
|
} else { |
|
b = serverNameIndication.getEncoded(); |
|
hos.putInt8(b.length); |
|
hos.write(b, 0, b.length); |
|
} |
|
|
|
|
|
hos.putInt16(requestedServerNames.size()); |
|
if (requestedServerNames.size() > 0) { |
|
for (SNIServerName sn : requestedServerNames) { |
|
b = sn.getEncoded(); |
|
hos.putInt8(b.length); |
|
hos.write(b, 0, b.length); |
|
} |
|
} |
|
|
|
|
|
hos.putInt32(maximumPacketSize); |
|
hos.putInt32(negotiatedMaxFragLen); |
|
|
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); |
|
hos.writeBytes(buffer.putLong(creationTime).array()); |
|
|
|
|
|
List<byte[]> list = getStatusResponses(); |
|
int l = list.size(); |
|
hos.putInt16(l); |
|
for (byte[] e : list) { |
|
hos.putInt16(e.length); |
|
hos.write(e); |
|
} |
|
|
|
|
|
if (host == null || host.length() == 0) { |
|
hos.putInt8(0); |
|
} else { |
|
hos.putInt8(host.length()); |
|
hos.writeBytes(host.getBytes()); |
|
} |
|
hos.putInt16(port); |
|
|
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
if (localCerts != null && localCerts.length > 0) { |
|
|
|
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) { |
|
|
|
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 { |
|
|
|
hos.putInt8(0); |
|
} |
|
|
|
return hos.toByteArray(); |
|
} |
|
|
|
void setMasterSecret(SecretKey secret) { |
|
masterSecret = secret; |
|
} |
|
|
|
void setResumptionMasterSecret(SecretKey secret) { |
|
resumptionMasterSecret = secret; |
|
} |
|
|
|
void setPreSharedKey(SecretKey key) { |
|
preSharedKey = key; |
|
} |
|
|
|
void addChild(SSLSessionImpl session) { |
|
childSessions.add(session); |
|
} |
|
|
|
void setTicketAgeAdd(int ticketAgeAdd) { |
|
this.ticketAgeAdd = ticketAgeAdd; |
|
} |
|
|
|
void setPskIdentity(byte[] pskIdentity) { |
|
this.pskIdentity = pskIdentity; |
|
} |
|
|
|
BigInteger incrTicketNonceCounter() { |
|
BigInteger result = ticketNonceCounter; |
|
ticketNonceCounter = ticketNonceCounter.add(BigInteger.ONE); |
|
return result; |
|
} |
|
|
|
boolean isPSKable() { |
|
return (ticketNonceCounter.compareTo(BigInteger.ZERO) > 0); |
|
} |
|
|
|
|
|
|
|
*/ |
|
SecretKey getMasterSecret() { |
|
return masterSecret; |
|
} |
|
|
|
SecretKey getResumptionMasterSecret() { |
|
return resumptionMasterSecret; |
|
} |
|
|
|
SecretKey getPreSharedKey() { |
|
sessionLock.lock(); |
|
try { |
|
return preSharedKey; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
SecretKey consumePreSharedKey() { |
|
sessionLock.lock(); |
|
try { |
|
return preSharedKey; |
|
} finally { |
|
preSharedKey = null; |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
int getTicketAgeAdd() { |
|
return ticketAgeAdd; |
|
} |
|
|
|
String getIdentificationProtocol() { |
|
return this.identificationProtocol; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
byte[] consumePskIdentity() { |
|
sessionLock.lock(); |
|
try { |
|
return pskIdentity; |
|
} finally { |
|
pskIdentity = null; |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
byte[] getPskIdentity() { |
|
return pskIdentity; |
|
} |
|
|
|
void setPeerCertificates(X509Certificate[] peer) { |
|
if (peerCerts == null) { |
|
peerCerts = peer; |
|
} |
|
} |
|
|
|
void setLocalCertificates(X509Certificate[] local) { |
|
localCerts = local; |
|
} |
|
|
|
void setLocalPrivateKey(PrivateKey privateKey) { |
|
localPrivateKey = privateKey; |
|
} |
|
|
|
void setPeerSupportedSignatureAlgorithms( |
|
Collection<SignatureScheme> signatureSchemes) { |
|
peerSupportedSignAlgs = signatureSchemes; |
|
} |
|
|
|
// TLS 1.2 only |
|
// |
|
// Per RFC 5246, If the client supports only the default hash |
|
// and signature algorithms, it MAY omit the |
|
// signature_algorithms extension. If the client does not |
|
// support the default algorithms, or supports other hash |
|
// and signature algorithms (and it is willing to use them |
|
// for verifying messages sent by the server, i.e., server |
|
// certificates and server key exchange), it MUST send the |
|
// signature_algorithms extension, listing the algorithms it |
|
|
|
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 = defaultPeerSupportedSignAlgs; |
|
} |
|
|
|
|
|
SSLSessionImpl finish() { |
|
if (useDefaultPeerSignAlgs) { |
|
peerSupportedSignAlgs = Collections.emptySet(); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void setStatusResponses(List<byte[]> responses) { |
|
if (responses != null && !responses.isEmpty()) { |
|
statusResponses = responses; |
|
} else { |
|
statusResponses = Collections.emptyList(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
boolean isRejoinable() { |
|
|
|
if (protocolVersion.useTLS13PlusSpec()) { |
|
return (!invalidated && isLocalAuthenticationValid()); |
|
} |
|
return sessionId != null && sessionId.length() != 0 && |
|
!invalidated && isLocalAuthenticationValid(); |
|
} |
|
|
|
@Override |
|
public boolean isValid() { |
|
sessionLock.lock(); |
|
try { |
|
return isRejoinable(); |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean isLocalAuthenticationValid() { |
|
if (localPrivateKey != null) { |
|
try { |
|
// if the private key is no longer valid, getAlgorithm() |
|
// should throw an exception |
|
|
|
localPrivateKey.getAlgorithm(); |
|
} catch (Exception e) { |
|
invalidate(); |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public byte[] getId() { |
|
return sessionId.getId(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
@Override |
|
public SSLSessionContext getSessionContext() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
SecurityManager sm; |
|
if ((sm = System.getSecurityManager()) != null) { |
|
sm.checkPermission(new SSLPermission("getSSLSessionContext")); |
|
} |
|
|
|
return context; |
|
} |
|
|
|
|
|
SessionId getSessionId() { |
|
return sessionId; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
CipherSuite getSuite() { |
|
return cipherSuite; |
|
} |
|
|
|
|
|
|
|
*/ |
|
void setSuite(CipherSuite suite) { |
|
cipherSuite = suite; |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("session")) { |
|
SSLLogger.finest("Negotiating session: " + this); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
boolean isSessionResumption() { |
|
return isSessionResumption; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
void setAsSessionResumption(boolean flag) { |
|
isSessionResumption = flag; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String getCipherSuite() { |
|
return getSuite().name; |
|
} |
|
|
|
ProtocolVersion getProtocolVersion() { |
|
return protocolVersion; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String getProtocol() { |
|
return getProtocolVersion().name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int hashCode() { |
|
return sessionId.hashCode(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public boolean equals(Object obj) { |
|
|
|
if (obj == this) { |
|
return true; |
|
} |
|
|
|
if (obj instanceof SSLSessionImpl) { |
|
SSLSessionImpl sess = (SSLSessionImpl) obj; |
|
return (sessionId != null) && (sessionId.equals( |
|
sess.getSessionId())); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public java.security.cert.Certificate[] getPeerCertificates() |
|
throws SSLPeerUnverifiedException { |
|
// |
|
// clone to preserve integrity of session ... caller can't |
|
// change record of peer identity even by accident, much |
|
// less do it intentionally. |
|
|
|
if (peerCerts == null) { |
|
throw new SSLPeerUnverifiedException("peer not authenticated"); |
|
} |
|
// Certs are immutable objects, therefore we don't clone them. |
|
// But do need to clone the array, so that nothing is inserted |
|
|
|
return (java.security.cert.Certificate[])peerCerts.clone(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public java.security.cert.Certificate[] getLocalCertificates() { |
|
// |
|
// clone to preserve integrity of session ... caller can't |
|
// change record of peer identity even by accident, much |
|
|
|
return (localCerts == null ? null : |
|
(java.security.cert.Certificate[])localCerts.clone()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509Certificate[] getCertificateChain() |
|
throws SSLPeerUnverifiedException { |
|
|
|
|
|
|
|
|
|
*/ |
|
if (peerCerts != null) { |
|
return peerCerts.clone(); |
|
} else { |
|
throw new SSLPeerUnverifiedException("peer not authenticated"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public List<byte[]> getStatusResponses() { |
|
if (statusResponses == null || statusResponses.isEmpty()) { |
|
return Collections.emptyList(); |
|
} else { |
|
|
|
List<byte[]> responses = new ArrayList<>(statusResponses.size()); |
|
for (byte[] respBytes : statusResponses) { |
|
responses.add(respBytes.clone()); |
|
} |
|
return Collections.unmodifiableList(responses); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Principal getPeerPrincipal() |
|
throws SSLPeerUnverifiedException |
|
{ |
|
if (peerCerts == null) { |
|
throw new SSLPeerUnverifiedException("peer not authenticated"); |
|
} |
|
return peerCerts[0].getSubjectX500Principal(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Principal getLocalPrincipal() { |
|
return ((localCerts == null || localCerts.length == 0) ? null : |
|
localCerts[0].getSubjectX500Principal()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public long getTicketCreationTime() { |
|
return ticketCreationTime; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public long getCreationTime() { |
|
return creationTime; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public long getLastAccessedTime() { |
|
return (lastUsedTime != 0) ? lastUsedTime : creationTime; |
|
} |
|
|
|
void setLastAccessedTime(long time) { |
|
lastUsedTime = time; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public InetAddress getPeerAddress() { |
|
try { |
|
return InetAddress.getByName(host); |
|
} catch (java.net.UnknownHostException e) { |
|
return null; |
|
} |
|
} |
|
|
|
@Override |
|
public String getPeerHost() { |
|
return host; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getPeerPort() { |
|
return port; |
|
} |
|
|
|
void setContext(SSLSessionContextImpl ctx) { |
|
if (context == null) { |
|
context = ctx; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void invalidate() { |
|
sessionLock.lock(); |
|
try { |
|
if (context != null) { |
|
context.remove(sessionId); |
|
context = null; |
|
} |
|
|
|
if (invalidated) { |
|
return; |
|
} |
|
invalidated = true; |
|
if (SSLLogger.isOn && SSLLogger.isOn("session")) { |
|
SSLLogger.finest("Invalidated session: " + this); |
|
} |
|
for (SSLSessionImpl child : childSessions) { |
|
child.invalidate(); |
|
} |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final ConcurrentHashMap<SecureKey, Object> boundValues; |
|
boolean updateNST; |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void putValue(String key, Object value) { |
|
if ((key == null) || (value == null)) { |
|
throw new IllegalArgumentException("arguments can not be null"); |
|
} |
|
|
|
SecureKey secureKey = new SecureKey(key); |
|
Object oldValue = boundValues.put(secureKey, value); |
|
|
|
if (oldValue instanceof SSLSessionBindingListener) { |
|
SSLSessionBindingEvent e; |
|
|
|
e = new SSLSessionBindingEvent(this, key); |
|
((SSLSessionBindingListener)oldValue).valueUnbound(e); |
|
} |
|
if (value instanceof SSLSessionBindingListener) { |
|
SSLSessionBindingEvent e; |
|
|
|
e = new SSLSessionBindingEvent(this, key); |
|
((SSLSessionBindingListener)value).valueBound(e); |
|
} |
|
if (protocolVersion.useTLS13PlusSpec()) { |
|
updateNST = true; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public Object getValue(String key) { |
|
if (key == null) { |
|
throw new IllegalArgumentException("argument can not be null"); |
|
} |
|
|
|
SecureKey secureKey = new SecureKey(key); |
|
return boundValues.get(secureKey); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public void removeValue(String key) { |
|
if (key == null) { |
|
throw new IllegalArgumentException("argument can not be null"); |
|
} |
|
|
|
SecureKey secureKey = new SecureKey(key); |
|
Object value = boundValues.remove(secureKey); |
|
|
|
if (value instanceof SSLSessionBindingListener) { |
|
SSLSessionBindingEvent e; |
|
|
|
e = new SSLSessionBindingEvent(this, key); |
|
((SSLSessionBindingListener)value).valueUnbound(e); |
|
} |
|
if (protocolVersion.useTLS13PlusSpec()) { |
|
updateNST = true; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String[] getValueNames() { |
|
ArrayList<Object> v = new ArrayList<>(); |
|
Object securityCtx = SecureKey.getCurrentSecurityContext(); |
|
for (Enumeration<SecureKey> e = boundValues.keys(); |
|
e.hasMoreElements(); ) { |
|
SecureKey key = e.nextElement(); |
|
if (securityCtx.equals(key.getSecurityContext())) { |
|
v.add(key.getAppKey()); |
|
} |
|
} |
|
|
|
return v.toArray(new String[0]); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean acceptLargeFragments = |
|
Utilities.getBooleanProperty( |
|
"jsse.SSLEngine.acceptLargeFragments", false); |
|
|
|
|
|
|
|
|
|
*/ |
|
protected void expandBufferSizes() { |
|
sessionLock.lock(); |
|
try { |
|
acceptLargeFragments = true; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getPacketBufferSize() { |
|
sessionLock.lock(); |
|
try { |
|
// Use the bigger packet size calculated from maximumPacketSize |
|
|
|
int packetSize = 0; |
|
if (negotiatedMaxFragLen > 0) { |
|
packetSize = cipherSuite.calculatePacketSize( |
|
negotiatedMaxFragLen, protocolVersion, |
|
protocolVersion.isDTLS); |
|
} |
|
|
|
if (maximumPacketSize > 0) { |
|
return (maximumPacketSize > packetSize) ? |
|
maximumPacketSize : packetSize; |
|
} |
|
|
|
if (packetSize != 0) { |
|
return packetSize; |
|
} |
|
|
|
if (protocolVersion.isDTLS) { |
|
return DTLSRecord.maxRecordSize; |
|
} else { |
|
return acceptLargeFragments ? |
|
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize; |
|
} |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public int getApplicationBufferSize() { |
|
sessionLock.lock(); |
|
try { |
|
// Use the bigger fragment size calculated from maximumPacketSize |
|
|
|
int fragmentSize = 0; |
|
if (maximumPacketSize > 0) { |
|
fragmentSize = cipherSuite.calculateFragSize( |
|
maximumPacketSize, protocolVersion, |
|
protocolVersion.isDTLS); |
|
} |
|
|
|
if (negotiatedMaxFragLen > 0) { |
|
return (negotiatedMaxFragLen > fragmentSize) ? |
|
negotiatedMaxFragLen : fragmentSize; |
|
} |
|
|
|
if (fragmentSize != 0) { |
|
return fragmentSize; |
|
} |
|
|
|
if (protocolVersion.isDTLS) { |
|
return Record.maxDataSize; |
|
} else { |
|
int maxPacketSize = acceptLargeFragments ? |
|
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize; |
|
return (maxPacketSize - SSLRecord.headerSize); |
|
} |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void setNegotiatedMaxFragSize( |
|
int negotiatedMaxFragLen) { |
|
sessionLock.lock(); |
|
try { |
|
this.negotiatedMaxFragLen = negotiatedMaxFragLen; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int getNegotiatedMaxFragSize() { |
|
sessionLock.lock(); |
|
try { |
|
return negotiatedMaxFragLen; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
void setMaximumPacketSize(int maximumPacketSize) { |
|
sessionLock.lock(); |
|
try { |
|
this.maximumPacketSize = maximumPacketSize; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
int getMaximumPacketSize() { |
|
sessionLock.lock(); |
|
try { |
|
return maximumPacketSize; |
|
} finally { |
|
sessionLock.unlock(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String[] getLocalSupportedSignatureAlgorithms() { |
|
return SignatureScheme.getAlgorithmNames(localSupportedSignAlgs); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Collection<SignatureScheme> getLocalSupportedSignatureSchemes() { |
|
return localSupportedSignAlgs; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String[] getPeerSupportedSignatureAlgorithms() { |
|
return SignatureScheme.getAlgorithmNames(peerSupportedSignAlgs); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public List<SNIServerName> getRequestedServerNames() { |
|
return requestedServerNames; |
|
} |
|
|
|
|
|
@Override |
|
public String toString() { |
|
return "Session(" + creationTime + "|" + getCipherSuite() + ")"; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
class SecureKey { |
|
private static final Object nullObject = new Object(); |
|
private final Object appKey; |
|
private final Object securityCtx; |
|
|
|
static Object getCurrentSecurityContext() { |
|
@SuppressWarnings("removal") |
|
SecurityManager sm = System.getSecurityManager(); |
|
Object context = null; |
|
|
|
if (sm != null) |
|
context = sm.getSecurityContext(); |
|
if (context == null) |
|
context = nullObject; |
|
return context; |
|
} |
|
|
|
SecureKey(Object key) { |
|
this.appKey = key; |
|
this.securityCtx = getCurrentSecurityContext(); |
|
} |
|
|
|
Object getAppKey() { |
|
return appKey; |
|
} |
|
|
|
Object getSecurityContext() { |
|
return securityCtx; |
|
} |
|
|
|
@Override |
|
public int hashCode() { |
|
return appKey.hashCode() ^ securityCtx.hashCode(); |
|
} |
|
|
|
@Override |
|
public boolean equals(Object o) { |
|
return o instanceof SecureKey && ((SecureKey)o).appKey.equals(appKey) |
|
&& ((SecureKey)o).securityCtx.equals(securityCtx); |
|
} |
|
} |