From 99ab5db3b118bf61ef003494768fb396c3b0a34b Mon Sep 17 00:00:00 2001 From: Xuelei Fan Date: Sat, 5 Feb 2022 11:10:25 -0800 Subject: [PATCH 01/12] 8273042: TLS Certificate Compression --- .../classes/javax/net/ssl/SSLParameters.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index 83b83c8427040..02b392bd7cfc6 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -696,4 +696,19 @@ public void setApplicationProtocols(String[] protocols) { } applicationProtocols = tempProtocols; } + + record CompressionFunction( + Function encoder, + Function decoder) { + } + + public Map getCertificateCompressionFunctions() { + return null; + } + + public void setCertificateCompressionAlgorithms( + Map certificateCompressionFunctions) { + // blank + } + } From 9a14011f453728aee28241b15ca5dc9069fa2fbb Mon Sep 17 00:00:00 2001 From: Xuelei Fan Date: Wed, 9 Feb 2022 14:44:17 -0800 Subject: [PATCH 02/12] 8273042: TLS Certificate Compression --- .../classes/javax/net/ssl/SSLParameters.java | 40 +- src/java.base/share/classes/javax/net/ssl/ssl | 728 ++++++++++++++++++ .../share/classes/sun/security/ssl/Alert.java | 2 + .../ssl/CertCompressionExtension.java | 449 +++++++++++ .../sun/security/ssl/CertificateMessage.java | 38 +- .../security/ssl/CompressedCertificate.java | 214 +++++ .../security/ssl/CompressionAlgorithm.java | 131 ++++ .../sun/security/ssl/HandshakeContext.java | 6 +- .../sun/security/ssl/SSLConfiguration.java | 20 +- .../sun/security/ssl/SSLExtension.java | 21 + .../sun/security/ssl/SSLHandshake.java | 18 + 11 files changed, 1634 insertions(+), 33 deletions(-) create mode 100644 src/java.base/share/classes/javax/net/ssl/ssl create mode 100644 src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java create mode 100644 src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java create mode 100644 src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index 24030cbbea46f..668c5ee7fcd53 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -27,6 +27,7 @@ import java.security.AlgorithmConstraints; import java.util.*; +import java.util.function.Function; /** * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters @@ -82,6 +83,8 @@ public class SSLParameters { private boolean enableRetransmissions = true; private int maximumPacketSize = 0; private String[] applicationProtocols = new String[0]; + private Map> certDeflaters = null; + private Map> certInflaters = null; /** * Constructs SSLParameters. @@ -687,18 +690,39 @@ public void setApplicationProtocols(String[] protocols) { applicationProtocols = tempProtocols; } - record CompressionFunction( - Function encoder, - Function decoder) { + /** + * Something goes here later. + * @return something + */ + public Map> getCertificateDeflaters() { + return this.certDeflaters; } - public Map getCertificateCompressionFunctions() { - return null; + /** + * Something goes here later. + * @param certDeflaters something + */ + public void setCertificateDeflaters( + Map> certDeflaters) { + // TODO + this.certDeflaters = certDeflaters; } - public void setCertificateCompressionAlgorithms( - Map certificateCompressionFunctions) { - // blank + /** + * Something goes here later. + * @return something + */ + public Map> getCertificateInflaters() { + return this.certInflaters; } + /** + * Something goes here later. + * @param certInflaters something + */ + public void setCertificateInflaters( + Map> certInflaters) { + // TODO + this.certInflaters = certInflaters; + } } diff --git a/src/java.base/share/classes/javax/net/ssl/ssl b/src/java.base/share/classes/javax/net/ssl/ssl new file mode 100644 index 0000000000000..668c5ee7fcd53 --- /dev/null +++ b/src/java.base/share/classes/javax/net/ssl/ssl @@ -0,0 +1,728 @@ +/* + * Copyright (c) 2005, 2022, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package javax.net.ssl; + +import java.security.AlgorithmConstraints; +import java.util.*; +import java.util.function.Function; + +/** + * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters + * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake, + * the list of protocols to be allowed, the endpoint identification + * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI), + * the maximum network packet size, the algorithm constraints and whether + * SSL/TLS/DTLS servers should request or require client authentication, etc. + *

+ * SSLParameters can be created via the constructors in this class. + * Objects can also be obtained using the {@code getSSLParameters()} + * methods in + * {@link SSLSocket#getSSLParameters SSLSocket} and + * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and + * {@link SSLEngine#getSSLParameters SSLEngine} or the + * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and + * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} + * methods in {@code SSLContext}. + *

+ * SSLParameters can be applied to a connection via the methods + * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and + * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} + * and {@link SSLEngine#setSSLParameters SSLEngine.setSSLParameters()}. + *

+ * For example: + * + *

+ *     SSLParameters p = sslSocket.getSSLParameters();
+ *     p.setProtocols(new String[] { "TLSv1.2" });
+ *     p.setCipherSuites(
+ *         new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ... });
+ *     p.setApplicationProtocols(new String[] {"h2", "http/1.1"});
+ *     sslSocket.setSSLParameters(p);
+ * 
+ * + * @see SSLSocket + * @see SSLEngine + * @see SSLContext + * + * @since 1.6 + */ +public class SSLParameters { + + private String[] cipherSuites; + private String[] protocols; + private boolean wantClientAuth; + private boolean needClientAuth; + private String identificationAlgorithm; + private AlgorithmConstraints algorithmConstraints; + private List sniNames = null; // immutable list + private Collection sniMatchers = null; // immutable collection + private boolean preferLocalCipherSuites; + private boolean enableRetransmissions = true; + private int maximumPacketSize = 0; + private String[] applicationProtocols = new String[0]; + private Map> certDeflaters = null; + private Map> certInflaters = null; + + /** + * Constructs SSLParameters. + *

+ * The values of cipherSuites, protocols, cryptographic algorithm + * constraints, endpoint identification algorithm, server names and + * server name matchers are set to {@code null}; useCipherSuitesOrder, + * wantClientAuth and needClientAuth are set to {@code false}; + * enableRetransmissions is set to {@code true}; maximum network packet + * size is set to {@code 0}. + */ + public SSLParameters() { + // empty + } + + /** + * Constructs SSLParameters from the specified array of ciphersuites. + *

+ * Calling this constructor is equivalent to calling the no-args + * constructor followed by + * {@code setCipherSuites(cipherSuites);}. Note that the + * standard list of cipher suite names may be found in the + * JSSE Cipher Suite Names section of the Java Security Standard + * Algorithm Names Specification. Providers may support cipher suite + * names not found in this list. + * + * @param cipherSuites the array of ciphersuites (or null) + */ + public SSLParameters(String[] cipherSuites) { + setCipherSuites(cipherSuites); + } + + /** + * Constructs SSLParameters from the specified array of ciphersuites + * and protocols. + *

+ * Calling this constructor is equivalent to calling the no-args + * constructor followed by + * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}. + * Note that the standard list of cipher suite names may be found in the + * + * JSSE Cipher Suite Names section of the Java Security Standard + * Algorithm Names Specification. Providers may support cipher suite + * names not found in this list. + * + * @param cipherSuites the array of ciphersuites (or null) + * @param protocols the array of protocols (or null) + */ + public SSLParameters(String[] cipherSuites, String[] protocols) { + setCipherSuites(cipherSuites); + setProtocols(protocols); + } + + private static String[] clone(String[] s) { + return (s == null) ? null : s.clone(); + } + + /** + * Returns a copy of the array of ciphersuites or null if none + * have been set. + *

+ * The returned array includes cipher suites from the list of standard + * cipher suite names in the + * JSSE Cipher Suite Names section of the Java Security Standard + * Algorithm Names Specification, and may also include other cipher suites + * that the provider supports. + * + * @return a copy of the array of ciphersuites or null if none + * have been set. + */ + public String[] getCipherSuites() { + return clone(cipherSuites); + } + + /** + * Sets the array of ciphersuites. + * + * @param cipherSuites the array of ciphersuites (or null). Note that the + * standard list of cipher suite names may be found in the + * JSSE Cipher Suite Names section of the Java Security Standard + * Algorithm Names Specification. Providers may support cipher suite + * names not found in this list or might not use the recommended name + * for a certain cipher suite. + */ + public void setCipherSuites(String[] cipherSuites) { + this.cipherSuites = clone(cipherSuites); + } + + /** + * Returns a copy of the array of protocols or null if none + * have been set. + * + * @return a copy of the array of protocols or null if none + * have been set. + */ + public String[] getProtocols() { + return clone(protocols); + } + + /** + * Sets the array of protocols. + * + * @param protocols the array of protocols (or null) + */ + public void setProtocols(String[] protocols) { + this.protocols = clone(protocols); + } + + /** + * Returns whether client authentication should be requested. + * + * @return whether client authentication should be requested. + */ + public boolean getWantClientAuth() { + return wantClientAuth; + } + + /** + * Sets whether client authentication should be requested. Calling + * this method clears the {@code needClientAuth} flag. + * + * @param wantClientAuth whether client authentication should be requested + */ + public void setWantClientAuth(boolean wantClientAuth) { + this.wantClientAuth = wantClientAuth; + this.needClientAuth = false; + } + + /** + * Returns whether client authentication should be required. + * + * @return whether client authentication should be required. + */ + public boolean getNeedClientAuth() { + return needClientAuth; + } + + /** + * Sets whether client authentication should be required. Calling + * this method clears the {@code wantClientAuth} flag. + * + * @param needClientAuth whether client authentication should be required + */ + public void setNeedClientAuth(boolean needClientAuth) { + this.wantClientAuth = false; + this.needClientAuth = needClientAuth; + } + + /** + * Returns the cryptographic algorithm constraints. + * + * @return the cryptographic algorithm constraints, or null if the + * constraints have not been set + * + * @see #setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + public AlgorithmConstraints getAlgorithmConstraints() { + return algorithmConstraints; + } + + /** + * Sets the cryptographic algorithm constraints, which will be used + * in addition to any configured by the runtime environment. + *

+ * If the {@code constraints} parameter is non-null, every + * cryptographic algorithm, key and algorithm parameters used in the + * SSL/TLS/DTLS handshake must be permitted by the constraints. + * + * @param constraints the algorithm constraints (or null) + * + * @since 1.7 + */ + public void setAlgorithmConstraints(AlgorithmConstraints constraints) { + // the constraints object is immutable + this.algorithmConstraints = constraints; + } + + /** + * Gets the endpoint identification algorithm. + * + * @return the endpoint identification algorithm, or null if none + * has been set. + * + * @see X509ExtendedTrustManager + * @see #setEndpointIdentificationAlgorithm(String) + * + * @since 1.7 + */ + public String getEndpointIdentificationAlgorithm() { + return identificationAlgorithm; + } + + /** + * Sets the endpoint identification algorithm. + *

+ * If the {@code algorithm} parameter is non-null or non-empty, the + * endpoint identification/verification procedures must be handled during + * SSL/TLS/DTLS handshaking. This is to prevent man-in-the-middle attacks. + * + * @param algorithm The standard string name of the endpoint + * identification algorithm (or null). + * See the + * Java Security Standard Algorithm Names document + * for information about standard algorithm names. + * + * @see X509ExtendedTrustManager + * + * @since 1.7 + */ + public void setEndpointIdentificationAlgorithm(String algorithm) { + this.identificationAlgorithm = algorithm; + } + + /** + * Sets the desired {@link SNIServerName}s of the Server Name + * Indication (SNI) parameter. + *

+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s + * operating in client mode. + *

+ * Note that the {@code serverNames} list is cloned + * to protect against subsequent modification. + * + * @param serverNames + * the list of desired {@link SNIServerName}s (or null) + * + * @throws NullPointerException if the {@code serverNames} + * contains {@code null} element + * @throws IllegalArgumentException if the {@code serverNames} + * contains more than one name of the same name type + * + * @see SNIServerName + * @see #getServerNames() + * + * @since 1.8 + */ + public final void setServerNames(List serverNames) { + if (this.sniNames == serverNames) { + return; + } + + if (serverNames == null) { + sniNames = null; + } else if (serverNames.isEmpty()) { + sniNames = Collections.emptyList(); + } else { + List sniTypes = new ArrayList<>(serverNames.size()); + List sniValues = new ArrayList<>(serverNames.size()); + for (SNIServerName serverName : serverNames) { + if (sniTypes.contains(serverName.getType())) { + throw new IllegalArgumentException( + "Duplicated server name of type " + + serverName.getType()); + } else { + sniTypes.add(serverName.getType()); + sniValues.add(serverName); + } + } + + sniNames = Collections.unmodifiableList(sniValues); + } + } + + /** + * Returns a {@link List} containing all {@link SNIServerName}s of the + * Server Name Indication (SNI) parameter, or null if none has been set. + *

+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s + * operating in client mode. + *

+ * For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS provider + * may specify a default value for a certain server name type. In + * client mode, it is recommended that, by default, providers should + * include the server name indication whenever the server can be located + * by a supported server name type. + *

+ * It is recommended that providers initialize default Server Name + * Indications when creating {@code SSLSocket}/{@code SSLEngine}s. + * In the following examples, the server name could be represented by an + * instance of {@link SNIHostName} which has been initialized with the + * hostname "www.example.com" and type + * {@link StandardConstants#SNI_HOST_NAME}. + * + *

+     *     Socket socket =
+     *         sslSocketFactory.createSocket("www.example.com", 443);
+     * 
+ * or + *
+     *     SSLEngine engine =
+     *         sslContext.createSSLEngine("www.example.com", 443);
+     * 
+ * + * @return null or an immutable list of non-null {@link SNIServerName}s + * + * @see List + * @see #setServerNames(List) + * + * @since 1.8 + */ + public final List getServerNames() { + return sniNames; + } + + /** + * Sets the {@link SNIMatcher}s of the Server Name Indication (SNI) + * parameter. + *

+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s + * operating in server mode. + *

+ * Note that the {@code matchers} collection is cloned to protect + * against subsequent modification. + * + * @param matchers + * the collection of {@link SNIMatcher}s (or null) + * + * @throws NullPointerException if the {@code matchers} + * contains {@code null} element + * @throws IllegalArgumentException if the {@code matchers} + * contains more than one name of the same name type + * + * @see Collection + * @see SNIMatcher + * @see #getSNIMatchers() + * + * @since 1.8 + */ + public final void setSNIMatchers(Collection matchers) { + if (this.sniMatchers == matchers) { + return; + } + + if (matchers == null) { + this.sniMatchers = null; + } else if (matchers.isEmpty()) { + sniMatchers = Collections.emptyList(); + } else { + List matcherTypes = new ArrayList<>(matchers.size()); + List matcherValues = new ArrayList<>(matchers.size()); + for (SNIMatcher matcher : matchers) { + if (matcherTypes.contains(matcher.getType())) { + throw new IllegalArgumentException( + "Duplicated server name of type " + + matcher.getType()); + } else { + matcherTypes.add(matcher.getType()); + matcherValues.add(matcher); + } + } + + this.sniMatchers = Collections.unmodifiableList(matcherValues); + } + } + + /** + * Returns a {@link Collection} containing all {@link SNIMatcher}s of the + * Server Name Indication (SNI) parameter, or null if none has been set. + *

+ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s + * operating in server mode. + *

+ * For better interoperability, providers generally will not define + * default matchers so that by default servers will ignore the SNI + * extension and continue the handshake. + * + * @return null or an immutable collection of non-null {@link SNIMatcher}s + * + * @see SNIMatcher + * @see #setSNIMatchers(Collection) + * + * @since 1.8 + */ + public final Collection getSNIMatchers() { + return sniMatchers; + } + + /** + * Sets whether the local cipher suites preference should be honored. + * + * @param honorOrder whether local cipher suites order in + * {@code #getCipherSuites} should be honored during + * SSL/TLS/DTLS handshaking. + * + * @see #getUseCipherSuitesOrder() + * + * @since 1.8 + */ + public final void setUseCipherSuitesOrder(boolean honorOrder) { + this.preferLocalCipherSuites = honorOrder; + } + + /** + * Returns whether the local cipher suites preference should be honored. + * + * @return whether local cipher suites order in {@code #getCipherSuites} + * should be honored during SSL/TLS/DTLS handshaking. + * + * @see #setUseCipherSuitesOrder(boolean) + * + * @since 1.8 + */ + public final boolean getUseCipherSuitesOrder() { + return preferLocalCipherSuites; + } + + /** + * Sets whether DTLS handshake retransmissions should be enabled. + * + * This method only applies to DTLS. + * + * @param enableRetransmissions + * {@code true} indicates that DTLS handshake retransmissions + * should be enabled; {@code false} indicates that DTLS handshake + * retransmissions should be disabled + * + * @see #getEnableRetransmissions() + * + * @since 9 + */ + public void setEnableRetransmissions(boolean enableRetransmissions) { + this.enableRetransmissions = enableRetransmissions; + } + + /** + * Returns whether DTLS handshake retransmissions should be enabled. + * + * This method only applies to DTLS. + * + * @return true, if DTLS handshake retransmissions should be enabled + * + * @see #setEnableRetransmissions(boolean) + * + * @since 9 + */ + public boolean getEnableRetransmissions() { + return enableRetransmissions; + } + + /** + * Sets the maximum expected network packet size in bytes for + * SSL/TLS/DTLS records. + * + * @apiNote It is recommended that if possible, the maximum packet size + * should not be less than 256 bytes so that small handshake + * messages, such as HelloVerifyRequests, are not fragmented. + * + * @implNote If the maximum packet size is too small to hold a minimal + * record, an implementation may attempt to generate as minimal + * records as possible. However, this may cause a generated + * packet to be larger than the maximum packet size. + * + * @param maximumPacketSize + * the maximum expected network packet size in bytes, or + * {@code 0} to use the implicit size that is automatically + * specified by the underlying implementation. + * @throws IllegalArgumentException + * if {@code maximumPacketSize} is negative. + * + * @see #getMaximumPacketSize() + * + * @since 9 + */ + public void setMaximumPacketSize(int maximumPacketSize) { + if (maximumPacketSize < 0) { + throw new IllegalArgumentException( + "The maximum packet size cannot be negative"); + } + + this.maximumPacketSize = maximumPacketSize; + } + + /** + * Returns the maximum expected network packet size in bytes for + * SSL/TLS/DTLS records. + * + * @apiNote The implicit size may not be a fixed value, especially + * for a DTLS protocols implementation. + * + * @implNote For SSL/TLS/DTLS connections, the underlying provider + * should calculate and specify the implicit value of the + * maximum expected network packet size if it is not + * configured explicitly. For any connection populated + * object, this method should never return {@code 0} so + * that applications can retrieve the actual implicit size + * of the underlying implementation. + *

+ * An implementation should attempt to comply with the maximum + * packet size configuration. However, if the maximum packet + * size is too small to hold a minimal record, an implementation + * may try to generate as minimal records as possible. This + * may cause a generated packet to be larger than the maximum + * packet size. + * + * @return the maximum expected network packet size, or {@code 0} if + * use the implicit size that is automatically specified by + * the underlying implementation and this object has not been + * populated by any connection. + * + * @see #setMaximumPacketSize(int) + * + * @since 9 + */ + public int getMaximumPacketSize() { + return maximumPacketSize; + } + + /** + * Returns a prioritized array of application-layer protocol names that + * can be negotiated over the SSL/TLS/DTLS protocols. + *

+ * The array could be empty (zero-length), in which case protocol + * indications will not be used. + *

+ * This method will return a new array each time it is invoked. + * + * @return a non-null, possibly zero-length array of application protocol + * {@code String}s. The array is ordered based on protocol + * preference, with the first entry being the most preferred. + * @see #setApplicationProtocols + * @since 9 + */ + public String[] getApplicationProtocols() { + return applicationProtocols.clone(); + } + + /** + * Sets the prioritized array of application-layer protocol names that + * can be negotiated over the SSL/TLS/DTLS protocols. + *

+ * If application-layer protocols are supported by the underlying + * SSL/TLS implementation, this method configures which values can + * be negotiated by protocols such as RFC 7301 , the + * Application Layer Protocol Negotiation (ALPN). + *

+ * If this end of the connection is expected to offer application protocol + * values, all protocols configured by this method will be sent to the + * peer. + *

+ * If this end of the connection is expected to select the application + * protocol value, the {@code protocols} configured by this method are + * compared with those sent by the peer. The first matched value becomes + * the negotiated value. If none of the {@code protocols} were actually + * requested by the peer, the underlying protocol will determine what + * action to take. (For example, ALPN will send a + * {@code "no_application_protocol"} alert and terminate the connection.) + *

+ * The {@code String} values must be presented using the network + * byte representation expected by the peer. For example, if an ALPN + * {@code String} should be exchanged using {@code UTF-8}, the + * {@code String} should be converted to its {@code byte[]} representation + * and stored as a byte-oriented {@code String} before calling this method. + * + *

+     *     // MEETEI MAYEK LETTERS HUK UN I (Unicode 0xabcd->0xabcf): 2 bytes
+     *     byte[] bytes = "\u005cuabcd\u005cuabce\u005cuabcf"
+     *             .getBytes(StandardCharsets.UTF_8);
+     *     String HUK_UN_I = new String(bytes, StandardCharsets.ISO_8859_1);
+     *
+     *     // 0x00-0xFF:  1 byte
+     *     String rfc7301Grease8A = "\u005cu008A\u005cu008A";
+     *
+     *     SSLParameters p = sslSocket.getSSLParameters();
+     *     p.setApplicationProtocols(new String[] {
+     *             "h2", "http/1.1", rfc7301Grease8A, HUK_UN_I});
+     *     sslSocket.setSSLParameters(p);
+     * 
+ * + * @implSpec + * This method will make a copy of the {@code protocols} array. + * + * @param protocols an ordered array of application protocols, + * with {@code protocols[0]} being the most preferred. + * If the array is empty (zero-length), protocol + * indications will not be used. + * @throws IllegalArgumentException if protocols is null, or if + * any element in a non-empty array is null or an + * empty (zero-length) string + * @see #getApplicationProtocols + * @since 9 + */ + public void setApplicationProtocols(String[] protocols) { + if (protocols == null) { + throw new IllegalArgumentException("protocols was null"); + } + + String[] tempProtocols = protocols.clone(); + + for (String p : tempProtocols) { + if (p == null || p.isEmpty()) { + throw new IllegalArgumentException( + "An element of protocols was null/empty"); + } + } + applicationProtocols = tempProtocols; + } + + /** + * Something goes here later. + * @return something + */ + public Map> getCertificateDeflaters() { + return this.certDeflaters; + } + + /** + * Something goes here later. + * @param certDeflaters something + */ + public void setCertificateDeflaters( + Map> certDeflaters) { + // TODO + this.certDeflaters = certDeflaters; + } + + /** + * Something goes here later. + * @return something + */ + public Map> getCertificateInflaters() { + return this.certInflaters; + } + + /** + * Something goes here later. + * @param certInflaters something + */ + public void setCertificateInflaters( + Map> certInflaters) { + // TODO + this.certInflaters = certInflaters; + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index bc034dc8c3bcf..613fa4501f3fe 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -277,6 +277,8 @@ public void consume(ConnectionContext context, // consumer so the state machine doesn't expect it. tc.handshakeContext.handshakeConsumers.remove( SSLHandshake.CERTIFICATE.id); + tc.handshakeContext.handshakeConsumers.remove( + SSLHandshake.COMPRESSED_CERTIFICATE.id); tc.handshakeContext.handshakeConsumers.remove( SSLHandshake.CERTIFICATE_VERIFY.id); } diff --git a/src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java b/src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java new file mode 100644 index 0000000000000..5cfa3068c2346 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.ssl; + +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; + +import javax.net.ssl.SSLProtocolException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.MessageFormat; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +/** + * Pack of the "compress_certificate" extensions [RFC 5246]. + */ +final class CertCompressionExtension { + static final HandshakeProducer chNetworkProducer = + new CHCompressCertificateProducer(); + static final ExtensionConsumer chOnLoadConsumer = + new CHCompressCertificateConsumer(); + static final HandshakeConsumer chOnTradeConsumer = + new CHCompressCertificateUpdate(); + + static final HandshakeProducer crNetworkProducer = + new CRCompressCertificateProducer(); + static final ExtensionConsumer crOnLoadConsumer = + new CRCompressCertificateConsumer(); + static final HandshakeConsumer crOnTradeConsumer = + new CRCompressCertificateUpdate(); + + static final SSLStringizer ccStringizer = + new CompressCertificateStringizer(); + + /** + * The "signature_algorithms" extension. + */ + static final class CertCompressionSpec implements SSLExtensionSpec { + final int[] compressionAlgorithms; + + CertCompressionSpec( + Map> certInflaters) { + compressionAlgorithms = new int[certInflaters.size()]; + int i = 0; + for (Integer id : certInflaters.keySet()) { + compressionAlgorithms[i++] = id; + } + } + + CertCompressionSpec(HandshakeContext hc, + ByteBuffer buffer) throws IOException { + if (buffer.remaining() < 2) { // 2: the length of the list + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: insufficient data")); + } + + byte[] algs = Record.getBytes8(buffer); + if (buffer.hasRemaining()) { + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: unknown extra data")); + } + + if (algs.length == 0 || (algs.length & 0x01) != 0) { + throw hc.conContext.fatal(Alert.DECODE_ERROR, + new SSLProtocolException( + "Invalid compress_certificate: incomplete data")); + } + + int[] compressionAlgs = new int[algs.length / 2]; + for (int i = 0, j = 0; i < algs.length;) { + byte hash = algs[i++]; + byte sign = algs[i++]; + compressionAlgs[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF); + } + + this.compressionAlgorithms = compressionAlgs; + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + "\"compression algorithms\": '['{0}']'", Locale.ENGLISH); + + if (compressionAlgorithms == null || compressionAlgorithms.length == 0) { + Object[] messageFields = { + "" + }; + return messageFormat.format(messageFields); + } else { + StringBuilder builder = new StringBuilder(512); + boolean isFirst = true; + for (int ca : compressionAlgorithms) { + if (isFirst) { + isFirst = false; + } else { + builder.append(", "); + } + + builder.append(CompressionAlgorithm.nameOf(ca)); + } + + Object[] messageFields = { + builder.toString() + }; + + return messageFormat.format(messageFields); + } + } + } + + private static final + class CompressCertificateStringizer implements SSLStringizer { + @Override + public String toString(HandshakeContext hc, ByteBuffer buffer) { + try { + return (new CertCompressionSpec(hc, buffer)).toString(); + } catch (IOException ioe) { + // For debug logging only, so please swallow exceptions. + return ioe.getMessage(); + } + } + } + + /** + * Network data producer of a "compress_certificate" extension in + * the ClientHello handshake message. + */ + private static final + class CHCompressCertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CHCompressCertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // Is it a supported and enabled extension? + if (!chc.sslConfig.isAvailable( + SSLExtension.CH_COMPRESS_CERTIFICATE)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable compress_certificate extension"); + } + return null; + } + + // Produce the extension. + if (chc.localCertInflaters == null) { + chc.localCertInflaters = + CompressionAlgorithm.findInflaters(chc.sslConfig); + } + + if (chc.localCertInflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unsupported compress_certificate extension"); + } + return null; + } + + int vectorLen = CompressionAlgorithm.sizeInRecord() * + chc.localCertInflaters.size(); + byte[] extData = new byte[vectorLen + 2]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt8(m, vectorLen); + for (Integer algId : chc.localCertInflaters.keySet()) { + Record.putInt16(m, algId); + } + + // Update the context. + chc.handshakeExtensions.put( + SSLExtension.CH_COMPRESS_CERTIFICATE, + new CertCompressionSpec(chc.localCertInflaters)); + + return extData; + } + } + + /** + * Network data consumer of a "compress_certificate" extension in + * the ClientHello handshake message. + */ + private static final + class CHCompressCertificateConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CHCompressCertificateConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // Is it a supported and enabled extension? + if (!shc.sslConfig.isAvailable( + SSLExtension.CH_COMPRESS_CERTIFICATE)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable compress_certificate extension"); + } + return; // ignore the extension + } + + if (shc.sslConfig.certDeflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unsupported compress_certificate extension"); + } + return; // ignore the extension + } + + // Parse the extension. + CertCompressionSpec spec = new CertCompressionSpec(shc, buffer); + + shc.certDeflater = CompressionAlgorithm.selectDeflater( + shc.sslConfig, spec.compressionAlgorithms); + if (shc.certDeflater == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore, no supported compress algorithms"); + } + return; // ignore the extension + } + + // Update the context. + // shc.handshakeExtensions.put( + // SSLExtension.CH_COMPRESS_CERTIFICATE, spec); + + // No impact on session resumption. + } + } + + /** + * After session creation consuming of a "compress_certificate" + * extension in the ClientHello handshake message. + */ + private static final class CHCompressCertificateUpdate + implements HandshakeConsumer { + // Prevent instantiation of this class. + private CHCompressCertificateUpdate() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + if (shc.certDeflater == null) { + // Ignore, no proper certificate compression algorithm. + return; + } + + if (!shc.isResumption && + shc.negotiatedProtocol.useTLS13PlusSpec()) { + // Remove Certificate producer. + shc.handshakeProducers.remove(SSLHandshake.CERTIFICATE.id); + + // Add the CompressedCertificate producer. + shc.handshakeProducers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } + } + } + + /** + * Network data producer of a "compress_certificate" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRCompressCertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CRCompressCertificateProducer() { + // blank + } + + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in server side only. + ServerHandshakeContext shc = (ServerHandshakeContext)context; + + // Is it a supported and enabled extension? + if (!shc.sslConfig.isAvailable( + SSLExtension.CR_COMPRESS_CERTIFICATE)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable compress_certificate extension"); + } + return null; + } + + // Produce the extension. + if (shc.localCertInflaters == null) { + shc.localCertInflaters = + CompressionAlgorithm.findInflaters(shc.sslConfig); + } + + if (shc.localCertInflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unsupported compress_certificate extension"); + } + return null; + } + + int vectorLen = CompressionAlgorithm.sizeInRecord() * + shc.localCertInflaters.size(); + byte[] extData = new byte[vectorLen + 2]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt8(m, vectorLen); + for (Integer algId : shc.localCertInflaters.keySet()) { + Record.putInt16(m, algId); + } + + // Update the context. + shc.handshakeExtensions.put( + SSLExtension.CR_COMPRESS_CERTIFICATE, + new CertCompressionSpec(shc.localCertInflaters)); + + return extData; + } + } + + /** + * Network data consumer of a "compress_certificate" extension in + * the CertificateRequest handshake message. + */ + private static final + class CRCompressCertificateConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. + private CRCompressCertificateConsumer() { + // blank + } + @Override + public void consume(ConnectionContext context, + HandshakeMessage message, ByteBuffer buffer) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + // Is it a supported and enabled extension? + if (!chc.sslConfig.isAvailable( + SSLExtension.CR_COMPRESS_CERTIFICATE)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unavailable compress_certificate extension"); + } + return; // ignore the extension + } + + if (chc.sslConfig.certDeflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore unsupported compress_certificate extension"); + } + return; // ignore the extension + } + + // Parse the extension. + CertCompressionSpec spec = new CertCompressionSpec(chc, buffer); + + chc.certDeflater = CompressionAlgorithm.selectDeflater( + chc.sslConfig, spec.compressionAlgorithms); + if (chc.certDeflater == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Ignore, no supported compress algorithms"); + } + return; // ignore the extension + } + + // Update the context. + // chc.handshakeExtensions.put( + // SSLExtension.CR_COMPRESS_CERTIFICATE, spec); + + // No impact on session resumption. + } + } + + /** + * After session creation consuming of a "compress_certificate" + * extension in the CertificateRequest handshake message. + */ + private static final class CRCompressCertificateUpdate + implements HandshakeConsumer { + // Prevent instantiation of this class. + private CRCompressCertificateUpdate() { + // blank + } + + @Override + public void consume(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The consuming happens in client side only. + ClientHandshakeContext chc = (ClientHandshakeContext)context; + + if (chc.certDeflater == null) { + // Ignore, no proper certificate compression algorithm. + return; + } + + if (chc.negotiatedProtocol.useTLS13PlusSpec()) { + // Remove Certificate producer. + chc.handshakeProducers.remove(SSLHandshake.CERTIFICATE.id); + + // Add the CompressedCertificate producer. + chc.handshakeProducers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index 5921b7cfca556..d7268f7de352e 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -954,16 +954,26 @@ public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { // The producing happens in handshake context only. HandshakeContext hc = (HandshakeContext)context; - if (hc.sslConfig.isClientMode) { - return onProduceCertificate( - (ClientHandshakeContext)context, message); - } else { - return onProduceCertificate( + T13CertificateMessage cm = hc.sslConfig.isClientMode ? + onProduceCertificate( + (ClientHandshakeContext)context, message) : + onProduceCertificate( (ServerHandshakeContext)context, message); + + // Output the handshake message. + if (hc.certDeflater == null) { + cm.write(hc.handshakeOutput); + hc.handshakeOutput.flush(); + } else { + // Replace with CompressedCertificate message + CompressedCertificate.handshakeProducer.produce(hc, cm); } + + // The handshake message has been delivered. + return null; } - private byte[] onProduceCertificate(ServerHandshakeContext shc, + private T13CertificateMessage onProduceCertificate(ServerHandshakeContext shc, HandshakeMessage message) throws IOException { ClientHelloMessage clientHello = (ClientHelloMessage)message; @@ -1022,12 +1032,7 @@ private byte[] onProduceCertificate(ServerHandshakeContext shc, SSLLogger.fine("Produced server Certificate message", cm); } - // Output the handshake message. - cm.write(shc.handshakeOutput); - shc.handshakeOutput.flush(); - - // The handshake message has been delivered. - return null; + return cm; } private static SSLPossession choosePossession( @@ -1090,7 +1095,7 @@ private static SSLPossession choosePossession( return pos; } - private byte[] onProduceCertificate(ClientHandshakeContext chc, + private T13CertificateMessage onProduceCertificate(ClientHandshakeContext chc, HandshakeMessage message) throws IOException { ClientHelloMessage clientHello = (ClientHelloMessage)message; SSLPossession pos = choosePossession(chc, clientHello); @@ -1134,12 +1139,7 @@ private byte[] onProduceCertificate(ClientHandshakeContext chc, SSLLogger.fine("Produced client Certificate message", cm); } - // Output the handshake message. - cm.write(chc.handshakeOutput); - chc.handshakeOutput.flush(); - - // The handshake message has been delivered. - return null; + return cm; } } diff --git a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java new file mode 100644 index 0000000000000..616546c49d824 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.ssl; + +import sun.security.ssl.SSLHandshake.HandshakeMessage; +import sun.security.util.HexDumpEncoder; + +import javax.net.ssl.*; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.MessageFormat; +import java.util.*; +import java.util.function.Function; + +/** + * Pack of the CompressedCertificate handshake message. + */ +final class CompressedCertificate { + static final SSLConsumer handshakeConsumer = + new CompressedCertConsumer(); + static final HandshakeProducer handshakeProducer = + new CompressedCertProducer(); + + /** + * The CompressedCertificate handshake message for TLS 1.3. + */ + static final class CompressedCertMessage extends HandshakeMessage { + private final int algorithmId; + private final int uncompressedLength; + private final byte[] compressedCert; + + CompressedCertMessage(HandshakeContext context, + int algorithmId, int uncompressedLength, + byte[] compressedCert) { + super(context); + + this.algorithmId = algorithmId; + this.uncompressedLength = uncompressedLength; + this.compressedCert = compressedCert; + } + + CompressedCertMessage(HandshakeContext handshakeContext, + ByteBuffer m) throws IOException { + super(handshakeContext); + + // struct { + // CertificateCompressionAlgorithm algorithm; + // uint24 uncompressed_length; + // opaque compressed_certificate_message<1..2^24-1>; + // } CompressedCertificate; + if (m.remaining() < 9) { + throw new SSLProtocolException( + "Invalid CompressedCertificate message: " + + "insufficient data (length=" + m.remaining() + ")"); + } + this.algorithmId = Record.getInt16(m); + this.uncompressedLength = Record.getInt24(m); + this.compressedCert = Record.getBytes24(m); + + if (m.hasRemaining()) { + throw handshakeContext.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "Invalid CompressedCertificate message: unknown extra data"); + } + } + + @Override + public SSLHandshake handshakeType() { + return SSLHandshake.COMPRESSED_CERTIFICATE; + } + + @Override + public int messageLength() { + return 8 + compressedCert.length; + } + + @Override + public void send(HandshakeOutStream hos) throws IOException { + hos.putInt16(algorithmId); + hos.putInt24(uncompressedLength); + hos.putBytes24(compressedCert); + } + + @Override + public String toString() { + MessageFormat messageFormat = new MessageFormat( + """ + "CompressedCertificate": '{' + "algorithm": "{0}", + "uncompressed_length": [{1} + ] + "compressed_certificate_message": [{2} + ] + '}'""", + Locale.ENGLISH); + + HexDumpEncoder hexEncoder = new HexDumpEncoder(); + Object[] messageFields = { + Utilities.toHexString(algorithmId), + Utilities.toHexString(uncompressedLength), + Utilities.indent(hexEncoder.encode(compressedCert)) + }; + + return messageFormat.format(messageFields); + } + } + + /** + * The "Certificate" handshake message producer for TLS 1.3. + */ + private static final + class CompressedCertProducer implements HandshakeProducer { + // Prevent instantiation of this class. + private CompressedCertProducer() { + // blank + } + + // Note this is a special producer, which can only be called from + // the CertificateMessage producer. The input 'message' parameter + // represents the Certificate handshake message. + @Override + public byte[] produce(ConnectionContext context, + HandshakeMessage message) throws IOException { + // The producing happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // Compress the Certificate message. + HandshakeOutStream hos = new HandshakeOutStream(null); + message.send(hos); + byte[] certMsg = hos.toByteArray(); + CompressedCertMessage ccm = new CompressedCertMessage(hc, + hc.certDeflater.getKey(), certMsg.length, + hc.certDeflater.getValue().apply(certMsg)); + + ccm.write(hc.handshakeOutput); + hc.handshakeOutput.flush(); + + // The handshake message has been delivered. + return null; + } + } + + /** + * The "Certificate" handshake message consumer for TLS 1.3. + */ + private static final class CompressedCertConsumer implements SSLConsumer { + // Prevent instantiation of this class. + private CompressedCertConsumer() { + // blank + } + + @Override + public void consume(ConnectionContext context, + ByteBuffer message) throws IOException { + // The consuming happens in handshake context only. + HandshakeContext hc = (HandshakeContext)context; + + // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.COMPRESSED_CERTIFICATE.id); + hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); + + // Parse the handshake message + CompressedCertMessage ccm = new CompressedCertMessage(hc, message); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine( + "Consuming CompressedCertificate handshake message", ccm); + } + + // check the compression algorithm + Function inflater = + hc.localCertInflaters.get(ccm.algorithmId); + if (inflater == null) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Unsupported certificate compression algorithm"); + } + + // decompress + byte[] certificateMessage = inflater.apply(ccm.compressedCert); + + // check the uncompressed length + if (certificateMessage == null || + certificateMessage.length != ccm.uncompressedLength) { + throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, + "Unsupported certificate compression algorithm"); + } + + // Call the Certificate handshake message consumer. + CertificateMessage.t13HandshakeConsumer.consume(hc, + ByteBuffer.wrap(certificateMessage)); + } + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java new file mode 100644 index 0000000000000..f7013155a734d --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.ssl; + +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * Enum for (D)TLS certificate compression algorithms. + */ +enum CompressionAlgorithm { + ZLIB (1, "zlib"), + BROTLI (2, "brotli"), + ZSTD (3, "zstd"); + + final int id; + final String name; + + CompressionAlgorithm(int id, String name) { + this.id = id; + this.name = name; + } + + static CompressionAlgorithm valueOf(int id) { + for (CompressionAlgorithm cca : + CompressionAlgorithm.values()) { + if (cca.id == id) { + return cca; + } + } + + return null; + } + + static CompressionAlgorithm nameOf(String name) { + for (CompressionAlgorithm cca : + CompressionAlgorithm.values()) { + if (cca.name.equals(name)) { + return cca; + } + } + + return null; + } + + static String nameOf(int id) { + for (CompressionAlgorithm cca : + CompressionAlgorithm.values()) { + if (cca.id == id) { + return cca.name; + } + } + + return ""; + } + + // Return the size of a SignatureScheme structure in TLS record + static int sizeInRecord() { + return 2; + } + + // Get local supported algorithm collection. + static Map> findInflaters( + SSLConfiguration config) { + Map> inflaters = + new LinkedHashMap<>(config.certInflaters.size()); + for (Map.Entry> entry : + config.certInflaters.entrySet()) { + CompressionAlgorithm ca = + CompressionAlgorithm.nameOf(entry.getKey()); + if (ca == null) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "Ignore unsupported certificate " + + "compression algorithm: " + entry.getKey()); + } + continue; + } + + inflaters.putIfAbsent(ca.id, entry.getValue()); + } + + return inflaters; + } + + static Map.Entry> selectDeflater( + SSLConfiguration config, + int[] compressionAlgorithmIds) { + for (Map.Entry> entry : + config.certDeflaters.entrySet()) { + CompressionAlgorithm ca = + CompressionAlgorithm.nameOf(entry.getKey()); + if (ca != null) { + for (int id : compressionAlgorithmIds) { + if (ca.id == id) { + return new AbstractMap.SimpleImmutableEntry<>( + id, entry.getValue()); + } + } + } + } + + return null; + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index ef13505dd87df..0faa503300e3d 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.function.Function; import javax.crypto.SecretKey; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLHandshakeException; @@ -140,6 +141,10 @@ abstract class HandshakeContext implements ConnectionContext { List peerRequestedSignatureSchemes; List peerRequestedCertSignSchemes; + // CertificateCompressionAlgorithm + Map> localCertInflaters; + Map.Entry> certDeflater; + // Known authorities X500Principal[] peerSupportedAuthorities = null; @@ -591,4 +596,3 @@ List getRequestedServerNames() { return requestedServerNames; } } - diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index d267c2e6b467c..a4c8b5789a6e0 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -29,12 +29,9 @@ import java.security.AccessController; import java.security.AlgorithmConstraints; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.function.BiFunction; +import java.util.function.Function; import javax.crypto.KeyGenerator; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SNIMatcher; @@ -68,6 +65,11 @@ final class SSLConfiguration implements Cloneable { // "signature_algorithms_cert" extensions List signatureSchemes; + // The configured certificate compression algorithms for + // "compress_certificate" extensions + Map> certDeflaters; // non-null + Map> certInflaters; // non-null + // the maximum protocol version of enabled protocols ProtocolVersion maximumProtocolVersion; @@ -150,6 +152,10 @@ final class SSLConfiguration implements Cloneable { this.signatureSchemes = isClientMode ? CustomizedClientSignatureSchemes.signatureSchemes : CustomizedServerSignatureSchemes.signatureSchemes; + + this.certDeflaters = Collections.emptyMap(); + this.certInflaters = Collections.emptyMap(); + this.maximumProtocolVersion = ProtocolVersion.NONE; for (ProtocolVersion pv : enabledProtocols) { if (pv.compareTo(maximumProtocolVersion) > 0) { @@ -200,6 +206,8 @@ SSLParameters getSSLParameters() { params.setSNIMatchers(this.sniMatchers); } + params.setCertificateDeflaters(this.certDeflaters); + params.setCertificateInflaters(this.certInflaters); params.setApplicationProtocols(this.applicationProtocols); params.setUseCipherSuitesOrder(this.preferLocalCipherSuites); params.setEnableRetransmissions(this.enableRetransmissions); @@ -261,6 +269,8 @@ void setSSLParameters(SSLParameters params) { this.applicationProtocols = sa; } // otherwise, use the default values + this.certDeflaters = params.getCertificateDeflaters(); + this.certInflaters = params.getCertificateInflaters(); this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); this.enableRetransmissions = params.getEnableRetransmissions(); this.maximumPacketSize = params.getMaximumPacketSize(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index 47272f8e0b850..8f57cce835e54 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -271,6 +271,27 @@ enum SSLExtension implements SSLStringizer { // Extensions defined in RFC 7924 (TLS Cached Information Extension) CACHED_INFO (0x0019, "cached_info"), + // Extensions defined in RFC 8879 (TLS Certificate Compression) + CH_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", + SSLHandshake.CERTIFICATE_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + CertCompressionExtension.chNetworkProducer, + CertCompressionExtension.chOnLoadConsumer, + null, + CertCompressionExtension.chOnTradeConsumer, + null, + CertCompressionExtension.ccStringizer), + + CR_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", + SSLHandshake.CERTIFICATE_REQUEST, + ProtocolVersion.PROTOCOLS_OF_13, + CertCompressionExtension.crNetworkProducer, + CertCompressionExtension.crOnLoadConsumer, + null, + CertCompressionExtension.crOnTradeConsumer, + null, + CertCompressionExtension.ccStringizer), + // Extensions defined in RFC 5077 (TLS Session Resumption without Server-Side State) CH_SESSION_TICKET (0x0023, "session_ticket", SSLHandshake.CLIENT_HELLO, diff --git a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java index 16481f800711e..0cf28fbb2c541 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java @@ -346,6 +346,24 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { ProtocolVersion.PROTOCOLS_OF_13 ) })), + + @SuppressWarnings({"unchecked", "rawtypes"}) + COMPRESSED_CERTIFICATE ((byte)0x19, "compressed_certificate", + (Map.Entry[])(new Map.Entry[] { + new SimpleImmutableEntry<>( + CompressedCertificate.handshakeConsumer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + }), + (Map.Entry[])(new Map.Entry[] { + // Note that the producing of this message is delegated to + // CertificateMessage producer. + new SimpleImmutableEntry<>( + CertificateMessage.t13HandshakeProducer, + ProtocolVersion.PROTOCOLS_OF_13 + ) + })), + MESSAGE_HASH ((byte)0xFE, "message_hash"), NOT_APPLICABLE ((byte)0xFF, "not_applicable"); From d088a0fabbe3e21706c86e7d38dd5b5b7ab2a963 Mon Sep 17 00:00:00 2001 From: Xuelei Fan Date: Fri, 11 Feb 2022 11:25:44 -0800 Subject: [PATCH 03/12] test and httpclient --- .../classes/javax/net/ssl/SSLParameters.java | 2 +- src/java.base/share/classes/javax/net/ssl/ssl | 728 ------------------ .../share/classes/sun/security/ssl/Alert.java | 2 +- .../sun/security/ssl/CertificateMessage.java | 1 + .../sun/security/ssl/CertificateRequest.java | 5 + ...ension.java => CompressCertExtension.java} | 159 +--- .../security/ssl/CompressedCertificate.java | 37 +- .../security/ssl/CompressionAlgorithm.java | 20 +- .../sun/security/ssl/HandshakeContext.java | 4 +- .../sun/security/ssl/SSLConfiguration.java | 6 + .../sun/security/ssl/SSLExtension.java | 18 +- .../classes/sun/security/ssl/ServerHello.java | 5 + .../jdk/internal/net/http/common/Utils.java | 2 + .../HttpsCompressedCert.java | 78 ++ .../net/ssl/SSLParameters/CompressedCert.java | 270 +++++++ .../SSLParameters/UnsetCompressedCert.java | 162 ++++ .../net/ssl/templates/SSLClientContext.java | 157 ++++ 17 files changed, 772 insertions(+), 884 deletions(-) delete mode 100644 src/java.base/share/classes/javax/net/ssl/ssl rename src/java.base/share/classes/sun/security/ssl/{CertCompressionExtension.java => CompressCertExtension.java} (70%) create mode 100644 test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java create mode 100644 test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java create mode 100644 test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java create mode 100644 test/jdk/javax/net/ssl/templates/SSLClientContext.java diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index 668c5ee7fcd53..d501e79237f84 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -705,7 +705,7 @@ public Map> getCertificateDeflaters() { public void setCertificateDeflaters( Map> certDeflaters) { // TODO - this.certDeflaters = certDeflaters; + this.certDeflaters = Map.of(); } /** diff --git a/src/java.base/share/classes/javax/net/ssl/ssl b/src/java.base/share/classes/javax/net/ssl/ssl deleted file mode 100644 index 668c5ee7fcd53..0000000000000 --- a/src/java.base/share/classes/javax/net/ssl/ssl +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Copyright (c) 2005, 2022, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package javax.net.ssl; - -import java.security.AlgorithmConstraints; -import java.util.*; -import java.util.function.Function; - -/** - * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters - * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake, - * the list of protocols to be allowed, the endpoint identification - * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI), - * the maximum network packet size, the algorithm constraints and whether - * SSL/TLS/DTLS servers should request or require client authentication, etc. - *

- * SSLParameters can be created via the constructors in this class. - * Objects can also be obtained using the {@code getSSLParameters()} - * methods in - * {@link SSLSocket#getSSLParameters SSLSocket} and - * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and - * {@link SSLEngine#getSSLParameters SSLEngine} or the - * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and - * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} - * methods in {@code SSLContext}. - *

- * SSLParameters can be applied to a connection via the methods - * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and - * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} - * and {@link SSLEngine#setSSLParameters SSLEngine.setSSLParameters()}. - *

- * For example: - * - *

- *     SSLParameters p = sslSocket.getSSLParameters();
- *     p.setProtocols(new String[] { "TLSv1.2" });
- *     p.setCipherSuites(
- *         new String[] { "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ... });
- *     p.setApplicationProtocols(new String[] {"h2", "http/1.1"});
- *     sslSocket.setSSLParameters(p);
- * 
- * - * @see SSLSocket - * @see SSLEngine - * @see SSLContext - * - * @since 1.6 - */ -public class SSLParameters { - - private String[] cipherSuites; - private String[] protocols; - private boolean wantClientAuth; - private boolean needClientAuth; - private String identificationAlgorithm; - private AlgorithmConstraints algorithmConstraints; - private List sniNames = null; // immutable list - private Collection sniMatchers = null; // immutable collection - private boolean preferLocalCipherSuites; - private boolean enableRetransmissions = true; - private int maximumPacketSize = 0; - private String[] applicationProtocols = new String[0]; - private Map> certDeflaters = null; - private Map> certInflaters = null; - - /** - * Constructs SSLParameters. - *

- * The values of cipherSuites, protocols, cryptographic algorithm - * constraints, endpoint identification algorithm, server names and - * server name matchers are set to {@code null}; useCipherSuitesOrder, - * wantClientAuth and needClientAuth are set to {@code false}; - * enableRetransmissions is set to {@code true}; maximum network packet - * size is set to {@code 0}. - */ - public SSLParameters() { - // empty - } - - /** - * Constructs SSLParameters from the specified array of ciphersuites. - *

- * Calling this constructor is equivalent to calling the no-args - * constructor followed by - * {@code setCipherSuites(cipherSuites);}. Note that the - * standard list of cipher suite names may be found in the - * JSSE Cipher Suite Names section of the Java Security Standard - * Algorithm Names Specification. Providers may support cipher suite - * names not found in this list. - * - * @param cipherSuites the array of ciphersuites (or null) - */ - public SSLParameters(String[] cipherSuites) { - setCipherSuites(cipherSuites); - } - - /** - * Constructs SSLParameters from the specified array of ciphersuites - * and protocols. - *

- * Calling this constructor is equivalent to calling the no-args - * constructor followed by - * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}. - * Note that the standard list of cipher suite names may be found in the - * - * JSSE Cipher Suite Names section of the Java Security Standard - * Algorithm Names Specification. Providers may support cipher suite - * names not found in this list. - * - * @param cipherSuites the array of ciphersuites (or null) - * @param protocols the array of protocols (or null) - */ - public SSLParameters(String[] cipherSuites, String[] protocols) { - setCipherSuites(cipherSuites); - setProtocols(protocols); - } - - private static String[] clone(String[] s) { - return (s == null) ? null : s.clone(); - } - - /** - * Returns a copy of the array of ciphersuites or null if none - * have been set. - *

- * The returned array includes cipher suites from the list of standard - * cipher suite names in the - * JSSE Cipher Suite Names section of the Java Security Standard - * Algorithm Names Specification, and may also include other cipher suites - * that the provider supports. - * - * @return a copy of the array of ciphersuites or null if none - * have been set. - */ - public String[] getCipherSuites() { - return clone(cipherSuites); - } - - /** - * Sets the array of ciphersuites. - * - * @param cipherSuites the array of ciphersuites (or null). Note that the - * standard list of cipher suite names may be found in the - * JSSE Cipher Suite Names section of the Java Security Standard - * Algorithm Names Specification. Providers may support cipher suite - * names not found in this list or might not use the recommended name - * for a certain cipher suite. - */ - public void setCipherSuites(String[] cipherSuites) { - this.cipherSuites = clone(cipherSuites); - } - - /** - * Returns a copy of the array of protocols or null if none - * have been set. - * - * @return a copy of the array of protocols or null if none - * have been set. - */ - public String[] getProtocols() { - return clone(protocols); - } - - /** - * Sets the array of protocols. - * - * @param protocols the array of protocols (or null) - */ - public void setProtocols(String[] protocols) { - this.protocols = clone(protocols); - } - - /** - * Returns whether client authentication should be requested. - * - * @return whether client authentication should be requested. - */ - public boolean getWantClientAuth() { - return wantClientAuth; - } - - /** - * Sets whether client authentication should be requested. Calling - * this method clears the {@code needClientAuth} flag. - * - * @param wantClientAuth whether client authentication should be requested - */ - public void setWantClientAuth(boolean wantClientAuth) { - this.wantClientAuth = wantClientAuth; - this.needClientAuth = false; - } - - /** - * Returns whether client authentication should be required. - * - * @return whether client authentication should be required. - */ - public boolean getNeedClientAuth() { - return needClientAuth; - } - - /** - * Sets whether client authentication should be required. Calling - * this method clears the {@code wantClientAuth} flag. - * - * @param needClientAuth whether client authentication should be required - */ - public void setNeedClientAuth(boolean needClientAuth) { - this.wantClientAuth = false; - this.needClientAuth = needClientAuth; - } - - /** - * Returns the cryptographic algorithm constraints. - * - * @return the cryptographic algorithm constraints, or null if the - * constraints have not been set - * - * @see #setAlgorithmConstraints(AlgorithmConstraints) - * - * @since 1.7 - */ - public AlgorithmConstraints getAlgorithmConstraints() { - return algorithmConstraints; - } - - /** - * Sets the cryptographic algorithm constraints, which will be used - * in addition to any configured by the runtime environment. - *

- * If the {@code constraints} parameter is non-null, every - * cryptographic algorithm, key and algorithm parameters used in the - * SSL/TLS/DTLS handshake must be permitted by the constraints. - * - * @param constraints the algorithm constraints (or null) - * - * @since 1.7 - */ - public void setAlgorithmConstraints(AlgorithmConstraints constraints) { - // the constraints object is immutable - this.algorithmConstraints = constraints; - } - - /** - * Gets the endpoint identification algorithm. - * - * @return the endpoint identification algorithm, or null if none - * has been set. - * - * @see X509ExtendedTrustManager - * @see #setEndpointIdentificationAlgorithm(String) - * - * @since 1.7 - */ - public String getEndpointIdentificationAlgorithm() { - return identificationAlgorithm; - } - - /** - * Sets the endpoint identification algorithm. - *

- * If the {@code algorithm} parameter is non-null or non-empty, the - * endpoint identification/verification procedures must be handled during - * SSL/TLS/DTLS handshaking. This is to prevent man-in-the-middle attacks. - * - * @param algorithm The standard string name of the endpoint - * identification algorithm (or null). - * See the - * Java Security Standard Algorithm Names document - * for information about standard algorithm names. - * - * @see X509ExtendedTrustManager - * - * @since 1.7 - */ - public void setEndpointIdentificationAlgorithm(String algorithm) { - this.identificationAlgorithm = algorithm; - } - - /** - * Sets the desired {@link SNIServerName}s of the Server Name - * Indication (SNI) parameter. - *

- * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s - * operating in client mode. - *

- * Note that the {@code serverNames} list is cloned - * to protect against subsequent modification. - * - * @param serverNames - * the list of desired {@link SNIServerName}s (or null) - * - * @throws NullPointerException if the {@code serverNames} - * contains {@code null} element - * @throws IllegalArgumentException if the {@code serverNames} - * contains more than one name of the same name type - * - * @see SNIServerName - * @see #getServerNames() - * - * @since 1.8 - */ - public final void setServerNames(List serverNames) { - if (this.sniNames == serverNames) { - return; - } - - if (serverNames == null) { - sniNames = null; - } else if (serverNames.isEmpty()) { - sniNames = Collections.emptyList(); - } else { - List sniTypes = new ArrayList<>(serverNames.size()); - List sniValues = new ArrayList<>(serverNames.size()); - for (SNIServerName serverName : serverNames) { - if (sniTypes.contains(serverName.getType())) { - throw new IllegalArgumentException( - "Duplicated server name of type " + - serverName.getType()); - } else { - sniTypes.add(serverName.getType()); - sniValues.add(serverName); - } - } - - sniNames = Collections.unmodifiableList(sniValues); - } - } - - /** - * Returns a {@link List} containing all {@link SNIServerName}s of the - * Server Name Indication (SNI) parameter, or null if none has been set. - *

- * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s - * operating in client mode. - *

- * For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS provider - * may specify a default value for a certain server name type. In - * client mode, it is recommended that, by default, providers should - * include the server name indication whenever the server can be located - * by a supported server name type. - *

- * It is recommended that providers initialize default Server Name - * Indications when creating {@code SSLSocket}/{@code SSLEngine}s. - * In the following examples, the server name could be represented by an - * instance of {@link SNIHostName} which has been initialized with the - * hostname "www.example.com" and type - * {@link StandardConstants#SNI_HOST_NAME}. - * - *

-     *     Socket socket =
-     *         sslSocketFactory.createSocket("www.example.com", 443);
-     * 
- * or - *
-     *     SSLEngine engine =
-     *         sslContext.createSSLEngine("www.example.com", 443);
-     * 
- * - * @return null or an immutable list of non-null {@link SNIServerName}s - * - * @see List - * @see #setServerNames(List) - * - * @since 1.8 - */ - public final List getServerNames() { - return sniNames; - } - - /** - * Sets the {@link SNIMatcher}s of the Server Name Indication (SNI) - * parameter. - *

- * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s - * operating in server mode. - *

- * Note that the {@code matchers} collection is cloned to protect - * against subsequent modification. - * - * @param matchers - * the collection of {@link SNIMatcher}s (or null) - * - * @throws NullPointerException if the {@code matchers} - * contains {@code null} element - * @throws IllegalArgumentException if the {@code matchers} - * contains more than one name of the same name type - * - * @see Collection - * @see SNIMatcher - * @see #getSNIMatchers() - * - * @since 1.8 - */ - public final void setSNIMatchers(Collection matchers) { - if (this.sniMatchers == matchers) { - return; - } - - if (matchers == null) { - this.sniMatchers = null; - } else if (matchers.isEmpty()) { - sniMatchers = Collections.emptyList(); - } else { - List matcherTypes = new ArrayList<>(matchers.size()); - List matcherValues = new ArrayList<>(matchers.size()); - for (SNIMatcher matcher : matchers) { - if (matcherTypes.contains(matcher.getType())) { - throw new IllegalArgumentException( - "Duplicated server name of type " + - matcher.getType()); - } else { - matcherTypes.add(matcher.getType()); - matcherValues.add(matcher); - } - } - - this.sniMatchers = Collections.unmodifiableList(matcherValues); - } - } - - /** - * Returns a {@link Collection} containing all {@link SNIMatcher}s of the - * Server Name Indication (SNI) parameter, or null if none has been set. - *

- * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s - * operating in server mode. - *

- * For better interoperability, providers generally will not define - * default matchers so that by default servers will ignore the SNI - * extension and continue the handshake. - * - * @return null or an immutable collection of non-null {@link SNIMatcher}s - * - * @see SNIMatcher - * @see #setSNIMatchers(Collection) - * - * @since 1.8 - */ - public final Collection getSNIMatchers() { - return sniMatchers; - } - - /** - * Sets whether the local cipher suites preference should be honored. - * - * @param honorOrder whether local cipher suites order in - * {@code #getCipherSuites} should be honored during - * SSL/TLS/DTLS handshaking. - * - * @see #getUseCipherSuitesOrder() - * - * @since 1.8 - */ - public final void setUseCipherSuitesOrder(boolean honorOrder) { - this.preferLocalCipherSuites = honorOrder; - } - - /** - * Returns whether the local cipher suites preference should be honored. - * - * @return whether local cipher suites order in {@code #getCipherSuites} - * should be honored during SSL/TLS/DTLS handshaking. - * - * @see #setUseCipherSuitesOrder(boolean) - * - * @since 1.8 - */ - public final boolean getUseCipherSuitesOrder() { - return preferLocalCipherSuites; - } - - /** - * Sets whether DTLS handshake retransmissions should be enabled. - * - * This method only applies to DTLS. - * - * @param enableRetransmissions - * {@code true} indicates that DTLS handshake retransmissions - * should be enabled; {@code false} indicates that DTLS handshake - * retransmissions should be disabled - * - * @see #getEnableRetransmissions() - * - * @since 9 - */ - public void setEnableRetransmissions(boolean enableRetransmissions) { - this.enableRetransmissions = enableRetransmissions; - } - - /** - * Returns whether DTLS handshake retransmissions should be enabled. - * - * This method only applies to DTLS. - * - * @return true, if DTLS handshake retransmissions should be enabled - * - * @see #setEnableRetransmissions(boolean) - * - * @since 9 - */ - public boolean getEnableRetransmissions() { - return enableRetransmissions; - } - - /** - * Sets the maximum expected network packet size in bytes for - * SSL/TLS/DTLS records. - * - * @apiNote It is recommended that if possible, the maximum packet size - * should not be less than 256 bytes so that small handshake - * messages, such as HelloVerifyRequests, are not fragmented. - * - * @implNote If the maximum packet size is too small to hold a minimal - * record, an implementation may attempt to generate as minimal - * records as possible. However, this may cause a generated - * packet to be larger than the maximum packet size. - * - * @param maximumPacketSize - * the maximum expected network packet size in bytes, or - * {@code 0} to use the implicit size that is automatically - * specified by the underlying implementation. - * @throws IllegalArgumentException - * if {@code maximumPacketSize} is negative. - * - * @see #getMaximumPacketSize() - * - * @since 9 - */ - public void setMaximumPacketSize(int maximumPacketSize) { - if (maximumPacketSize < 0) { - throw new IllegalArgumentException( - "The maximum packet size cannot be negative"); - } - - this.maximumPacketSize = maximumPacketSize; - } - - /** - * Returns the maximum expected network packet size in bytes for - * SSL/TLS/DTLS records. - * - * @apiNote The implicit size may not be a fixed value, especially - * for a DTLS protocols implementation. - * - * @implNote For SSL/TLS/DTLS connections, the underlying provider - * should calculate and specify the implicit value of the - * maximum expected network packet size if it is not - * configured explicitly. For any connection populated - * object, this method should never return {@code 0} so - * that applications can retrieve the actual implicit size - * of the underlying implementation. - *

- * An implementation should attempt to comply with the maximum - * packet size configuration. However, if the maximum packet - * size is too small to hold a minimal record, an implementation - * may try to generate as minimal records as possible. This - * may cause a generated packet to be larger than the maximum - * packet size. - * - * @return the maximum expected network packet size, or {@code 0} if - * use the implicit size that is automatically specified by - * the underlying implementation and this object has not been - * populated by any connection. - * - * @see #setMaximumPacketSize(int) - * - * @since 9 - */ - public int getMaximumPacketSize() { - return maximumPacketSize; - } - - /** - * Returns a prioritized array of application-layer protocol names that - * can be negotiated over the SSL/TLS/DTLS protocols. - *

- * The array could be empty (zero-length), in which case protocol - * indications will not be used. - *

- * This method will return a new array each time it is invoked. - * - * @return a non-null, possibly zero-length array of application protocol - * {@code String}s. The array is ordered based on protocol - * preference, with the first entry being the most preferred. - * @see #setApplicationProtocols - * @since 9 - */ - public String[] getApplicationProtocols() { - return applicationProtocols.clone(); - } - - /** - * Sets the prioritized array of application-layer protocol names that - * can be negotiated over the SSL/TLS/DTLS protocols. - *

- * If application-layer protocols are supported by the underlying - * SSL/TLS implementation, this method configures which values can - * be negotiated by protocols such as RFC 7301 , the - * Application Layer Protocol Negotiation (ALPN). - *

- * If this end of the connection is expected to offer application protocol - * values, all protocols configured by this method will be sent to the - * peer. - *

- * If this end of the connection is expected to select the application - * protocol value, the {@code protocols} configured by this method are - * compared with those sent by the peer. The first matched value becomes - * the negotiated value. If none of the {@code protocols} were actually - * requested by the peer, the underlying protocol will determine what - * action to take. (For example, ALPN will send a - * {@code "no_application_protocol"} alert and terminate the connection.) - *

- * The {@code String} values must be presented using the network - * byte representation expected by the peer. For example, if an ALPN - * {@code String} should be exchanged using {@code UTF-8}, the - * {@code String} should be converted to its {@code byte[]} representation - * and stored as a byte-oriented {@code String} before calling this method. - * - *

-     *     // MEETEI MAYEK LETTERS HUK UN I (Unicode 0xabcd->0xabcf): 2 bytes
-     *     byte[] bytes = "\u005cuabcd\u005cuabce\u005cuabcf"
-     *             .getBytes(StandardCharsets.UTF_8);
-     *     String HUK_UN_I = new String(bytes, StandardCharsets.ISO_8859_1);
-     *
-     *     // 0x00-0xFF:  1 byte
-     *     String rfc7301Grease8A = "\u005cu008A\u005cu008A";
-     *
-     *     SSLParameters p = sslSocket.getSSLParameters();
-     *     p.setApplicationProtocols(new String[] {
-     *             "h2", "http/1.1", rfc7301Grease8A, HUK_UN_I});
-     *     sslSocket.setSSLParameters(p);
-     * 
- * - * @implSpec - * This method will make a copy of the {@code protocols} array. - * - * @param protocols an ordered array of application protocols, - * with {@code protocols[0]} being the most preferred. - * If the array is empty (zero-length), protocol - * indications will not be used. - * @throws IllegalArgumentException if protocols is null, or if - * any element in a non-empty array is null or an - * empty (zero-length) string - * @see #getApplicationProtocols - * @since 9 - */ - public void setApplicationProtocols(String[] protocols) { - if (protocols == null) { - throw new IllegalArgumentException("protocols was null"); - } - - String[] tempProtocols = protocols.clone(); - - for (String p : tempProtocols) { - if (p == null || p.isEmpty()) { - throw new IllegalArgumentException( - "An element of protocols was null/empty"); - } - } - applicationProtocols = tempProtocols; - } - - /** - * Something goes here later. - * @return something - */ - public Map> getCertificateDeflaters() { - return this.certDeflaters; - } - - /** - * Something goes here later. - * @param certDeflaters something - */ - public void setCertificateDeflaters( - Map> certDeflaters) { - // TODO - this.certDeflaters = certDeflaters; - } - - /** - * Something goes here later. - * @return something - */ - public Map> getCertificateInflaters() { - return this.certInflaters; - } - - /** - * Something goes here later. - * @param certInflaters something - */ - public void setCertificateInflaters( - Map> certInflaters) { - // TODO - this.certInflaters = certInflaters; - } -} diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index 613fa4501f3fe..b334ebb2779c7 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -272,7 +272,7 @@ public void consume(ConnectionContext context, throw tc.fatal(Alert.HANDSHAKE_FAILURE, "received handshake warning: " + alert.description); } else { - // Otherwise ignore the warning but remove the + // Otherwise, ignore the warning but remove the // Certificate and CertificateVerify handshake // consumer so the state machine doesn't expect it. tc.handshakeContext.handshakeConsumers.remove( diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index d7268f7de352e..23be04a6db622 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -1159,6 +1159,7 @@ public void consume(ConnectionContext context, HandshakeContext hc = (HandshakeContext)context; // clean up this consumer + hc.handshakeConsumers.remove(SSLHandshake.COMPRESSED_CERTIFICATE.id); hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id); T13CertificateMessage cm = new T13CertificateMessage(hc, message); if (hc.sslConfig.isClientMode) { diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index e13fcd6b9733d..8c3f5fd6e5438 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -935,6 +935,11 @@ public byte[] produce(ConnectionContext context, // update // shc.certRequestContext = crm.requestContext.clone(); + if (shc.certInflaters != null && !shc.certInflaters.isEmpty()) { + shc.handshakeConsumers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE.id, SSLHandshake.CERTIFICATE); shc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_VERIFY.id, diff --git a/src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java similarity index 70% rename from src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java rename to src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java index 5cfa3068c2346..6fd75c31156f1 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertCompressionExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -40,20 +40,16 @@ /** * Pack of the "compress_certificate" extensions [RFC 5246]. */ -final class CertCompressionExtension { +final class CompressCertExtension { static final HandshakeProducer chNetworkProducer = new CHCompressCertificateProducer(); static final ExtensionConsumer chOnLoadConsumer = new CHCompressCertificateConsumer(); - static final HandshakeConsumer chOnTradeConsumer = - new CHCompressCertificateUpdate(); static final HandshakeProducer crNetworkProducer = new CRCompressCertificateProducer(); static final ExtensionConsumer crOnLoadConsumer = new CRCompressCertificateConsumer(); - static final HandshakeConsumer crOnTradeConsumer = - new CRCompressCertificateUpdate(); static final SSLStringizer ccStringizer = new CompressCertificateStringizer(); @@ -62,7 +58,7 @@ final class CertCompressionExtension { * The "signature_algorithms" extension. */ static final class CertCompressionSpec implements SSLExtensionSpec { - final int[] compressionAlgorithms; + private final int[] compressionAlgorithms; // non-null CertCompressionSpec( Map> certInflaters) { @@ -109,7 +105,7 @@ public String toString() { MessageFormat messageFormat = new MessageFormat( "\"compression algorithms\": '['{0}']'", Locale.ENGLISH); - if (compressionAlgorithms == null || compressionAlgorithms.length == 0) { + if (compressionAlgorithms.length == 0) { Object[] messageFields = { "" }; @@ -170,39 +166,39 @@ public byte[] produce(ConnectionContext context, if (!chc.sslConfig.isAvailable( SSLExtension.CH_COMPRESS_CERTIFICATE)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unavailable compress_certificate extension"); + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); } return null; } // Produce the extension. - if (chc.localCertInflaters == null) { - chc.localCertInflaters = + if (chc.certInflaters == null) { + chc.certInflaters = CompressionAlgorithm.findInflaters(chc.sslConfig); } - if (chc.localCertInflaters.isEmpty()) { + if (chc.certInflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unsupported compress_certificate extension"); + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } return null; } int vectorLen = CompressionAlgorithm.sizeInRecord() * - chc.localCertInflaters.size(); - byte[] extData = new byte[vectorLen + 2]; + chc.certInflaters.size(); + byte[] extData = new byte[vectorLen + 1]; ByteBuffer m = ByteBuffer.wrap(extData); Record.putInt8(m, vectorLen); - for (Integer algId : chc.localCertInflaters.keySet()) { + for (Integer algId : chc.certInflaters.keySet()) { Record.putInt16(m, algId); } // Update the context. chc.handshakeExtensions.put( SSLExtension.CH_COMPRESS_CERTIFICATE, - new CertCompressionSpec(chc.localCertInflaters)); + new CertCompressionSpec(chc.certInflaters)); return extData; } @@ -229,16 +225,16 @@ public void consume(ConnectionContext context, if (!shc.sslConfig.isAvailable( SSLExtension.CH_COMPRESS_CERTIFICATE)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unavailable compress_certificate extension"); + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); } return; // ignore the extension } if (shc.sslConfig.certDeflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unsupported compress_certificate extension"); + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } return; // ignore the extension } @@ -246,59 +242,20 @@ public void consume(ConnectionContext context, // Parse the extension. CertCompressionSpec spec = new CertCompressionSpec(shc, buffer); + // Update the context. shc.certDeflater = CompressionAlgorithm.selectDeflater( shc.sslConfig, spec.compressionAlgorithms); if (shc.certDeflater == null) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore, no supported compress algorithms"); + SSLLogger.fine("Ignore, no supported " + + "certificate compression algorithms"); } - return; // ignore the extension } - // Update the context. - // shc.handshakeExtensions.put( - // SSLExtension.CH_COMPRESS_CERTIFICATE, spec); - // No impact on session resumption. } } - /** - * After session creation consuming of a "compress_certificate" - * extension in the ClientHello handshake message. - */ - private static final class CHCompressCertificateUpdate - implements HandshakeConsumer { - // Prevent instantiation of this class. - private CHCompressCertificateUpdate() { - // blank - } - - @Override - public void consume(ConnectionContext context, - HandshakeMessage message) throws IOException { - // The consuming happens in server side only. - ServerHandshakeContext shc = (ServerHandshakeContext)context; - - if (shc.certDeflater == null) { - // Ignore, no proper certificate compression algorithm. - return; - } - - if (!shc.isResumption && - shc.negotiatedProtocol.useTLS13PlusSpec()) { - // Remove Certificate producer. - shc.handshakeProducers.remove(SSLHandshake.CERTIFICATE.id); - - // Add the CompressedCertificate producer. - shc.handshakeProducers.put( - SSLHandshake.COMPRESSED_CERTIFICATE.id, - SSLHandshake.COMPRESSED_CERTIFICATE); - } - } - } - /** * Network data producer of a "compress_certificate" extension in * the CertificateRequest handshake message. @@ -320,39 +277,39 @@ public byte[] produce(ConnectionContext context, if (!shc.sslConfig.isAvailable( SSLExtension.CR_COMPRESS_CERTIFICATE)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unavailable compress_certificate extension"); + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); } return null; } // Produce the extension. - if (shc.localCertInflaters == null) { - shc.localCertInflaters = + if (shc.certInflaters == null) { + shc.certInflaters = CompressionAlgorithm.findInflaters(shc.sslConfig); } - if (shc.localCertInflaters.isEmpty()) { + if (shc.certInflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unsupported compress_certificate extension"); + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } return null; } int vectorLen = CompressionAlgorithm.sizeInRecord() * - shc.localCertInflaters.size(); - byte[] extData = new byte[vectorLen + 2]; + shc.certInflaters.size(); + byte[] extData = new byte[vectorLen + 1]; ByteBuffer m = ByteBuffer.wrap(extData); Record.putInt8(m, vectorLen); - for (Integer algId : shc.localCertInflaters.keySet()) { + for (Integer algId : shc.certInflaters.keySet()) { Record.putInt16(m, algId); } // Update the context. shc.handshakeExtensions.put( SSLExtension.CR_COMPRESS_CERTIFICATE, - new CertCompressionSpec(shc.localCertInflaters)); + new CertCompressionSpec(shc.certInflaters)); return extData; } @@ -378,16 +335,16 @@ public void consume(ConnectionContext context, if (!chc.sslConfig.isAvailable( SSLExtension.CR_COMPRESS_CERTIFICATE)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unavailable compress_certificate extension"); + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); } return; // ignore the extension } if (chc.sslConfig.certDeflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore unsupported compress_certificate extension"); + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } return; // ignore the extension } @@ -395,55 +352,17 @@ public void consume(ConnectionContext context, // Parse the extension. CertCompressionSpec spec = new CertCompressionSpec(chc, buffer); + // Update the context. chc.certDeflater = CompressionAlgorithm.selectDeflater( chc.sslConfig, spec.compressionAlgorithms); if (chc.certDeflater == null) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine( - "Ignore, no supported compress algorithms"); + SSLLogger.fine("Ignore, no supported " + + "certificate compression algorithms"); } - return; // ignore the extension } - // Update the context. - // chc.handshakeExtensions.put( - // SSLExtension.CR_COMPRESS_CERTIFICATE, spec); - // No impact on session resumption. } } - - /** - * After session creation consuming of a "compress_certificate" - * extension in the CertificateRequest handshake message. - */ - private static final class CRCompressCertificateUpdate - implements HandshakeConsumer { - // Prevent instantiation of this class. - private CRCompressCertificateUpdate() { - // blank - } - - @Override - public void consume(ConnectionContext context, - HandshakeMessage message) throws IOException { - // The consuming happens in client side only. - ClientHandshakeContext chc = (ClientHandshakeContext)context; - - if (chc.certDeflater == null) { - // Ignore, no proper certificate compression algorithm. - return; - } - - if (chc.negotiatedProtocol.useTLS13PlusSpec()) { - // Remove Certificate producer. - chc.handshakeProducers.remove(SSLHandshake.CERTIFICATE.id); - - // Add the CompressedCertificate producer. - chc.handshakeProducers.put( - SSLHandshake.COMPRESSED_CERTIFICATE.id, - SSLHandshake.COMPRESSED_CERTIFICATE); - } - } - } } 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 616546c49d824..e391c493345dc 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -81,8 +81,10 @@ static final class CompressedCertMessage extends HandshakeMessage { this.compressedCert = Record.getBytes24(m); if (m.hasRemaining()) { - throw handshakeContext.conContext.fatal(Alert.HANDSHAKE_FAILURE, - "Invalid CompressedCertificate message: unknown extra data"); + throw handshakeContext.conContext.fatal( + Alert.HANDSHAKE_FAILURE, + "Invalid CompressedCertificate message: " + + "unknown extra data"); } } @@ -109,18 +111,18 @@ public String toString() { """ "CompressedCertificate": '{' "algorithm": "{0}", - "uncompressed_length": [{1} - ] - "compressed_certificate_message": [{2} - ] + "uncompressed_length": {1} + "compressed_certificate_message": [ + {2} + ] '}'""", Locale.ENGLISH); HexDumpEncoder hexEncoder = new HexDumpEncoder(); Object[] messageFields = { - Utilities.toHexString(algorithmId), - Utilities.toHexString(uncompressedLength), - Utilities.indent(hexEncoder.encode(compressedCert)) + CompressionAlgorithm.nameOf(algorithmId), + uncompressedLength, + Utilities.indent(hexEncoder.encode(compressedCert), " ") }; return messageFormat.format(messageFields); @@ -150,9 +152,20 @@ public byte[] produce(ConnectionContext context, HandshakeOutStream hos = new HandshakeOutStream(null); message.send(hos); byte[] certMsg = hos.toByteArray(); + byte[] compressedCertMsg = + hc.certDeflater.getValue().apply(certMsg); + if (compressedCertMsg == null || compressedCertMsg.length == 0) { + throw hc.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No compressed Certificate data"); + } + CompressedCertMessage ccm = new CompressedCertMessage(hc, hc.certDeflater.getKey(), certMsg.length, - hc.certDeflater.getValue().apply(certMsg)); + compressedCertMsg); + + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Produced Compressed Certificate message", ccm); + } ccm.write(hc.handshakeOutput); hc.handshakeOutput.flush(); @@ -190,7 +203,7 @@ public void consume(ConnectionContext context, // check the compression algorithm Function inflater = - hc.localCertInflaters.get(ccm.algorithmId); + hc.certInflaters.get(ccm.algorithmId); if (inflater == null) { throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, "Unsupported certificate compression algorithm"); @@ -203,7 +216,7 @@ public void consume(ConnectionContext context, if (certificateMessage == null || certificateMessage.length != ccm.uncompressedLength) { throw hc.conContext.fatal(Alert.BAD_CERTIFICATE, - "Unsupported certificate compression algorithm"); + "Improper certificate compression"); } // Call the Certificate handshake message consumer. diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index f7013155a734d..52543c6f55842 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -46,17 +46,6 @@ enum CompressionAlgorithm { this.name = name; } - static CompressionAlgorithm valueOf(int id) { - for (CompressionAlgorithm cca : - CompressionAlgorithm.values()) { - if (cca.id == id) { - return cca; - } - } - - return null; - } - static CompressionAlgorithm nameOf(String name) { for (CompressionAlgorithm cca : CompressionAlgorithm.values()) { @@ -87,6 +76,15 @@ static int sizeInRecord() { // Get local supported algorithm collection. static Map> findInflaters( SSLConfiguration config) { + if (config.certInflaters == null || config.certInflaters.isEmpty()) { + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "No supported certificate compression algorithms"); + } + return Map.of(); + } + Map> inflaters = new LinkedHashMap<>(config.certInflaters.size()); for (Map.Entry> entry : diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 0faa503300e3d..6f4048ce96b8f 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -142,8 +142,8 @@ abstract class HandshakeContext implements ConnectionContext { List peerRequestedCertSignSchemes; // CertificateCompressionAlgorithm - Map> localCertInflaters; - Map.Entry> certDeflater; + Map> certInflaters; + Map.Entry> certDeflater; // Known authorities X500Principal[] peerSupportedAuthorities = null; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index a4c8b5789a6e0..808077e700cbf 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -271,6 +271,12 @@ void setSSLParameters(SSLParameters params) { this.certDeflaters = params.getCertificateDeflaters(); this.certInflaters = params.getCertificateInflaters(); + if (SSLLogger.isOn && + SSLLogger.isOn("ssl,handshake,verbose")) { + SSLLogger.finest( + "certInflaters: " + this.certInflaters); + } + this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); this.enableRetransmissions = params.getEnableRetransmissions(); this.maximumPacketSize = params.getMaximumPacketSize(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index 8f57cce835e54..e63ba37b2dcab 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -273,24 +273,24 @@ enum SSLExtension implements SSLStringizer { // Extensions defined in RFC 8879 (TLS Certificate Compression) CH_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", - SSLHandshake.CERTIFICATE_REQUEST, + SSLHandshake.CLIENT_HELLO, ProtocolVersion.PROTOCOLS_OF_13, - CertCompressionExtension.chNetworkProducer, - CertCompressionExtension.chOnLoadConsumer, + CompressCertExtension.chNetworkProducer, + CompressCertExtension.chOnLoadConsumer, + null, null, - CertCompressionExtension.chOnTradeConsumer, null, - CertCompressionExtension.ccStringizer), + CompressCertExtension.ccStringizer), CR_COMPRESS_CERTIFICATE (0x001B, "compress_certificate", SSLHandshake.CERTIFICATE_REQUEST, ProtocolVersion.PROTOCOLS_OF_13, - CertCompressionExtension.crNetworkProducer, - CertCompressionExtension.crOnLoadConsumer, + CompressCertExtension.crNetworkProducer, + CompressCertExtension.crOnLoadConsumer, + null, null, - CertCompressionExtension.crOnTradeConsumer, null, - CertCompressionExtension.ccStringizer), + CompressCertExtension.ccStringizer), // Extensions defined in RFC 5077 (TLS Session Resumption without Server-Side State) CH_SESSION_TICKET (0x0023, "session_ticket", diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 193a71cde861e..fe18ac7f125a6 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -1395,6 +1395,11 @@ public void consume(ConnectionContext context, chc.handshakeConsumers.put( SSLHandshake.CERTIFICATE_REQUEST.id, SSLHandshake.CERTIFICATE_REQUEST); + if (chc.certInflaters != null && !chc.certInflaters.isEmpty()) { + chc.handshakeConsumers.put( + SSLHandshake.COMPRESSED_CERTIFICATE.id, + SSLHandshake.COMPRESSED_CERTIFICATE); + } chc.handshakeConsumers.put( SSLHandshake.CERTIFICATE.id, SSLHandshake.CERTIFICATE); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index 09457b795b4b6..a0f9032ed52c8 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -542,6 +542,8 @@ public static SSLParameters copySSLParameters(SSLParameters p) { p1.setServerNames(p.getServerNames()); p1.setUseCipherSuitesOrder(p.getUseCipherSuitesOrder()); p1.setWantClientAuth(p.getWantClientAuth()); + p1.setCertificateDeflaters(p.getCertificateDeflaters()); + p1.setCertificateInflaters(p.getCertificateInflaters()); return p1; } diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java new file mode 100644 index 0000000000000..cb021b8bcfdf4 --- /dev/null +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. + */ + +/** + * @test + * @bug 8273042 + * @summary TLS certificate compression + * @library /javax/net/ssl/templates + * @run main/othervm HttpsCompressedCert + */ + +import javax.net.ssl.SSLParameters; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Map; +import java.util.function.Function; +import java.util.zip.Inflater; + +import static java.net.http.HttpResponse.BodyHandlers.ofString; + +public class HttpsCompressedCert { + private static final Function certInflater = (input) -> { + try { + Inflater inflater = new Inflater(); + inflater.setInput(input); + byte[] output = new byte[1024 * 8]; + int l = inflater.inflate(output); + inflater.end(); + + byte[] data = new byte[l]; + System.arraycopy(output, 0, data, 0, l); + + return data; + } catch (Exception ex) { + // just ignore + return null; + } + }; + + public static void main(String[] args) throws Exception { + SSLParameters sslParameters = new SSLParameters(); + sslParameters.setCertificateInflaters(Map.of("brotli", certInflater)); + HttpClient httpClient = HttpClient.newBuilder() + .sslContext(SSLClientContext.createClientSSLContext()) + .version(HttpClient.Version.HTTP_2) + .sslParameters(sslParameters) + .build(); + + HttpRequest httpRequest = HttpRequest.newBuilder( + new URI("https://www.google.com/")) + .GET() + .build(); + HttpResponse response = httpClient.send(httpRequest, ofString()); + } +} + diff --git a/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java new file mode 100644 index 0000000000000..fee0abbc6a1b6 --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @bug 8273042 + * @summary TLS Certificate Compression + * @library /javax/net/ssl/templates + * @run main/othervm CompressedCert + */ + +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; +import java.security.Security; +import java.util.Map; +import java.util.function.Function; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class CompressedCert extends SSLSocketTemplate { + private final Map> certDeflaters; + private final Map> certInflaters; + private final boolean requireClientCert; + private final boolean exceptionExpected; + + private static final Function certDeflater = (input) -> { + Deflater deflater = new Deflater(); + deflater.setInput(input); + deflater.finish(); + byte[] output = new byte[input.length]; + int l = deflater.deflate(output); + deflater.end(); + + byte[] data = new byte[l]; + System.arraycopy(output, 0, data, 0, l); + + return data; + }; + + private static final Function certInflater = (input) -> { + try { + Inflater inflater = new Inflater(); + inflater.setInput(input); + byte[] output = new byte[1024 * 8]; + int l = inflater.inflate(output); + inflater.end(); + + byte[] data = new byte[l]; + System.arraycopy(output, 0, data, 0, l); + + return data; + } catch (Exception ex) { + // just ignore + return null; + } + }; + + private static final Function invalidDeflater = (input) -> { + return input; + }; + + private static final Function invalidInflater = (input) -> { + return input; + }; + + public CompressedCert( + Map> certDeflaters, + Map> certInflaters, + boolean requireClientCert, + boolean exceptionExpected) { + this.certDeflaters = certDeflaters; + this.certInflaters = certInflaters; + this.requireClientCert = requireClientCert; + this.exceptionExpected = exceptionExpected; + } + + @Override + protected void configureServerSocket(SSLServerSocket sslServerSocket) { + SSLParameters sslParameters = sslServerSocket.getSSLParameters(); + sslParameters.setCertificateDeflaters(certDeflaters); + sslParameters.setCertificateInflaters(certInflaters); + sslParameters.setNeedClientAuth(requireClientCert); + sslServerSocket.setSSLParameters(sslParameters); + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + SSLParameters sslParameters = socket.getSSLParameters(); + sslParameters.setCertificateDeflaters(certDeflaters); + sslParameters.setCertificateInflaters(certInflaters); + socket.setSSLParameters(sslParameters); + } + + @Override + protected void runServerApplication(SSLSocket socket) { + try { + super.runServerApplication(socket); + } catch (Exception ex) { + // Just ignore, let the client handle the failure information. + } + } + + @Override + protected void runClientApplication(SSLSocket sslSocket) throws Exception { + try { + super.runClientApplication(sslSocket); + if (exceptionExpected) { + throw new RuntimeException("Unexpected success!"); + } + } catch (Exception ex) { + if (!exceptionExpected) { + throw ex; + } + } + } + + public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + runTest(Map.of("zlib", certDeflater), + Map.of("zlib", certInflater), + false, + false); + runTest(Map.of("zlib", certDeflater), + Map.of("zlib", certInflater), + true, + false); + + runTest(Map.of("zlib", certDeflater), + Map.of(), + false, + false); + runTest(Map.of("zlib", certDeflater), + Map.of(), + true, + false); + + runTest(Map.of("zlib", certDeflater), + null, + false, + false); + runTest(Map.of("zlib", certDeflater), + null, + true, + false); + + runTest(Map.of(), + Map.of("zlib", certInflater), + false, + false); + runTest(Map.of(), + Map.of("zlib", certInflater), + true, + false); + + runTest(Map.of(), + Map.of(), + false, + false); + runTest(Map.of(), + Map.of(), + true, + false); + + runTest(Map.of(), + null, + false, + false); + runTest(Map.of(), + null, + true, + false); + + runTest(null, + Map.of("zlib", certInflater), + false, + false); + runTest(null, + Map.of("zlib", certInflater), + true, + false); + + runTest(null, + Map.of(), + false, + false); + runTest(null, + Map.of(), + true, + false); + + runTest(null, + null, + false, + false); + runTest(null, + null, + true, + false); + + runTest(Map.of("zlib", certDeflater), + Map.of("brotli", certInflater), + false, + false); + runTest(Map.of("zlib", certDeflater), + Map.of("brotli", certInflater), + true, + false); + + runTest(Map.of("brotli", certDeflater), + Map.of("zlib", certInflater), + false, + false); + runTest(Map.of("brotli", certDeflater), + Map.of("zlib", certInflater), + true, + false); + + runTest(Map.of("zlib", certDeflater), + Map.of("zlib", invalidInflater), + false, + true); + runTest(Map.of("zlib", certDeflater), + Map.of("zlib", invalidInflater), + true, + true); + + runTest(Map.of("zlib", invalidDeflater), + Map.of("zlib", certInflater), + false, + true); + runTest(Map.of("zlib", invalidDeflater), + Map.of("zlib", certInflater), + true, + true); + } + + private static void runTest( + Map> certDeflaters, + Map> certInflaters, + boolean requireClientCert, + boolean exceptionExpected) throws Exception { + new CompressedCert(certDeflaters, certInflaters, + requireClientCert, exceptionExpected).run(); + } +} diff --git a/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java b/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java new file mode 100644 index 0000000000000..c6c67d04abb8b --- /dev/null +++ b/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. + */ + +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. + +/* + * @test + * @bug 8273042 + * @summary TLS Certificate Compression + * @library /javax/net/ssl/templates + * @run main/othervm UnsetCompressedCert + */ + +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; +import java.security.Security; +import java.util.Map; +import java.util.function.Function; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class UnsetCompressedCert extends SSLSocketTemplate { + private final Map> certDeflaters; + private final Map> certInflaters; + private final boolean requireClientCert; + + private static final Function certDeflater = (input) -> { + Deflater deflater = new Deflater(); + deflater.setInput(input); + deflater.finish(); + byte[] output = new byte[input.length]; + int l = deflater.deflate(output); + deflater.end(); + + byte[] data = new byte[l]; + System.arraycopy(output, 0, data, 0, l); + + return data; + }; + + private static final Function certInflater = (input) -> { + try { + Inflater inflater = new Inflater(); + inflater.setInput(input); + byte[] output = new byte[1024 * 8]; + int l = inflater.inflate(output); + inflater.end(); + + byte[] data = new byte[l]; + System.arraycopy(output, 0, data, 0, l); + + return data; + } catch (Exception ex) { + // just ignore + return null; + } + }; + + public UnsetCompressedCert( + Map> certDeflaters, + Map> certInflaters, + boolean requireClientCert) { + this.certDeflaters = certDeflaters; + this.certInflaters = certInflaters; + this.requireClientCert = requireClientCert; + } + + @Override + protected void configureServerSocket(SSLServerSocket sslServerSocket) { + SSLParameters sslParameters = sslServerSocket.getSSLParameters(); + if (certDeflaters != null) { + sslParameters.setCertificateDeflaters(certDeflaters); + } + if (certInflaters != null) { + sslParameters.setCertificateInflaters(certInflaters); + } + sslParameters.setNeedClientAuth(requireClientCert); + sslServerSocket.setSSLParameters(sslParameters); + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + SSLParameters sslParameters = socket.getSSLParameters(); + if (certDeflaters != null) { + sslParameters.setCertificateDeflaters(certDeflaters); + } + + if (certInflaters != null) { + sslParameters.setCertificateInflaters(certInflaters); + } + socket.setSSLParameters(sslParameters); + } + + public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + runTest(Map.of("zlib", certDeflater), + null, + false); + runTest(Map.of("zlib", certDeflater), + null, + true); + + runTest(Map.of(), + null, + false); + runTest(Map.of(), + null, + true); + + runTest(null, + Map.of("zlib", certInflater), + false); + runTest(null, + Map.of("zlib", certInflater), + true); + + runTest(null, + Map.of(), + false); + runTest(null, + Map.of(), + true); + + runTest(null, + null, + false); + runTest(null, + null, + true); + } + + private static void runTest( + Map> certDeflaters, + Map> certInflaters, + boolean requireClientCert) throws Exception { + new UnsetCompressedCert( + certDeflaters, certInflaters, requireClientCert).run(); + } +} diff --git a/test/jdk/javax/net/ssl/templates/SSLClientContext.java b/test/jdk/javax/net/ssl/templates/SSLClientContext.java new file mode 100644 index 0000000000000..46fcbaedcfd17 --- /dev/null +++ b/test/jdk/javax/net/ssl/templates/SSLClientContext.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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 javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; + +public enum SSLClientContext { + GTS_ROOT_R1(""" + -----BEGIN CERTIFICATE----- + MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH + MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM + QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy + MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl + cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB + AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM + f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX + mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 + zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P + fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc + vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 + Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp + zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO + Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW + k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ + DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF + lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV + HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW + Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 + d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z + XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR + gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 + d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv + J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg + DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM + +SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy + F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 + SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws + E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl + -----END CERTIFICATE-----"""), + + GTS_ROOT_R2(""" + -----BEGIN CERTIFICATE----- + MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH + MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM + QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy + MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl + cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB + AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv + CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg + GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu + XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd + re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu + PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 + mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K + 8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj + x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR + nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 + kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok + twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV + HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp + 8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT + vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT + z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA + pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb + pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB + R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R + RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk + 0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC + 5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF + izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn + yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC + -----END CERTIFICATE-----"""), + + GTS_ROOT_R3(""" + -----BEGIN CERTIFICATE----- + MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw + CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU + MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw + MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp + Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA + IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout + 736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A + DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud + DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk + fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA + njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd + -----END CERTIFICATE-----"""), + + GTS_ROOT_R4(""" + -----BEGIN CERTIFICATE----- + MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw + CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU + MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw + MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp + Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA + IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu + hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l + xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud + DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 + CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx + sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== + -----END CERTIFICATE-----"""); + + final String certStr; + + SSLClientContext(String certStr) { + this.certStr = certStr; + } + + public static SSLContext createClientSSLContext() throws Exception { + // Generate certificate from cert string. + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // Import the trusted certs. + KeyStore ts = KeyStore.getInstance("PKCS12"); + ts.load(null, null); + for (SSLClientContext clientContext : SSLClientContext.values()) { + try (ByteArrayInputStream is = new ByteArrayInputStream( + clientContext.certStr.getBytes())) { + ts.setCertificateEntry("trusted-cert-" + + clientContext.name(), cf.generateCertificate(is)); + } + } + + // Create an SSLContext object. + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(ts); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, tmf.getTrustManagers(), null); + + return context; + } +} From 3b507c4f72688b34d6167ebc789f983647c23276 Mon Sep 17 00:00:00 2001 From: Xuelei Fan Date: Tue, 15 Feb 2022 15:13:09 -0800 Subject: [PATCH 04/12] Describe public APIs --- .../classes/javax/net/ssl/SSLParameters.java | 300 +++++++++++++++++- .../security/ssl/CompressCertExtension.java | 6 +- .../security/ssl/CompressionAlgorithm.java | 4 + .../sun/security/ssl/SSLConfiguration.java | 14 +- 4 files changed, 300 insertions(+), 24 deletions(-) diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index d501e79237f84..cf116ac5e23b0 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -691,38 +691,314 @@ public void setApplicationProtocols(String[] protocols) { } /** - * Something goes here later. - * @return something + * Returns a prioritized map of certificate compression algorithm names + * and functions that can be used over the SSL/TLS/DTLS protocols. + *

+ * Note that the standard list of certificate compression algorithm + * names are defined in the + * Certificate Compression section of the Java Security Standard + * Algorithm Names Specification. Providers may support certificate + * compression algorithms not defined in this list or may not use the + * recommended name for a certain certificate compression algorithm. + *

+ * The set of certificate compression algorithm names and functions + * that will be used over the SSL/TLS/DTLS connections is determined by + * the returned map of this method and the underlying provider-specific + * default certificate compression algorithm names and functions. + *

+ * If the returned map is {@code null}, then the underlying + * provider-specific default certificate compression algorithm names + * and functions will be used over the SSL/TLS/DTLS connections. + *

+ * If the returned map is empty (zero-length), then the certificate + * compression mechanism is turned off for SSL/TLS/DTLS protocols. + *

+ * If the returned map is not {@code null} or empty (zero-length), then + * the certificate compression mechanism is turned on for SSL/TLS/DTLS + * protocols. The certificate compression algorithm names and + * functions in the returned map will be used over the connections. + *

+ * If the {@link #setCertificateDeflaters} method has not been called, + * this method should return the default certificate compression + * algorithm names and functions for connection populated objects, or + * {@code null} for pre-populated objects. + * + * @apiNote + * Note that a provider may not have been updated to support this method + * and in that case may return {@code null} instead of the default + * certificate compression algorithm names and functions for connection + * populated objects. + * + * @implNote + * The SunJSSE provider supports this method, but does not define the + * default certificate compression algorithm names and functions. The + * certificate compression mechanism is turned off by default for the + * SunJSSE provider. Applications could enable the mechanism in the + * SunJSSE provider by calling {@link #setCertificateDeflaters} in + * the compression side and {@link #setCertificateInflaters} in the + * decompression side. + * + * @return an immutable map of certificate compression algorithm names + * {@code String}s and {@link Function}s, or {@code null} if + * none have been set. Each map entry in the map is non-null, and + * represents the certificate compression algorithm. The key of + * the map entry is a non-null {@code String} object which + * represents the certificate compression algorithm name. The + * value of the map entry is a non-null {@link Function} object + * which will be applied to the Certificate message byte array, + * and produce Compressed Certificate message byte array. The + * map entries are ordered based on certificate compression + * algorithm preference, with the first entry being the most + * preferred. Providers should ignore unknown certificate + * compression algorithm names while establishing the + * SSL/TLS/DTLS connections. + * + * @see #setCertificateDeflaters + * + * @since 19 */ public Map> getCertificateDeflaters() { return this.certDeflaters; } /** - * Something goes here later. - * @param certDeflaters something + * Sets a prioritized map of certificate compression algorithm names and + * functions that can be used over the SSL/TLS/DTLS protocols. + *

+ * Note that the standard list of certificate compression algorithm + * names are defined in the + * Certificate Compression section of the Java Security Standard + * Algorithm Names Specification. Providers may support certificate + * compression algorithms not defined in this list or may not use the + * recommended name for a certain certificate compression algorithm. + *

+ * The set of certificate compression algorithm names and functions + * that will be used over the SSL/TLS/DTLS connections is determined by + * the returned map of this method and the underlying provider-specific + * default certificate compression algorithm names and functions. See + * {@link #getCertificateDeflaters} for specific details on how the + * parameters are used in SSL/TLS/DTLS connections. + * + * @apiNote + * Note that a provider may not have been updated to support this method + * and in that case may ignore the certificate compression algorithm + * names and functions that are set. + * + * @implNote + * The SunJSSE provider supports this method. + * + * @param certDeflaters an ordered map of certificate compression + * algorithm names {@code String}s and {@link Function}s with the + * first entry being the most preferred, or {@code null}. Each + * entry in the map is non-null, and represents the certificate + * compression algorithm. The key of the map entry is a non-null + * {@code String} object which represents the certificate + * compression algorithm name. The value of the map entry is a + * non-null {@link Function} object which will be applied to the + * Certificate message byte array, and produce Compressed + * Certificate message byte array. This method will make a copy of + * this map if necessary to protect against subsequent modification. + * Providers should ignore unknown certificate compression + * algorithm names and the relevant functions while establishing + * the SSL/TLS/DTLS connections. + * @throws IllegalArgumentException if any entry, key, or value in the + * {@code certDeflaters} map is {@code null}, or any key is + * {@linkplain String#isBlank() blank}. + * + * @see #getCertificateDeflaters + * + * @since 19 */ public void setCertificateDeflaters( Map> certDeflaters) { - // TODO - this.certDeflaters = Map.of(); + if (this.certDeflaters == certDeflaters) { + return; + } + + if (certDeflaters == null) { + this.certDeflaters = null; + } else if (certDeflaters.isEmpty()) { + this.certDeflaters = Map.of(); + } else { + @SuppressWarnings({"unchecked", "rawtypes"}) + Map.Entry>[] entries = + new Map.Entry[certDeflaters.size()]; + int i = 0; + for (Map.Entry> entry : + certDeflaters.entrySet()) { + if (entry == null) { + throw new IllegalArgumentException( + "Null entry is not allowed"); + } else if (entry.getKey() == null || entry.getKey().isBlank()) { + throw new IllegalArgumentException( + "Null or blank compression algorithm " + + "name is not allowed"); + } else if (entry.getValue() == null) { + throw new IllegalArgumentException( + "Null compression function is not allowed"); + } + entries[i++] = entry; + } + + this.certDeflaters = Map.ofEntries(entries); + } } /** - * Something goes here later. - * @return something + * Returns a prioritized map of certificate decompression algorithm + * names and functions that can be used over the SSL/TLS/DTLS protocols. + *

+ * Note that the standard list of certificate decompression algorithm + * names are defined in the + * Certificate Compression section of the Java Security Standard + * Algorithm Names Specification. Providers may support certificate + * decompression algorithms not defined in this list or may not use the + * recommended name for a certain certificate decompression algorithm. + *

+ * The set of certificate decompression algorithm names and functions + * that will be used over the SSL/TLS/DTLS connections is determined by + * the returned map of this method and the underlying provider-specific + * default certificate decompression algorithm names and functions. + *

+ * If the returned map is {@code null}, then the underlying + * provider-specific default certificate decompression algorithm names + * and functions will be used over the connections. + *

+ * If the returned map is empty (zero-length), then the certificate + * compression mechanism is turned off for SSL/TLS/DTLS protocols. + *

+ * If the returned map is not {@code null} or empty (zero-length), then + * the certificate compression mechanism is turned on for SSL/TLS/DTLS + * protocols. The certificate decompression algorithm names and + * functions in the returned map will be used over the connections. + *

+ * If the {@link #setCertificateInflaters} method has not been called, + * this method should return the default certificate decompression + * algorithm names and functions for connection populated objects, or + * {@code null} for pre-populated objects. + * + * @apiNote + * Note that a provider may not have been updated to support this method + * and in that case may return {@code null} instead of the default + * certificate decompression algorithm names and functions for connection + * populated objects. + * + * @implNote + * The SunJSSE provider supports this method, but does not define the + * default certificate decompression algorithm names and functions. The + * certificate compression mechanism is turned off by default for the + * SunJSSE provider. Applications could enable the mechanism in the + * SunJSSE provider by calling {@link #setCertificateDeflaters} in + * the compression side and {@link #setCertificateInflaters} in the + * decompression side. + * + * @return an immutable map of certificate decompression algorithm names + * {@code String}s and {@link Function}s, or {@code null} if + * none have been set. Each map entry in the map is non-null, and + * represents the certificate decompression algorithm. The key of + * the map entry is a non-null {@code String} object which + * represents the certificate decompression algorithm name. The + * value of the map entry is a non-null {@link Function} object + * which will be applied to the Compressed Certificate message + * byte array, and produce Certificate message byte array. The + * map entries are ordered based on certificate decompression + * algorithm preference, with the first entry being the most + * preferred. Providers should ignore unknown certificate + * decompression algorithm names while establishing the + * SSL/TLS/DTLS connections. + * + * @see #setCertificateDeflaters + * + * @since 19 */ public Map> getCertificateInflaters() { return this.certInflaters; } /** - * Something goes here later. - * @param certInflaters something + * Sets a prioritized map of certificate decompression algorithm names + * and functions that can be used over the SSL/TLS/DTLS protocols. + *

+ * Note that the standard list of certificate decompression algorithm + * names are defined in the + * Certificate Compression section of the Java Security Standard + * Algorithm Names Specification. Providers may support certificate + * decompression algorithms not defined in this list or may not use the + * recommended name for a certain certificate decompression algorithm. + *

+ * The set of certificate decompression algorithm names and functions + * that will be used over the SSL/TLS/DTLS connections is determined by + * the returned map of this method and the underlying provider-specific + * default certificate decompression algorithm names and functions. See + * {@link #getCertificateInflaters} for specific details on how the + * parameters are used in SSL/TLS/DTLS connections. + * + * @apiNote + * Note that a provider may not have been updated to support this method + * and in that case may ignore the certificate decompression algorithm + * names and functions that are set. + * + * @implNote + * The SunJSSE provider supports this method. + * + * @param certInflaters an ordered map of certificate decompression + * algorithm names {@code String}s and {@link Function}s with the + * first entry being the most preferred, or {@code null}. Each + * entry in the map is non-null, and represents the certificate + * decompression algorithm. The key of the map entry is a non-null + * {@code String} object which represents the certificate + * decompression algorithm name. The value of the map entry is a + * non-null {@link Function} object which will be applied to the + * Compressed Certificate message byte array, and produce + * Certificate message byte array. This method will make a copy of + * this map if necessary to protect against subsequent modification. + * Providers should ignore unknown certificate decompression + * algorithm names and the relevant functions while establishing + * the SSL/TLS/DTLS connections. + * @throws IllegalArgumentException if any entry, key, or value in the + * {@code certInflaters} map is {@code null}, or any key is + * {@linkplain String#isBlank() blank}. + * + * @see #getCertificateInflaters + * + * @since 19 */ public void setCertificateInflaters( Map> certInflaters) { - // TODO - this.certInflaters = certInflaters; + if (this.certInflaters == certInflaters) { + return; + } + + if (certInflaters == null) { + this.certInflaters = null; + } else if (certInflaters.isEmpty()) { + this.certInflaters = Map.of(); + } else { + @SuppressWarnings({"unchecked", "rawtypes"}) + Map.Entry>[] entries = + new Map.Entry[certInflaters.size()]; + int i = 0; + for (Map.Entry> entry : + certInflaters.entrySet()) { + if (entry == null) { + throw new IllegalArgumentException( + "Null entry is not allowed"); + } else if (entry.getKey() == null || entry.getKey().isBlank()) { + throw new IllegalArgumentException( + "Null or blank decompression algorithm " + + "name is not allowed"); + } else if (entry.getValue() == null) { + throw new IllegalArgumentException( + "Null decompression function is not allowed"); + } + entries[i++] = entry; + } + + this.certInflaters = Map.ofEntries(entries); + } } } diff --git a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java index 6fd75c31156f1..1a23107264bc4 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -231,7 +231,8 @@ public void consume(ConnectionContext context, return; // ignore the extension } - if (shc.sslConfig.certDeflaters.isEmpty()) { + if (shc.sslConfig.certDeflaters == null || + shc.sslConfig.certDeflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unsupported " + "compress_certificate extension"); @@ -341,7 +342,8 @@ public void consume(ConnectionContext context, return; // ignore the extension } - if (chc.sslConfig.certDeflaters.isEmpty()) { + if (chc.sslConfig.certDeflaters == null || + chc.sslConfig.certDeflaters.isEmpty()) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unsupported " + "compress_certificate extension"); diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index 52543c6f55842..3b561ebf2d22d 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -110,6 +110,10 @@ static Map> findInflaters( static Map.Entry> selectDeflater( SSLConfiguration config, int[] compressionAlgorithmIds) { + if (config.certDeflaters == null) { + return null; + } + for (Map.Entry> entry : config.certDeflaters.entrySet()) { CompressionAlgorithm ca = diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 808077e700cbf..bd299a400b1fc 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -67,8 +67,8 @@ final class SSLConfiguration implements Cloneable { // The configured certificate compression algorithms for // "compress_certificate" extensions - Map> certDeflaters; // non-null - Map> certInflaters; // non-null + Map> certDeflaters; + Map> certInflaters; // the maximum protocol version of enabled protocols ProtocolVersion maximumProtocolVersion; @@ -153,8 +153,8 @@ final class SSLConfiguration implements Cloneable { CustomizedClientSignatureSchemes.signatureSchemes : CustomizedServerSignatureSchemes.signatureSchemes; - this.certDeflaters = Collections.emptyMap(); - this.certInflaters = Collections.emptyMap(); + this.certDeflaters = Map.of(); + this.certInflaters = Map.of(); this.maximumProtocolVersion = ProtocolVersion.NONE; for (ProtocolVersion pv : enabledProtocols) { @@ -271,12 +271,6 @@ void setSSLParameters(SSLParameters params) { this.certDeflaters = params.getCertificateDeflaters(); this.certInflaters = params.getCertificateInflaters(); - if (SSLLogger.isOn && - SSLLogger.isOn("ssl,handshake,verbose")) { - SSLLogger.finest( - "certInflaters: " + this.certInflaters); - } - this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); this.enableRetransmissions = params.getEnableRetransmissions(); this.maximumPacketSize = params.getMaximumPacketSize(); From f30e3c549a11fae554bd29325d135c311be259c9 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Wed, 4 Jun 2025 17:16:22 -0400 Subject: [PATCH 05/12] Fixing a few problems after merging with master --- .../share/classes/sun/security/ssl/HandshakeContext.java | 7 ++++--- .../share/classes/sun/security/ssl/SSLConfiguration.java | 4 +++- .../share/classes/sun/security/ssl/SSLHandshake.java | 6 +++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 05ce284f1f83a..721c105f27840 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -32,13 +32,14 @@ import java.security.AlgorithmConstraints; import java.security.CryptoPrimitive; import java.util.*; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.function.Function; import javax.crypto.SecretKey; import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLHandshakeException; import javax.security.auth.x500.X500Principal; import sun.security.ssl.NamedGroup.NamedGroupSpec; import static sun.security.ssl.NamedGroup.NamedGroupSpec.*; -import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; abstract class HandshakeContext implements ConnectionContext { // System properties @@ -72,7 +73,7 @@ abstract class HandshakeContext implements ConnectionContext { // consolidated parameters final List activeProtocols; final List activeCipherSuites; - final AlgorithmConstraints algorithmConstraints; + final SSLAlgorithmConstraints algorithmConstraints; final ProtocolVersion maximumActiveProtocol; // output stream diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 1c4011cf7cc08..168937f246fe7 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -37,6 +37,8 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; +import sun.security.ssl.SSLExtension.ClientExtensions; +import sun.security.ssl.SSLExtension.ServerExtensions; /** * SSL/(D)TLS configuration. diff --git a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java index 05a5ff545b52b..7fe31e5c3c46a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, 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 @@ -369,6 +369,7 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { ) }), + // RFC 8879 - TLS Certificate Compression @SuppressWarnings({"unchecked", "rawtypes"}) COMPRESSED_CERTIFICATE ((byte)0x19, "compressed_certificate", (new Map.Entry[] { @@ -386,6 +387,9 @@ enum SSLHandshake implements SSLConsumer, HandshakeProducer { ) })), + // RFC 8870 - Encrypted Key Transport for DTLS/Secure RTP + EKT_KEY ((byte)0x1A, "ekt_key"), + MESSAGE_HASH ((byte)0xFE, "message_hash"), NOT_APPLICABLE ((byte)0xFF, "not_applicable"); From 0a3ba7474aaf194f9394c116779386887c5d7b8c Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Mon, 9 Jun 2025 17:35:26 -0400 Subject: [PATCH 06/12] Use supported ZLIB compression --- .../ssl/HttpsURLConnection/HttpsCompressedCert.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java index cb021b8bcfdf4..a132b26dc6188 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java @@ -25,11 +25,13 @@ * @test * @bug 8273042 * @summary TLS certificate compression - * @library /javax/net/ssl/templates + * @library /test/lib * @run main/othervm HttpsCompressedCert */ -import javax.net.ssl.SSLParameters; +import static java.net.http.HttpResponse.BodyHandlers.ofString; +import static jdk.test.lib.Asserts.assertEquals; + import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -37,8 +39,7 @@ import java.util.Map; import java.util.function.Function; import java.util.zip.Inflater; - -import static java.net.http.HttpResponse.BodyHandlers.ofString; +import javax.net.ssl.SSLParameters; public class HttpsCompressedCert { private static final Function certInflater = (input) -> { @@ -61,7 +62,7 @@ public class HttpsCompressedCert { public static void main(String[] args) throws Exception { SSLParameters sslParameters = new SSLParameters(); - sslParameters.setCertificateInflaters(Map.of("brotli", certInflater)); + sslParameters.setCertificateInflaters(Map.of("zlib", certInflater)); HttpClient httpClient = HttpClient.newBuilder() .sslContext(SSLClientContext.createClientSSLContext()) .version(HttpClient.Version.HTTP_2) @@ -73,6 +74,7 @@ public static void main(String[] args) throws Exception { .GET() .build(); HttpResponse response = httpClient.send(httpRequest, ofString()); + assertEquals(response.statusCode(), 200); } } From 9adde3b3b0ed51a89e3434b9189de553de5e5a3f Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 20 Nov 2025 17:22:15 -0500 Subject: [PATCH 07/12] Fix HttpsCompressedCert test. Fix a merge error --- .../share/classes/sun/security/ssl/SSLConfiguration.java | 2 -- .../javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 77a8946f43d85..50b5d7c1b4c68 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -254,8 +254,6 @@ final class SSLConfiguration implements Cloneable { CustomizedServerSignatureSchemes.signatureSchemes != null ? CustomizedServerSignatureSchemes.signatureSchemes : SupportedSigSchemes.DEFAULT; - CustomizedClientSignatureSchemes.signatureSchemes : - CustomizedServerSignatureSchemes.signatureSchemes; this.certDeflaters = Map.of(); this.certInflaters = Map.of(); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java index a132b26dc6188..13e1c0bfb67d0 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java @@ -26,6 +26,7 @@ * @bug 8273042 * @summary TLS certificate compression * @library /test/lib + * /javax/net/ssl/templates * @run main/othervm HttpsCompressedCert */ From cd5cd114a01d24e61112ca97f2ce1888afc3d02c Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Mon, 24 Nov 2025 15:36:59 -0500 Subject: [PATCH 08/12] Support ZLIB compression only --- .../classes/java/util/function/Function.java | 1 + .../classes/javax/net/ssl/SSLParameters.java | 317 ++---------------- .../security/ssl/CompressCertExtension.java | 279 +++++++-------- .../security/ssl/CompressedCertificate.java | 1 + .../security/ssl/CompressionAlgorithm.java | 67 +++- .../sun/security/ssl/SSLConfiguration.java | 20 +- .../classes/sun/security/ssl/SSLLogger.java | 5 +- .../jdk/internal/net/http/common/Utils.java | 3 +- .../HttpsCompressedCert.java | 45 +-- .../net/ssl/SSLParameters/CompressedCert.java | 239 ++----------- .../SSLParameters/UnsetCompressedCert.java | 162 --------- .../net/ssl/templates/SSLClientContext.java | 1 + 12 files changed, 255 insertions(+), 885 deletions(-) delete mode 100644 test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java diff --git a/src/java.base/share/classes/java/util/function/Function.java b/src/java.base/share/classes/java/util/function/Function.java index 7bcbfd06bb6a3..649acc848b170 100644 --- a/src/java.base/share/classes/java/util/function/Function.java +++ b/src/java.base/share/classes/java/util/function/Function.java @@ -24,6 +24,7 @@ */ package java.util.function; +import java.io.IOException; import java.util.Objects; /** diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index cfcf01e7004a2..ae8872c89e584 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -87,8 +87,7 @@ public class SSLParameters { private String[] applicationProtocols = new String[0]; private String[] signatureSchemes = null; private String[] namedGroups = null; - private Map> certDeflaters = null; - private Map> certInflaters = null; + private boolean enableCertificateCompression = true; /** * Constructs SSLParameters. @@ -965,314 +964,36 @@ public void setNamedGroups(String[] namedGroups) { } /** - * Returns a prioritized map of certificate compression algorithm names - * and functions that can be used over the SSL/TLS/DTLS protocols. + * Sets whether TLS certificate compression should be enabled. *

- * Note that the standard list of certificate compression algorithm - * names are defined in the - * Certificate Compression section of the Java Security Standard - * Algorithm Names Specification. Providers may support certificate - * compression algorithms not defined in this list or may not use the - * recommended name for a certain certificate compression algorithm. - *

- * The set of certificate compression algorithm names and functions - * that will be used over the SSL/TLS/DTLS connections is determined by - * the returned map of this method and the underlying provider-specific - * default certificate compression algorithm names and functions. - *

- * If the returned map is {@code null}, then the underlying - * provider-specific default certificate compression algorithm names - * and functions will be used over the SSL/TLS/DTLS connections. - *

- * If the returned map is empty (zero-length), then the certificate - * compression mechanism is turned off for SSL/TLS/DTLS protocols. - *

- * If the returned map is not {@code null} or empty (zero-length), then - * the certificate compression mechanism is turned on for SSL/TLS/DTLS - * protocols. The certificate compression algorithm names and - * functions in the returned map will be used over the connections. - *

- * If the {@link #setCertificateDeflaters} method has not been called, - * this method should return the default certificate compression - * algorithm names and functions for connection populated objects, or - * {@code null} for pre-populated objects. - * - * @apiNote - * Note that a provider may not have been updated to support this method - * and in that case may return {@code null} instead of the default - * certificate compression algorithm names and functions for connection - * populated objects. - * - * @implNote - * The SunJSSE provider supports this method, but does not define the - * default certificate compression algorithm names and functions. The - * certificate compression mechanism is turned off by default for the - * SunJSSE provider. Applications could enable the mechanism in the - * SunJSSE provider by calling {@link #setCertificateDeflaters} in - * the compression side and {@link #setCertificateInflaters} in the - * decompression side. - * - * @return an immutable map of certificate compression algorithm names - * {@code String}s and {@link Function}s, or {@code null} if - * none have been set. Each map entry in the map is non-null, and - * represents the certificate compression algorithm. The key of - * the map entry is a non-null {@code String} object which - * represents the certificate compression algorithm name. The - * value of the map entry is a non-null {@link Function} object - * which will be applied to the Certificate message byte array, - * and produce Compressed Certificate message byte array. The - * map entries are ordered based on certificate compression - * algorithm preference, with the first entry being the most - * preferred. Providers should ignore unknown certificate - * compression algorithm names while establishing the - * SSL/TLS/DTLS connections. - * - * @see #setCertificateDeflaters - * - * @since 19 - */ - public Map> getCertificateDeflaters() { - return this.certDeflaters; - } - - /** - * Sets a prioritized map of certificate compression algorithm names and - * functions that can be used over the SSL/TLS/DTLS protocols. - *

- * Note that the standard list of certificate compression algorithm - * names are defined in the - * Certificate Compression section of the Java Security Standard - * Algorithm Names Specification. Providers may support certificate - * compression algorithms not defined in this list or may not use the - * recommended name for a certain certificate compression algorithm. - *

- * The set of certificate compression algorithm names and functions - * that will be used over the SSL/TLS/DTLS connections is determined by - * the returned map of this method and the underlying provider-specific - * default certificate compression algorithm names and functions. See - * {@link #getCertificateDeflaters} for specific details on how the - * parameters are used in SSL/TLS/DTLS connections. - * - * @apiNote - * Note that a provider may not have been updated to support this method - * and in that case may ignore the certificate compression algorithm - * names and functions that are set. - * - * @implNote - * The SunJSSE provider supports this method. - * - * @param certDeflaters an ordered map of certificate compression - * algorithm names {@code String}s and {@link Function}s with the - * first entry being the most preferred, or {@code null}. Each - * entry in the map is non-null, and represents the certificate - * compression algorithm. The key of the map entry is a non-null - * {@code String} object which represents the certificate - * compression algorithm name. The value of the map entry is a - * non-null {@link Function} object which will be applied to the - * Certificate message byte array, and produce Compressed - * Certificate message byte array. This method will make a copy of - * this map if necessary to protect against subsequent modification. - * Providers should ignore unknown certificate compression - * algorithm names and the relevant functions while establishing - * the SSL/TLS/DTLS connections. - * @throws IllegalArgumentException if any entry, key, or value in the - * {@code certDeflaters} map is {@code null}, or any key is - * {@linkplain String#isBlank() blank}. - * - * @see #getCertificateDeflaters - * - * @since 19 - */ - public void setCertificateDeflaters( - Map> certDeflaters) { - if (this.certDeflaters == certDeflaters) { - return; - } - - if (certDeflaters == null) { - this.certDeflaters = null; - } else if (certDeflaters.isEmpty()) { - this.certDeflaters = Map.of(); - } else { - @SuppressWarnings({"unchecked", "rawtypes"}) - Map.Entry>[] entries = - new Map.Entry[certDeflaters.size()]; - int i = 0; - for (Map.Entry> entry : - certDeflaters.entrySet()) { - if (entry == null) { - throw new IllegalArgumentException( - "Null entry is not allowed"); - } else if (entry.getKey() == null || entry.getKey().isBlank()) { - throw new IllegalArgumentException( - "Null or blank compression algorithm " + - "name is not allowed"); - } else if (entry.getValue() == null) { - throw new IllegalArgumentException( - "Null compression function is not allowed"); - } - entries[i++] = entry; - } - - this.certDeflaters = Map.ofEntries(entries); - } - } - - /** - * Returns a prioritized map of certificate decompression algorithm - * names and functions that can be used over the SSL/TLS/DTLS protocols. - *

- * Note that the standard list of certificate decompression algorithm - * names are defined in the - * Certificate Compression section of the Java Security Standard - * Algorithm Names Specification. Providers may support certificate - * decompression algorithms not defined in this list or may not use the - * recommended name for a certain certificate decompression algorithm. - *

- * The set of certificate decompression algorithm names and functions - * that will be used over the SSL/TLS/DTLS connections is determined by - * the returned map of this method and the underlying provider-specific - * default certificate decompression algorithm names and functions. - *

- * If the returned map is {@code null}, then the underlying - * provider-specific default certificate decompression algorithm names - * and functions will be used over the connections. - *

- * If the returned map is empty (zero-length), then the certificate - * compression mechanism is turned off for SSL/TLS/DTLS protocols. - *

- * If the returned map is not {@code null} or empty (zero-length), then - * the certificate compression mechanism is turned on for SSL/TLS/DTLS - * protocols. The certificate decompression algorithm names and - * functions in the returned map will be used over the connections. - *

- * If the {@link #setCertificateInflaters} method has not been called, - * this method should return the default certificate decompression - * algorithm names and functions for connection populated objects, or - * {@code null} for pre-populated objects. + * This method only applies to TLSv1.3. * - * @apiNote - * Note that a provider may not have been updated to support this method - * and in that case may return {@code null} instead of the default - * certificate decompression algorithm names and functions for connection - * populated objects. + * @param enableCertificateCompression + * {@code true} indicates that TLS certificate compression + * should be enabled; {@code false} indicates that TLS certificate + * compression should be disabled * - * @implNote - * The SunJSSE provider supports this method, but does not define the - * default certificate decompression algorithm names and functions. The - * certificate compression mechanism is turned off by default for the - * SunJSSE provider. Applications could enable the mechanism in the - * SunJSSE provider by calling {@link #setCertificateDeflaters} in - * the compression side and {@link #setCertificateInflaters} in the - * decompression side. - * - * @return an immutable map of certificate decompression algorithm names - * {@code String}s and {@link Function}s, or {@code null} if - * none have been set. Each map entry in the map is non-null, and - * represents the certificate decompression algorithm. The key of - * the map entry is a non-null {@code String} object which - * represents the certificate decompression algorithm name. The - * value of the map entry is a non-null {@link Function} object - * which will be applied to the Compressed Certificate message - * byte array, and produce Certificate message byte array. The - * map entries are ordered based on certificate decompression - * algorithm preference, with the first entry being the most - * preferred. Providers should ignore unknown certificate - * decompression algorithm names while establishing the - * SSL/TLS/DTLS connections. - * - * @see #setCertificateDeflaters + * @see #getEnableCertificateCompression() * - * @since 19 + * @since 27 */ - public Map> getCertificateInflaters() { - return this.certInflaters; + public void setEnableCertificateCompression( + boolean enableCertificateCompression) { + this.enableCertificateCompression = enableCertificateCompression; } /** - * Sets a prioritized map of certificate decompression algorithm names - * and functions that can be used over the SSL/TLS/DTLS protocols. + * Returns whether TLS certificate compression should be enabled *

- * Note that the standard list of certificate decompression algorithm - * names are defined in the - * Certificate Compression section of the Java Security Standard - * Algorithm Names Specification. Providers may support certificate - * decompression algorithms not defined in this list or may not use the - * recommended name for a certain certificate decompression algorithm. - *

- * The set of certificate decompression algorithm names and functions - * that will be used over the SSL/TLS/DTLS connections is determined by - * the returned map of this method and the underlying provider-specific - * default certificate decompression algorithm names and functions. See - * {@link #getCertificateInflaters} for specific details on how the - * parameters are used in SSL/TLS/DTLS connections. + * This method only applies to TLSv1.3. * - * @apiNote - * Note that a provider may not have been updated to support this method - * and in that case may ignore the certificate decompression algorithm - * names and functions that are set. - * - * @implNote - * The SunJSSE provider supports this method. + * @return true, if TLS certificate compression should be enabled * - * @param certInflaters an ordered map of certificate decompression - * algorithm names {@code String}s and {@link Function}s with the - * first entry being the most preferred, or {@code null}. Each - * entry in the map is non-null, and represents the certificate - * decompression algorithm. The key of the map entry is a non-null - * {@code String} object which represents the certificate - * decompression algorithm name. The value of the map entry is a - * non-null {@link Function} object which will be applied to the - * Compressed Certificate message byte array, and produce - * Certificate message byte array. This method will make a copy of - * this map if necessary to protect against subsequent modification. - * Providers should ignore unknown certificate decompression - * algorithm names and the relevant functions while establishing - * the SSL/TLS/DTLS connections. - * @throws IllegalArgumentException if any entry, key, or value in the - * {@code certInflaters} map is {@code null}, or any key is - * {@linkplain String#isBlank() blank}. - * - * @see #getCertificateInflaters + * @see #setEnableCertificateCompression(boolean) * - * @since 19 + * @since 27 */ - public void setCertificateInflaters( - Map> certInflaters) { - if (this.certInflaters == certInflaters) { - return; - } - - if (certInflaters == null) { - this.certInflaters = null; - } else if (certInflaters.isEmpty()) { - this.certInflaters = Map.of(); - } else { - @SuppressWarnings({"unchecked", "rawtypes"}) - Map.Entry>[] entries = - new Map.Entry[certInflaters.size()]; - int i = 0; - for (Map.Entry> entry : - certInflaters.entrySet()) { - if (entry == null) { - throw new IllegalArgumentException( - "Null entry is not allowed"); - } else if (entry.getKey() == null || entry.getKey().isBlank()) { - throw new IllegalArgumentException( - "Null or blank decompression algorithm " + - "name is not allowed"); - } else if (entry.getValue() == null) { - throw new IllegalArgumentException( - "Null decompression function is not allowed"); - } - entries[i++] = entry; - } - - this.certInflaters = Map.ofEntries(entries); - } + public boolean getEnableCertificateCompression() { + return this.enableCertificateCompression; } } diff --git a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java index 1a23107264bc4..3a5ceb75784a0 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 @@ -25,22 +26,22 @@ package sun.security.ssl; -import sun.security.ssl.SSLExtension.ExtensionConsumer; -import sun.security.ssl.SSLExtension.SSLExtensionSpec; -import sun.security.ssl.SSLHandshake.HandshakeMessage; - -import javax.net.ssl.SSLProtocolException; import java.io.IOException; import java.nio.ByteBuffer; import java.text.MessageFormat; import java.util.Locale; import java.util.Map; import java.util.function.Function; +import javax.net.ssl.SSLProtocolException; +import sun.security.ssl.SSLExtension.ExtensionConsumer; +import sun.security.ssl.SSLExtension.SSLExtensionSpec; +import sun.security.ssl.SSLHandshake.HandshakeMessage; /** * Pack of the "compress_certificate" extensions [RFC 5246]. */ final class CompressCertExtension { + static final HandshakeProducer chNetworkProducer = new CHCompressCertificateProducer(); static final ExtensionConsumer chOnLoadConsumer = @@ -58,10 +59,11 @@ final class CompressCertExtension { * The "signature_algorithms" extension. */ static final class CertCompressionSpec implements SSLExtensionSpec { + private final int[] compressionAlgorithms; // non-null CertCompressionSpec( - Map> certInflaters) { + Map> certInflaters) { compressionAlgorithms = new int[certInflaters.size()]; int i = 0; for (Integer id : certInflaters.keySet()) { @@ -70,28 +72,28 @@ static final class CertCompressionSpec implements SSLExtensionSpec { } CertCompressionSpec(HandshakeContext hc, - ByteBuffer buffer) throws IOException { + ByteBuffer buffer) throws IOException { if (buffer.remaining() < 2) { // 2: the length of the list throw hc.conContext.fatal(Alert.DECODE_ERROR, new SSLProtocolException( - "Invalid compress_certificate: insufficient data")); + "Invalid compress_certificate: insufficient data")); } byte[] algs = Record.getBytes8(buffer); if (buffer.hasRemaining()) { throw hc.conContext.fatal(Alert.DECODE_ERROR, new SSLProtocolException( - "Invalid compress_certificate: unknown extra data")); + "Invalid compress_certificate: unknown extra data")); } if (algs.length == 0 || (algs.length & 0x01) != 0) { throw hc.conContext.fatal(Alert.DECODE_ERROR, new SSLProtocolException( - "Invalid compress_certificate: incomplete data")); + "Invalid compress_certificate: incomplete data")); } int[] compressionAlgs = new int[algs.length / 2]; - for (int i = 0, j = 0; i < algs.length;) { + for (int i = 0, j = 0; i < algs.length; ) { byte hash = algs[i++]; byte sign = algs[i++]; compressionAlgs[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF); @@ -103,12 +105,12 @@ static final class CertCompressionSpec implements SSLExtensionSpec { @Override public String toString() { MessageFormat messageFormat = new MessageFormat( - "\"compression algorithms\": '['{0}']'", Locale.ENGLISH); + "\"compression algorithms\": '['{0}']'", Locale.ENGLISH); if (compressionAlgorithms.length == 0) { Object[] messageFields = { "" - }; + }; return messageFormat.format(messageFields); } else { StringBuilder builder = new StringBuilder(512); @@ -125,7 +127,7 @@ public String toString() { Object[] messageFields = { builder.toString() - }; + }; return messageFormat.format(messageFields); } @@ -133,7 +135,8 @@ public String toString() { } private static final - class CompressCertificateStringizer implements SSLStringizer { + class CompressCertificateStringizer implements SSLStringizer { + @Override public String toString(HandshakeContext hc, ByteBuffer buffer) { try { @@ -150,7 +153,8 @@ public String toString(HandshakeContext hc, ByteBuffer buffer) { * the ClientHello handshake message. */ private static final - class CHCompressCertificateProducer implements HandshakeProducer { + class CHCompressCertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. private CHCompressCertificateProducer() { // blank @@ -160,47 +164,8 @@ private CHCompressCertificateProducer() { public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { // The producing happens in client side only. - ClientHandshakeContext chc = (ClientHandshakeContext)context; - - // Is it a supported and enabled extension? - if (!chc.sslConfig.isAvailable( - SSLExtension.CH_COMPRESS_CERTIFICATE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unavailable " + - "compress_certificate extension"); - } - return null; - } - - // Produce the extension. - if (chc.certInflaters == null) { - chc.certInflaters = - CompressionAlgorithm.findInflaters(chc.sslConfig); - } - - if (chc.certInflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unsupported " + - "compress_certificate extension"); - } - return null; - } - - int vectorLen = CompressionAlgorithm.sizeInRecord() * - chc.certInflaters.size(); - byte[] extData = new byte[vectorLen + 1]; - ByteBuffer m = ByteBuffer.wrap(extData); - Record.putInt8(m, vectorLen); - for (Integer algId : chc.certInflaters.keySet()) { - Record.putInt16(m, algId); - } - - // Update the context. - chc.handshakeExtensions.put( - SSLExtension.CH_COMPRESS_CERTIFICATE, - new CertCompressionSpec(chc.certInflaters)); - - return extData; + return produceCompCertExt(context, + SSLExtension.CH_COMPRESS_CERTIFICATE); } } @@ -209,7 +174,8 @@ public byte[] produce(ConnectionContext context, * the ClientHello handshake message. */ private static final - class CHCompressCertificateConsumer implements ExtensionConsumer { + class CHCompressCertificateConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. private CHCompressCertificateConsumer() { // blank @@ -217,43 +183,11 @@ private CHCompressCertificateConsumer() { @Override public void consume(ConnectionContext context, - HandshakeMessage message, ByteBuffer buffer) throws IOException { + HandshakeMessage message, ByteBuffer buffer) + throws IOException { // The consuming happens in server side only. - ServerHandshakeContext shc = (ServerHandshakeContext)context; - - // Is it a supported and enabled extension? - if (!shc.sslConfig.isAvailable( - SSLExtension.CH_COMPRESS_CERTIFICATE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unavailable " + - "compress_certificate extension"); - } - return; // ignore the extension - } - - if (shc.sslConfig.certDeflaters == null || - shc.sslConfig.certDeflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unsupported " + - "compress_certificate extension"); - } - return; // ignore the extension - } - - // Parse the extension. - CertCompressionSpec spec = new CertCompressionSpec(shc, buffer); - - // Update the context. - shc.certDeflater = CompressionAlgorithm.selectDeflater( - shc.sslConfig, spec.compressionAlgorithms); - if (shc.certDeflater == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore, no supported " + - "certificate compression algorithms"); - } - } - - // No impact on session resumption. + consumeCompCertExt(context, buffer, + SSLExtension.CH_COMPRESS_CERTIFICATE); } } @@ -262,7 +196,8 @@ public void consume(ConnectionContext context, * the CertificateRequest handshake message. */ private static final - class CRCompressCertificateProducer implements HandshakeProducer { + class CRCompressCertificateProducer implements HandshakeProducer { + // Prevent instantiation of this class. private CRCompressCertificateProducer() { // blank @@ -272,47 +207,8 @@ private CRCompressCertificateProducer() { public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { // The producing happens in server side only. - ServerHandshakeContext shc = (ServerHandshakeContext)context; - - // Is it a supported and enabled extension? - if (!shc.sslConfig.isAvailable( - SSLExtension.CR_COMPRESS_CERTIFICATE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unavailable " + - "compress_certificate extension"); - } - return null; - } - - // Produce the extension. - if (shc.certInflaters == null) { - shc.certInflaters = - CompressionAlgorithm.findInflaters(shc.sslConfig); - } - - if (shc.certInflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unsupported " + - "compress_certificate extension"); - } - return null; - } - - int vectorLen = CompressionAlgorithm.sizeInRecord() * - shc.certInflaters.size(); - byte[] extData = new byte[vectorLen + 1]; - ByteBuffer m = ByteBuffer.wrap(extData); - Record.putInt8(m, vectorLen); - for (Integer algId : shc.certInflaters.keySet()) { - Record.putInt16(m, algId); - } - - // Update the context. - shc.handshakeExtensions.put( - SSLExtension.CR_COMPRESS_CERTIFICATE, - new CertCompressionSpec(shc.certInflaters)); - - return extData; + return produceCompCertExt(context, + SSLExtension.CR_COMPRESS_CERTIFICATE); } } @@ -321,50 +217,101 @@ public byte[] produce(ConnectionContext context, * the CertificateRequest handshake message. */ private static final - class CRCompressCertificateConsumer implements ExtensionConsumer { + class CRCompressCertificateConsumer implements ExtensionConsumer { + // Prevent instantiation of this class. private CRCompressCertificateConsumer() { // blank } + @Override public void consume(ConnectionContext context, - HandshakeMessage message, ByteBuffer buffer) throws IOException { + HandshakeMessage message, ByteBuffer buffer) + throws IOException { // The consuming happens in client side only. - ClientHandshakeContext chc = (ClientHandshakeContext)context; - - // Is it a supported and enabled extension? - if (!chc.sslConfig.isAvailable( - SSLExtension.CR_COMPRESS_CERTIFICATE)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unavailable " + - "compress_certificate extension"); - } - return; // ignore the extension + consumeCompCertExt(context, buffer, + SSLExtension.CR_COMPRESS_CERTIFICATE); + } + } + + private static byte[] produceCompCertExt( + ConnectionContext context, SSLExtension extension) + throws IOException { + + HandshakeContext hc = (HandshakeContext) context; + // Is it a supported and enabled extension? + if (!hc.sslConfig.isAvailable(extension)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); } + return null; + } - if (chc.sslConfig.certDeflaters == null || - chc.sslConfig.certDeflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore unsupported " + - "compress_certificate extension"); - } - return; // ignore the extension + // Produce the extension. + if (hc.certInflaters == null) { + hc.certInflaters = + CompressionAlgorithm.findInflaters(hc.sslConfig); + } + + if (hc.certInflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } + return null; + } - // Parse the extension. - CertCompressionSpec spec = new CertCompressionSpec(chc, buffer); + int vectorLen = CompressionAlgorithm.sizeInRecord() * + hc.certInflaters.size(); + byte[] extData = new byte[vectorLen + 1]; + ByteBuffer m = ByteBuffer.wrap(extData); + Record.putInt8(m, vectorLen); + for (Integer algId : hc.certInflaters.keySet()) { + Record.putInt16(m, algId); + } - // Update the context. - chc.certDeflater = CompressionAlgorithm.selectDeflater( - chc.sslConfig, spec.compressionAlgorithms); - if (chc.certDeflater == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { - SSLLogger.fine("Ignore, no supported " + - "certificate compression algorithms"); - } + // Update the context. + hc.handshakeExtensions.put( + extension, new CertCompressionSpec(hc.certInflaters)); + + return extData; + } + + private static void consumeCompCertExt(ConnectionContext context, + ByteBuffer buffer, SSLExtension extension) throws IOException { + + HandshakeContext hc = (HandshakeContext) context; + // Is it a supported and enabled extension? + if (!hc.sslConfig.isAvailable(extension)) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore unavailable " + + "compress_certificate extension"); + } + return; // ignore the extension + } + + if (hc.sslConfig.certDeflaters == null || + hc.sslConfig.certDeflaters.isEmpty()) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore unsupported " + + "compress_certificate extension"); } + return; // ignore the extension + } - // No impact on session resumption. + // Parse the extension. + CertCompressionSpec spec = new CertCompressionSpec(hc, buffer); + + // Update the context. + hc.certDeflater = CompressionAlgorithm.selectDeflater( + hc.sslConfig, spec.compressionAlgorithms); + if (hc.certDeflater == null) { + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.fine("Ignore, no supported " + + "certificate compression algorithms"); + } } + // No impact on session resumption. } } 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 e391c493345dc..e08b942e7481a 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index 3b561ebf2d22d..6ac8589c0a9e1 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 @@ -25,18 +26,23 @@ package sun.security.ssl; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.AbstractMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; /** - * Enum for (D)TLS certificate compression algorithms. + * Enum for TLS certificate compression algorithms. */ enum CompressionAlgorithm { - ZLIB (1, "zlib"), - BROTLI (2, "brotli"), - ZSTD (3, "zstd"); + ZLIB(1, "zlib"), + BROTLI(2, "brotli"), + ZSTD(3, "zstd"); final int id; final String name; @@ -68,7 +74,7 @@ static String nameOf(int id) { return ""; } - // Return the size of a SignatureScheme structure in TLS record + // Return the size of a compression algorithms structure in TLS record static int sizeInRecord() { return 2; } @@ -87,6 +93,7 @@ static Map> findInflaters( Map> inflaters = new LinkedHashMap<>(config.certInflaters.size()); + for (Map.Entry> entry : config.certInflaters.entrySet()) { CompressionAlgorithm ca = @@ -130,4 +137,54 @@ static Map.Entry> selectDeflater( return null; } + + // Default Deflaters and Inflaters. + // We currently support only ZLIB internally. + + static Map> getDefaultDeflaters() { + return Map.of(ZLIB.name, (input) -> { + try (Deflater deflater = new Deflater(); + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(input.length)) { + + deflater.setInput(input); + deflater.finish(); + byte[] buffer = new byte[1024]; + + while (!deflater.finished()) { + int compressedSize = deflater.deflate(buffer); + outputStream.write(buffer, 0, compressedSize); + } + + return outputStream.toByteArray(); + } catch (Exception e) { + SSLLogger.logWarning("ssl", + "Exception during certificate compression: ", e); + return null; + } + }); + } + + static Map> getDefaultInflaters() { + return Map.of(ZLIB.name, (input) -> { + try (Inflater inflater = new Inflater(); + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(input.length)) { + + inflater.setInput(input); + byte[] buffer = new byte[1024]; + + while (!inflater.finished()) { + int decompressedSize = inflater.inflate(buffer); + outputStream.write(buffer, 0, decompressedSize); + } + + return outputStream.toByteArray(); + } catch (Exception e) { + SSLLogger.logWarning("ssl", + "Exception during certificate decompression: ", e); + return null; + } + }); + } } diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 50b5d7c1b4c68..f80872b2be0af 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -90,6 +90,11 @@ final class SSLConfiguration implements Cloneable { // To switch off the extended_master_secret extension. static final boolean useExtendedMasterSecret; + // Enable certificate compression. + private final boolean enableCertificateCompression = + Utilities.getBooleanProperty( + "jdk.tls.enableCertificateCompression", true); + // Allow session resumption without Extended Master Secret extension. static final boolean allowLegacyResumption = Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true); @@ -255,8 +260,10 @@ final class SSLConfiguration implements Cloneable { CustomizedServerSignatureSchemes.signatureSchemes : SupportedSigSchemes.DEFAULT; - this.certDeflaters = Map.of(); - this.certInflaters = Map.of(); + this.certDeflaters = this.enableCertificateCompression + ? CompressionAlgorithm.getDefaultDeflaters() : Map.of(); + this.certInflaters = this.enableCertificateCompression + ? CompressionAlgorithm.getDefaultInflaters() : Map.of(); this.namedGroups = NamedGroup.SupportedGroups.namedGroups; this.maximumProtocolVersion = ProtocolVersion.NONE; @@ -390,8 +397,11 @@ void setSSLParameters(SSLParameters params) { this.namedGroups = NamedGroup.SupportedGroups.namedGroups; } - this.certDeflaters = params.getCertificateDeflaters(); - this.certInflaters = params.getCertificateInflaters(); + this.certDeflaters = params.getEnableCertificateCompression() + ? CompressionAlgorithm.getDefaultDeflaters() : Map.of(); + this.certInflaters = params.getEnableCertificateCompression() + ? CompressionAlgorithm.getDefaultInflaters() : Map.of(); + this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); this.enableRetransmissions = params.getEnableRetransmissions(); this.maximumPacketSize = params.getMaximumPacketSize(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index f55ab27d297bf..a3ccdba81519c 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -215,9 +215,10 @@ static String toString(Object... params) { // Logs a warning message and always returns false. This method // can be used as an OR Predicate to add a log in a stream filter. - public static boolean logWarning(String option, String s) { + public static boolean logWarning( + String option, String s, Object... params) { if (SSLLogger.isOn && SSLLogger.isOn(option)) { - SSLLogger.warning(s); + SSLLogger.warning(s, params); } return false; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index 85c3225c25a20..c39ad1f9ea12f 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -711,8 +711,7 @@ public static SSLParameters copySSLParameters(SSLParameters p) { p1.setSignatureSchemes(p.getSignatureSchemes()); p1.setNamedGroups(p.getNamedGroups()); p1.setWantClientAuth(p.getWantClientAuth()); - p1.setCertificateDeflaters(p.getCertificateDeflaters()); - p1.setCertificateInflaters(p.getCertificateInflaters()); + p1.setEnableCertificateCompression(p.getEnableCertificateCompression()); return p1; } diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java index 13e1c0bfb67d0..389148a042ed1 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 @@ -21,15 +22,6 @@ * questions. */ -/** - * @test - * @bug 8273042 - * @summary TLS certificate compression - * @library /test/lib - * /javax/net/ssl/templates - * @run main/othervm HttpsCompressedCert - */ - import static java.net.http.HttpResponse.BodyHandlers.ofString; import static jdk.test.lib.Asserts.assertEquals; @@ -37,33 +29,21 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.util.Map; -import java.util.function.Function; -import java.util.zip.Inflater; import javax.net.ssl.SSLParameters; -public class HttpsCompressedCert { - private static final Function certInflater = (input) -> { - try { - Inflater inflater = new Inflater(); - inflater.setInput(input); - byte[] output = new byte[1024 * 8]; - int l = inflater.inflate(output); - inflater.end(); - - byte[] data = new byte[l]; - System.arraycopy(output, 0, data, 0, l); - - return data; - } catch (Exception ex) { - // just ignore - return null; - } - }; +/* + * @test + * @bug 8273042 + * @summary TLS certificate compression + * @library /test/lib + * /javax/net/ssl/templates + * @run main/othervm HttpsCompressedCert + */ +public class HttpsCompressedCert { public static void main(String[] args) throws Exception { SSLParameters sslParameters = new SSLParameters(); - sslParameters.setCertificateInflaters(Map.of("zlib", certInflater)); + sslParameters.setEnableCertificateCompression(true); HttpClient httpClient = HttpClient.newBuilder() .sslContext(SSLClientContext.createClientSSLContext()) .version(HttpClient.Version.HTTP_2) @@ -74,7 +54,8 @@ public static void main(String[] args) throws Exception { new URI("https://www.google.com/")) .GET() .build(); - HttpResponse response = httpClient.send(httpRequest, ofString()); + HttpResponse response = httpClient.send( + httpRequest, ofString()); assertEquals(response.statusCode(), 200); } } diff --git a/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java index fee0abbc6a1b6..0219c76aba147 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java +++ b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 @@ -21,8 +22,9 @@ * questions. */ -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; /* * @test @@ -32,77 +34,25 @@ * @run main/othervm CompressedCert */ -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; -import java.security.Security; -import java.util.Map; -import java.util.function.Function; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - public class CompressedCert extends SSLSocketTemplate { - private final Map> certDeflaters; - private final Map> certInflaters; - private final boolean requireClientCert; - private final boolean exceptionExpected; - - private static final Function certDeflater = (input) -> { - Deflater deflater = new Deflater(); - deflater.setInput(input); - deflater.finish(); - byte[] output = new byte[input.length]; - int l = deflater.deflate(output); - deflater.end(); - byte[] data = new byte[l]; - System.arraycopy(output, 0, data, 0, l); - - return data; - }; - - private static final Function certInflater = (input) -> { - try { - Inflater inflater = new Inflater(); - inflater.setInput(input); - byte[] output = new byte[1024 * 8]; - int l = inflater.inflate(output); - inflater.end(); - - byte[] data = new byte[l]; - System.arraycopy(output, 0, data, 0, l); - - return data; - } catch (Exception ex) { - // just ignore - return null; - } - }; - - private static final Function invalidDeflater = (input) -> { - return input; - }; - - private static final Function invalidInflater = (input) -> { - return input; - }; + private final boolean enableClientCertComp; + private final boolean enableServerCertComp; + private final boolean requireClientCert; public CompressedCert( - Map> certDeflaters, - Map> certInflaters, - boolean requireClientCert, - boolean exceptionExpected) { - this.certDeflaters = certDeflaters; - this.certInflaters = certInflaters; - this.requireClientCert = requireClientCert; - this.exceptionExpected = exceptionExpected; + boolean enableClientCertComp, + boolean enableServerCertComp, + boolean requireClientCert) { + this.enableClientCertComp = enableClientCertComp; + this.enableServerCertComp = enableServerCertComp; + this.requireClientCert = requireClientCert; } @Override protected void configureServerSocket(SSLServerSocket sslServerSocket) { SSLParameters sslParameters = sslServerSocket.getSSLParameters(); - sslParameters.setCertificateDeflaters(certDeflaters); - sslParameters.setCertificateInflaters(certInflaters); + sslParameters.setEnableCertificateCompression(enableClientCertComp); sslParameters.setNeedClientAuth(requireClientCert); sslServerSocket.setSSLParameters(sslParameters); } @@ -110,161 +60,24 @@ protected void configureServerSocket(SSLServerSocket sslServerSocket) { @Override protected void configureClientSocket(SSLSocket socket) { SSLParameters sslParameters = socket.getSSLParameters(); - sslParameters.setCertificateDeflaters(certDeflaters); - sslParameters.setCertificateInflaters(certInflaters); + sslParameters.setEnableCertificateCompression(enableServerCertComp); socket.setSSLParameters(sslParameters); } - @Override - protected void runServerApplication(SSLSocket socket) { - try { - super.runServerApplication(socket); - } catch (Exception ex) { - // Just ignore, let the client handle the failure information. - } - } - - @Override - protected void runClientApplication(SSLSocket sslSocket) throws Exception { - try { - super.runClientApplication(sslSocket); - if (exceptionExpected) { - throw new RuntimeException("Unexpected success!"); - } - } catch (Exception ex) { - if (!exceptionExpected) { - throw ex; - } - } - } - public static void main(String[] args) throws Exception { - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - - runTest(Map.of("zlib", certDeflater), - Map.of("zlib", certInflater), - false, - false); - runTest(Map.of("zlib", certDeflater), - Map.of("zlib", certInflater), - true, - false); - - runTest(Map.of("zlib", certDeflater), - Map.of(), - false, - false); - runTest(Map.of("zlib", certDeflater), - Map.of(), - true, - false); - - runTest(Map.of("zlib", certDeflater), - null, - false, - false); - runTest(Map.of("zlib", certDeflater), - null, - true, - false); - - runTest(Map.of(), - Map.of("zlib", certInflater), - false, - false); - runTest(Map.of(), - Map.of("zlib", certInflater), - true, - false); - - runTest(Map.of(), - Map.of(), - false, - false); - runTest(Map.of(), - Map.of(), - true, - false); - - runTest(Map.of(), - null, - false, - false); - runTest(Map.of(), - null, - true, - false); - - runTest(null, - Map.of("zlib", certInflater), - false, - false); - runTest(null, - Map.of("zlib", certInflater), - true, - false); - - runTest(null, - Map.of(), - false, - false); - runTest(null, - Map.of(), - true, - false); - - runTest(null, - null, - false, - false); - runTest(null, - null, - true, - false); - - runTest(Map.of("zlib", certDeflater), - Map.of("brotli", certInflater), - false, - false); - runTest(Map.of("zlib", certDeflater), - Map.of("brotli", certInflater), - true, - false); - - runTest(Map.of("brotli", certDeflater), - Map.of("zlib", certInflater), - false, - false); - runTest(Map.of("brotli", certDeflater), - Map.of("zlib", certInflater), - true, - false); - - runTest(Map.of("zlib", certDeflater), - Map.of("zlib", invalidInflater), - false, - true); - runTest(Map.of("zlib", certDeflater), - Map.of("zlib", invalidInflater), - true, - true); - - runTest(Map.of("zlib", invalidDeflater), - Map.of("zlib", certInflater), - false, - true); - runTest(Map.of("zlib", invalidDeflater), - Map.of("zlib", certInflater), - true, - true); + runTest(false, false, false); + runTest(false, false, true); + runTest(false, true, false); + runTest(true, false, true); + runTest(true, true, false); + runTest(true, true, true); } private static void runTest( - Map> certDeflaters, - Map> certInflaters, - boolean requireClientCert, - boolean exceptionExpected) throws Exception { - new CompressedCert(certDeflaters, certInflaters, - requireClientCert, exceptionExpected).run(); + boolean enableClientCertComp, + boolean enableServerCertComp, + boolean requireClientCert) throws Exception { + new CompressedCert(enableClientCertComp, enableServerCertComp, + requireClientCert).run(); } } diff --git a/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java b/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java deleted file mode 100644 index c6c67d04abb8b..0000000000000 --- a/test/jdk/javax/net/ssl/SSLParameters/UnsetCompressedCert.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2022 THL A29 Limited, a Tencent company. 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. - */ - -// SunJSSE does not support dynamic system properties, no way to re-use -// system properties in samevm/agentvm mode. - -/* - * @test - * @bug 8273042 - * @summary TLS Certificate Compression - * @library /javax/net/ssl/templates - * @run main/othervm UnsetCompressedCert - */ - -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; -import java.security.Security; -import java.util.Map; -import java.util.function.Function; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -public class UnsetCompressedCert extends SSLSocketTemplate { - private final Map> certDeflaters; - private final Map> certInflaters; - private final boolean requireClientCert; - - private static final Function certDeflater = (input) -> { - Deflater deflater = new Deflater(); - deflater.setInput(input); - deflater.finish(); - byte[] output = new byte[input.length]; - int l = deflater.deflate(output); - deflater.end(); - - byte[] data = new byte[l]; - System.arraycopy(output, 0, data, 0, l); - - return data; - }; - - private static final Function certInflater = (input) -> { - try { - Inflater inflater = new Inflater(); - inflater.setInput(input); - byte[] output = new byte[1024 * 8]; - int l = inflater.inflate(output); - inflater.end(); - - byte[] data = new byte[l]; - System.arraycopy(output, 0, data, 0, l); - - return data; - } catch (Exception ex) { - // just ignore - return null; - } - }; - - public UnsetCompressedCert( - Map> certDeflaters, - Map> certInflaters, - boolean requireClientCert) { - this.certDeflaters = certDeflaters; - this.certInflaters = certInflaters; - this.requireClientCert = requireClientCert; - } - - @Override - protected void configureServerSocket(SSLServerSocket sslServerSocket) { - SSLParameters sslParameters = sslServerSocket.getSSLParameters(); - if (certDeflaters != null) { - sslParameters.setCertificateDeflaters(certDeflaters); - } - if (certInflaters != null) { - sslParameters.setCertificateInflaters(certInflaters); - } - sslParameters.setNeedClientAuth(requireClientCert); - sslServerSocket.setSSLParameters(sslParameters); - } - - @Override - protected void configureClientSocket(SSLSocket socket) { - SSLParameters sslParameters = socket.getSSLParameters(); - if (certDeflaters != null) { - sslParameters.setCertificateDeflaters(certDeflaters); - } - - if (certInflaters != null) { - sslParameters.setCertificateInflaters(certInflaters); - } - socket.setSSLParameters(sslParameters); - } - - public static void main(String[] args) throws Exception { - Security.setProperty("jdk.tls.disabledAlgorithms", ""); - - runTest(Map.of("zlib", certDeflater), - null, - false); - runTest(Map.of("zlib", certDeflater), - null, - true); - - runTest(Map.of(), - null, - false); - runTest(Map.of(), - null, - true); - - runTest(null, - Map.of("zlib", certInflater), - false); - runTest(null, - Map.of("zlib", certInflater), - true); - - runTest(null, - Map.of(), - false); - runTest(null, - Map.of(), - true); - - runTest(null, - null, - false); - runTest(null, - null, - true); - } - - private static void runTest( - Map> certDeflaters, - Map> certInflaters, - boolean requireClientCert) throws Exception { - new UnsetCompressedCert( - certDeflaters, certInflaters, requireClientCert).run(); - } -} diff --git a/test/jdk/javax/net/ssl/templates/SSLClientContext.java b/test/jdk/javax/net/ssl/templates/SSLClientContext.java index 46fcbaedcfd17..0b1281241c36a 100644 --- a/test/jdk/javax/net/ssl/templates/SSLClientContext.java +++ b/test/jdk/javax/net/ssl/templates/SSLClientContext.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2025, 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 From d6e2379a665dc1d773ef0586cca2150cb84dffdb Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Wed, 26 Nov 2025 12:13:01 -0500 Subject: [PATCH 09/12] Fix merge errors --- .../sun/security/ssl/CompressCertExtension.java | 10 +++++----- .../sun/security/ssl/CompressedCertificate.java | 4 ++-- .../classes/sun/security/ssl/CompressionAlgorithm.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java index 3a5ceb75784a0..ff4aae18e8c9c 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -241,7 +241,7 @@ private static byte[] produceCompCertExt( HandshakeContext hc = (HandshakeContext) context; // Is it a supported and enabled extension? if (!hc.sslConfig.isAvailable(extension)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable " + "compress_certificate extension"); } @@ -255,7 +255,7 @@ private static byte[] produceCompCertExt( } if (hc.certInflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unsupported " + "compress_certificate extension"); } @@ -284,7 +284,7 @@ private static void consumeCompCertExt(ConnectionContext context, HandshakeContext hc = (HandshakeContext) context; // Is it a supported and enabled extension? if (!hc.sslConfig.isAvailable(extension)) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unavailable " + "compress_certificate extension"); } @@ -293,7 +293,7 @@ private static void consumeCompCertExt(ConnectionContext context, if (hc.sslConfig.certDeflaters == null || hc.sslConfig.certDeflaters.isEmpty()) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore unsupported " + "compress_certificate extension"); } @@ -307,7 +307,7 @@ private static void consumeCompCertExt(ConnectionContext context, hc.certDeflater = CompressionAlgorithm.selectDeflater( hc.sslConfig, spec.compressionAlgorithms); if (hc.certDeflater == null) { - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Ignore, no supported " + "certificate compression algorithms"); } 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 e08b942e7481a..01a57c14c3733 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -164,7 +164,7 @@ public byte[] produce(ConnectionContext context, hc.certDeflater.getKey(), certMsg.length, compressedCertMsg); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced Compressed Certificate message", ccm); } @@ -197,7 +197,7 @@ public void consume(ConnectionContext context, // Parse the handshake message CompressedCertMessage ccm = new CompressedCertMessage(hc, message); - if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming CompressedCertificate handshake message", ccm); } diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index 6ac8589c0a9e1..9a95ee1063606 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -83,7 +83,7 @@ static int sizeInRecord() { static Map> findInflaters( SSLConfiguration config) { if (config.certInflaters == null || config.certInflaters.isEmpty()) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "No supported certificate compression algorithms"); @@ -99,7 +99,7 @@ static Map> findInflaters( CompressionAlgorithm ca = CompressionAlgorithm.nameOf(entry.getKey()); if (ca == null) { - if (SSLLogger.isOn && + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake,verbose")) { SSLLogger.finest( "Ignore unsupported certificate " + From cfa688ab8e9eb01f8e4e01e18ceb10edbc181052 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Thu, 4 Dec 2025 16:15:05 -0500 Subject: [PATCH 10/12] Revert SSLLogger changes --- .../classes/java/util/function/Function.java | 1 - .../security/ssl/CompressionAlgorithm.java | 29 +++++++++---------- .../classes/sun/security/ssl/SSLLogger.java | 5 ++-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/java/util/function/Function.java b/src/java.base/share/classes/java/util/function/Function.java index 649acc848b170..7bcbfd06bb6a3 100644 --- a/src/java.base/share/classes/java/util/function/Function.java +++ b/src/java.base/share/classes/java/util/function/Function.java @@ -24,7 +24,6 @@ */ package java.util.function; -import java.io.IOException; import java.util.Objects; /** diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index 9a95ee1063606..4c21eaae9796b 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -27,12 +27,10 @@ package sun.security.ssl; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.AbstractMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; -import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; @@ -83,10 +81,9 @@ static int sizeInRecord() { static Map> findInflaters( SSLConfiguration config) { if (config.certInflaters == null || config.certInflaters.isEmpty()) { - if (SSLLogger.isOn() && - SSLLogger.isOn("ssl,handshake,verbose")) { + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { SSLLogger.finest( - "No supported certificate compression algorithms"); + "No supported certificate compression algorithms"); } return Map.of(); } @@ -99,11 +96,9 @@ static Map> findInflaters( CompressionAlgorithm ca = CompressionAlgorithm.nameOf(entry.getKey()); if (ca == null) { - if (SSLLogger.isOn() && - SSLLogger.isOn("ssl,handshake,verbose")) { - SSLLogger.finest( - "Ignore unsupported certificate " + - "compression algorithm: " + entry.getKey()); + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.finest("Ignore unsupported certificate " + + "compression algorithm: " + entry.getKey()); } continue; } @@ -126,7 +121,7 @@ static Map.Entry> selectDeflater( CompressionAlgorithm ca = CompressionAlgorithm.nameOf(entry.getKey()); if (ca != null) { - for (int id : compressionAlgorithmIds) { + for (int id : compressionAlgorithmIds) { if (ca.id == id) { return new AbstractMap.SimpleImmutableEntry<>( id, entry.getValue()); @@ -158,8 +153,10 @@ static Map> getDefaultDeflaters() { return outputStream.toByteArray(); } catch (Exception e) { - SSLLogger.logWarning("ssl", - "Exception during certificate compression: ", e); + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Exception during certificate compression: ", e); + } return null; } }); @@ -181,8 +178,10 @@ static Map> getDefaultInflaters() { return outputStream.toByteArray(); } catch (Exception e) { - SSLLogger.logWarning("ssl", - "Exception during certificate decompression: ", e); + if (SSLLogger.isOn() && SSLLogger.isOn("ssl,handshake")) { + SSLLogger.warning( + "Exception during certificate decompression: ", e); + } return null; } }); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java index b6dce4429ea8e..7fa6fbf91b574 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLLogger.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLLogger.java @@ -228,10 +228,9 @@ static String toString(Object... params) { // Logs a warning message and always returns false. This method // can be used as an OR Predicate to add a log in a stream filter. - public static boolean logWarning( - String option, String s, Object... params) { + public static boolean logWarning(String option, String s) { if (SSLLogger.isOn() && SSLLogger.isOn(option)) { - SSLLogger.warning(s, params); + SSLLogger.warning(s); } return false; } From 32a2d2528f037ba728e666960a788465141282c8 Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Fri, 5 Dec 2025 14:56:22 -0500 Subject: [PATCH 11/12] Update copyright year and bug number plus some small changes --- .../classes/javax/net/ssl/SSLParameters.java | 3 +- .../share/classes/sun/security/ssl/Alert.java | 2 +- .../sun/security/ssl/CertificateMessage.java | 2 +- .../sun/security/ssl/CertificateRequest.java | 2 +- .../security/ssl/CompressCertExtension.java | 2 +- .../security/ssl/CompressedCertificate.java | 2 +- .../security/ssl/CompressionAlgorithm.java | 5 ++- .../sun/security/ssl/HandshakeContext.java | 2 +- .../sun/security/ssl/SSLConfiguration.java | 32 +++++++++++-------- .../sun/security/ssl/SSLExtension.java | 2 +- .../sun/security/ssl/SSLHandshake.java | 2 +- .../classes/sun/security/ssl/ServerHello.java | 2 +- .../jdk/internal/net/http/common/Utils.java | 2 +- .../HttpsCompressedCert.java | 10 +++--- .../net/ssl/SSLParameters/CompressedCert.java | 4 +-- .../net/ssl/templates/SSLClientContext.java | 1 - 16 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index ae8872c89e584..7272d9793a557 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -27,7 +27,6 @@ import java.security.AlgorithmConstraints; import java.util.*; -import java.util.function.Function; /** * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters diff --git a/src/java.base/share/classes/sun/security/ssl/Alert.java b/src/java.base/share/classes/sun/security/ssl/Alert.java index 391b350020efb..52541a5f18ff2 100644 --- a/src/java.base/share/classes/sun/security/ssl/Alert.java +++ b/src/java.base/share/classes/sun/security/ssl/Alert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java index 4cfbaf8caa858..bc6423945fadc 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateMessage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java index 6a7e21f24c896..44ae7e117403f 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java +++ b/src/java.base/share/classes/sun/security/ssl/CertificateRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java index ff4aae18e8c9c..6f69081995f13 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressCertExtension.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 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 01a57c14c3733..a2e57ce5b009f 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressedCertificate.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 diff --git a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java index 4c21eaae9796b..d44a107dfd2f1 100644 --- a/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java +++ b/src/java.base/share/classes/sun/security/ssl/CompressionAlgorithm.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -110,8 +110,7 @@ static Map> findInflaters( } static Map.Entry> selectDeflater( - SSLConfiguration config, - int[] compressionAlgorithmIds) { + SSLConfiguration config, int[] compressionAlgorithmIds) { if (config.certDeflaters == null) { return null; } diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java index 04c00f3155b60..0503e12c92be5 100644 --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java index 207fcceae944f..bb62801d16860 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -90,11 +90,6 @@ final class SSLConfiguration implements Cloneable { // To switch off the extended_master_secret extension. static final boolean useExtendedMasterSecret; - // Enable certificate compression. - private final boolean enableCertificateCompression = - Utilities.getBooleanProperty( - "jdk.tls.enableCertificateCompression", true); - // Allow session resumption without Extended Master Secret extension. static final boolean allowLegacyResumption = Utilities.getBooleanProperty("jdk.tls.allowLegacyResumption", true); @@ -260,10 +255,14 @@ final class SSLConfiguration implements Cloneable { CustomizedServerSignatureSchemes.signatureSchemes : SupportedSigSchemes.DEFAULT; - this.certDeflaters = this.enableCertificateCompression - ? CompressionAlgorithm.getDefaultDeflaters() : Map.of(); - this.certInflaters = this.enableCertificateCompression - ? CompressionAlgorithm.getDefaultInflaters() : Map.of(); + if (Utilities.getBooleanProperty( + "jdk.tls.enableCertificateCompression", true)) { + this.certDeflaters = CompressionAlgorithm.getDefaultDeflaters(); + this.certInflaters = CompressionAlgorithm.getDefaultInflaters(); + } else { + this.certDeflaters = Map.of(); + this.certInflaters = Map.of(); + } this.namedGroups = NamedGroup.SupportedGroups.namedGroups; this.maximumProtocolVersion = ProtocolVersion.NONE; @@ -322,6 +321,8 @@ SSLParameters getSSLParameters() { params.setMaximumPacketSize(this.maximumPacketSize); params.setSignatureSchemes(this.signatureSchemes); params.setNamedGroups(this.namedGroups); + params.setEnableCertificateCompression(!this.certInflaters.isEmpty() + && !this.certDeflaters.isEmpty()); return params; } @@ -397,10 +398,13 @@ void setSSLParameters(SSLParameters params) { this.namedGroups = NamedGroup.SupportedGroups.namedGroups; } - this.certDeflaters = params.getEnableCertificateCompression() - ? CompressionAlgorithm.getDefaultDeflaters() : Map.of(); - this.certInflaters = params.getEnableCertificateCompression() - ? CompressionAlgorithm.getDefaultInflaters() : Map.of(); + if (params.getEnableCertificateCompression()) { + this.certDeflaters = CompressionAlgorithm.getDefaultDeflaters(); + this.certInflaters = CompressionAlgorithm.getDefaultInflaters(); + } else { + this.certDeflaters = Map.of(); + this.certInflaters = Map.of(); + } this.preferLocalCipherSuites = params.getUseCipherSuitesOrder(); this.enableRetransmissions = params.getEnableRetransmissions(); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java index d0284ffc6e895..2c43605f75416 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java index 7fe31e5c3c46a..2c6b58bafa51a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLHandshake.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java index 23885417b54ca..903f43fa7eb98 100644 --- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java +++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index b119ec735aa62..96894ed5ee37b 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java index 389148a042ed1..064bf2ebb6b42 100644 --- a/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/HttpsCompressedCert.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -33,17 +33,19 @@ /* * @test - * @bug 8273042 + * @bug 8372526 * @summary TLS certificate compression * @library /test/lib * /javax/net/ssl/templates - * @run main/othervm HttpsCompressedCert + * @run main/othervm HttpsCompressedCert true + * @run main/othervm HttpsCompressedCert false */ public class HttpsCompressedCert { public static void main(String[] args) throws Exception { SSLParameters sslParameters = new SSLParameters(); - sslParameters.setEnableCertificateCompression(true); + sslParameters.setEnableCertificateCompression( + Boolean.parseBoolean(args[0])); HttpClient httpClient = HttpClient.newBuilder() .sslContext(SSLClientContext.createClientSSLContext()) .version(HttpClient.Version.HTTP_2) diff --git a/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java index 0219c76aba147..3de208a3009a5 100644 --- a/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java +++ b/test/jdk/javax/net/ssl/SSLParameters/CompressedCert.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,7 +28,7 @@ /* * @test - * @bug 8273042 + * @bug 8372526 * @summary TLS Certificate Compression * @library /javax/net/ssl/templates * @run main/othervm CompressedCert diff --git a/test/jdk/javax/net/ssl/templates/SSLClientContext.java b/test/jdk/javax/net/ssl/templates/SSLClientContext.java index 0b1281241c36a..46fcbaedcfd17 100644 --- a/test/jdk/javax/net/ssl/templates/SSLClientContext.java +++ b/test/jdk/javax/net/ssl/templates/SSLClientContext.java @@ -1,6 +1,5 @@ /* * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. - * Copyright (c) 2025, 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 From c07d4fc70bb4d880f2b1d38cf27caa7118ea32ed Mon Sep 17 00:00:00 2001 From: Artur Barashev Date: Fri, 5 Dec 2025 17:24:58 -0500 Subject: [PATCH 12/12] Add DefaultCertCompression unit test --- .../DefaultCertCompression.java | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 test/jdk/sun/security/ssl/CertificateCompression/DefaultCertCompression.java diff --git a/test/jdk/sun/security/ssl/CertificateCompression/DefaultCertCompression.java b/test/jdk/sun/security/ssl/CertificateCompression/DefaultCertCompression.java new file mode 100644 index 0000000000000..8c9b25013fc8c --- /dev/null +++ b/test/jdk/sun/security/ssl/CertificateCompression/DefaultCertCompression.java @@ -0,0 +1,154 @@ +/* + * 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.assertTrue; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/* + * @test + * @bug 8372526 + * @summary Add support for ZLIB TLS Certificate Compression. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DefaultCertCompression + * @run main/othervm DefaultCertCompression -Djdk.tls.enableCertificateCompression=false + */ + +public class DefaultCertCompression extends SSLEngineTemplate { + + protected static final int CLI_HELLO_MSG = 1; + protected static final int COMP_CERT_EXT = 27; + // zlib(1), brotli(2), zstd(3) + protected static final List DEFAULT_COMP_ALGS = List.of(1); + private final boolean certCompEnabled; + + protected DefaultCertCompression() throws Exception { + certCompEnabled = Boolean.parseBoolean(System.getProperty( + "jdk.tls.enableCertificateCompression", "true")); + super(); + } + + public static void main(String[] args) throws Exception { + new DefaultCertCompression().run(); + } + + protected void run() throws Exception { + + // Produce client_hello + clientEngine.wrap(clientOut, cTOs); + cTOs.flip(); + + checkClientHello(); + } + + protected void checkClientHello() throws Exception { + if (certCompEnabled) { + assertTrue(DEFAULT_COMP_ALGS.equals(getCompAlgsCliHello( + extractHandshakeMsg(cTOs, CLI_HELLO_MSG, false)))); + } else { + assertEquals(getCompAlgsCliHello( + extractHandshakeMsg(cTOs, CLI_HELLO_MSG, false)).size(), 0, + "compress_certificate extension present in ClientHello"); + } + } + + /** + * Parses the ClientHello message and extracts from it a list of + * compression algorithm values. It is assumed that the provided + * ByteBuffer has its position set at the first byte of the ClientHello + * message body (AFTER the handshake header) and contains the entire + * hello message. Upon successful completion of this method the ByteBuffer + * will have its position reset to the initial offset in the buffer. + * If an exception is thrown the position at the time of the exception + * will be preserved. + * + * @param data The ByteBuffer containing the ClientHello bytes. + * @return A List of the compression algorithm values. + */ + protected List getCompAlgsCliHello(ByteBuffer data) { + Objects.requireNonNull(data); + data.mark(); + + // Skip over the protocol version and client random + data.position(data.position() + 34); + + // Jump past the session ID (if there is one) + int sessLen = Byte.toUnsignedInt(data.get()); + if (sessLen != 0) { + data.position(data.position() + sessLen); + } + + // Jump past the cipher suites + int csLen = Short.toUnsignedInt(data.getShort()); + if (csLen != 0) { + data.position(data.position() + csLen); + } + + // ...and the compression + int compLen = Byte.toUnsignedInt(data.get()); + if (compLen != 0) { + data.position(data.position() + compLen); + } + + List extSigAlgs = getCompAlgsFromExt(data); + + // We should be at the end of the ClientHello + data.reset(); + return extSigAlgs; + } + + /** + * Gets compression algorithms from the given TLS extension. + * The buffer should be positioned at the start of the extension. + */ + protected List getCompAlgsFromExt(ByteBuffer data) { + + List extCompAlgs = new ArrayList<>(); + data.getShort(); // read length + + while (data.hasRemaining()) { + int extType = Short.toUnsignedInt(data.getShort()); + int extLen = Short.toUnsignedInt(data.getShort()); + + if (extType == COMP_CERT_EXT) { + int sigSchemeLen = data.get(); + + for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { + Integer schemeName = Short.toUnsignedInt(data.getShort()); + extCompAlgs.add(schemeName); + } + } else { + // Not the extension we're looking for. Skip past the + // extension data + data.position(data.position() + extLen); + } + } + + return extCompAlgs; + } +}