8298381: Improve handling of session tickets for multiple SSLContexts

Reviewed-by: xuelei, ascarpino, serb
This commit is contained in:
Volker Simonis 2023-01-10 11:49:36 +00:00
parent eab1e6260d
commit debe5879aa
3 changed files with 78 additions and 76 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, 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
@ -28,7 +28,11 @@ package sun.security.ssl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
@ -69,6 +73,11 @@ final class SSLSessionContextImpl implements SSLSessionContext {
private int cacheLimit; // the max cache size
private int timeout; // timeout in seconds
// The current session ticket encryption key ID (only used in server context)
private int currentKeyID;
// Session ticket encryption keys and IDs map (only used in server context)
private final Map<Integer, SessionTicketExtension.StatelessKey> keyHashMap;
// Default setting for stateless session resumption support (RFC 5077)
private boolean statelessSession = true;
@ -80,6 +89,14 @@ final class SSLSessionContextImpl implements SSLSessionContext {
// use soft reference
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
if (server) {
keyHashMap = new ConcurrentHashMap<>();
// Should be "randomly generated" according to RFC 5077,
// but doesn't necessarily has to be a true random number.
currentKeyID = new Random(System.nanoTime()).nextInt();
} else {
keyHashMap = Map.of();
}
}
// Stateless sessions when available, but there is a cache
@ -170,6 +187,51 @@ final class SSLSessionContextImpl implements SSLSessionContext {
return cacheLimit;
}
private void cleanupStatelessKeys() {
Iterator<Map.Entry<Integer, SessionTicketExtension.StatelessKey>> it =
keyHashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, SessionTicketExtension.StatelessKey> entry = it.next();
SessionTicketExtension.StatelessKey k = entry.getValue();
if (k.isInvalid(this)) {
it.remove();
try {
k.key.destroy();
} catch (Exception e) {
// Suppress
}
}
}
}
// Package-private, used only from SessionTicketExtension.KeyState::getCurrentKey.
SessionTicketExtension.StatelessKey getKey(HandshakeContext hc) {
SessionTicketExtension.StatelessKey ssk = keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
synchronized (this) {
// If the current key is no longer expired, it was already
// updated by a concurrent request, and we can return.
ssk = keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
int newID = currentKeyID + 1;
ssk = new SessionTicketExtension.StatelessKey(hc, newID);
keyHashMap.put(Integer.valueOf(newID), ssk);
currentKeyID = newID;
}
// Check for and delete invalid keys every time we create a new stateless key.
cleanupStatelessKeys();
return ssk;
}
// Package-private, used only from SessionTicketExtension.KeyState::getKey.
SessionTicketExtension.StatelessKey getKey(int id) {
return keyHashMap.get(id);
}
// package-private method, used ONLY by ServerHandshaker
SSLSessionImpl get(byte[] id) {
return (SSLSessionImpl)getSession(id);