diff --git a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java index 753c8213a59..8f219586dba 100644 --- a/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/CertSignAlgsExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 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 @@ -25,6 +25,8 @@ package sun.security.ssl; +import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; + import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; @@ -96,26 +98,27 @@ public byte[] produce(ConnectionContext context, } // Produce the extension. - if (chc.localSupportedSignAlgs == null) { - chc.localSupportedSignAlgs = - SignatureScheme.getSupportedAlgorithms( - chc.sslConfig, - chc.algorithmConstraints, chc.activeProtocols); + if (chc.localSupportedCertSignAlgs == null) { + chc.localSupportedCertSignAlgs = + SignatureScheme.getSupportedAlgorithms( + chc.sslConfig, + chc.algorithmConstraints, chc.activeProtocols, + CERTIFICATE_SCOPE); } int vectorLen = SignatureScheme.sizeInRecord() * - chc.localSupportedSignAlgs.size(); + chc.localSupportedCertSignAlgs.size(); byte[] extData = new byte[vectorLen + 2]; ByteBuffer m = ByteBuffer.wrap(extData); Record.putInt16(m, vectorLen); - for (SignatureScheme ss : chc.localSupportedSignAlgs) { + for (SignatureScheme ss : chc.localSupportedCertSignAlgs) { Record.putInt16(m, ss.id); } // Update the context. chc.handshakeExtensions.put( SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT, - new SignatureSchemesSpec(chc.localSupportedSignAlgs)); + new SignatureSchemesSpec(chc.localSupportedCertSignAlgs)); return extData; } @@ -195,7 +198,9 @@ public void consume(ConnectionContext context, SignatureScheme.getSupportedAlgorithms( shc.sslConfig, shc.algorithmConstraints, shc.negotiatedProtocol, - spec.signatureSchemes); + spec.signatureSchemes, + CERTIFICATE_SCOPE); + shc.peerRequestedCertSignSchemes = schemes; shc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes); @@ -244,24 +249,28 @@ public byte[] produce(ConnectionContext context, } // Produce the extension. - List sigAlgs = - SignatureScheme.getSupportedAlgorithms( - shc.sslConfig, - shc.algorithmConstraints, - List.of(shc.negotiatedProtocol)); + if (shc.localSupportedCertSignAlgs == null) { + shc.localSupportedCertSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.sslConfig, + shc.algorithmConstraints, + List.of(shc.negotiatedProtocol), + CERTIFICATE_SCOPE); + } - int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size(); + int vectorLen = SignatureScheme.sizeInRecord() + * shc.localSupportedCertSignAlgs.size(); byte[] extData = new byte[vectorLen + 2]; ByteBuffer m = ByteBuffer.wrap(extData); Record.putInt16(m, vectorLen); - for (SignatureScheme ss : sigAlgs) { + for (SignatureScheme ss : shc.localSupportedCertSignAlgs) { Record.putInt16(m, ss.id); } // Update the context. shc.handshakeExtensions.put( SSLExtension.CR_SIGNATURE_ALGORITHMS_CERT, - new SignatureSchemesSpec(shc.localSupportedSignAlgs)); + new SignatureSchemesSpec(shc.localSupportedCertSignAlgs)); return extData; } @@ -340,7 +349,9 @@ public void consume(ConnectionContext context, SignatureScheme.getSupportedAlgorithms( chc.sslConfig, chc.algorithmConstraints, chc.negotiatedProtocol, - spec.signatureSchemes); + spec.signatureSchemes, + CERTIFICATE_SCOPE); + chc.peerRequestedCertSignSchemes = schemes; chc.handshakeSession.setPeerSupportedSignatureAlgorithms(schemes); } 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 753219f4590..2647672c1a5 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, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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,6 +25,9 @@ package sun.security.ssl; +import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; +import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE; + import java.io.IOException; import java.nio.ByteBuffer; import java.security.PrivateKey; @@ -380,7 +383,6 @@ public void consume(ConnectionContext context, crm.getAuthorities(), (SSLEngine)chc.conContext.transport); } - if (clientAlias == null) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.warning("No available client authentication"); @@ -607,16 +609,33 @@ private T12CertificateRequestProducer() { public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { // The producing happens in server side only. - ServerHandshakeContext shc = (ServerHandshakeContext)context; + ServerHandshakeContext shc = (ServerHandshakeContext) context; + if (shc.localSupportedSignAlgs == null) { shc.localSupportedSignAlgs = - SignatureScheme.getSupportedAlgorithms( - shc.sslConfig, - shc.algorithmConstraints, shc.activeProtocols); + SignatureScheme.getSupportedAlgorithms( + shc.sslConfig, + shc.algorithmConstraints, shc.activeProtocols, + HANDSHAKE_SCOPE); + } + + if (shc.localSupportedCertSignAlgs == null) { + shc.localSupportedCertSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.sslConfig, + shc.algorithmConstraints, shc.activeProtocols, + CERTIFICATE_SCOPE); } - if (shc.localSupportedSignAlgs == null || - shc.localSupportedSignAlgs.isEmpty()) { + // According to TLSv1.2 RFC, CertificateRequest message must + // contain signature schemes supported for both: + // handshake signatures and certificate signatures. + List certReqSignAlgs = + new ArrayList<>(shc.localSupportedSignAlgs); + certReqSignAlgs.retainAll(shc.localSupportedCertSignAlgs); + + if (certReqSignAlgs == null || + certReqSignAlgs.isEmpty()) { throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "No supported signature algorithm"); } @@ -625,7 +644,7 @@ public byte[] produce(ConnectionContext context, shc.sslContext.getX509TrustManager().getAcceptedIssuers(); T12CertificateRequestMessage crm = new T12CertificateRequestMessage( shc, caCerts, shc.negotiatedCipherSuite.keyExchange, - shc.localSupportedSignAlgs); + certReqSignAlgs); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced CertificateRequest handshake message", crm); @@ -706,19 +725,28 @@ public void consume(ConnectionContext context, chc.handshakeProducers.put(SSLHandshake.CERTIFICATE.id, SSLHandshake.CERTIFICATE); - List sss = + List signAlgs = + SignatureScheme.getSupportedAlgorithms( + chc.sslConfig, + chc.algorithmConstraints, chc.negotiatedProtocol, + crm.algorithmIds, + HANDSHAKE_SCOPE); + + List signCertAlgs = SignatureScheme.getSupportedAlgorithms( chc.sslConfig, chc.algorithmConstraints, chc.negotiatedProtocol, - crm.algorithmIds); - if (sss == null || sss.isEmpty()) { + crm.algorithmIds, + CERTIFICATE_SCOPE); + + if (signAlgs == null || signAlgs.isEmpty() || signCertAlgs.isEmpty()) { throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "No supported signature algorithm"); } - chc.peerRequestedSignatureSchemes = sss; - chc.peerRequestedCertSignSchemes = sss; // use the same schemes - chc.handshakeSession.setPeerSupportedSignatureAlgorithms(sss); + chc.peerRequestedSignatureSchemes = signAlgs; + chc.peerRequestedCertSignSchemes = signCertAlgs; + chc.handshakeSession.setPeerSupportedSignatureAlgorithms(signCertAlgs); chc.peerSupportedAuthorities = crm.getAuthorities(); // For TLS 1.2, we need to use a combination of the CR message's 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 44a3cc38ef0..55b3a3dacb6 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, 2022, 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 @@ -82,7 +82,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 @@ -135,6 +135,7 @@ abstract class HandshakeContext implements ConnectionContext { // SignatureScheme List localSupportedSignAlgs; + List localSupportedCertSignAlgs; List peerRequestedSignatureSchemes; List peerRequestedCertSignSchemes; diff --git a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java index 692fd74ba10..a5f9c8c5491 100644 --- a/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java +++ b/src/java.base/share/classes/sun/security/ssl/PostHandshakeContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, 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 @@ -43,7 +43,7 @@ final class PostHandshakeContext extends HandshakeContext { "Post-handshake not supported in " + negotiatedProtocol.name); } - this.localSupportedSignAlgs = new ArrayList<>( + this.localSupportedCertSignAlgs = new ArrayList<>( context.conSession.getLocalSupportedSignatureSchemes()); // Add the potential post-handshake consumers. diff --git a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java index 4bcf8dce262..9f0b6716624 100644 --- a/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/PreSharedKeyExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -42,6 +42,7 @@ import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; import static sun.security.ssl.SSLExtension.*; +import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; /** * Pack of the "pre_shared_key" extension. @@ -414,15 +415,16 @@ private static boolean canRejoin(ClientHelloMessage clientHello, result = false; } - // Make sure that the server handshake context's localSupportedSignAlgs - // field is populated. This is particularly important when - // client authentication was used in an initial session and it is - // now being resumed. - if (shc.localSupportedSignAlgs == null) { - shc.localSupportedSignAlgs = + // Make sure that the server handshake context's + // localSupportedCertSignAlgs field is populated. This is particularly + // important when client authentication was used in an initial session, + // and it is now being resumed. + if (shc.localSupportedCertSignAlgs == null) { + shc.localSupportedCertSignAlgs = SignatureScheme.getSupportedAlgorithms( shc.sslConfig, - shc.algorithmConstraints, shc.activeProtocols); + shc.algorithmConstraints, shc.activeProtocols, + CERTIFICATE_SCOPE); } // Validate the required client authentication. @@ -444,7 +446,7 @@ private static boolean canRejoin(ClientHelloMessage clientHello, Collection sessionSigAlgs = s.getLocalSupportedSignatureSchemes(); if (result && - !shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) { + !shc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Can't resume. Session uses different " + @@ -638,7 +640,7 @@ public byte[] produce(ConnectionContext context, // Make sure the list of supported signature algorithms matches Collection sessionSigAlgs = chc.resumingSession.getLocalSupportedSignatureSchemes(); - if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) { + if (!chc.localSupportedCertSignAlgs.containsAll(sessionSigAlgs)) { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Existing session uses different " + "signature algorithms"); diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java index 59662ab8fd2..171026f1a93 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -42,11 +42,11 @@ */ final class SSLAlgorithmConstraints implements AlgorithmConstraints { - private static final AlgorithmConstraints tlsDisabledAlgConstraints = + private static final DisabledAlgorithmConstraints tlsDisabledAlgConstraints = new DisabledAlgorithmConstraints(PROPERTY_TLS_DISABLED_ALGS, new SSLAlgorithmDecomposer()); - private static final AlgorithmConstraints x509DisabledAlgConstraints = + private static final DisabledAlgorithmConstraints x509DisabledAlgConstraints = new DisabledAlgorithmConstraints(PROPERTY_CERTPATH_DISABLED_ALGS, new SSLAlgorithmDecomposer(true)); @@ -56,11 +56,11 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints { private final boolean enabledX509DisabledAlgConstraints; // the default algorithm constraints - static final AlgorithmConstraints DEFAULT = + static final SSLAlgorithmConstraints DEFAULT = new SSLAlgorithmConstraints(null, true); // the default SSL only algorithm constraints - static final AlgorithmConstraints DEFAULT_SSL_ONLY = + static final SSLAlgorithmConstraints DEFAULT_SSL_ONLY = new SSLAlgorithmConstraints(null, false); private SSLAlgorithmConstraints(AlgorithmConstraints userSpecifiedConstraints, @@ -84,11 +84,11 @@ private SSLAlgorithmConstraints( * @param userSpecifiedConstraints additional constraints to check * @return a SSLAlgorithmConstraints instance */ - static AlgorithmConstraints wrap(AlgorithmConstraints userSpecifiedConstraints) { + static SSLAlgorithmConstraints wrap(AlgorithmConstraints userSpecifiedConstraints) { return wrap(userSpecifiedConstraints, true); } - private static AlgorithmConstraints wrap( + private static SSLAlgorithmConstraints wrap( AlgorithmConstraints userSpecifiedConstraints, boolean withDefaultCertPathConstraints) { if (nullIfDefault(userSpecifiedConstraints) == null) { @@ -199,22 +199,22 @@ public boolean permits(Set primitives, if (peerSpecifiedConstraints != null) { permitted = peerSpecifiedConstraints.permits( - primitives, algorithm, parameters); + primitives, algorithm, parameters); } if (permitted && userSpecifiedConstraints != null) { permitted = userSpecifiedConstraints.permits( - primitives, algorithm, parameters); + primitives, algorithm, parameters); } if (permitted) { permitted = tlsDisabledAlgConstraints.permits( - primitives, algorithm, parameters); + primitives, algorithm, parameters); } if (permitted && enabledX509DisabledAlgConstraints) { permitted = x509DisabledAlgConstraints.permits( - primitives, algorithm, parameters); + primitives, algorithm, parameters); } return permitted; @@ -252,27 +252,31 @@ public boolean permits(Set primitives, if (peerSpecifiedConstraints != null) { permitted = peerSpecifiedConstraints.permits( - primitives, algorithm, key, parameters); + primitives, algorithm, key, parameters); } if (permitted && userSpecifiedConstraints != null) { permitted = userSpecifiedConstraints.permits( - primitives, algorithm, key, parameters); + primitives, algorithm, key, parameters); } if (permitted) { permitted = tlsDisabledAlgConstraints.permits( - primitives, algorithm, key, parameters); + primitives, algorithm, key, parameters); } if (permitted && enabledX509DisabledAlgConstraints) { permitted = x509DisabledAlgConstraints.permits( - primitives, algorithm, key, parameters); + primitives, algorithm, key, parameters); } return permitted; } + // Checks if algorithm is disabled for the given TLS scopes. + boolean permits(String algorithm, Set scopes) { + return tlsDisabledAlgConstraints.permits(algorithm, scopes); + } private static class SupportedSignatureAlgorithmConstraints implements AlgorithmConstraints { diff --git a/src/java.base/share/classes/sun/security/ssl/SSLScope.java b/src/java.base/share/classes/sun/security/ssl/SSLScope.java new file mode 100644 index 00000000000..d2f4dfc14a3 --- /dev/null +++ b/src/java.base/share/classes/sun/security/ssl/SSLScope.java @@ -0,0 +1,55 @@ +/* + * 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 + * 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; + +/* + * Scopes defining different parts of TLS protocol. + */ + +public enum SSLScope { + // Handshake signature scope as in signature_algorithms extension. + HANDSHAKE_SIGNATURE("HandshakeSignature"), + + // Certificate signature scope as in signature_algorithms_cert extension. + CERTIFICATE_SIGNATURE("CertificateSignature"); + + private final String name; + + SSLScope(String name) { + this.name = name; + } + + // Note: the SSLScope name is case-insensitive. + public static SSLScope nameOf(String scopeName) { + for (SSLScope scope : SSLScope.values()) { + if (scope.name.equalsIgnoreCase(scopeName)) { + return scope; + } + } + + return null; + } +} diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 8ab7b7917b5..263313b9f8a 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -183,10 +183,10 @@ final class SSLSessionImpl extends ExtendedSSLSession { this.sessionId = id; this.host = hc.conContext.transport.getPeerHost(); this.port = hc.conContext.transport.getPeerPort(); - this.localSupportedSignAlgs = hc.localSupportedSignAlgs == null ? + this.localSupportedSignAlgs = hc.localSupportedCertSignAlgs == null ? Collections.emptySet() : Collections.unmodifiableCollection( - new ArrayList<>(hc.localSupportedSignAlgs)); + new ArrayList<>(hc.localSupportedCertSignAlgs)); this.serverNameIndication = hc.negotiatedServerName; this.requestedServerNames = Collections.unmodifiableList( new ArrayList<>(hc.getRequestedServerNames())); 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 812ec896d28..1c9076b3811 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, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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,6 +25,9 @@ package sun.security.ssl; +import static sun.security.ssl.SignatureScheme.CERTIFICATE_SCOPE; +import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE; + import java.io.IOException; import java.nio.ByteBuffer; import java.security.AlgorithmConstraints; @@ -276,7 +279,16 @@ public byte[] produce(ConnectionContext context, shc.localSupportedSignAlgs = SignatureScheme.getSupportedAlgorithms( shc.sslConfig, - shc.algorithmConstraints, shc.activeProtocols); + shc.algorithmConstraints, shc.activeProtocols, + HANDSHAKE_SCOPE); + } + + if (shc.localSupportedCertSignAlgs == null) { + shc.localSupportedCertSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.sslConfig, + shc.algorithmConstraints, shc.activeProtocols, + CERTIFICATE_SCOPE); } SSLSessionImpl session = @@ -505,7 +517,16 @@ public byte[] produce(ConnectionContext context, shc.localSupportedSignAlgs = SignatureScheme.getSupportedAlgorithms( shc.sslConfig, - shc.algorithmConstraints, shc.activeProtocols); + shc.algorithmConstraints, shc.activeProtocols, + HANDSHAKE_SCOPE); + } + + if (shc.localSupportedCertSignAlgs == null) { + shc.localSupportedCertSignAlgs = + SignatureScheme.getSupportedAlgorithms( + shc.sslConfig, + shc.algorithmConstraints, shc.activeProtocols, + CERTIFICATE_SCOPE); } SSLSessionImpl session = diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java index 541c51d2c64..5511d459a4e 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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,6 +25,8 @@ package sun.security.ssl; +import static sun.security.ssl.SignatureScheme.HANDSHAKE_SCOPE; + import java.io.IOException; import java.nio.ByteBuffer; import java.text.MessageFormat; @@ -186,7 +188,8 @@ public byte[] produce(ConnectionContext context, chc.localSupportedSignAlgs = SignatureScheme.getSupportedAlgorithms( chc.sslConfig, - chc.algorithmConstraints, chc.activeProtocols); + chc.algorithmConstraints, chc.activeProtocols, + HANDSHAKE_SCOPE); } int vectorLen = SignatureScheme.sizeInRecord() * @@ -280,7 +283,9 @@ public void consume(ConnectionContext context, SignatureScheme.getSupportedAlgorithms( shc.sslConfig, shc.algorithmConstraints, shc.negotiatedProtocol, - spec.signatureSchemes); + spec.signatureSchemes, + HANDSHAKE_SCOPE); + if (sss == null || sss.isEmpty()) { throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "No supported signature algorithm"); @@ -418,7 +423,8 @@ public byte[] produce(ConnectionContext context, SignatureScheme.getSupportedAlgorithms( shc.sslConfig, shc.algorithmConstraints, - List.of(shc.negotiatedProtocol)); + List.of(shc.negotiatedProtocol), + HANDSHAKE_SCOPE); int vectorLen = SignatureScheme.sizeInRecord() * sigAlgs.size(); byte[] extData = new byte[vectorLen + 2]; @@ -519,7 +525,9 @@ public void consume(ConnectionContext context, SignatureScheme.getSupportedAlgorithms( chc.sslConfig, chc.algorithmConstraints, chc.negotiatedProtocol, - spec.signatureSchemes); + spec.signatureSchemes, + HANDSHAKE_SCOPE); + if (sss == null || sss.isEmpty()) { throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "No supported signature algorithm"); diff --git a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java index b6003816eda..55ee2e4bc2e 100644 --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -35,7 +35,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -219,10 +218,17 @@ static enum SigAlgParamSpec { // support RSASSA-PSS only now } } - // performance optimization - private static final Set SIGNATURE_PRIMITIVE_SET = - Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); + // Handshake signature scope. + static final Set HANDSHAKE_SCOPE = + Set.of(SSLScope.HANDSHAKE_SIGNATURE); + + // Certificate signature scope. + static final Set CERTIFICATE_SCOPE = + Set.of(SSLScope.CERTIFICATE_SIGNATURE); + // Non-TLS specific SIGNATURE CryptoPrimitive. + private static final Set SIGNATURE_PRIMITIVE_SET = + Set.of(CryptoPrimitive.SIGNATURE); private SignatureScheme(int id, String name, String algorithm, String keyAlgorithm, @@ -355,24 +361,25 @@ static int sizeInRecord() { return 2; } - private boolean isPermitted(AlgorithmConstraints constraints) { - return constraints.permits(SIGNATURE_PRIMITIVE_SET, - this.name, null) && - constraints.permits(SIGNATURE_PRIMITIVE_SET, - this.keyAlgorithm, null) && - constraints.permits(SIGNATURE_PRIMITIVE_SET, - this.algorithm, (signAlgParams != null ? - signAlgParams.parameters : null)) && - (namedGroup != null ? - namedGroup.isPermitted(constraints) : true); + private boolean isPermitted( + SSLAlgorithmConstraints constraints, Set scopes) { + return constraints.permits(this.name, scopes) + && constraints.permits(this.keyAlgorithm, scopes) + && constraints.permits(this.algorithm, scopes) + && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.name, null) + && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.keyAlgorithm, null) + && constraints.permits(SIGNATURE_PRIMITIVE_SET, this.algorithm, + (signAlgParams != null ? signAlgParams.parameters : null)) + && (namedGroup == null || namedGroup.isPermitted(constraints)); } // Get local supported algorithm collection complying to algorithm - // constraints. + // constraints and SSL scopes. static List getSupportedAlgorithms( SSLConfiguration config, - AlgorithmConstraints constraints, - List activeProtocols) { + SSLAlgorithmConstraints constraints, + List activeProtocols, + Set scopes) { List supported = new LinkedList<>(); for (SignatureScheme ss: SignatureScheme.values()) { if (!ss.isAvailable || @@ -388,14 +395,14 @@ static List getSupportedAlgorithms( boolean isMatch = false; for (ProtocolVersion pv : activeProtocols) { - if (ss.supportedProtocols.contains(pv)) { + if (ss.isSupportedProtocol(pv, scopes)) { isMatch = true; break; } } if (isMatch) { - if (ss.isPermitted(constraints)) { + if (ss.isPermitted(constraints, scopes)) { supported.add(ss); } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { @@ -414,8 +421,10 @@ static List getSupportedAlgorithms( static List getSupportedAlgorithms( SSLConfiguration config, - AlgorithmConstraints constraints, - ProtocolVersion protocolVersion, int[] algorithmIds) { + SSLAlgorithmConstraints constraints, + ProtocolVersion protocolVersion, + int[] algorithmIds, + Set scopes) { List supported = new LinkedList<>(); for (int ssid : algorithmIds) { SignatureScheme ss = SignatureScheme.valueOf(ssid); @@ -425,11 +434,9 @@ static List getSupportedAlgorithms( "Unsupported signature scheme: " + SignatureScheme.nameOf(ssid)); } - } else if (ss.isAvailable && - ss.supportedProtocols.contains(protocolVersion) && - (config.signatureSchemes.isEmpty() || - config.signatureSchemes.contains(ss)) && - ss.isPermitted(constraints)) { + } else if ((config.signatureSchemes.isEmpty() + || config.signatureSchemes.contains(ss)) + && ss.isAllowed(constraints, protocolVersion, scopes)) { supported.add(ss); } else { if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -443,16 +450,14 @@ static List getSupportedAlgorithms( } static SignatureScheme getPreferableAlgorithm( - AlgorithmConstraints constraints, + SSLAlgorithmConstraints constraints, List schemes, SignatureScheme certScheme, ProtocolVersion version) { for (SignatureScheme ss : schemes) { - if (ss.isAvailable && - ss.handshakeSupportedProtocols.contains(version) && - certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) && - ss.isPermitted(constraints)) { + if (certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) + && ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) { return ss; } } @@ -461,7 +466,7 @@ static SignatureScheme getPreferableAlgorithm( } static Map.Entry getSignerOfPreferableAlgorithm( - AlgorithmConstraints constraints, + SSLAlgorithmConstraints constraints, List schemes, X509Possession x509Possession, ProtocolVersion version) { @@ -477,10 +482,9 @@ static Map.Entry getSignerOfPreferableAlgorithm( keySize = Integer.MAX_VALUE; } for (SignatureScheme ss : schemes) { - if (ss.isAvailable && (keySize >= ss.minimalKeySize) && - ss.handshakeSupportedProtocols.contains(version) && + if (keySize >= ss.minimalKeySize && keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm) && - ss.isPermitted(constraints)) { + ss.isAllowed(constraints, version, HANDSHAKE_SCOPE)) { if ((ss.namedGroup != null) && (ss.namedGroup.spec == NamedGroupSpec.NAMED_GROUP_ECDHE)) { ECParameterSpec params = @@ -540,6 +544,26 @@ static Map.Entry getSignerOfPreferableAlgorithm( return null; } + // Returns true if this signature scheme is supported for the given + // protocol version and SSL scopes. + private boolean isSupportedProtocol( + ProtocolVersion version, Set scopes) { + if (scopes != null && scopes.equals(HANDSHAKE_SCOPE)) { + return this.handshakeSupportedProtocols.contains(version); + } else { + return this.supportedProtocols.contains(version); + } + } + + // Returns true if this signature scheme is available, supported and + // permitted for the given constraints, protocol version and SSL scopes. + private boolean isAllowed(SSLAlgorithmConstraints constraints, + ProtocolVersion version, Set scopes) { + return isAvailable + && isSupportedProtocol(version, scopes) + && isPermitted(constraints, scopes); + } + static String[] getAlgorithmNames(Collection schemes) { if (schemes != null) { ArrayList names = new ArrayList<>(schemes.size()); diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java index 00e4031921f..ce8c845fe01 100644 --- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -25,6 +25,7 @@ package sun.security.util; +import sun.security.ssl.SSLScope; import sun.security.validator.Validator; import java.lang.ref.SoftReference; @@ -47,6 +48,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -179,6 +181,12 @@ public final boolean permits(Set primitives, return true; } + // Checks if algorithm is disabled for the given TLS scopes. + public boolean permits(String algorithm, Set scopes) { + List list = algorithmConstraints.getConstraints(algorithm); + return list == null || list.stream().allMatch(c -> c.permits(scopes)); + } + /* * Checks if the key algorithm has been disabled or constraints have been * placed on the key. @@ -431,7 +439,7 @@ public Constraints(String propertyName, Set constraintSet) { denyAfterLimit = true; } else if (entry.startsWith("usage")) { String s[] = (entry.substring(5)).trim().split(" "); - c = new UsageConstraint(algorithm, s); + c = new UsageConstraint(algorithm, s, propertyName); if (debug != null) { debug.println("Constraints usage length is " + s.length); } @@ -602,6 +610,17 @@ public boolean permits(AlgorithmParameters parameters) { return true; } + /** + * Check if the algorithm constraint permits the given TLS scopes. + * + * @param scopes TLS scopes + * @return 'true' if TLS scopes are allowed, + * 'false' otherwise. + */ + public boolean permits(Set scopes) { + return true; + } + /** * Check if an algorithm constraint is permitted with a given * ConstraintsParameters. @@ -779,14 +798,49 @@ public boolean permits(Key key) { /* * The usage constraint is for the "usage" keyword. It checks against the - * variant value in ConstraintsParameters. + * variant value in ConstraintsParameters and against TLS scopes. */ private static class UsageConstraint extends Constraint { String[] usages; + Set scopes; - UsageConstraint(String algorithm, String[] usages) { + UsageConstraint( + String algorithm, String[] usages, String propertyName) { this.algorithm = algorithm; - this.usages = usages; + + // Support TLS scopes only for jdk.tls.disabledAlgorithms property. + if (PROPERTY_TLS_DISABLED_ALGS.equals(propertyName)) { + for (String usage : usages) { + SSLScope scope = SSLScope.nameOf(usage); + + if (scope != null) { + if (this.scopes == null) { + this.scopes = new HashSet<>(usages.length); + } + this.scopes.add(scope); + } else { + this.usages = usages; + } + } + + if (this.scopes != null && this.usages != null) { + throw new IllegalArgumentException( + "Can't mix TLS protocol specific constraints" + + " with other usage constraints"); + } + + } else { + this.usages = usages; + } + } + + @Override + public boolean permits(Set scopes) { + if (this.scopes == null || scopes == null) { + return true; + } + + return Collections.disjoint(this.scopes, scopes); } @Override diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 34711c29f9b..1beae6493b6 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -745,11 +745,30 @@ jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \ # This is in addition to the jdk.certpath.disabledAlgorithms property above. # # See the specification of "jdk.certpath.disabledAlgorithms" for the -# syntax of the disabled algorithm string. Additionally, TLS cipher suites -# can be disabled with this property using one or more "*" wildcard characters. -# For example, "TLS_RSA_*" disables all cipher suites that start with -# "TLS_RSA_". Only cipher suites starting with "TLS_" are allowed to have -# wildcard characters. +# syntax of the disabled algorithm string. +# +# Additional TLS-specific syntax supported by this property: +# +# - TLS cipher suites can be disabled with this property using one or more +# "*" wildcard characters. For example, "TLS_RSA_*" disables all cipher +# suites that start with "TLS_RSA_". Only cipher suites starting with +# "TLS_" are allowed to have wildcard characters. +# +# - TLS protocol specific usage constraints are supported by this property: +# +# UsageConstraint: +# usage UsageType { UsageType } +# +# UsageType: +# HandshakeSignature | CertificateSignature +# +# HandshakeSignature restricts the use of the algorithm in TLS handshake +# signatures. CertificateSignature restricts the use of the algorithm in +# certificate signatures. An algorithm with this constraint cannot include +# other usage types defined in the jdk.certpath.disabledAlgorithms +# property. The usage type follows the keyword and more than one usage type +# can be specified with a whitespace delimiter. +# Example: "rsa_pkcs1_sha1 usage HandshakeSignature" # # Note: The algorithm restrictions do not apply to trust anchors or # self-signed certificates. diff --git a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java index 621aaea3546..2f24128ea56 100644 --- a/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java +++ b/test/jdk/javax/net/ssl/templates/SSLEngineTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -31,6 +31,8 @@ * @build SSLContextTemplate * @run main/othervm SSLEngineTemplate */ + +import java.util.Objects; import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import java.nio.ByteBuffer; @@ -256,4 +258,87 @@ static void checkTransfer(ByteBuffer a, ByteBuffer b) a.limit(a.capacity()); b.limit(b.capacity()); } + + /* Implementation of ByteBuffer.slice(int, int) for JDK11 */ + protected static final ByteBuffer slice(ByteBuffer buffer, int index, int length) { + final int limit = buffer.limit(); + final int position = buffer.position(); + buffer.position(index); + buffer.limit(index + length); + ByteBuffer slice = buffer.slice(); + buffer.limit(limit); + buffer.position(position); + return slice; + } + + /** + * Given a TLS record containing one or more handshake messages, return + * the specific handshake message as a ByteBuffer (a slice of the record) + * + * @param tlsRecord A ByteBuffer containing a TLS record. It is + * assumed that the position of the ByteBuffer is on the + * first byte of the TLS record header. + * @param hsMsgId The message identifier for the handshake message + * being sought. + * + * Message Identifiers + * + * @param isDtls Indicates whether DTLS protocol being used. + * @return a ByteBuffer containing the TLS handshake message. + * The position of the returned ByteBuffer will be on the + * first byte of the TLS handshake message data, + * immediately following the handshake header. + * If the message is not found, null will be returned. + * @throws SSLException if the incoming ByteBuffer does not contain + * a well-formed TLS message. + */ + protected static ByteBuffer extractHandshakeMsg( + ByteBuffer tlsRecord, int hsMsgId, boolean isDtls) + throws SSLException { + Objects.requireNonNull(tlsRecord); + tlsRecord.mark(); + + // Process the TLS record header + int type = Byte.toUnsignedInt(tlsRecord.get()); + int ver_major = Byte.toUnsignedInt(tlsRecord.get()); + int ver_minor = Byte.toUnsignedInt(tlsRecord.get()); + // Skip DTLS-specific bytes + if (isDtls) { + tlsRecord.position(tlsRecord.position() + 8); + } + int recLen = Short.toUnsignedInt(tlsRecord.getShort()); + + if (recLen > tlsRecord.remaining()) { + throw new SSLException("Incomplete record in buffer: " + + "Record length = " + recLen + + ", Remaining = " + + tlsRecord.remaining()); + } + + while (tlsRecord.hasRemaining()) { + // Grab the handshake message header. + int msgHdr = tlsRecord.getInt(); + int msgType = (msgHdr >> 24) & 0x000000FF; + int msgLen = msgHdr & 0x00FFFFFF; + // Skip DTLS-specific bytes + if (isDtls) { + tlsRecord.position(tlsRecord.position() + 8); + } + + if (msgType == hsMsgId) { + // Slice the buffer such that it contains the entire + // handshake message (less the handshake header). + ByteBuffer buf = slice(tlsRecord, tlsRecord.position(), msgLen); + tlsRecord.reset(); + return buf; + } else { + // Skip to the next handshake message, if there is one + tlsRecord.position(tlsRecord.position() + msgLen); + } + } + + tlsRecord.reset(); + return null; + } } diff --git a/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java new file mode 100644 index 00000000000..3b7d84a1edb --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/AbstractCheckSignatureSchemes.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2021, 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 + * 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 java.nio.ByteBuffer; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; + +/** + * This is not a test. Actual tests are implemented by concrete subclasses. + * The abstract class AbstractCheckSignatureSchemes provides a base framework + * for checking TLS signature schemes. + */ + +public abstract class AbstractCheckSignatureSchemes extends SSLEngineTemplate { + + // Helper map to correlate integral SignatureScheme identifiers to + // their IANA string name counterparts. + protected static final Map sigSchemeMap = Map.ofEntries( + new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"), + new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"), + new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"), + new SimpleImmutableEntry(0x0403, "ecdsa_secp256r1_sha256"), + new SimpleImmutableEntry(0x0503, "ecdsa_secp384r1_sha384"), + new SimpleImmutableEntry(0x0603, "ecdsa_secp521r1_sha512"), + new SimpleImmutableEntry(0x0804, "rsa_pss_rsae_sha256"), + new SimpleImmutableEntry(0x0805, "rsa_pss_rsae_sha384"), + new SimpleImmutableEntry(0x0806, "rsa_pss_rsae_sha512"), + new SimpleImmutableEntry(0x0807, "ed25519"), + new SimpleImmutableEntry(0x0808, "ed448"), + new SimpleImmutableEntry(0x0809, "rsa_pss_pss_sha256"), + new SimpleImmutableEntry(0x080a, "rsa_pss_pss_sha384"), + new SimpleImmutableEntry(0x080b, "rsa_pss_pss_sha512"), + new SimpleImmutableEntry(0x0101, "rsa_md5"), + new SimpleImmutableEntry(0x0201, "rsa_pkcs1_sha1"), + new SimpleImmutableEntry(0x0202, "dsa_sha1"), + new SimpleImmutableEntry(0x0203, "ecdsa_sha1"), + new SimpleImmutableEntry(0x0301, "rsa_sha224"), + new SimpleImmutableEntry(0x0302, "dsa_sha224"), + new SimpleImmutableEntry(0x0303, "ecdsa_sha224"), + new SimpleImmutableEntry(0x0402, "rsa_pkcs1_sha256")); + + // Other useful TLS definitions for these tests + protected static final int TLS_HS_CLI_HELLO = 1; + protected static final int TLS_HS_CERT_REQ = 13; + protected static final int SIG_ALGS_EXT = 13; + protected static final int SIG_ALGS_CERT_EXT = 50; + + protected AbstractCheckSignatureSchemes() throws Exception { + super(); + } + + // Returns the protocol for test to use. + abstract String getProtocol(); + + protected boolean isDtls() { + return getProtocol().startsWith("DTLS"); + } + + @Override + protected SSLEngine configureClientEngine(SSLEngine clientEngine) { + clientEngine.setUseClientMode(true); + clientEngine.setEnabledProtocols(new String[]{getProtocol()}); + return clientEngine; + } + + @Override + protected SSLEngine configureServerEngine(SSLEngine serverEngine) { + serverEngine.setUseClientMode(false); + serverEngine.setWantClientAuth(true); + serverEngine.setEnabledProtocols(new String[]{getProtocol()}); + return serverEngine; + } + + @Override + public ContextParameters getServerContextParameters() { + return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); + } + + @Override + public ContextParameters getClientContextParameters() { + return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); + } + + protected ByteBuffer extractHandshakeMsg(ByteBuffer tlsRecord, int hsMsgId) + throws SSLException { + return extractHandshakeMsg(tlsRecord, hsMsgId, isDtls()); + } + + /** + * Parses the ClientHello message and extracts from it a list of + * SignatureScheme values in string form. 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. + * @param extCode Code of the TLS extension from which to extract + * signature schemes. + * @return A List of the signature schemes in string form. + */ + protected List getSigSchemesCliHello( + ByteBuffer data, int extCode) { + 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); + } + + // Skip DTLS-specific opaque cookie if any + if (isDtls()) { + int cookieLen = Byte.toUnsignedInt(data.get()); + if (cookieLen != 0) { + data.position(data.position() + cookieLen); + } + } + + // 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); + } + + // Now for the fun part. Go through the extensions and look + // for the two status request exts. + List extSigAlgs = getSigSchemesFromExt(data, extCode); + + // We should be at the end of the ClientHello + data.reset(); + return extSigAlgs; + } + + /** + * Parses the CertificateRequest message and extracts from it a list of + * SignatureScheme values in string form. It is assumed that the provided + * ByteBuffer has its position set at the first byte of the + * CertificateRequest message body (AFTER the handshake header) and + * contains the entire CR 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 CertificateRequest bytes + * + * @return A List of the signature schemes in string form. If no + * signature_algorithms extension is present in the CertificateRequest + * then an empty list will be returned. + */ + protected List getSigSchemesCertReq(ByteBuffer data) { + Objects.requireNonNull(data); + data.mark(); + + // Jump past the certificate types + int certTypeLen = Byte.toUnsignedInt(data.get()); + if (certTypeLen != 0) { + data.position(data.position() + certTypeLen); + } + + // Collect the SignatureAndHashAlgorithms + List extSigAlgs = new ArrayList(); + int sigSchemeLen = Short.toUnsignedInt(data.getShort()); + for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { + String schemeName = sigSchemeMap.get( + Short.toUnsignedInt(data.getShort())); + if (schemeName != null) { + extSigAlgs.add(schemeName); + } + } + + data.reset(); + return extSigAlgs; + } + + /** + * Gets signatures schemes from the given TLS extension. + * The buffer should be positioned at the start of the extension. + */ + protected List getSigSchemesFromExt( + ByteBuffer data, int extCode) { + + List extSigAlgs = new ArrayList<>(); + data.getShort(); // read length + + while (data.hasRemaining()) { + int extType = Short.toUnsignedInt(data.getShort()); + int extLen = Short.toUnsignedInt(data.getShort()); + if (extType == extCode) { + // Start processing signature algorithms + int sigSchemeLen = Short.toUnsignedInt(data.getShort()); + for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { + String schemeName = sigSchemeMap.get( + Short.toUnsignedInt(data.getShort())); + if (schemeName != null) { + extSigAlgs.add(schemeName); + } + } + } else { + // Not the extension we're looking for. Skip past the + // extension data + data.position(data.position() + extLen); + } + } + + return extSigAlgs; + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java new file mode 100644 index 00000000000..55f1002e894 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeDTLS12.java @@ -0,0 +1,58 @@ +/* + * 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 + * 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 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers DTLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeDTLS12 + */ + +import java.security.Security; + +public class DisableSignatureSchemePerScopeDTLS12 + extends DisableSignatureSchemePerScopeTLS12 { + + protected DisableSignatureSchemePerScopeDTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeDTLS12().run(); + } + + @Override + protected String getProtocol() { + return "DTLSv1.2"; + } + + // No CertificateRequest in DTLS server flight. + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java new file mode 100644 index 00000000000..e2ee9f0889a --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS12.java @@ -0,0 +1,149 @@ +/* + * 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 + * 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 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers TLS 1.2. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeTLS12 + */ + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.security.Security; +import java.util.List; + +public class DisableSignatureSchemePerScopeTLS12 extends + AbstractCheckSignatureSchemes { + + // Disabled for Handshake scope. + protected static final String HANDSHAKE_DISABLED_SIG = "rsa_pss_rsae_sha384"; + + // Disabled for Certificate scope. + protected static final String CERTIFICATE_DISABLED_SIG = "ecdsa_secp384r1_sha384"; + + // jdk.tls.disabledAlgorithms value + // We differ from "HandshakeSignature" and "CertificateSignature" specified + // in java.security to check case-insensitive matching. + protected static final String DISABLED_CONSTRAINTS = + HANDSHAKE_DISABLED_SIG + " usage HandShakesignature, " + + CERTIFICATE_DISABLED_SIG + " usage certificateSignature"; + + protected DisableSignatureSchemePerScopeTLS12() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeTLS12().run(); + } + + protected String getProtocol() { + return "TLSv1.2"; + } + + // Run things in TLS handshake order. + protected void run() throws Exception { + + // Produce client_hello + clientEngine.wrap(clientOut, cTOs); + cTOs.flip(); + + checkClientHello(); + + // Consume client_hello. + serverEngine.unwrap(cTOs, serverIn); + runDelegatedTasks(serverEngine); + + // Produce server_hello. + serverEngine.wrap(serverOut, sTOc); + sTOc.flip(); + + checkCertificateRequest(); + } + + protected void checkClientHello() throws Exception { + // --- Check signature_algorithms extension --- + + // Get signature_algorithms extension signature schemes. + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + // signature_algorithms extension MUST NOT contain disabled + // handshake signature scheme. + assertFalse(sigAlgsSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " present in ClientHello's signature_algorithms extension"); + + // signature_algorithms extension MUST contain disabled + // certificate signature scheme. + assertTrue(sigAlgsSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " isn't present in ClientHello's" + + " signature_algorithms extension"); + + // --- Check signature_algorithms_cert extension --- + + // Get signature_algorithms_cert extension signature schemes. + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + // signature_algorithms_cert extension MUST contain disabled + // handshake signature scheme. + assertTrue(sigAlgsCertSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " isn't present in ClientHello's" + + " signature_algorithms extension"); + + // signature_algorithms_cert extension MUST NOT contain disabled + // certificate signature scheme. + assertFalse(sigAlgsCertSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " present in ClientHello's signature_algorithms extension"); + } + + protected void checkCertificateRequest() throws Exception { + // Get CertificateRequest message signature schemes. + List sigAlgsCertSS = getSigSchemesCertReq( + extractHandshakeMsg(sTOc, TLS_HS_CERT_REQ)); + + // TLSv1.2 CertificateRequest message MUST NOT contain both: + // disabled handshake signature scheme and disabled + // certificate signature scheme + + assertFalse(sigAlgsCertSS.contains(HANDSHAKE_DISABLED_SIG), + "Signature Scheme " + HANDSHAKE_DISABLED_SIG + + " present in CertificateRequest"); + + assertFalse(sigAlgsCertSS.contains(CERTIFICATE_DISABLED_SIG), + "Signature Scheme " + CERTIFICATE_DISABLED_SIG + + " present in CertificateRequest"); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java new file mode 100644 index 00000000000..5d5d445cb45 --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/DisableSignatureSchemePerScopeTLS13.java @@ -0,0 +1,103 @@ +/* + * 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 + * 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 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope. + * This test only covers TLS 1.3. + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm DisableSignatureSchemePerScopeTLS13 + */ + + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.security.Security; +import java.util.List; + +public class DisableSignatureSchemePerScopeTLS13 + extends DisableSignatureSchemePerScopeTLS12 { + + // Signature schemes not supported in TLSv1.3 only for the handshake. + // This is regardless of jdk.tls.disabledAlgorithms configuration. + List NOT_SUPPORTED_FOR_HANDSHAKE = List.of( + "rsa_pkcs1_sha1", + "rsa_pkcs1_sha256", + "rsa_pkcs1_sha384", + "rsa_pkcs1_sha512" + ); + + protected DisableSignatureSchemePerScopeTLS13() throws Exception { + super(); + } + + public static void main(String[] args) throws Exception { + Security.setProperty( + "jdk.tls.disabledAlgorithms", DISABLED_CONSTRAINTS); + new DisableSignatureSchemePerScopeTLS13().run(); + } + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @Override + protected void checkClientHello() throws Exception { + super.checkClientHello(); + + // Get signature_algorithms extension signature schemes. + List sigAlgsSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_EXT); + + // Should not be present in signature_algorithms extension. + NOT_SUPPORTED_FOR_HANDSHAKE.forEach(ss -> + assertFalse(sigAlgsSS.contains(ss), + "Signature Scheme " + ss + + " present in ClientHello's signature_algorithms extension")); + + // Get signature_algorithms_cert extension signature schemes. + List sigAlgsCertSS = getSigSchemesCliHello( + extractHandshakeMsg(cTOs, TLS_HS_CLI_HELLO), + SIG_ALGS_CERT_EXT); + + // Should be present in signature_algorithms_cert extension. + NOT_SUPPORTED_FOR_HANDSHAKE.forEach(ss -> + assertTrue(sigAlgsCertSS.contains(ss), + "Signature Scheme " + ss + + " isn't present in ClientHello's" + + " signature_algorithms extension")); + } + + // TLSv1.3 sends CertificateRequest signature schemes in + // signature_algorithms and signature_algorithms_cert extensions. Same as + // ClientHello, but they are encrypted. So we skip CertificateRequest + // signature schemes verification for TLSv1.3. + @Override + protected void checkCertificateRequest() { + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java b/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java new file mode 100644 index 00000000000..3dc811ca66c --- /dev/null +++ b/test/jdk/sun/security/ssl/SignatureScheme/MixingTLSUsageConstraintsWithNonTLS.java @@ -0,0 +1,56 @@ +/* + * 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 + * 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 8349583 + * @summary Add mechanism to disable signature schemes based on their TLS scope + * @library /javax/net/ssl/templates + * /test/lib + * @run main/othervm MixingTLSUsageConstraintsWithNonTLS + */ + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; +import static jdk.test.lib.Utils.runAndCheckException; + +import java.security.Security; + +public class MixingTLSUsageConstraintsWithNonTLS extends SSLSocketTemplate { + + public static void main(String[] args) throws Exception { + Security.setProperty("jdk.tls.disabledAlgorithms", + "rsa_pkcs1_sha1 usage handshakeSignature certificateSignature TLSServer"); + + runAndCheckException( + () -> new MixingTLSUsageConstraintsWithNonTLS().run(), + e -> { + assertTrue(e instanceof ExceptionInInitializerError); + assertTrue( + e.getCause() instanceof IllegalArgumentException); + assertEquals(e.getCause().getMessage(), + "Can't mix TLS protocol specific constraints" + + " with other usage constraints"); + }); + } +} diff --git a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java index f7eb7cf3e91..53f75410952 100644 --- a/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java +++ b/test/jdk/sun/security/ssl/SignatureScheme/SigAlgosExtTestWithTLS12.java @@ -345,18 +345,6 @@ private static void twistCertReqMsg( tlsRecord.reset(); } - /* Implementation of ByteBuffer.slice(int, int) for JDK11 */ - private static final ByteBuffer slice(ByteBuffer buffer, int index, int length) { - final int limit = buffer.limit(); - final int position = buffer.position(); - buffer.position(index); - buffer.limit(index + length); - ByteBuffer slice = buffer.slice(); - buffer.limit(limit); - buffer.position(position); - return slice; - } - /** * Replace the signature schemes in CertificateRequest message with an * alternative value. It is assumed that the provided ByteBuffer has its