diff --git a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java
index 80eb70984254a..2441ad91fde19 100644
--- a/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/SunX509KeyManagerImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -26,6 +26,7 @@
package sun.security.ssl;
import java.net.Socket;
+import java.security.AlgorithmConstraints;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -39,85 +40,58 @@
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.X509ExtendedKeyManager;
import javax.security.auth.x500.X500Principal;
/**
* An implementation of X509KeyManager backed by a KeyStore.
- *
+ *
* The backing KeyStore is inspected when this object is constructed.
* All key entries containing a PrivateKey and a non-empty chain of
* X509Certificate are then copied into an internal store. This means
* that subsequent modifications of the KeyStore have no effect on the
* X509KeyManagerImpl object.
- *
+ *
* Note that this class assumes that all keys are protected by the same
* password.
- *
- * The JSSE handshake code currently calls into this class via
- * chooseClientAlias() and chooseServerAlias() to find the certificates to
- * use. As implemented here, both always return the first alias returned by
- * getClientAliases() and getServerAliases(). In turn, these methods are
- * implemented by calling getAliases(), which performs the actual lookup.
- *
- * Note that this class currently implements no checking of the local
- * certificates. In particular, it is *not* guaranteed that:
- * . the certificates are within their validity period and not revoked
- * . the signatures verify
- * . they form a PKIX compliant chain.
- * . the certificate extensions allow the certificate to be used for
- * the desired purpose.
- *
- * Chains that fail any of these criteria will probably be rejected by
- * the remote peer.
+ *
+ * Algorithm constraints and certificate checks can be disabled by setting
+ * "jdk.tls.SunX509KeyManager.certChecking" system property to "false"
+ * before calling a class constructor.
*
*/
-final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
- private static final String[] STRING0 = new String[0];
+final class SunX509KeyManagerImpl extends X509KeyManagerCertChecking {
/*
* The credentials from the KeyStore as
* Map: String(alias) -> X509Credentials(credentials)
*/
- private final Map credentialsMap;
+ private final Map credentialsMap;
- /*
- * Cached server aliases for the case issuers == null.
- * (in the current JSSE implementation, issuers are always null for
- * server certs). See chooseServerAlias() for details.
- *
- * Map: String(keyType) -> String[](alias)
- */
- private final Map serverAliasCache;
+ @Override
+ protected boolean isCheckingDisabled() {
+ return "false".equalsIgnoreCase(System.getProperty(
+ "jdk.tls.SunX509KeyManager.certChecking", "true"));
+ }
/*
* Basic container for credentials implemented as an inner class.
*/
private static class X509Credentials {
+
final PrivateKey privateKey;
final X509Certificate[] certificates;
- private final Set issuerX500Principals;
X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) {
// assert privateKey and certificates != null
this.privateKey = privateKey;
this.certificates = certificates;
- this.issuerX500Principals = HashSet.newHashSet(certificates.length);
- for (X509Certificate certificate : certificates) {
- issuerX500Principals.add(certificate.getIssuerX500Principal());
- }
- }
-
- Set getIssuerX500Principals() {
- return issuerX500Principals;
}
}
@@ -126,14 +100,13 @@ Set getIssuerX500Principals() {
NoSuchAlgorithmException, UnrecoverableKeyException {
credentialsMap = new HashMap<>();
- serverAliasCache = Collections.synchronizedMap(
- new HashMap<>());
+
if (ks == null) {
return;
}
for (Enumeration aliases = ks.aliases();
- aliases.hasMoreElements(); ) {
+ aliases.hasMoreElements(); ) {
String alias = aliases.nextElement();
if (!ks.isKeyEntry(alias)) {
continue;
@@ -153,11 +126,11 @@ Set getIssuerX500Principals() {
certs = tmp;
}
- X509Credentials cred = new X509Credentials((PrivateKey)key,
- (X509Certificate[])certs);
+ X509Credentials cred = new X509Credentials((PrivateKey) key,
+ (X509Certificate[]) certs);
credentialsMap.put(alias, cred);
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("found key for : " + alias, (Object[])certs);
+ SSLLogger.fine("found key for : " + alias, (Object[]) certs);
}
}
}
@@ -205,24 +178,8 @@ public PrivateKey getPrivateKey(String alias) {
@Override
public String chooseClientAlias(String[] keyTypes, Principal[] issuers,
Socket socket) {
- /*
- * We currently don't do anything with socket, but
- * someday we might. It might be a useful hint for
- * selecting one of the aliases we get back from
- * getClientAliases().
- */
-
- if (keyTypes == null) {
- return null;
- }
-
- for (int i = 0; i < keyTypes.length; i++) {
- String[] aliases = getClientAliases(keyTypes[i], issuers);
- if ((aliases != null) && (aliases.length > 0)) {
- return aliases[0];
- }
- }
- return null;
+ return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
+ getAlgorithmConstraints(socket), null, null);
}
/*
@@ -230,17 +187,12 @@ public String chooseClientAlias(String[] keyTypes, Principal[] issuers,
* SSLEngine
connection given the public key type
* and the list of certificate issuer authorities recognized by
* the peer (if any).
- *
- * @since 1.5
*/
@Override
- public String chooseEngineClientAlias(String[] keyType,
+ public String chooseEngineClientAlias(String[] keyTypes,
Principal[] issuers, SSLEngine engine) {
- /*
- * If we ever start using socket as a selection criteria,
- * we'll need to adjust this.
- */
- return chooseClientAlias(keyType, issuers, null);
+ return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
+ getAlgorithmConstraints(engine), null, null);
}
/*
@@ -251,35 +203,9 @@ public String chooseEngineClientAlias(String[] keyType,
@Override
public String chooseServerAlias(String keyType,
Principal[] issuers, Socket socket) {
- /*
- * We currently don't do anything with socket, but
- * someday we might. It might be a useful hint for
- * selecting one of the aliases we get back from
- * getServerAliases().
- */
- if (keyType == null) {
- return null;
- }
-
- String[] aliases;
-
- if (issuers == null || issuers.length == 0) {
- aliases = serverAliasCache.get(keyType);
- if (aliases == null) {
- aliases = getServerAliases(keyType, issuers);
- // Cache the result (positive and negative lookups)
- if (aliases == null) {
- aliases = STRING0;
- }
- serverAliasCache.put(keyType, aliases);
- }
- } else {
- aliases = getServerAliases(keyType, issuers);
- }
- if ((aliases != null) && (aliases.length > 0)) {
- return aliases[0];
- }
- return null;
+ return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
+ getAlgorithmConstraints(socket),
+ X509TrustManagerImpl.getRequestedServerNames(socket), "HTTPS");
}
/*
@@ -287,17 +213,13 @@ public String chooseServerAlias(String keyType,
* SSLEngine
connection given the public key type
* and the list of certificate issuer authorities recognized by
* the peer (if any).
- *
- * @since 1.5
*/
@Override
public String chooseEngineServerAlias(String keyType,
Principal[] issuers, SSLEngine engine) {
- /*
- * If we ever start using socket as a selection criteria,
- * we'll need to adjust this.
- */
- return chooseServerAlias(keyType, issuers, null);
+ return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
+ getAlgorithmConstraints(engine),
+ X509TrustManagerImpl.getRequestedServerNames(engine), "HTTPS");
}
/*
@@ -307,7 +229,8 @@ public String chooseEngineServerAlias(String keyType,
*/
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
- return getAliases(keyType, issuers);
+ return getAliases(getKeyTypes(keyType), issuers, CheckType.CLIENT,
+ null, null, null);
}
/*
@@ -317,7 +240,23 @@ public String[] getClientAliases(String keyType, Principal[] issuers) {
*/
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
- return getAliases(keyType, issuers);
+ return getAliases(getKeyTypes(keyType), issuers, CheckType.SERVER,
+ null, null, null);
+ }
+
+ private String chooseAlias(List keyTypes, Principal[] issuers,
+ CheckType checkType, AlgorithmConstraints constraints,
+ List requestedServerNames, String idAlgorithm) {
+
+ String[] aliases = getAliases(
+ keyTypes, issuers, checkType,
+ constraints, requestedServerNames, idAlgorithm);
+
+ if (aliases != null && aliases.length > 0) {
+ return aliases[0];
+ }
+
+ return null;
}
/*
@@ -327,103 +266,46 @@ public String[] getServerAliases(String keyType, Principal[] issuers) {
*
* Issuers come to us in the form of X500Principal[].
*/
- private String[] getAliases(String keyType, Principal[] issuers) {
- if (keyType == null) {
+ private String[] getAliases(List keyTypes, Principal[] issuers,
+ CheckType checkType, AlgorithmConstraints constraints,
+ List requestedServerNames,
+ String idAlgorithm) {
+
+ if (keyTypes == null || keyTypes.isEmpty()) {
return null;
}
- if (issuers == null) {
- issuers = new X500Principal[0];
- }
- if (!(issuers instanceof X500Principal[])) {
- // normally, this will never happen but try to recover if it does
- issuers = convertPrincipals(issuers);
- }
- String sigType;
- if (keyType.contains("_")) {
- int k = keyType.indexOf('_');
- sigType = keyType.substring(k + 1);
- keyType = keyType.substring(0, k);
- } else {
- sigType = null;
- }
- X500Principal[] x500Issuers = (X500Principal[])issuers;
- // the algorithm below does not produce duplicates, so avoid Set
- List aliases = new ArrayList<>();
+ Set issuerSet = getIssuerSet(issuers);
+ List results = null;
- for (Map.Entry entry :
- credentialsMap.entrySet()) {
+ for (Map.Entry entry :
+ credentialsMap.entrySet()) {
- String alias = entry.getKey();
- X509Credentials credentials = entry.getValue();
- X509Certificate[] certs = credentials.certificates;
+ EntryStatus status = checkAlias(0, entry.getKey(),
+ entry.getValue().certificates,
+ null, keyTypes, issuerSet, checkType,
+ constraints, requestedServerNames, idAlgorithm);
- if (!keyType.equals(certs[0].getPublicKey().getAlgorithm())) {
+ if (status == null) {
continue;
}
- if (sigType != null) {
- if (certs.length > 1) {
- // if possible, check the public key in the issuer cert
- if (!sigType.equals(
- certs[1].getPublicKey().getAlgorithm())) {
- continue;
- }
- } else {
- // Check the signature algorithm of the certificate itself.
- // Look for the "withRSA" in "SHA1withRSA", etc.
- String sigAlgName =
- certs[0].getSigAlgName().toUpperCase(Locale.ENGLISH);
- String pattern = "WITH" +
- sigType.toUpperCase(Locale.ENGLISH);
- if (!sigAlgName.contains(pattern)) {
- continue;
- }
- }
- }
- if (issuers.length == 0) {
- // no issuer specified, match all
- aliases.add(alias);
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("matching alias: " + alias);
- }
- } else {
- Set certIssuers =
- credentials.getIssuerX500Principals();
- for (int i = 0; i < x500Issuers.length; i++) {
- if (certIssuers.contains(issuers[i])) {
- aliases.add(alias);
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("matching alias: " + alias);
- }
- break;
- }
- }
+ if (results == null) {
+ results = new ArrayList<>();
}
- }
- String[] aliasStrings = aliases.toArray(STRING0);
- return ((aliasStrings.length == 0) ? null : aliasStrings);
- }
+ results.add(status);
+ }
- /*
- * Convert an array of Principals to an array of X500Principals, if
- * possible. Principals that cannot be converted are ignored.
- */
- private static X500Principal[] convertPrincipals(Principal[] principals) {
- List list = new ArrayList<>(principals.length);
- for (int i = 0; i < principals.length; i++) {
- Principal p = principals[i];
- if (p instanceof X500Principal) {
- list.add((X500Principal)p);
- } else {
- try {
- list.add(new X500Principal(p.getName()));
- } catch (IllegalArgumentException e) {
- // ignore
- }
+ if (results == null) {
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine("KeyMgr: no matching key found");
}
+ return null;
}
- return list.toArray(new X500Principal[0]);
+
+ // Sort results in order of alias preference.
+ Collections.sort(results);
+ return results.stream().map(r -> r.alias).toArray(String[]::new);
}
}
diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java
new file mode 100644
index 0000000000000..00a7ae8435239
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerCertChecking.java
@@ -0,0 +1,579 @@
+/*
+ * 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;
+
+import java.net.Socket;
+import java.security.AlgorithmConstraints;
+import java.security.Principal;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import javax.net.ssl.ExtendedSSLSession;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.StandardConstants;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.security.auth.x500.X500Principal;
+import sun.security.provider.certpath.AlgorithmChecker;
+import sun.security.util.KnownOIDs;
+import sun.security.validator.Validator;
+
+/*
+ * Layer that adds algorithm constraints and certificate checking functionality
+ * to a key manager:
+ * 1) Check against peer supported certificate signature algorithms (sent with
+ * "signature_algorithms_cert" TLS extension).
+ * 2) Check against local TLS algorithm constraints ("java.security" config
+ * file).
+ * 3) Mark alias results based on validity period and certificate extensions,
+ * so results can be sorted to find the best match. See "CheckResult" and
+ * "EntryStatus" for details.
+ */
+
+abstract class X509KeyManagerCertChecking extends X509ExtendedKeyManager {
+
+ // Indicates whether we should skip the certificate checks.
+ private final boolean checksDisabled;
+
+ protected X509KeyManagerCertChecking() {
+ checksDisabled = isCheckingDisabled();
+ }
+
+ abstract boolean isCheckingDisabled();
+
+ // Entry point to do all certificate checks.
+ protected EntryStatus checkAlias(int keyStoreIndex, String alias,
+ Certificate[] chain, Date verificationDate, List keyTypes,
+ Set issuerSet, CheckType checkType,
+ AlgorithmConstraints constraints,
+ List requestedServerNames, String idAlgorithm) {
+
+ // --- Mandatory checks ---
+
+ if ((chain == null) || (chain.length == 0)) {
+ return null;
+ }
+
+ for (Certificate cert : chain) {
+ if (!(cert instanceof X509Certificate)) {
+ // Not an X509Certificate, ignore this alias
+ return null;
+ }
+ }
+
+ // Check key type, get key type index.
+ int keyIndex = -1;
+ int j = 0;
+
+ for (KeyType keyType : keyTypes) {
+ if (keyType.matches(chain)) {
+ keyIndex = j;
+ break;
+ }
+ j++;
+ }
+
+ if (keyIndex == -1) {
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine("Ignore alias " + alias
+ + ": key algorithm does not match");
+ }
+ return null;
+ }
+
+ // Check issuers
+ if (issuerSet != null && !issuerSet.isEmpty()) {
+ boolean found = false;
+ for (Certificate cert : chain) {
+ X509Certificate xcert = (X509Certificate) cert;
+ if (issuerSet.contains(xcert.getIssuerX500Principal())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine(
+ "Ignore alias " + alias
+ + ": issuers do not match");
+ }
+ return null;
+ }
+ }
+
+ // --- Optional checks, depending on "checksDisabled" toggle ---
+
+ // Check the algorithm constraints
+ if (constraints != null &&
+ !conformsToAlgorithmConstraints(constraints, chain,
+ checkType.getValidator())) {
+
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine("Ignore alias " + alias +
+ ": certificate chain does not conform to " +
+ "algorithm constraints");
+ }
+ return null;
+ }
+
+ // Endpoint certificate check
+ CheckResult checkResult = certificateCheck(checkType,
+ (X509Certificate) chain[0],
+ verificationDate == null ? new Date() : verificationDate,
+ requestedServerNames, idAlgorithm);
+
+ return new EntryStatus(
+ keyStoreIndex, keyIndex, alias, chain, checkResult);
+ }
+
+ // Gets algorithm constraints of the socket.
+ protected AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
+
+ if (checksDisabled) {
+ return null;
+ }
+
+ if (socket != null && socket.isConnected() &&
+ socket instanceof SSLSocket sslSocket) {
+
+ SSLSession session = sslSocket.getHandshakeSession();
+
+ if (session != null) {
+ if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
+ String[] peerSupportedSignAlgs = null;
+
+ if (session instanceof ExtendedSSLSession extSession) {
+ // Peer supported certificate signature algorithms
+ // sent with "signature_algorithms_cert" TLS extension.
+ peerSupportedSignAlgs =
+ extSession.getPeerSupportedSignatureAlgorithms();
+ }
+
+ return SSLAlgorithmConstraints.forSocket(
+ sslSocket, peerSupportedSignAlgs, true);
+ }
+ }
+
+ return SSLAlgorithmConstraints.forSocket(sslSocket, true);
+ }
+
+ return SSLAlgorithmConstraints.DEFAULT;
+ }
+
+ // Gets algorithm constraints of the engine.
+ protected AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
+
+ if (checksDisabled) {
+ return null;
+ }
+
+ if (engine != null) {
+ SSLSession session = engine.getHandshakeSession();
+ if (session != null) {
+ if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
+ String[] peerSupportedSignAlgs = null;
+
+ if (session instanceof ExtendedSSLSession extSession) {
+ // Peer supported certificate signature algorithms
+ // sent with "signature_algorithms_cert" TLS extension.
+ peerSupportedSignAlgs =
+ extSession.getPeerSupportedSignatureAlgorithms();
+ }
+
+ return SSLAlgorithmConstraints.forEngine(
+ engine, peerSupportedSignAlgs, true);
+ }
+ }
+ }
+
+ return SSLAlgorithmConstraints.forEngine(engine, true);
+ }
+
+ // Algorithm constraints check.
+ private boolean conformsToAlgorithmConstraints(
+ AlgorithmConstraints constraints, Certificate[] chain,
+ String variant) {
+
+ if (checksDisabled) {
+ return true;
+ }
+
+ AlgorithmChecker checker = new AlgorithmChecker(constraints, variant);
+ try {
+ checker.init(false);
+ } catch (CertPathValidatorException cpve) {
+ // unlikely to happen
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine(
+ "Cannot initialize algorithm constraints checker",
+ cpve);
+ }
+
+ return false;
+ }
+
+ // It is a forward checker, so we need to check from trust to target.
+ for (int i = chain.length - 1; i >= 0; i--) {
+ Certificate cert = chain[i];
+ try {
+ // We don't care about the unresolved critical extensions.
+ checker.check(cert, Collections.emptySet());
+ } catch (CertPathValidatorException cpve) {
+ if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine("Certificate does not conform to " +
+ "algorithm constraints", cert, cpve);
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Certificate check.
+ private CheckResult certificateCheck(
+ CheckType checkType, X509Certificate cert, Date date,
+ List serverNames, String idAlgorithm) {
+ return checksDisabled ? CheckResult.OK
+ : checkType.check(cert, date, serverNames, idAlgorithm);
+ }
+
+ // enum for the result of the extension check
+ // NOTE: the order of the constants is important as they are used
+ // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
+ enum CheckResult {
+ OK, // ok or not checked
+ INSENSITIVE, // server name indication insensitive
+ EXPIRED, // extensions valid but cert expired
+ EXTENSION_MISMATCH, // extensions invalid (expiration not checked)
+ }
+
+ // enum for the type of certificate check we want to perform
+ // (client or server)
+ // also includes the check code itself
+ enum CheckType {
+
+ // enum constant for "no check" (currently not used)
+ NONE(Collections.emptySet()),
+
+ // enum constant for "tls client" check
+ // valid EKU for TLS client: any, tls_client
+ CLIENT(new HashSet<>(List.of(
+ KnownOIDs.anyExtendedKeyUsage.value(),
+ KnownOIDs.clientAuth.value()
+ ))),
+
+ // enum constant for "tls server" check
+ // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc
+ SERVER(new HashSet<>(List.of(
+ KnownOIDs.anyExtendedKeyUsage.value(),
+ KnownOIDs.serverAuth.value(),
+ KnownOIDs.NETSCAPE_ExportApproved.value(),
+ KnownOIDs.MICROSOFT_ExportApproved.value()
+ )));
+
+ // set of valid EKU values for this type
+ final Set validEku;
+
+ CheckType(Set validEku) {
+ this.validEku = validEku;
+ }
+
+ private static boolean getBit(boolean[] keyUsage, int bit) {
+ return (bit < keyUsage.length) && keyUsage[bit];
+ }
+
+ // Check if this certificate is appropriate for this type of use.
+ // First check extensions, if they match then check expiration.
+ // NOTE: `conformsToAlgorithmConstraints` call above also does some
+ // basic keyUsage checks.
+ CheckResult check(X509Certificate cert, Date date,
+ List serverNames, String idAlgorithm) {
+
+ if (this == NONE) {
+ return CheckResult.OK;
+ }
+
+ // check extensions
+ try {
+ // check extended key usage
+ List certEku = cert.getExtendedKeyUsage();
+ if ((certEku != null) &&
+ Collections.disjoint(validEku, certEku)) {
+ // if extension is present and does not contain any of
+ // the valid EKU OIDs, return extension_mismatch
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+
+ // check key usage
+ boolean[] ku = cert.getKeyUsage();
+ if (ku != null) {
+ String algorithm = cert.getPublicKey().getAlgorithm();
+ boolean supportsDigitalSignature = getBit(ku, 0);
+ switch (algorithm) {
+ case "RSA":
+ // require either signature bit
+ // or if server also allow key encipherment bit
+ if (!supportsDigitalSignature) {
+ if (this == CLIENT || !getBit(ku, 2)) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ }
+ break;
+ case "RSASSA-PSS":
+ if (!supportsDigitalSignature && (this == SERVER)) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ break;
+ case "DSA":
+ // require signature bit
+ if (!supportsDigitalSignature) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ break;
+ case "DH":
+ // require key agreement bit
+ if (!getBit(ku, 4)) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ break;
+ case "EC":
+ // require signature bit
+ if (!supportsDigitalSignature) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ // For servers, also require key agreement.
+ // This is not totally accurate as the keyAgreement
+ // bit is only necessary for static ECDH key
+ // exchange and not ephemeral ECDH. We leave it in
+ // for now until there are signs that this check
+ // causes problems for real world EC certificates.
+ if (this == SERVER && !getBit(ku, 4)) {
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+ break;
+ }
+ }
+ } catch (CertificateException e) {
+ // extensions unparseable, return failure
+ return CheckResult.EXTENSION_MISMATCH;
+ }
+
+ try {
+ cert.checkValidity(date);
+ } catch (CertificateException e) {
+ return CheckResult.EXPIRED;
+ }
+
+ if (serverNames != null && !serverNames.isEmpty()) {
+ for (SNIServerName serverName : serverNames) {
+ if (serverName.getType() ==
+ StandardConstants.SNI_HOST_NAME) {
+ if (!(serverName instanceof SNIHostName)) {
+ try {
+ serverName = new SNIHostName(
+ serverName.getEncoded());
+ } catch (IllegalArgumentException iae) {
+ // unlikely to happen, just in case ...
+ if (SSLLogger.isOn &&
+ SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine("Illegal server name: "
+ + serverName);
+ }
+
+ return CheckResult.INSENSITIVE;
+ }
+ }
+ String hostname =
+ ((SNIHostName) serverName).getAsciiName();
+
+ try {
+ X509TrustManagerImpl.checkIdentity(hostname,
+ cert, idAlgorithm);
+ } catch (CertificateException e) {
+ if (SSLLogger.isOn &&
+ SSLLogger.isOn("keymanager")) {
+ SSLLogger.fine(
+ "Certificate identity does not match "
+ + "Server Name Indication (SNI): "
+ + hostname);
+ }
+ return CheckResult.INSENSITIVE;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return CheckResult.OK;
+ }
+
+ String getValidator() {
+ if (this == CLIENT) {
+ return Validator.VAR_TLS_CLIENT;
+ } else if (this == SERVER) {
+ return Validator.VAR_TLS_SERVER;
+ }
+ return Validator.VAR_GENERIC;
+ }
+ }
+
+ // A candidate match.
+ // Identifies the entry by key store index and alias
+ // and includes the result of the certificate check.
+ protected static class EntryStatus implements Comparable {
+
+ final int keyStoreIndex;
+ final int keyIndex;
+ final String alias;
+ final CheckResult checkResult;
+
+ EntryStatus(int keyStoreIndex, int keyIndex, String alias,
+ Certificate[] chain, CheckResult checkResult) {
+ this.keyStoreIndex = keyStoreIndex;
+ this.keyIndex = keyIndex;
+ this.alias = alias;
+ this.checkResult = checkResult;
+ }
+
+ @Override
+ public int compareTo(EntryStatus other) {
+ int result = this.checkResult.compareTo(other.checkResult);
+ return (result == 0) ? (this.keyIndex - other.keyIndex) : result;
+ }
+
+ @Override
+ public String toString() {
+ String s = alias + " (verified: " + checkResult + ")";
+ if (keyStoreIndex == 0) {
+ return s;
+ } else {
+ return "KeyStore #" + keyStoreIndex + ", alias: " + s;
+ }
+ }
+ }
+
+ // Class to help verify that the public key algorithm (and optionally
+ // the signature algorithm) of a certificate matches what we need.
+ protected static class KeyType {
+
+ final String keyAlgorithm;
+
+ // In TLS 1.2, the signature algorithm has been obsoleted by the
+ // supported_signature_algorithms, and the certificate type no longer
+ // restricts the algorithm used to sign the certificate.
+ //
+ // However, because we don't support certificate type checking other
+ // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the
+ // protocol version here.
+ final String sigKeyAlgorithm;
+
+ KeyType(String algorithm) {
+ int k = algorithm.indexOf('_');
+ if (k == -1) {
+ keyAlgorithm = algorithm;
+ sigKeyAlgorithm = null;
+ } else {
+ keyAlgorithm = algorithm.substring(0, k);
+ sigKeyAlgorithm = algorithm.substring(k + 1);
+ }
+ }
+
+ boolean matches(Certificate[] chain) {
+ if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) {
+ return false;
+ }
+ if (sigKeyAlgorithm == null) {
+ return true;
+ }
+ if (chain.length > 1) {
+ // if possible, check the public key in the issuer cert
+ return sigKeyAlgorithm.equals(
+ chain[1].getPublicKey().getAlgorithm());
+ } else {
+ // Check the signature algorithm of the certificate itself.
+ // Look for the "withRSA" in "SHA1withRSA", etc.
+ X509Certificate issuer = (X509Certificate) chain[0];
+ String sigAlgName =
+ issuer.getSigAlgName().toUpperCase(Locale.ENGLISH);
+ String pattern =
+ "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH);
+ return sigAlgName.endsWith(pattern);
+ }
+ }
+ }
+
+ // Make a list of key types.
+ protected static List getKeyTypes(String... keyTypes) {
+ if ((keyTypes == null) ||
+ (keyTypes.length == 0) || (keyTypes[0] == null)) {
+ return null;
+ }
+ List list = new ArrayList<>(keyTypes.length);
+ for (String keyType : keyTypes) {
+ list.add(new KeyType(keyType));
+ }
+ return list;
+ }
+
+ // Make a set out of the array.
+ protected static Set getIssuerSet(Principal[] issuers) {
+
+ if (issuers != null && issuers.length != 0) {
+ Set ret = new HashSet<>(issuers.length);
+
+ for (Principal p : issuers) {
+ if (p instanceof X500Principal) {
+ ret.add((X500Principal) p);
+ } else {
+ // Normally, this will never happen but try to recover if
+ // it does.
+ try {
+ ret.add(new X500Principal(p.getName()));
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+ return ret.isEmpty() ? null : ret;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java
index fffbddf7d121c..df6ecaf7a4241 100644
--- a/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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,18 +32,14 @@
import java.security.KeyStore.Builder;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
import java.security.Principal;
import java.security.PrivateKey;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.*;
-import sun.security.provider.certpath.AlgorithmChecker;
-import sun.security.validator.Validator;
-import sun.security.util.KnownOIDs;
+import javax.security.auth.x500.X500Principal;
/**
* The new X509 key manager implementation. The main differences to the
@@ -62,8 +58,8 @@
*
* @author Andreas Sterbenz
*/
-final class X509KeyManagerImpl extends X509ExtendedKeyManager
- implements X509KeyManager {
+
+final class X509KeyManagerImpl extends X509KeyManagerCertChecking {
// for unit testing only, set via privileged reflection
private static Date verificationDate;
@@ -84,12 +80,15 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
X509KeyManagerImpl(List builders) {
this.builders = builders;
uidCounter = new AtomicLong();
- entryCacheMap = Collections.synchronizedMap
- (new SizedMap<>());
+ entryCacheMap = Collections.synchronizedMap(new SizedMap<>());
}
- // LinkedHashMap with a max size of 10
- // see LinkedHashMap JavaDocs
+ @Override
+ protected boolean isCheckingDisabled() {
+ return false;
+ }
+
+ // LinkedHashMap with a max size of 10, see LinkedHashMap JavaDocs
private static class SizedMap extends LinkedHashMap {
@java.io.Serial
private static final long serialVersionUID = -8211222668790986062L;
@@ -120,14 +119,14 @@ public PrivateKey getPrivateKey(String alias) {
public String chooseClientAlias(String[] keyTypes, Principal[] issuers,
Socket socket) {
return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
- getAlgorithmConstraints(socket));
+ getAlgorithmConstraints(socket), null, null);
}
@Override
public String chooseEngineClientAlias(String[] keyTypes,
Principal[] issuers, SSLEngine engine) {
return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
- getAlgorithmConstraints(engine));
+ getAlgorithmConstraints(engine), null, null);
}
@Override
@@ -168,73 +167,24 @@ public String chooseEngineServerAlias(String keyType,
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
- return getAliases(keyType, issuers, CheckType.CLIENT, null);
+ return getAliases(keyType, issuers, CheckType.CLIENT);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
- return getAliases(keyType, issuers, CheckType.SERVER, null);
+ return getAliases(keyType, issuers, CheckType.SERVER);
}
//
// implementation private methods
//
- // Gets algorithm constraints of the socket.
- private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
- if (socket != null && socket.isConnected() &&
- socket instanceof SSLSocket sslSocket) {
-
- SSLSession session = sslSocket.getHandshakeSession();
-
- if (session != null) {
- if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
- String[] peerSupportedSignAlgs = null;
-
- if (session instanceof ExtendedSSLSession extSession) {
- peerSupportedSignAlgs =
- extSession.getPeerSupportedSignatureAlgorithms();
- }
-
- return SSLAlgorithmConstraints.forSocket(
- sslSocket, peerSupportedSignAlgs, true);
- }
- }
-
- return SSLAlgorithmConstraints.forSocket(sslSocket, true);
- }
-
- return SSLAlgorithmConstraints.DEFAULT;
- }
-
- // Gets algorithm constraints of the engine.
- private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
- if (engine != null) {
- SSLSession session = engine.getHandshakeSession();
- if (session != null) {
- if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
- String[] peerSupportedSignAlgs = null;
-
- if (session instanceof ExtendedSSLSession extSession) {
- peerSupportedSignAlgs =
- extSession.getPeerSupportedSignatureAlgorithms();
- }
-
- return SSLAlgorithmConstraints.forEngine(
- engine, peerSupportedSignAlgs, true);
- }
- }
- }
-
- return SSLAlgorithmConstraints.forEngine(engine, true);
- }
-
// we construct the alias we return to JSSE as seen in the code below
// a unique id is included to allow us to reliably cache entries
// between the calls to getCertificateChain() and getPrivateKey()
// even if tokens are inserted or removed
private String makeAlias(EntryStatus entry) {
- return uidCounter.incrementAndGet() + "." + entry.builderIndex + "."
+ return uidCounter.incrementAndGet() + "." + entry.keyStoreIndex + "."
+ entry.alias;
}
@@ -279,68 +229,6 @@ private PrivateKeyEntry getEntry(String alias) {
}
}
- // Class to help verify that the public key algorithm (and optionally
- // the signature algorithm) of a certificate matches what we need.
- private static class KeyType {
-
- final String keyAlgorithm;
-
- // In TLS 1.2, the signature algorithm has been obsoleted by the
- // supported_signature_algorithms, and the certificate type no longer
- // restricts the algorithm used to sign the certificate.
- //
- // However, because we don't support certificate type checking other
- // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the
- // protocol version here.
- final String sigKeyAlgorithm;
-
- KeyType(String algorithm) {
- int k = algorithm.indexOf('_');
- if (k == -1) {
- keyAlgorithm = algorithm;
- sigKeyAlgorithm = null;
- } else {
- keyAlgorithm = algorithm.substring(0, k);
- sigKeyAlgorithm = algorithm.substring(k + 1);
- }
- }
-
- boolean matches(Certificate[] chain) {
- if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) {
- return false;
- }
- if (sigKeyAlgorithm == null) {
- return true;
- }
- if (chain.length > 1) {
- // if possible, check the public key in the issuer cert
- return sigKeyAlgorithm.equals(
- chain[1].getPublicKey().getAlgorithm());
- } else {
- // Check the signature algorithm of the certificate itself.
- // Look for the "withRSA" in "SHA1withRSA", etc.
- X509Certificate issuer = (X509Certificate)chain[0];
- String sigAlgName =
- issuer.getSigAlgName().toUpperCase(Locale.ENGLISH);
- String pattern =
- "WITH" + sigKeyAlgorithm.toUpperCase(Locale.ENGLISH);
- return sigAlgName.contains(pattern);
- }
- }
- }
-
- private static List getKeyTypes(String ... keyTypes) {
- if ((keyTypes == null) ||
- (keyTypes.length == 0) || (keyTypes[0] == null)) {
- return null;
- }
- List list = new ArrayList<>(keyTypes.length);
- for (String keyType : keyTypes) {
- list.add(new KeyType(keyType));
- }
- return list;
- }
-
/*
* Return the best alias that fits the given parameters.
* The algorithm we use is:
@@ -354,13 +242,6 @@ private static List getKeyTypes(String ... keyTypes) {
* with appropriate key usage to certs with the wrong key usage.
* return the first one of them.
*/
- private String chooseAlias(List keyTypeList, Principal[] issuers,
- CheckType checkType, AlgorithmConstraints constraints) {
-
- return chooseAlias(keyTypeList, issuers,
- checkType, constraints, null, null);
- }
-
private String chooseAlias(List keyTypeList, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints,
List requestedServerNames, String idAlgorithm) {
@@ -369,8 +250,9 @@ private String chooseAlias(List keyTypeList, Principal[] issuers,
return null;
}
- Set issuerSet = getIssuerSet(issuers);
+ Set issuerSet = getIssuerSet(issuers);
List allResults = null;
+
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List results = getAliases(i, keyTypeList,
@@ -390,7 +272,7 @@ private String chooseAlias(List keyTypeList, Principal[] issuers,
}
allResults.addAll(results);
}
- } catch (Exception e) {
+ } catch (KeyStoreException e) {
// ignore
}
}
@@ -415,27 +297,28 @@ private String chooseAlias(List keyTypeList, Principal[] issuers,
* and certificates with the wrong extensions).
* The perfect matches will be first in the array.
*/
- public String[] getAliases(String keyType, Principal[] issuers,
- CheckType checkType, AlgorithmConstraints constraints) {
+ private String[] getAliases(
+ String keyType, Principal[] issuers, CheckType checkType) {
+
if (keyType == null) {
return null;
}
- Set issuerSet = getIssuerSet(issuers);
+ Set issuerSet = getIssuerSet(issuers);
List keyTypeList = getKeyTypes(keyType);
List allResults = null;
+
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List results = getAliases(i, keyTypeList,
- issuerSet, true, checkType, constraints,
- null, null);
+ issuerSet, true, checkType, null, null, null);
if (results != null) {
if (allResults == null) {
allResults = new ArrayList<>();
}
allResults.addAll(results);
}
- } catch (Exception e) {
+ } catch (KeyStoreException e) {
// ignore
}
}
@@ -462,232 +345,6 @@ private String[] toAliases(List results) {
return s;
}
- // make a Set out of the array
- private Set getIssuerSet(Principal[] issuers) {
- if ((issuers != null) && (issuers.length != 0)) {
- return new HashSet<>(Arrays.asList(issuers));
- } else {
- return null;
- }
- }
-
- // a candidate match
- // identifies the entry by builder and alias
- // and includes the result of the certificate check
- private static class EntryStatus implements Comparable {
-
- final int builderIndex;
- final int keyIndex;
- final String alias;
- final CheckResult checkResult;
-
- EntryStatus(int builderIndex, int keyIndex, String alias,
- Certificate[] chain, CheckResult checkResult) {
- this.builderIndex = builderIndex;
- this.keyIndex = keyIndex;
- this.alias = alias;
- this.checkResult = checkResult;
- }
-
- @Override
- public int compareTo(EntryStatus other) {
- int result = this.checkResult.compareTo(other.checkResult);
- return (result == 0) ? (this.keyIndex - other.keyIndex) : result;
- }
-
- @Override
- public String toString() {
- String s = alias + " (verified: " + checkResult + ")";
- if (builderIndex == 0) {
- return s;
- } else {
- return "Builder #" + builderIndex + ", alias: " + s;
- }
- }
- }
-
- // enum for the type of certificate check we want to perform
- // (client or server)
- // also includes the check code itself
- private enum CheckType {
-
- // enum constant for "no check" (currently not used)
- NONE(Collections.emptySet()),
-
- // enum constant for "tls client" check
- // valid EKU for TLS client: any, tls_client
- CLIENT(new HashSet<>(List.of(
- KnownOIDs.anyExtendedKeyUsage.value(),
- KnownOIDs.clientAuth.value()
- ))),
-
- // enum constant for "tls server" check
- // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc
- SERVER(new HashSet<>(List.of(
- KnownOIDs.anyExtendedKeyUsage.value(),
- KnownOIDs.serverAuth.value(),
- KnownOIDs.NETSCAPE_ExportApproved.value(),
- KnownOIDs.MICROSOFT_ExportApproved.value()
- )));
-
- // set of valid EKU values for this type
- final Set validEku;
-
- CheckType(Set validEku) {
- this.validEku = validEku;
- }
-
- private static boolean getBit(boolean[] keyUsage, int bit) {
- return (bit < keyUsage.length) && keyUsage[bit];
- }
-
- // Check if this certificate is appropriate for this type of use
- // first check extensions, if they match, check expiration.
- //
- // Note: we may want to move this code into the sun.security.validator
- // package
- CheckResult check(X509Certificate cert, Date date,
- List serverNames, String idAlgorithm) {
-
- if (this == NONE) {
- return CheckResult.OK;
- }
-
- // check extensions
- try {
- // check extended key usage
- List certEku = cert.getExtendedKeyUsage();
- if ((certEku != null) &&
- Collections.disjoint(validEku, certEku)) {
- // if extension is present and does not contain any of
- // the valid EKU OIDs, return extension_mismatch
- return CheckResult.EXTENSION_MISMATCH;
- }
-
- // check key usage
- boolean[] ku = cert.getKeyUsage();
- if (ku != null) {
- String algorithm = cert.getPublicKey().getAlgorithm();
- boolean supportsDigitalSignature = getBit(ku, 0);
- switch (algorithm) {
- case "RSA":
- // require either signature bit
- // or if server also allow key encipherment bit
- if (!supportsDigitalSignature) {
- if (this == CLIENT || !getBit(ku, 2)) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- }
- break;
- case "RSASSA-PSS":
- if (!supportsDigitalSignature && (this == SERVER)) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- break;
- case "DSA":
- // require signature bit
- if (!supportsDigitalSignature) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- break;
- case "DH":
- // require keyagreement bit
- if (!getBit(ku, 4)) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- break;
- case "EC":
- // require signature bit
- if (!supportsDigitalSignature) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- // For servers, also require key agreement.
- // This is not totally accurate as the keyAgreement
- // bit is only necessary for static ECDH key
- // exchange and not ephemeral ECDH. We leave it in
- // for now until there are signs that this check
- // causes problems for real world EC certificates.
- if (this == SERVER && !getBit(ku, 4)) {
- return CheckResult.EXTENSION_MISMATCH;
- }
- break;
- }
- }
- } catch (CertificateException e) {
- // extensions unparseable, return failure
- return CheckResult.EXTENSION_MISMATCH;
- }
-
- try {
- cert.checkValidity(date);
- } catch (CertificateException e) {
- return CheckResult.EXPIRED;
- }
-
- if (serverNames != null && !serverNames.isEmpty()) {
- for (SNIServerName serverName : serverNames) {
- if (serverName.getType() ==
- StandardConstants.SNI_HOST_NAME) {
- if (!(serverName instanceof SNIHostName)) {
- try {
- serverName =
- new SNIHostName(serverName.getEncoded());
- } catch (IllegalArgumentException iae) {
- // unlikely to happen, just in case ...
- if (SSLLogger.isOn &&
- SSLLogger.isOn("keymanager")) {
- SSLLogger.fine(
- "Illegal server name: " + serverName);
- }
-
- return CheckResult.INSENSITIVE;
- }
- }
- String hostname =
- ((SNIHostName)serverName).getAsciiName();
-
- try {
- X509TrustManagerImpl.checkIdentity(hostname,
- cert, idAlgorithm);
- } catch (CertificateException e) {
- if (SSLLogger.isOn &&
- SSLLogger.isOn("keymanager")) {
- SSLLogger.fine(
- "Certificate identity does not match " +
- "Server Name Indication (SNI): " +
- hostname);
- }
- return CheckResult.INSENSITIVE;
- }
-
- break;
- }
- }
- }
-
- return CheckResult.OK;
- }
-
- public String getValidator() {
- if (this == CLIENT) {
- return Validator.VAR_TLS_CLIENT;
- } else if (this == SERVER) {
- return Validator.VAR_TLS_SERVER;
- }
- return Validator.VAR_GENERIC;
- }
- }
-
- // enum for the result of the extension check
- // NOTE: the order of the constants is important as they are used
- // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
- private enum CheckResult {
- OK, // ok or not checked
- INSENSITIVE, // server name indication insensitive
- EXPIRED, // extensions valid but cert expired
- EXTENSION_MISMATCH, // extensions invalid (expiration not checked)
- }
-
/*
* Return a List of all candidate matches in the specified builder
* that fit the parameters.
@@ -715,106 +372,42 @@ private enum CheckResult {
* matches
*/
private List getAliases(int builderIndex,
- List keyTypes, Set issuerSet,
+ List keyTypes, Set issuerSet,
boolean findAll, CheckType checkType,
AlgorithmConstraints constraints,
List requestedServerNames,
- String idAlgorithm) throws Exception {
+ String idAlgorithm) throws KeyStoreException {
Builder builder = builders.get(builderIndex);
KeyStore ks = builder.getKeyStore();
List results = null;
- Date date = verificationDate;
boolean preferred = false;
+
for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) {
+
String alias = e.nextElement();
+
// check if it is a key entry (private key or secret key)
if (!ks.isKeyEntry(alias)) {
continue;
}
- Certificate[] chain = ks.getCertificateChain(alias);
- if ((chain == null) || (chain.length == 0)) {
- // must be secret key entry, ignore
- continue;
- }
-
- boolean incompatible = false;
- for (Certificate cert : chain) {
- if (!(cert instanceof X509Certificate)) {
- // not an X509Certificate, ignore this alias
- incompatible = true;
- break;
- }
- }
- if (incompatible) {
- continue;
- }
-
- // check keytype
- int keyIndex = -1;
- int j = 0;
- for (KeyType keyType : keyTypes) {
- if (keyType.matches(chain)) {
- keyIndex = j;
- break;
- }
- j++;
- }
- if (keyIndex == -1) {
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("Ignore alias " + alias
- + ": key algorithm does not match");
- }
- continue;
- }
- // check issuers
- if (issuerSet != null) {
- boolean found = false;
- for (Certificate cert : chain) {
- X509Certificate xcert = (X509Certificate)cert;
- if (issuerSet.contains(xcert.getIssuerX500Principal())) {
- found = true;
- break;
- }
- }
- if (!found) {
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine(
- "Ignore alias " + alias
- + ": issuers do not match");
- }
- continue;
- }
- }
-
- // check the algorithm constraints
- if (constraints != null &&
- !conformsToAlgorithmConstraints(constraints, chain,
- checkType.getValidator())) {
+ EntryStatus status = checkAlias(builderIndex, alias,
+ ks.getCertificateChain(alias),
+ verificationDate, keyTypes, issuerSet, checkType,
+ constraints, requestedServerNames, idAlgorithm);
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("Ignore alias " + alias +
- ": certificate list does not conform to " +
- "algorithm constraints");
- }
+ if (status == null) {
continue;
}
- if (date == null) {
- date = new Date();
- }
- CheckResult checkResult =
- checkType.check((X509Certificate)chain[0], date,
- requestedServerNames, idAlgorithm);
- EntryStatus status =
- new EntryStatus(builderIndex, keyIndex,
- alias, chain, checkResult);
- if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
+ if (!preferred && status.checkResult == CheckResult.OK
+ && status.keyIndex == 0) {
preferred = true;
}
+
if (preferred && !findAll) {
- // if we have a good match and do not need all matches,
+ // If we have a good match and do not need all matches,
// return immediately
return Collections.singletonList(status);
} else {
@@ -824,42 +417,7 @@ private List getAliases(int builderIndex,
results.add(status);
}
}
- return results;
- }
- private static boolean conformsToAlgorithmConstraints(
- AlgorithmConstraints constraints, Certificate[] chain,
- String variant) {
-
- AlgorithmChecker checker = new AlgorithmChecker(constraints, variant);
- try {
- checker.init(false);
- } catch (CertPathValidatorException cpve) {
- // unlikely to happen
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine(
- "Cannot initialize algorithm constraints checker", cpve);
- }
-
- return false;
- }
-
- // It is a forward checker, so we need to check from trust to target.
- for (int i = chain.length - 1; i >= 0; i--) {
- Certificate cert = chain[i];
- try {
- // We don't care about the unresolved critical extensions.
- checker.check(cert, Collections.emptySet());
- } catch (CertPathValidatorException cpve) {
- if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
- SSLLogger.fine("Certificate does not conform to " +
- "algorithm constraints", cert, cpve);
- }
-
- return false;
- }
- }
-
- return true;
+ return results;
}
}
diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java
index 6da3289458751..3aa7a98c39403 100644
--- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java
+++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -24,7 +24,8 @@
/*
* @test
* @bug 5016500
- * @library /test/lib/
+ * @library /javax/net/ssl/templates
+ * /test/lib/
* @summary Test SslRmi[Client|Server]SocketFactory SSL socket parameters.
* @run main/othervm SSLSocketParametersTest 1
* @run main/othervm SSLSocketParametersTest 2
@@ -36,8 +37,6 @@
*/
import jdk.test.lib.Asserts;
-import java.io.IOException;
-import java.io.File;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.rmi.ConnectIOException;
@@ -49,13 +48,17 @@
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
-public class SSLSocketParametersTest implements Serializable {
+public class SSLSocketParametersTest extends SSLContextTemplate {
+
+ public SSLSocketParametersTest() throws Exception {
+ SSLContext.setDefault(createServerSSLContext());
+ }
public interface Hello extends Remote {
String sayHello() throws RemoteException;
}
- public class HelloImpl implements Hello {
+ public static class HelloImpl implements Hello {
public String sayHello() {
return "Hello World!";
}
@@ -134,23 +137,7 @@ public void runTest(int testNumber) throws Exception {
}
public static void main(String[] args) throws Exception {
- // Set keystore properties (server-side)
- //
- final String keystore = System.getProperty("test.src") +
- File.separator + "keystore";
- System.out.println("KeyStore = " + keystore);
- System.setProperty("javax.net.ssl.keyStore", keystore);
- System.setProperty("javax.net.ssl.keyStorePassword", "password");
-
- // Set truststore properties (client-side)
- //
- final String truststore = System.getProperty("test.src") +
- File.separator + "truststore";
- System.out.println("TrustStore = " + truststore);
- System.setProperty("javax.net.ssl.trustStore", truststore);
- System.setProperty("javax.net.ssl.trustStorePassword", "trustword");
-
SSLSocketParametersTest test = new SSLSocketParametersTest();
test.runTest(Integer.parseInt(args[0]));
}
-}
\ No newline at end of file
+}
diff --git a/test/jdk/javax/rmi/ssl/keystore b/test/jdk/javax/rmi/ssl/keystore
deleted file mode 100644
index 05f535645827b..0000000000000
Binary files a/test/jdk/javax/rmi/ssl/keystore and /dev/null differ
diff --git a/test/jdk/javax/rmi/ssl/truststore b/test/jdk/javax/rmi/ssl/truststore
deleted file mode 100644
index 2f5ba3433dc61..0000000000000
Binary files a/test/jdk/javax/rmi/ssl/truststore and /dev/null differ
diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java b/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java
index 15224e22e6eea..1e0afd0e586a4 100644
--- a/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java
+++ b/test/jdk/sun/net/www/protocol/https/HttpsClient/ServerIdentityTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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,31 +31,58 @@
* @bug 4328195
* @summary Need to include the alternate subject DN for certs,
* https should check for this
+ * @modules java.base/sun.security.x509
+ * java.base/sun.security.util
* @library /javax/net/ssl/templates
- * @run main/othervm ServerIdentityTest dnsstore localhost
- * @run main/othervm ServerIdentityTest ipstore 127.0.0.1
+ * /test/lib
+ * @run main/othervm ServerIdentityTest dns localhost
+ * @run main/othervm ServerIdentityTest ip 127.0.0.1
*
* @author Yingxian Wang
*/
-import java.io.InputStream;
+import static jdk.test.lib.Asserts.fail;
+
import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStreamWriter;
+import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.UnknownHostException;
-
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.List;
import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManagerFactory;
+import jdk.test.lib.security.CertificateBuilder;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.X500Name;
public final class ServerIdentityTest extends SSLSocketTemplate {
- private static String keystore;
private static String hostname;
- private static SSLContext context;
+ private static SSLContext serverContext;
/*
* Run the test case.
@@ -64,7 +91,7 @@ public static void main(String[] args) throws Exception {
// Get the customized arguments.
initialize(args);
- (new ServerIdentityTest()).run();
+ new ServerIdentityTest().run();
}
ServerIdentityTest() throws UnknownHostException {
@@ -95,7 +122,7 @@ protected void runClientApplication(int serverPort) throws Exception {
HttpURLConnection urlc = null;
InputStream is = null;
try {
- urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
+ urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
is = urlc.getInputStream();
} finally {
if (is != null) {
@@ -109,31 +136,127 @@ protected void runClientApplication(int serverPort) throws Exception {
@Override
protected SSLContext createServerSSLContext() throws Exception {
- return context;
- }
-
- @Override
- protected SSLContext createClientSSLContext() throws Exception {
- return context;
+ return serverContext;
}
private static void initialize(String[] args) throws Exception {
- keystore = args[0];
+ String mode = args[0];
hostname = args[1];
- String password = "changeit";
- String keyFilename =
- System.getProperty("test.src", ".") + "/" + keystore;
- String trustFilename =
- System.getProperty("test.src", ".") + "/" + keystore;
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+ KeyPair caKeys = kpg.generateKeyPair();
+ KeyPair serverKeys = kpg.generateKeyPair();
+ KeyPair clientKeys = kpg.generateKeyPair();
+
+ CertificateBuilder serverCertificateBuilder = customCertificateBuilder(
+ "CN=server, O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ serverKeys.getPublic(), caKeys.getPublic())
+ .addBasicConstraintsExt(false, false, -1);
- System.setProperty("javax.net.ssl.keyStore", keyFilename);
- System.setProperty("javax.net.ssl.keyStorePassword", password);
- System.setProperty("javax.net.ssl.trustStore", trustFilename);
- System.setProperty("javax.net.ssl.trustStorePassword", password);
+ if (mode.equalsIgnoreCase("dns")) {
+ serverCertificateBuilder.addSubjectAltNameDNSExt(List.of(hostname));
+ } else if (mode.equalsIgnoreCase("ip")) {
+ serverCertificateBuilder.addSubjectAltNameIPExt(List.of(hostname));
+ } else {
+ fail("Unknown mode: " + mode);
+ }
+
+ X509Certificate trustedCert = createTrustedCert(caKeys);
+
+ X509Certificate serverCert = serverCertificateBuilder.build(
+ trustedCert, caKeys.getPrivate(), "SHA256WithRSA");
+
+ X509Certificate clientCert = customCertificateBuilder(
+ "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ clientKeys.getPublic(), caKeys.getPublic())
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), "SHA256WithRSA");
+
+ serverContext = getSSLContext(
+ trustedCert, serverCert, serverKeys.getPrivate());
+
+ SSLContext clientContext = getSSLContext(
+ trustedCert, clientCert, clientKeys.getPrivate());
- context = SSLContext.getDefault();
HttpsURLConnection.setDefaultSSLSocketFactory(
- context.getSocketFactory());
+ clientContext.getSocketFactory());
}
+
+ private static SSLContext getSSLContext(
+ X509Certificate trustedCertificate, X509Certificate keyCertificate,
+ PrivateKey privateKey)
+ throws Exception {
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+
+ // import the trusted cert
+ ks.setCertificateEntry("TLS Signer", trustedCertificate);
+
+ // generate certificate chain
+ Certificate[] chain = new Certificate[2];
+ chain[0] = keyCertificate;
+ chain[1] = trustedCertificate;
+
+ // import the key entry.
+ final char[] passphrase = "passphrase".toCharArray();
+ ks.setKeyEntry("Whatever", privateKey, passphrase, chain);
+
+ // Using PKIX TrustManager
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
+ tmf.init(ks);
+
+ // Using PKIX KeyManager
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
+
+ // create SSL context
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ kmf.init(ks, passphrase);
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ return ctx;
+ }
+
+ private static X509Certificate createTrustedCert(KeyPair caKeys)
+ throws Exception {
+ SecureRandom random = new SecureRandom();
+
+ KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic());
+ GeneralNames gns = new GeneralNames();
+ GeneralName name = new GeneralName(new X500Name(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US"));
+ gns.add(name);
+ BigInteger serialNumber = BigInteger.valueOf(
+ random.nextLong(1000000) + 1);
+ return customCertificateBuilder(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ caKeys.getPublic(), caKeys.getPublic())
+ .setSerialNumber(serialNumber)
+ .addExtension(new AuthorityKeyIdentifierExtension(kid, gns,
+ new SerialNumber(serialNumber)))
+ .addBasicConstraintsExt(true, true, -1)
+ .build(null, caKeys.getPrivate(), "SHA256WithRSA");
+ }
+
+ private static CertificateBuilder customCertificateBuilder(
+ String subjectName, PublicKey publicKey, PublicKey caKey)
+ throws CertificateException, IOException {
+ SecureRandom random = new SecureRandom();
+
+ CertificateBuilder builder = new CertificateBuilder()
+ .setSubjectName(subjectName)
+ .setPublicKey(publicKey)
+ .setNotBefore(
+ Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))
+ .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
+ .setSerialNumber(
+ BigInteger.valueOf(random.nextLong(1000000) + 1))
+ .addSubjectKeyIdExt(publicKey)
+ .addAuthorityKeyIdExt(caKey);
+ builder.addKeyUsageExt(
+ new boolean[]{true, true, true, true, true, true});
+
+ return builder;
+ }
+
}
diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore b/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore
deleted file mode 100644
index 5f4fc81c9b7ce..0000000000000
Binary files a/test/jdk/sun/net/www/protocol/https/HttpsClient/dnsstore and /dev/null differ
diff --git a/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore b/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore
deleted file mode 100644
index 04a9508e0a1c4..0000000000000
Binary files a/test/jdk/sun/net/www/protocol/https/HttpsClient/ipstore and /dev/null differ
diff --git a/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java b/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java
index 26180a8fcdcba..5ecbb60227358 100644
--- a/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java
+++ b/test/jdk/sun/security/mscapi/ShortRSAKeyWithinTLS.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -29,6 +29,7 @@
* @modules java.base/sun.security.util
* java.base/sun.security.tools.keytool
* java.base/sun.security.x509
+ * @library /test/lib
* @run main ShortRSAKeyWithinTLS 1024
* @run main ShortRSAKeyWithinTLS 768
* @run main ShortRSAKeyWithinTLS 512
@@ -42,6 +43,7 @@
import javax.net.*;
import javax.net.ssl.*;
+import jdk.test.lib.security.SecurityUtils;
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.util.KeyUtil;
import sun.security.x509.X500Name;
@@ -233,6 +235,10 @@ private void checkKeySize(KeyStore ks) throws Exception {
private static String clientCiperSuite = null;
public static void main(String[] args) throws Exception {
+ // Make sure we don't block the key on algorithm constraints check.
+ SecurityUtils.removeFromDisabledAlgs("jdk.certpath.disabledAlgorithms",
+ List.of("RSA keySize < 1024"));
+
if (debug) {
System.setProperty("javax.net.debug", "all");
}
diff --git a/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java
index ed61aace3f476..2fa046e1dc2e1 100644
--- a/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java
+++ b/test/jdk/sun/security/ssl/SignatureScheme/MD5NotAllowedInTLS13CertificateSignature.java
@@ -150,9 +150,11 @@ private static SSLContext getSSLContext(
// create SSL context
SSLContext ctx = SSLContext.getInstance(protocol);
- // Using "SunX509" which doesn't check peer supported signature
- // algorithms, so we check against local supported signature
+ // Disable KeyManager's algorithm constraints checking,
+ // so we check against local supported signature
// algorithms which constitutes the fix being tested.
+ System.setProperty(
+ "jdk.tls.SunX509KeyManager.certChecking", "false");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
@@ -166,7 +168,6 @@ private static SSLContext getSSLContext(
private void setupCertificates() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
- kpg.initialize(1024);
KeyPair caKeys = kpg.generateKeyPair();
this.serverKeys = kpg.generateKeyPair();
this.clientKeys = kpg.generateKeyPair();
@@ -215,7 +216,7 @@ private static CertificateBuilder customCertificateBuilder(
CertificateBuilder builder = new CertificateBuilder()
.setSubjectName(subjectName)
.setPublicKey(publicKey)
- .setNotAfter(
+ .setNotBefore(
Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))
.setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
.setSerialNumber(
diff --git a/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java b/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java
new file mode 100644
index 0000000000000..997fde5a07ade
--- /dev/null
+++ b/test/jdk/sun/security/ssl/X509KeyManager/AlgorithmConstraintsCheck.java
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+import static jdk.test.lib.Asserts.assertEquals;
+import static jdk.test.lib.Asserts.assertNull;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import jdk.test.lib.security.CertificateBuilder;
+import jdk.test.lib.security.SecurityUtils;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.X500Name;
+
+/*
+ * @test
+ * @bug 8359956
+ * @summary Support algorithm constraints and certificate checks in SunX509
+ * key manager
+ * @modules java.base/sun.security.x509
+ * java.base/sun.security.util
+ * @library /test/lib
+ * @run main/othervm AlgorithmConstraintsCheck false SunX509 SHA256withRSA
+ * @run main/othervm AlgorithmConstraintsCheck true SunX509 SHA256withRSA
+ * @run main/othervm AlgorithmConstraintsCheck false PKIX SHA256withRSA
+ * @run main/othervm AlgorithmConstraintsCheck true PKIX SHA256withRSA
+ */
+
+public class AlgorithmConstraintsCheck {
+
+ private static final String CERT_ALIAS = "testalias";
+ private static final String KEY_TYPE = "RSA";
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 3) {
+ throw new RuntimeException("Wrong number of arguments");
+ }
+
+ String enabled = args[0];
+ String kmAlg = args[1];
+ String certSignatureAlg = args[2];
+
+ System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled);
+ SecurityUtils.addToDisabledTlsAlgs(certSignatureAlg);
+
+ X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager(
+ kmAlg, certSignatureAlg);
+ String serverAlias = km.chooseServerAlias(KEY_TYPE, null, null);
+ String engineServerAlias = km.chooseEngineServerAlias(
+ KEY_TYPE, null, null);
+ String clientAlias = km.chooseClientAlias(
+ new String[]{KEY_TYPE}, null, null);
+ String engineClientAlias = km.chooseEngineClientAlias(
+ new String[]{KEY_TYPE}, null, null);
+
+ // PKIX KeyManager adds a cache prefix to an alias.
+ String serverAliasPrefix = kmAlg.equalsIgnoreCase("PKIX") ? "1.0." : "";
+ String clientAliasPrefix = kmAlg.equalsIgnoreCase("PKIX") ? "2.0." : "";
+
+ if ("false".equals(enabled) && kmAlg.equals("SunX509")) {
+ assertEquals(CERT_ALIAS, normalizeAlias(serverAlias));
+ assertEquals(CERT_ALIAS, normalizeAlias(engineServerAlias));
+ assertEquals(CERT_ALIAS, normalizeAlias(clientAlias));
+ assertEquals(CERT_ALIAS, normalizeAlias(engineClientAlias));
+ } else {
+ assertNull(serverAlias);
+ assertNull(engineServerAlias);
+ assertNull(clientAlias);
+ assertNull(engineClientAlias);
+ }
+ }
+
+ // PKIX KeyManager adds a cache prefix to an alias.
+ private static String normalizeAlias(String alias) {
+ return alias.substring(alias.lastIndexOf(".") + 1);
+ }
+
+ private static X509KeyManager getKeyManager(String kmAlg,
+ String certSignatureAlg) throws Exception {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_TYPE);
+ KeyPair caKeys = kpg.generateKeyPair();
+ KeyPair endpointKeys = kpg.generateKeyPair();
+
+ X509Certificate trustedCert = createTrustedCert(caKeys,
+ certSignatureAlg);
+
+ X509Certificate endpointCert = customCertificateBuilder(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ endpointKeys.getPublic(), caKeys.getPublic())
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), certSignatureAlg);
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+
+ // import the trusted cert
+ ks.setCertificateEntry("TLS Signer", trustedCert);
+
+ // generate certificate chain
+ Certificate[] chain = new Certificate[2];
+ chain[0] = endpointCert;
+ chain[1] = trustedCert;
+
+ // import the key entry.
+ final char[] passphrase = "passphrase".toCharArray();
+ ks.setKeyEntry(CERT_ALIAS, endpointKeys.getPrivate(), passphrase,
+ chain);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg);
+ kmf.init(ks, passphrase);
+
+ return (X509KeyManager) kmf.getKeyManagers()[0];
+ }
+
+ // Certificate-building helper methods.
+
+ private static X509Certificate createTrustedCert(KeyPair caKeys,
+ String certSignatureAlg)
+ throws Exception {
+ SecureRandom random = new SecureRandom();
+
+ KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic());
+ GeneralNames gns = new GeneralNames();
+ GeneralName name = new GeneralName(new X500Name(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US"));
+ gns.add(name);
+ BigInteger serialNumber = BigInteger.valueOf(
+ random.nextLong(1000000) + 1);
+ return customCertificateBuilder(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ caKeys.getPublic(), caKeys.getPublic())
+ .setSerialNumber(serialNumber)
+ .addExtension(new AuthorityKeyIdentifierExtension(kid, gns,
+ new SerialNumber(serialNumber)))
+ .addBasicConstraintsExt(true, true, -1)
+ .build(null, caKeys.getPrivate(), certSignatureAlg);
+ }
+
+ private static CertificateBuilder customCertificateBuilder(
+ String subjectName, PublicKey publicKey, PublicKey caKey)
+ throws CertificateException, IOException {
+ SecureRandom random = new SecureRandom();
+
+ CertificateBuilder builder = new CertificateBuilder()
+ .setSubjectName(subjectName)
+ .setPublicKey(publicKey)
+ .setNotBefore(
+ Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))
+ .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
+ .setSerialNumber(
+ BigInteger.valueOf(random.nextLong(1000000) + 1))
+ .addSubjectKeyIdExt(publicKey)
+ .addAuthorityKeyIdExt(caKey);
+ builder.addKeyUsageExt(
+ new boolean[]{true, true, true, true, true, true});
+
+ return builder;
+ }
+}
diff --git a/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java b/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java
new file mode 100644
index 0000000000000..81e16b925fcb8
--- /dev/null
+++ b/test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+import static jdk.test.lib.Asserts.assertEquals;
+import static jdk.test.lib.Asserts.assertNull;
+
+import com.sun.security.auth.UserPrincipal;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+import javax.security.auth.x500.X500Principal;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.security.CertificateBuilder;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.X500Name;
+
+/*
+ * @test
+ * @bug 8359956
+ * @summary Support algorithm constraints and certificate checks in SunX509
+ * key manager
+ * @modules java.base/sun.security.x509
+ * java.base/sun.security.util
+ * @library /test/lib
+ * @run main/othervm CertChecking false SunX509
+ * @run main/othervm CertChecking true SunX509
+ * @run main/othervm CertChecking false PKIX
+ * @run main/othervm CertChecking true PKIX
+ */
+
+/*
+ * This class tests against the certificate's expiration, key usage, key type
+ * and issuers.
+ */
+
+public class CertChecking {
+
+ private static final String PREFERRED_ALIAS = "preferred-alias";
+ private static final String EXPIRED_ALIAS = "expired-alias";
+ private static final String USAGE_MISMATCH_ALIAS = "usage-mismatch-alias";
+ private static final String CA_KEY_TYPE = "RSA";
+ private static final String CERT_SIG_ALG = "SHA256withRSA";
+ private static final String CA_ISSUER_STRING =
+ "O=TrustedCert, L=Some-City, ST=Some-State, C=US";
+ private static final String EE_ISSUER_STRING =
+ "O=EndpointCert, L=Some-City, ST=Some-State, C=US";
+ private static final String UNKNOWN_ISSUER_STRING =
+ "O=UnknownCert, L=Some-City, ST=Some-State, C=US";
+
+ /*
+ * Certificate KeyUsage reference:
+ *
+ * digitalSignature (0),
+ * nonRepudiation (1),
+ * keyEncipherment (2),
+ * dataEncipherment (3),
+ * keyAgreement (4),
+ * keyCertSign (5),
+ * cRLSign (6),
+ * encipherOnly (7),
+ * decipherOnly (8)
+ */
+
+ private static final boolean[] DEFAULT_KEY_USAGES =
+ new boolean[]{true, true, true, true, true, true};
+ private static final boolean[] NONE_KEY_USAGES =
+ new boolean[]{false, false, false, false, false, false};
+ private static final boolean[] NO_DS_USAGE =
+ new boolean[]{false, true, true, true, true, true};
+ private static final boolean[] NO_DS_NO_KE_USAGE =
+ new boolean[]{false, true, false, true, true, true};
+ private static final boolean[] NO_KA_USAGE =
+ new boolean[]{true, true, true, true, false, true};
+
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ throw new RuntimeException("Wrong number of arguments");
+ }
+
+ String enabled = args[0];
+ String kmAlg = args[1];
+
+ System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled);
+
+ // --- Usage and expired test cases --
+
+ // Both client and server should be checked with no usages at all
+ usageTestCase(enabled, kmAlg, "RSA", NONE_KEY_USAGES, true, true);
+
+ // Only client should be checked with RSA algorithm and
+ // no digital signature bit set
+ usageTestCase(enabled, kmAlg, "RSA", NO_DS_USAGE, false, true);
+
+ // Only server should be checked with RSA algorithm and
+ // no digital signature bit set
+ usageTestCase(enabled, kmAlg, "RSASSA-PSS", NO_DS_USAGE, true, false);
+
+ // Both client and server should be checked with DSA algorithm and no
+ // digital signature bit set
+ usageTestCase(enabled, kmAlg, "DSA", NO_DS_USAGE, true, true);
+
+ // Both client and server should be checked with EC algorithm and no
+ // digital signature bit set
+ usageTestCase(enabled, kmAlg, "EC", NO_DS_USAGE, true, true);
+
+ // Both client and server should be checked with RSA algorithm and
+ // missing digital signature and key encipherment bits.
+ usageTestCase(enabled, kmAlg, "RSA", NO_DS_NO_KE_USAGE, true, true);
+
+ // Both client and server should be checked with DH algorithm and no
+ // key agreement bit set.
+ usageTestCase(enabled, kmAlg, "DH", NO_KA_USAGE, true, true);
+
+ // Only server should be checked with EC algorithm and
+ // no digital signature bit set
+ usageTestCase(enabled, kmAlg, "EC", NO_KA_USAGE, true, false);
+
+ // --- Issuer match test cases ---
+
+ // Check CA issuer match
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA",
+ new Principal[]{new X500Principal(CA_ISSUER_STRING)}, true);
+
+ // Check CA issuer match with non-X500 principal
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA",
+ new Principal[]{new UserPrincipal(CA_ISSUER_STRING)}, true);
+
+ // Non-convertable principal should match all
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA",
+ new Principal[]{new InvalidPrincipal()}, true);
+
+ // Empty issuer array should match all
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA",
+ new Principal[]{}, true);
+
+ // Null issuer array should match all
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA", null, true);
+
+ // Issuer that is not in the chain should not match.
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA",
+ new Principal[]{new X500Principal(UNKNOWN_ISSUER_STRING)},
+ false);
+
+ // --- Key Type match test cases ---
+
+ // Exact key type match.
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "EC", "EC", null, true);
+
+ // Key type with a signature algorithm match.
+ issuerAndKeyTypeTestCase(
+ enabled, kmAlg, "EC", "EC_" + CA_KEY_TYPE, null, true);
+
+ // Null KeyType should not match.
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", null, null, false);
+
+ // Wrong KeyType should not match.
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "EC", null, false);
+
+ // Wrong signature algorithm should not match.
+ issuerAndKeyTypeTestCase(enabled, kmAlg, "RSA", "RSA_EC", null, false);
+
+ // Correct signature algorithm but incorrect key algorithm
+ // should not match.
+ issuerAndKeyTypeTestCase(
+ enabled, kmAlg, "RSA", "EC_" + CA_KEY_TYPE, null, false);
+ }
+
+ private static void usageTestCase(String enabled, String kmAlg,
+ String keyAlg, boolean[] certKeyUsages, boolean checkServer,
+ boolean checkClient) throws Exception {
+
+ X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager(
+ kmAlg, keyAlg, certKeyUsages);
+
+ String chosenServerAlias = km.chooseServerAlias(keyAlg, null, null);
+ String chosenEngineServerAlias = km.chooseEngineServerAlias(
+ keyAlg, null, null);
+ String chosenClientAlias = km.chooseClientAlias(
+ new String[]{keyAlg}, null, null);
+ String chosenEngineClientAlias = km.chooseEngineClientAlias(
+ new String[]{keyAlg}, null, null);
+
+ String[] allServerAliases = km.getServerAliases(keyAlg, null);
+ String[] allClientAliases = km.getClientAliases(keyAlg, null);
+
+ if ("false".equals(enabled) && kmAlg.equals("SunX509")) {
+ // Initial order alias returned
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(chosenServerAlias));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(chosenClientAlias));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(chosenEngineServerAlias));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(chosenEngineClientAlias));
+
+ // Assert the initial order of all aliases.
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allServerAliases[0]));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allClientAliases[0]));
+ assertEquals(PREFERRED_ALIAS, normalizeAlias(allServerAliases[1]));
+ assertEquals(PREFERRED_ALIAS, normalizeAlias(allClientAliases[1]));
+ assertEquals(EXPIRED_ALIAS, normalizeAlias(allServerAliases[2]));
+ assertEquals(EXPIRED_ALIAS, normalizeAlias(allClientAliases[2]));
+
+ } else {
+ if (checkServer) {
+ // Preferred alias returned
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(chosenServerAlias));
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(chosenEngineServerAlias));
+
+ // Assert the correct sorted order of all aliases.
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(allServerAliases[0]));
+ assertEquals(EXPIRED_ALIAS,
+ normalizeAlias(allServerAliases[1]));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allServerAliases[2]));
+ }
+
+ if (checkClient) {
+ // Preferred alias returned
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(chosenClientAlias));
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(chosenEngineClientAlias));
+
+ // Assert the correct sorted order of all aliases.
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(allClientAliases[0]));
+ assertEquals(EXPIRED_ALIAS,
+ normalizeAlias(allClientAliases[1]));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allClientAliases[2]));
+ }
+ }
+ }
+
+ private static void issuerAndKeyTypeTestCase(String enabled, String kmAlg,
+ String keyAlg, String keyType, Principal[] issuers, boolean found)
+ throws Exception {
+
+ X509ExtendedKeyManager km = (X509ExtendedKeyManager) getKeyManager(
+ kmAlg, keyAlg, NONE_KEY_USAGES);
+
+ List chosenAliases = new ArrayList<>(4);
+
+ chosenAliases.add(km.chooseServerAlias(keyType, issuers, null));
+ chosenAliases.add(km.chooseEngineServerAlias(keyType, issuers, null));
+ chosenAliases.add(
+ km.chooseClientAlias(new String[]{keyType}, issuers, null));
+ chosenAliases.add(km.chooseEngineClientAlias(
+ new String[]{keyType}, issuers, null));
+
+ String[] allServerAliases = km.getServerAliases(keyType, issuers);
+ String[] allClientAliases = km.getClientAliases(keyType, issuers);
+
+ if (found) {
+ if ("false".equals(enabled) && kmAlg.equals("SunX509")) {
+ chosenAliases.forEach(a ->
+ assertEquals(USAGE_MISMATCH_ALIAS, normalizeAlias(a)));
+
+ // Assert the initial order of all aliases.
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allServerAliases[0]));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allClientAliases[0]));
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(allServerAliases[1]));
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(allClientAliases[1]));
+ assertEquals(EXPIRED_ALIAS,
+ normalizeAlias(allServerAliases[2]));
+ assertEquals(EXPIRED_ALIAS,
+ normalizeAlias(allClientAliases[2]));
+ } else {
+ chosenAliases.forEach(a ->
+ assertEquals(PREFERRED_ALIAS, normalizeAlias(a)));
+
+ // Assert the correct sorted order of all aliases.
+ assertEquals(PREFERRED_ALIAS,
+ normalizeAlias(allServerAliases[0]));
+ assertEquals(EXPIRED_ALIAS,
+ normalizeAlias(allServerAliases[1]));
+ assertEquals(USAGE_MISMATCH_ALIAS,
+ normalizeAlias(allServerAliases[2]));
+ }
+ } else {
+ chosenAliases.forEach(Asserts::assertNull);
+ assertNull(allServerAliases);
+ assertNull(allClientAliases);
+ }
+ }
+
+ // PKIX KeyManager adds a cache prefix to an alias.
+ private static String normalizeAlias(String alias) {
+ return alias.substring(alias.lastIndexOf(".") + 1);
+
+ }
+
+ private static class InvalidPrincipal implements Principal {
+
+ @Override
+ public String getName() {
+ return null;
+ }
+ }
+
+ private static X509KeyManager getKeyManager(String kmAlg,
+ String keyAlg, boolean[] certKeyUsages)
+ throws Exception {
+
+ // Create a key store.
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+
+ // Generate and set the trusted cert.
+ KeyPair caKeys = KeyPairGenerator.getInstance(CA_KEY_TYPE)
+ .generateKeyPair();
+ X509Certificate trustedCert = createTrustedCert(caKeys);
+ ks.setCertificateEntry("CA entry", trustedCert);
+
+ // Generate valid certificate chain.
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyAlg);
+ KeyPair validEndpointKeys = kpg.generateKeyPair();
+
+ X509Certificate validEndpointCert = customCertificateBuilder(
+ EE_ISSUER_STRING,
+ validEndpointKeys.getPublic(), caKeys.getPublic(),
+ Instant.now(), DEFAULT_KEY_USAGES)
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG);
+
+ Certificate[] validChain = new Certificate[2];
+ validChain[0] = validEndpointCert;
+ validChain[1] = trustedCert;
+
+ // Generate expired certificate chain.
+ KeyPair expiredEndpointKeys = kpg.generateKeyPair();
+
+ X509Certificate expiredEndpointCert = customCertificateBuilder(
+ EE_ISSUER_STRING,
+ expiredEndpointKeys.getPublic(), caKeys.getPublic(),
+ Instant.now().minus(1, ChronoUnit.DAYS), DEFAULT_KEY_USAGES)
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG);
+
+ Certificate[] expiredChain = new Certificate[2];
+ expiredChain[0] = expiredEndpointCert;
+ expiredChain[1] = trustedCert;
+
+ // Generate usage mismatch certificate chain.
+ KeyPair usageMismatchEndpointKeys = kpg.generateKeyPair();
+
+ X509Certificate usageMismatchEndpointCert = customCertificateBuilder(
+ EE_ISSUER_STRING,
+ usageMismatchEndpointKeys.getPublic(), caKeys.getPublic(),
+ Instant.now(), certKeyUsages)
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), CERT_SIG_ALG);
+
+ Certificate[] usageMismatchChain = new Certificate[2];
+ usageMismatchChain[0] = usageMismatchEndpointCert;
+ usageMismatchChain[1] = trustedCert;
+
+ // Import the key entries, order matters.
+ final char[] passphrase = "passphrase".toCharArray();
+ ks.setKeyEntry(USAGE_MISMATCH_ALIAS,
+ usageMismatchEndpointKeys.getPrivate(), passphrase,
+ usageMismatchChain);
+ ks.setKeyEntry(PREFERRED_ALIAS, validEndpointKeys.getPrivate(),
+ passphrase,
+ validChain);
+ ks.setKeyEntry(EXPIRED_ALIAS, expiredEndpointKeys.getPrivate(),
+ passphrase,
+ expiredChain);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg);
+ kmf.init(ks, passphrase);
+
+ return (X509KeyManager) kmf.getKeyManagers()[0];
+ }
+
+ // Certificate-building helper methods.
+
+ private static X509Certificate createTrustedCert(KeyPair caKeys)
+ throws Exception {
+ SecureRandom random = new SecureRandom();
+
+ KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic());
+ GeneralNames gns = new GeneralNames();
+ GeneralName name = new GeneralName(new X500Name(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US"));
+ gns.add(name);
+ BigInteger serialNumber = BigInteger.valueOf(
+ random.nextLong(1000000) + 1);
+ return customCertificateBuilder(
+ CA_ISSUER_STRING,
+ caKeys.getPublic(), caKeys.getPublic(), Instant.now(),
+ DEFAULT_KEY_USAGES)
+ .setSerialNumber(serialNumber)
+ .addExtension(new AuthorityKeyIdentifierExtension(kid, gns,
+ new SerialNumber(serialNumber)))
+ .addBasicConstraintsExt(true, true, -1)
+ .build(null, caKeys.getPrivate(), CERT_SIG_ALG);
+ }
+
+ private static CertificateBuilder customCertificateBuilder(
+ String subjectName, PublicKey publicKey, PublicKey caKey,
+ Instant certDate, boolean[] certKeyUsages)
+ throws CertificateException, IOException {
+ SecureRandom random = new SecureRandom();
+
+ CertificateBuilder builder = new CertificateBuilder()
+ .setSubjectName(subjectName)
+ .setPublicKey(publicKey)
+ .setNotBefore(
+ Date.from(certDate.minus(1, ChronoUnit.HOURS)))
+ .setNotAfter(Date.from(certDate.plus(1, ChronoUnit.HOURS)))
+ .setSerialNumber(
+ BigInteger.valueOf(random.nextLong(1000000) + 1))
+ .addSubjectKeyIdExt(publicKey)
+ .addAuthorityKeyIdExt(caKey);
+ builder.addKeyUsageExt(certKeyUsages);
+
+ return builder;
+ }
+}
diff --git a/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java b/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java
new file mode 100644
index 0000000000000..bbfbfe0b46e8a
--- /dev/null
+++ b/test/jdk/sun/security/ssl/X509KeyManager/PeerConstraintsCheck.java
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+import static jdk.test.lib.Asserts.assertEquals;
+import static jdk.test.lib.Asserts.assertTrue;
+import static jdk.test.lib.Utils.runAndCheckException;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.Socket;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.X509ExtendedTrustManager;
+import jdk.test.lib.security.CertificateBuilder;
+import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.GeneralName;
+import sun.security.x509.GeneralNames;
+import sun.security.x509.KeyIdentifier;
+import sun.security.x509.SerialNumber;
+import sun.security.x509.X500Name;
+
+/*
+ * @test
+ * @bug 8359956
+ * @summary Support algorithm constraints and certificate checks in SunX509
+ * key manager
+ * @modules java.base/sun.security.x509
+ * java.base/sun.security.util
+ * @library /javax/net/ssl/templates
+ * /test/lib
+ * @run main/othervm PeerConstraintsCheck false SunX509
+ * @run main/othervm PeerConstraintsCheck true SunX509
+ * @run main/othervm PeerConstraintsCheck false PKIX
+ * @run main/othervm PeerConstraintsCheck true PKIX
+ */
+
+/*
+ * This class tests against the peer supported certificate signatures sent in
+ * "signature_algorithms_cert" extension.
+ */
+
+public class PeerConstraintsCheck extends SSLSocketTemplate {
+
+ private static final String KEY_ALGORITHM = "EC";
+ private static final String CLIENT_CERT_SIG_SCHEME =
+ "ecdsa_secp384r1_sha384";
+ private static final String CLIENT_CERT_SIG_ALG = "SHA384withECDSA";
+ private static final String SERVER_CERT_SIG_ALG = "SHA256withECDSA";
+ private static final String TRUSTED_CERT_SIG_ALG = "SHA512withECDSA";
+
+ private final String kmAlg;
+ private X509Certificate trustedCert;
+ private X509Certificate serverCert;
+ private X509Certificate clientCert;
+ private KeyPair serverKeys;
+ private KeyPair clientKeys;
+
+ protected PeerConstraintsCheck(String kmAlg) throws Exception {
+ super();
+ this.kmAlg = kmAlg;
+ setupCertificates();
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Make sure both client and server support client's signature scheme,
+ // so the exception happens later during KeyManager's algorithm check.
+ System.setProperty(
+ "jdk.tls.client.SignatureSchemes", CLIENT_CERT_SIG_SCHEME);
+ System.setProperty(
+ "jdk.tls.server.SignatureSchemes", CLIENT_CERT_SIG_SCHEME);
+
+ String enabled = args[0];
+ String kmAlg = args[1];
+
+ System.setProperty("jdk.tls.SunX509KeyManager.certChecking", enabled);
+
+ if ("false".equals(enabled) && kmAlg.equals("SunX509")) {
+ new PeerConstraintsCheck(kmAlg).run();
+ } else {
+ // "jdk.tls.client.SignatureSchemes" and
+ // "jdk.tls.server.SignatureSchemes" system properties set
+ // signature schemes for both "signature_algorithms" and
+ // "signature_algorithms_cert" extensions. Then we fail because
+ // server's certificate is signed with "SHA256withECDSA" while
+ // "signature_algorithms_cert" extension only contains an
+ // "ecdsa_secp384r1_sha384" signature scheme corresponding to
+ // "SHA384withECDSA" certificate signature.
+ runAndCheckException(
+ () -> new PeerConstraintsCheck(kmAlg).run(),
+ ex -> {
+ assertTrue(ex instanceof SSLHandshakeException);
+ assertEquals(ex.getMessage(), "(handshake_failure) "
+ + "No available authentication scheme");
+ }
+ );
+ }
+ }
+
+ @Override
+ public SSLContext createServerSSLContext() throws Exception {
+ return getSSLContext(
+ trustedCert, serverCert, serverKeys.getPrivate(), kmAlg);
+ }
+
+ @Override
+ public SSLContext createClientSSLContext() throws Exception {
+ return getSSLContext(
+ trustedCert, clientCert, clientKeys.getPrivate(), kmAlg);
+ }
+
+ private static SSLContext getSSLContext(X509Certificate trustedCertificate,
+ X509Certificate keyCertificate, PrivateKey privateKey, String kmAlg)
+ throws Exception {
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(null, null);
+
+ // import the trusted cert
+ ks.setCertificateEntry("TLS Signer", trustedCertificate);
+
+ // generate certificate chain
+ Certificate[] chain = new Certificate[2];
+ chain[0] = keyCertificate;
+ chain[1] = trustedCertificate;
+
+ // import the key entry.
+ final char[] passphrase = "passphrase".toCharArray();
+ ks.setKeyEntry("Whatever", privateKey, passphrase, chain);
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmAlg);
+ kmf.init(ks, passphrase);
+
+ // Use custom trust-all TrustManager so we perform only KeyManager's
+ // constraints check.
+ X509ExtendedTrustManager[] trustAll = new X509ExtendedTrustManager[]{
+ new X509ExtendedTrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain,
+ String authType, Socket socket)
+ throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain,
+ String authType, Socket socket)
+ throws CertificateException {
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain,
+ String authType, SSLEngine engine)
+ throws CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain,
+ String authType, SSLEngine engine)
+ throws CertificateException {
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain,
+ String authType) throws CertificateException {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain,
+ String authType) throws CertificateException {
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ }
+ };
+
+ ctx.init(kmf.getKeyManagers(), trustAll, null);
+ return ctx;
+ }
+
+ // Certificate-building helper methods.
+
+ private void setupCertificates() throws Exception {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
+ KeyPair caKeys = kpg.generateKeyPair();
+ this.serverKeys = kpg.generateKeyPair();
+ this.clientKeys = kpg.generateKeyPair();
+
+ this.trustedCert = createTrustedCert(caKeys);
+
+ this.serverCert = customCertificateBuilder(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ serverKeys.getPublic(), caKeys.getPublic())
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), SERVER_CERT_SIG_ALG);
+
+ this.clientCert = customCertificateBuilder(
+ "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City,"
+ + " ST=Some-State, C=US",
+ clientKeys.getPublic(), caKeys.getPublic())
+ .addBasicConstraintsExt(false, false, -1)
+ .build(trustedCert, caKeys.getPrivate(), CLIENT_CERT_SIG_ALG);
+ }
+
+ private static X509Certificate createTrustedCert(KeyPair caKeys)
+ throws Exception {
+ SecureRandom random = new SecureRandom();
+ KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic());
+ GeneralNames gns = new GeneralNames();
+ GeneralName name = new GeneralName(new X500Name(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US"));
+ gns.add(name);
+ BigInteger serialNumber = BigInteger.valueOf(
+ random.nextLong(1000000) + 1);
+ return customCertificateBuilder(
+ "O=Some-Org, L=Some-City, ST=Some-State, C=US",
+ caKeys.getPublic(), caKeys.getPublic())
+ .setSerialNumber(serialNumber)
+ .addExtension(new AuthorityKeyIdentifierExtension(kid, gns,
+ new SerialNumber(serialNumber)))
+ .addBasicConstraintsExt(true, true, -1)
+ .build(null, caKeys.getPrivate(), TRUSTED_CERT_SIG_ALG);
+ }
+
+ private static CertificateBuilder customCertificateBuilder(
+ String subjectName, PublicKey publicKey, PublicKey caKey)
+ throws CertificateException, IOException {
+ SecureRandom random = new SecureRandom();
+
+ CertificateBuilder builder = new CertificateBuilder()
+ .setSubjectName(subjectName)
+ .setPublicKey(publicKey)
+ .setNotBefore(
+ Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))
+ .setNotAfter(Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
+ .setSerialNumber(
+ BigInteger.valueOf(random.nextLong(1000000) + 1))
+ .addSubjectKeyIdExt(publicKey)
+ .addAuthorityKeyIdExt(caKey);
+ builder.addKeyUsageExt(
+ new boolean[]{true, true, true, true, true, true});
+
+ return builder;
+ }
+}
diff --git a/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java b/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java
index e2e5e066f9c6a..fcc7cbf73f090 100644
--- a/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java
+++ b/test/jdk/sun/security/ssl/X509TrustManagerImpl/PKIXExtendedTM.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2024, 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
@@ -36,8 +36,6 @@
* @run main/othervm PKIXExtendedTM 3
*/
-import java.net.*;
-import java.util.*;
import java.io.*;
import javax.net.ssl.*;
import java.security.Security;
@@ -1114,6 +1112,10 @@ static class Test {
};
public static void main(String args[]) throws Exception {
+ // Disable KeyManager's algorithm constraints checking as this test
+ // is about TrustManager's constraints check.
+ System.setProperty("jdk.tls.SunX509KeyManager.certChecking", "false");
+
if (args.length != 1) {
throw new Exception("Incorrect number of arguments");
}
diff --git a/test/jdk/sun/security/tools/keytool/PrintSSL.java b/test/jdk/sun/security/tools/keytool/PrintSSL.java
index 7cdc0a4577104..9403ae62d924a 100644
--- a/test/jdk/sun/security/tools/keytool/PrintSSL.java
+++ b/test/jdk/sun/security/tools/keytool/PrintSSL.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -48,6 +48,12 @@
public class PrintSSL {
public static void main(String[] args) throws Throwable {
+ // Disable KeyManager's algorithm constraints checking,
+ // so we can make keytool print certificate with weak
+ // MD5withRSA signature algorithm.
+ System.setProperty(
+ "jdk.tls.SunX509KeyManager.certChecking", "false");
+
Files.deleteIfExists(Paths.get("keystore"));
// make sure that "-printcert" works with weak algorithms
diff --git a/test/lib/jdk/test/lib/security/CertificateBuilder.java b/test/lib/jdk/test/lib/security/CertificateBuilder.java
index 60358c9a4eabf..857d585f02973 100644
--- a/test/lib/jdk/test/lib/security/CertificateBuilder.java
+++ b/test/lib/jdk/test/lib/security/CertificateBuilder.java
@@ -43,6 +43,7 @@
import sun.security.x509.AlgorithmId;
import sun.security.x509.AuthorityInfoAccessExtension;
import sun.security.x509.AuthorityKeyIdentifierExtension;
+import sun.security.x509.IPAddressName;
import sun.security.x509.SubjectKeyIdentifierExtension;
import sun.security.x509.BasicConstraintsExtension;
import sun.security.x509.CertificateSerialNumber;
@@ -233,6 +234,26 @@ public CertificateBuilder addSubjectAltNameDNSExt(List dnsNames)
return this;
}
+ /**
+ * Helper method to add IPAddress types for the SAN extension
+ *
+ * @param ipAddresses A {@code List} of names to add as IPAddress
+ * types
+ * @throws IOException if an encoding error occurs.
+ */
+ public CertificateBuilder addSubjectAltNameIPExt(List ipAddresses)
+ throws IOException {
+ if (!ipAddresses.isEmpty()) {
+ GeneralNames gNames = new GeneralNames();
+ for (String name : ipAddresses) {
+ gNames.add(new GeneralName(new IPAddressName(name)));
+ }
+ addExtension(new SubjectAlternativeNameExtension(false,
+ gNames));
+ }
+ return this;
+ }
+
/**
* Helper method to add one or more OCSP URIs to the Authority Info Access
* certificate extension. Location strings can be in two forms: