Bound the memory usage when decompressing CompressedCertificate

This commit is contained in:
Artur Barashev 2026-02-19 10:50:00 -05:00
parent 04c9aa21f5
commit eee029aa73
3 changed files with 118 additions and 11 deletions

View File

@ -171,6 +171,22 @@ enum CompressionAlgorithm {
while (!inflater.finished()) {
int decompressedSize = inflater.inflate(buffer);
outputStream.write(buffer, 0, decompressedSize);
// Bound the memory usage.
if (outputStream.size()
> SSLConfiguration.maxHandshakeMessageSize) {
if (SSLLogger.isOn()
&& SSLLogger.isOn(SSLLogger.Opt.HANDSHAKE)) {
SSLLogger.warning("The size of the "
+ "uncompressed certificate message "
+ "exceeds maximum allowed size of "
+ SSLConfiguration.maxHandshakeMessageSize
+ " bytes; compressed size: "
+ input.length);
}
return null;
}
}
return outputStream.toByteArray();

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertNotNull;
import static jdk.test.lib.Asserts.assertTrue;
import static jdk.test.lib.Utils.runAndCheckException;
import static jdk.test.lib.security.SecurityUtils.runAndGetLog;
import java.util.Scanner;
import javax.net.ssl.SSLHandshakeException;
/*
* @test
* @bug 8372526
* @summary Bound the memory usage when decompressing CompressedCertificate.
* @modules java.base/sun.security.x509
* java.base/sun.security.util
* @library /javax/net/ssl/templates
* /test/lib
* @run main/othervm BoundDecompressMemory
*/
public class BoundDecompressMemory extends CompressedCertMsgCache {
private static final int MAX_HANDSHAKE_MESSAGE_SIZE = 4096;
public static void main(String[] args) throws Exception {
System.setProperty("jdk.tls.maxHandshakeMessageSize",
Integer.toString(MAX_HANDSHAKE_MESSAGE_SIZE));
String log = runAndGetLog(() -> {
try {
// Use highly compressible subject name for server's certificate.
serverCertSubjectName = "O=Some-Org"
+ "A".repeat(MAX_HANDSHAKE_MESSAGE_SIZE)
+ ", L=Some-City, ST=Some-State, C=US";
setupCertificates();
serverSslContext = getSSLContext(trustedCert, serverCert,
serverKeys.getPrivate(), "TLSv1.3");
clientSslContext = getSSLContext(trustedCert, clientCert,
clientKeys.getPrivate(), "TLSv1.3");
runAndCheckException(() -> new BoundDecompressMemory().run(),
serverEx -> {
Throwable clientEx = serverEx.getSuppressed()[0];
assertTrue(
clientEx instanceof SSLHandshakeException);
assertEquals("(bad_certificate) Improper "
+ "certificate compression",
clientEx.getMessage());
}
);
} catch (Exception _) {
}
});
// Check for the specific decompression error message.
assertNotNull(new Scanner(log).findWithinHorizon("The size of the "
+ "uncompressed certificate message "
+ "exceeds maximum allowed size of "
+ MAX_HANDSHAKE_MESSAGE_SIZE
+ " bytes; compressed size: \\d+", 0));
}
}

View File

@ -57,13 +57,18 @@ import jdk.test.lib.security.CertificateBuilder;
public class CompressedCertMsgCache extends SSLSocketTemplate {
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;
protected static String clientCertSubjectName =
"CN=localhost, OU=SSL-Client, ST=Some-State, C=US";
protected static String serverCertSubjectName =
"O=Some-Org, L=Some-City, ST=Some-State, C=US";
protected static X509Certificate trustedCert;
protected static X509Certificate serverCert;
protected static X509Certificate clientCert;
protected static KeyPair serverKeys;
protected static KeyPair clientKeys;
protected static SSLContext serverSslContext;
protected static SSLContext clientSslContext;
public static void main(String[] args) throws Exception {
@ -106,7 +111,7 @@ public class CompressedCertMsgCache extends SSLSocketTemplate {
return clientSslContext;
}
private static SSLContext getSSLContext(
protected static SSLContext getSSLContext(
X509Certificate trustedCertificate, X509Certificate keyCertificate,
PrivateKey privateKey, String protocol)
throws Exception {
@ -143,7 +148,7 @@ public class CompressedCertMsgCache extends SSLSocketTemplate {
// Certificate-building helper methods.
private static void setupCertificates() throws Exception {
protected static void setupCertificates() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
KeyPair caKeys = kpg.generateKeyPair();
serverKeys = kpg.generateKeyPair();
@ -152,13 +157,13 @@ public class CompressedCertMsgCache extends SSLSocketTemplate {
trustedCert = createTrustedCert(caKeys);
serverCert = customCertificateBuilder(
"O=Some-Org, L=Some-City, ST=Some-State, C=US",
serverCertSubjectName,
serverKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(false, false, -1)
.build(trustedCert, caKeys.getPrivate(), "SHA256withECDSA");
clientCert = customCertificateBuilder(
"CN=localhost, OU=SSL-Client, ST=Some-State, C=US",
clientCertSubjectName,
clientKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(false, false, -1)
.build(trustedCert, caKeys.getPrivate(), "SHA256withECDSA");