8359956: Support algorithm constraints and certificate checks in SunX509 key manager

Reviewed-by: mullan
This commit is contained in:
Artur Barashev 2025-07-31 13:57:19 +00:00
parent 458f033d4d
commit e544cd9920
17 changed files with 1855 additions and 741 deletions

View File

@ -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.ArrayList;
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.
*
* <p>
* 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.
*
* <p>
* 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.
* <p>
* 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<String,X509Credentials> credentialsMap;
private final Map<String, X509Credentials> 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<String,String[]> 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<X500Principal> 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<X500Principal> getIssuerX500Principals() {
return issuerX500Principals;
}
}
@ -126,14 +100,13 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
NoSuchAlgorithmException, UnrecoverableKeyException {
credentialsMap = new HashMap<>();
serverAliasCache = Collections.synchronizedMap(
new HashMap<>());
if (ks == null) {
return;
}
for (Enumeration<String> aliases = ks.aliases();
aliases.hasMoreElements(); ) {
aliases.hasMoreElements(); ) {
String alias = aliases.nextElement();
if (!ks.isKeyEntry(alias)) {
continue;
@ -153,11 +126,11 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
@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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
* <code>SSLEngine</code> 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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
@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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
* <code>SSLEngine</code> 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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
*/
@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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
*/
@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<KeyType> keyTypes, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints,
List<SNIServerName> 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 @@ final class SunX509KeyManagerImpl extends X509ExtendedKeyManager {
*
* Issuers come to us in the form of X500Principal[].
*/
private String[] getAliases(String keyType, Principal[] issuers) {
if (keyType == null) {
private String[] getAliases(List<KeyType> keyTypes, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints,
List<SNIServerName> 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<String> aliases = new ArrayList<>();
Set<X500Principal> issuerSet = getIssuerSet(issuers);
List<EntryStatus> results = null;
for (Map.Entry<String,X509Credentials> entry :
credentialsMap.entrySet()) {
for (Map.Entry<String, X509Credentials> 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 (results == null) {
results = new ArrayList<>();
}
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<X500Principal> 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;
}
}
}
results.add(status);
}
String[] aliasStrings = aliases.toArray(STRING0);
return ((aliasStrings.length == 0) ? null : aliasStrings);
}
/*
* 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<X500Principal> 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);
}
}

View File

@ -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<KeyType> keyTypes,
Set<X500Principal> issuerSet, CheckType checkType,
AlgorithmConstraints constraints,
List<SNIServerName> 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<SNIServerName> 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<String> validEku;
CheckType(Set<String> 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<SNIServerName> serverNames, String idAlgorithm) {
if (this == NONE) {
return CheckResult.OK;
}
// check extensions
try {
// check extended key usage
List<String> 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<EntryStatus> {
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<KeyType> getKeyTypes(String... keyTypes) {
if ((keyTypes == null) ||
(keyTypes.length == 0) || (keyTypes[0] == null)) {
return null;
}
List<KeyType> 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<X500Principal> getIssuerSet(Principal[] issuers) {
if (issuers != null && issuers.length != 0) {
Set<X500Principal> 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;
}
}
}

View File

@ -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;
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 @@ import sun.security.util.KnownOIDs;
*
* @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<Builder> 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<K,V> extends LinkedHashMap<K,V> {
@java.io.Serial
private static final long serialVersionUID = -8211222668790986062L;
@ -120,14 +119,14 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
@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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
}
}
// 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<KeyType> getKeyTypes(String ... keyTypes) {
if ((keyTypes == null) ||
(keyTypes.length == 0) || (keyTypes[0] == null)) {
return null;
}
List<KeyType> 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
* with appropriate key usage to certs with the wrong key usage.
* return the first one of them.
*/
private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints) {
return chooseAlias(keyTypeList, issuers,
checkType, constraints, null, null);
}
private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
CheckType checkType, AlgorithmConstraints constraints,
List<SNIServerName> requestedServerNames, String idAlgorithm) {
@ -369,8 +250,9 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
return null;
}
Set<Principal> issuerSet = getIssuerSet(issuers);
Set<X500Principal> issuerSet = getIssuerSet(issuers);
List<EntryStatus> allResults = null;
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List<EntryStatus> results = getAliases(i, keyTypeList,
@ -390,7 +272,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
}
allResults.addAll(results);
}
} catch (Exception e) {
} catch (KeyStoreException e) {
// ignore
}
}
@ -415,27 +297,28 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
* 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<Principal> issuerSet = getIssuerSet(issuers);
Set<X500Principal> issuerSet = getIssuerSet(issuers);
List<KeyType> keyTypeList = getKeyTypes(keyType);
List<EntryStatus> allResults = null;
for (int i = 0, n = builders.size(); i < n; i++) {
try {
List<EntryStatus> 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
return s;
}
// make a Set out of the array
private Set<Principal> 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<EntryStatus> {
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<String> validEku;
CheckType(Set<String> 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<SNIServerName> serverNames, String idAlgorithm) {
if (this == NONE) {
return CheckResult.OK;
}
// check extensions
try {
// check extended key usage
List<String> 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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
* matches
*/
private List<EntryStatus> getAliases(int builderIndex,
List<KeyType> keyTypes, Set<Principal> issuerSet,
List<KeyType> keyTypes, Set<X500Principal> issuerSet,
boolean findAll, CheckType checkType,
AlgorithmConstraints constraints,
List<SNIServerName> requestedServerNames,
String idAlgorithm) throws Exception {
String idAlgorithm) throws KeyStoreException {
Builder builder = builders.get(builderIndex);
KeyStore ks = builder.getKeyStore();
List<EntryStatus> results = null;
Date date = verificationDate;
boolean preferred = false;
for (Enumeration<String> 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
EntryStatus status = checkAlias(builderIndex, alias,
ks.getCertificateChain(alias),
verificationDate, keyTypes, issuerSet, checkType,
constraints, requestedServerNames, idAlgorithm);
if (status == null) {
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())) {
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
SSLLogger.fine("Ignore alias " + alias +
": certificate list does not conform to " +
"algorithm constraints");
}
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 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
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;
}
}

View File

@ -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.net.ssl.SSLContext;
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 class SSLSocketParametersTest implements Serializable {
}
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]));
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -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 final class ServerIdentityTest extends SSLSocketTemplate {
// Get the customized arguments.
initialize(args);
(new ServerIdentityTest()).run();
new ServerIdentityTest().run();
}
ServerIdentityTest() throws UnknownHostException {
@ -95,7 +122,7 @@ public final class ServerIdentityTest extends SSLSocketTemplate {
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 @@ public final class ServerIdentityTest extends SSLSocketTemplate {
@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();
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);
CertificateBuilder serverCertificateBuilder = customCertificateBuilder(
"CN=server, O=Some-Org, L=Some-City, ST=Some-State, C=US",
serverKeys.getPublic(), caKeys.getPublic())
.addBasicConstraintsExt(false, false, -1);
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;
}
}

View File

@ -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 java.security.cert.*;
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 @@ public class ShortRSAKeyWithinTLS {
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");
}

View File

@ -150,9 +150,11 @@ public class MD5NotAllowedInTLS13CertificateSignature extends
// 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 @@ public class MD5NotAllowedInTLS13CertificateSignature extends
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 @@ public class MD5NotAllowedInTLS13CertificateSignature extends
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(

View File

@ -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;
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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;
}
}

View File

@ -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 @@ public class PKIXExtendedTM {
};
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");
}

View File

@ -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 @@ import jdk.test.lib.process.OutputAnalyzer;
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

View File

@ -43,6 +43,7 @@ import sun.security.x509.AccessDescription;
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 class CertificateBuilder {
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<String> 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: