mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8359956: Support algorithm constraints and certificate checks in SunX509 key manager
Reviewed-by: mullan
This commit is contained in:
parent
458f033d4d
commit
e544cd9920
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -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");
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
472
test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java
Normal file
472
test/jdk/sun/security/ssl/X509KeyManager/CertChecking.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user