Attach cache to SSLContext instance

This commit is contained in:
Artur Barashev 2026-02-03 16:00:47 -05:00
parent 6b23c05f2a
commit 9570cafb4b
3 changed files with 65 additions and 80 deletions

View File

@ -47,6 +47,8 @@ final class CompressedCertificate {
static final HandshakeProducer handshakeProducer =
new CompressedCertProducer();
record CompCertCacheKey(EqualByteArray eba, int algId) {}
/**
* The CompressedCertificate handshake message for TLS 1.3.
*/
@ -140,14 +142,6 @@ final class CompressedCertificate {
private static final
class CompressedCertProducer implements HandshakeProducer {
private record CompCertCacheKey(EqualByteArray eba, int algId) {}
// Only local certificates are compressed, so it makes sense to store
// the deflated certificate data in a memory cache statically and avoid
// compressing local certificates repeatedly for every handshake.
private static final Cache<CompCertCacheKey, byte[]> CACHE =
Cache.newSoftMemoryCache(92);
// Prevent instantiation of this class.
private CompressedCertProducer() {
// blank
@ -167,9 +161,11 @@ final class CompressedCertificate {
message.send(hos);
byte[] certMsg = hos.toByteArray();
Cache<CompCertCacheKey, byte[]> cache =
hc.sslContext.getCompCertCache();
CompCertCacheKey key = new CompCertCacheKey(
new EqualByteArray(certMsg), hc.certDeflater.getKey());
byte[] compressedCertMsg = CACHE.get(key);
byte[] compressedCertMsg = cache.get(key);
if (compressedCertMsg == null) {
compressedCertMsg = hc.certDeflater.getValue().apply(certMsg);
@ -183,7 +179,7 @@ final class CompressedCertificate {
SSLLogger.fine("Caching CompressedCertificate message");
}
CACHE.put(key, compressedCertMsg);
cache.put(key, compressedCertMsg);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2026, 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
@ -33,7 +33,9 @@ import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.*;
import sun.security.provider.certpath.AlgorithmChecker;
import sun.security.ssl.CompressedCertificate.CompCertCacheKey;
import sun.security.ssl.SSLAlgorithmConstraints.SIGNATURE_CONSTRAINTS_MODE;
import sun.security.util.Cache;
import sun.security.validator.Validator;
/**
@ -72,6 +74,10 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private final ReentrantLock contextLock = new ReentrantLock();
// Avoid compressing local certificates repeatedly for every handshake.
private final Cache<CompCertCacheKey, byte[]> compCertCache =
Cache.newSoftMemoryCache(12);
SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl(false);
@ -224,6 +230,10 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return ephemeralKeyManager;
}
Cache<CompCertCacheKey, byte[]> getCompCertCache() {
return compCertCache;
}
// Used for DTLS in server mode only.
HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) {
if (helloCookieManagerBuilder == null) {

View File

@ -57,80 +57,59 @@ import jdk.test.lib.security.CertificateBuilder;
public class CompressedCertMsgCache extends SSLSocketTemplate {
private final String protocol;
private final String keyAlg;
private final String certSigAlg;
private X509Certificate trustedCert;
private X509Certificate serverCert;
private X509Certificate clientCert;
private KeyPair serverKeys;
private KeyPair clientKeys;
protected CompressedCertMsgCache(
String protocol, String keyAlg,
String certSigAlg) throws Exception {
super();
this.protocol = protocol;
this.keyAlg = keyAlg;
this.certSigAlg = certSigAlg;
setupCertificates();
}
private static X509Certificate trustedCert;
private static X509Certificate serverCert;
private static X509Certificate clientCert;
private static KeyPair serverKeys;
private static KeyPair clientKeys;
private static SSLContext serverSslContext;
private static SSLContext clientSslContext;
public static void main(String[] args) throws Exception {
// Complete 2 handshakes with the same certificate.
String log = runAndGetLog(() -> {
try {
new SSLSocketTemplate().run();
new SSLSocketTemplate().run();
} catch (Exception _) {
}
});
// Use 2 different SSLContext instances.
for (int i = 0; i < 2; i++) {
// Make sure the same CompressedCertificate message is cached only once
assertEquals(1, countSubstringOccurrences(log,
"Caching CompressedCertificate message"));
// Complete 3 handshakes with the same SSLContext.
String log = runAndGetLog(() -> {
try {
setupCertificates();
serverSslContext = getSSLContext(
trustedCert, serverCert, serverKeys.getPrivate(),
"TLSv1.3");
clientSslContext = getSSLContext(
trustedCert, clientCert, clientKeys.getPrivate(),
"TLSv1.3");
// Complete 92 handshakes, all with different certificates.
log = runAndGetLog(() -> {
try {
for (int i = 0; i < 92; i++) {
new CompressedCertMsgCache(
"TLSv1.3", "EC", "SHA256withECDSA").run();
new CompressedCertMsgCache().run();
new CompressedCertMsgCache().run();
new CompressedCertMsgCache().run();
} catch (Exception _) {
}
} catch (Exception _) {
}
});
});
// Make sure all 92 CompressedCertificate messages are cached.
assertEquals(92, countSubstringOccurrences(log,
"Caching CompressedCertificate message"));
// The same CompressedCertificate message must be cached only once.
assertEquals(1, countSubstringOccurrences(log,
"Caching CompressedCertificate message"));
// Complete 1 handshake with the same certificate as the very first one.
log = runAndGetLog(() -> {
try {
new SSLSocketTemplate().run();
} catch (Exception _) {
}
});
// Make sure the same CompressedCertificate message is cached again
// because it was removed from cache due to LRU policy.
assertEquals(1, countSubstringOccurrences(log,
"Caching CompressedCertificate message"));
// Make sure CompressedCertificate message is produced 3 times.
assertEquals(3, countSubstringOccurrences(log,
"Produced CompressedCertificate handshake message"));
// Make sure CompressedCertificate message is consumed 3 times.
assertEquals(3, countSubstringOccurrences(log,
"Consuming CompressedCertificate handshake message"));
}
}
@Override
public SSLContext createServerSSLContext() throws Exception {
return getSSLContext(
trustedCert, serverCert, serverKeys.getPrivate(), protocol);
return serverSslContext;
}
@Override
public SSLContext createClientSSLContext() throws Exception {
return getSSLContext(
trustedCert, clientCert, clientKeys.getPrivate(), protocol);
return clientSslContext;
}
private static SSLContext getSSLContext(
@ -170,34 +149,34 @@ public class CompressedCertMsgCache extends SSLSocketTemplate {
// Certificate-building helper methods.
private void setupCertificates() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlg);
private static void setupCertificates() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
KeyPair caKeys = kpg.generateKeyPair();
this.serverKeys = kpg.generateKeyPair();
this.clientKeys = kpg.generateKeyPair();
serverKeys = kpg.generateKeyPair();
clientKeys = kpg.generateKeyPair();
this.trustedCert = createTrustedCert(caKeys, certSigAlg);
trustedCert = createTrustedCert(caKeys);
this.serverCert = customCertificateBuilder(
serverCert = customCertificateBuilder(
"O=Some-Org, L=Some-City, ST=Some-State, C=US",
serverKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(false, false, -1)
.build(trustedCert, caKeys.getPrivate(), certSigAlg);
.build(trustedCert, caKeys.getPrivate(), "SHA256withECDSA");
this.clientCert = customCertificateBuilder(
clientCert = customCertificateBuilder(
"CN=localhost, OU=SSL-Client, ST=Some-State, C=US",
clientKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(false, false, -1)
.build(trustedCert, caKeys.getPrivate(), certSigAlg);
.build(trustedCert, caKeys.getPrivate(), "SHA256withECDSA");
}
private static X509Certificate createTrustedCert(
KeyPair caKeys, String certSigAlg) throws Exception {
private static X509Certificate createTrustedCert(KeyPair caKeys)
throws Exception {
return customCertificateBuilder(
"O=CA-Org, L=Some-City, ST=Some-State, C=US",
caKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(true, true, 1)
.build(null, caKeys.getPrivate(), certSigAlg);
.build(null, caKeys.getPrivate(), "SHA256withECDSA");
}
private static CertificateBuilder customCertificateBuilder(