diff --git a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java index e7e7c314d3d..d95dadcabde 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -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 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 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); } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index be324eb0949..69dd2014bcb 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -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 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 getCompCertCache() { + return compCertCache; + } + // Used for DTLS in server mode only. HelloCookieManager getHelloCookieManager(ProtocolVersion protocolVersion) { if (helloCookieManagerBuilder == null) { diff --git a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java index c7e9951120c..6e0446301a8 100644 --- a/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java +++ b/test/jdk/sun/security/ssl/CertificateCompression/CompressedCertMsgCache.java @@ -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(