|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import java.lang.ref.*; |
|
import java.net.Socket; |
|
import java.security.AlgorithmConstraints; |
|
import java.security.KeyStore; |
|
import java.security.KeyStore.Builder; |
|
import java.security.KeyStore.Entry; |
|
import java.security.KeyStore.PrivateKeyEntry; |
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class X509KeyManagerImpl extends X509ExtendedKeyManager |
|
implements X509KeyManager { |
|
|
|
|
|
private static Date verificationDate; |
|
|
|
|
|
private final List<Builder> builders; |
|
|
|
|
|
private final AtomicLong uidCounter; |
|
|
|
|
|
private final Map<String,Reference<PrivateKeyEntry>> entryCacheMap; |
|
|
|
X509KeyManagerImpl(Builder builder) { |
|
this(Collections.singletonList(builder)); |
|
} |
|
|
|
X509KeyManagerImpl(List<Builder> builders) { |
|
this.builders = builders; |
|
uidCounter = new AtomicLong(); |
|
entryCacheMap = Collections.synchronizedMap |
|
(new SizedMap<String,Reference<PrivateKeyEntry>>()); |
|
} |
|
|
|
// LinkedHashMap with a max size of 10 |
|
|
|
private static class SizedMap<K,V> extends LinkedHashMap<K,V> { |
|
@java.io.Serial |
|
private static final long serialVersionUID = -8211222668790986062L; |
|
|
|
@Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { |
|
return size() > 10; |
|
} |
|
} |
|
|
|
// |
|
// public methods |
|
// |
|
|
|
@Override |
|
public X509Certificate[] getCertificateChain(String alias) { |
|
PrivateKeyEntry entry = getEntry(alias); |
|
return entry == null ? null : |
|
(X509Certificate[])entry.getCertificateChain(); |
|
} |
|
|
|
@Override |
|
public PrivateKey getPrivateKey(String alias) { |
|
PrivateKeyEntry entry = getEntry(alias); |
|
return entry == null ? null : entry.getPrivateKey(); |
|
} |
|
|
|
@Override |
|
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, |
|
Socket socket) { |
|
return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, |
|
getAlgorithmConstraints(socket)); |
|
} |
|
|
|
@Override |
|
public String chooseEngineClientAlias(String[] keyTypes, |
|
Principal[] issuers, SSLEngine engine) { |
|
return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, |
|
getAlgorithmConstraints(engine)); |
|
} |
|
|
|
@Override |
|
public String chooseServerAlias(String keyType, |
|
Principal[] issuers, Socket socket) { |
|
return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, |
|
getAlgorithmConstraints(socket), |
|
X509TrustManagerImpl.getRequestedServerNames(socket), |
|
"HTTPS"); |
|
// The certificate selection scheme for SNI HostName |
|
// is similar to HTTPS endpoint identification scheme |
|
// implemented in this provider. |
|
// |
|
// Using HTTPS endpoint identification scheme to guide |
|
// the selection of an appropriate authentication |
|
// certificate according to requested SNI extension. |
|
// |
|
// It is not a really HTTPS endpoint identification. |
|
} |
|
|
|
@Override |
|
public String chooseEngineServerAlias(String keyType, |
|
Principal[] issuers, SSLEngine engine) { |
|
return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, |
|
getAlgorithmConstraints(engine), |
|
X509TrustManagerImpl.getRequestedServerNames(engine), |
|
"HTTPS"); |
|
// The certificate selection scheme for SNI HostName |
|
// is similar to HTTPS endpoint identification scheme |
|
// implemented in this provider. |
|
// |
|
// Using HTTPS endpoint identification scheme to guide |
|
// the selection of an appropriate authentication |
|
// certificate according to requested SNI extension. |
|
// |
|
// It is not a really HTTPS endpoint identification. |
|
} |
|
|
|
@Override |
|
public String[] getClientAliases(String keyType, Principal[] issuers) { |
|
return getAliases(keyType, issuers, CheckType.CLIENT, null); |
|
} |
|
|
|
@Override |
|
public String[] getServerAliases(String keyType, Principal[] issuers) { |
|
return getAliases(keyType, issuers, CheckType.SERVER, null); |
|
} |
|
|
|
// |
|
// implementation private methods |
|
// |
|
|
|
|
|
private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { |
|
if (socket != null && socket.isConnected() && |
|
socket instanceof SSLSocket) { |
|
|
|
SSLSocket sslSocket = (SSLSocket)socket; |
|
SSLSession session = sslSocket.getHandshakeSession(); |
|
|
|
if (session != null) { |
|
if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { |
|
String[] peerSupportedSignAlgs = null; |
|
|
|
if (session instanceof ExtendedSSLSession) { |
|
ExtendedSSLSession extSession = |
|
(ExtendedSSLSession)session; |
|
peerSupportedSignAlgs = |
|
extSession.getPeerSupportedSignatureAlgorithms(); |
|
} |
|
|
|
return new SSLAlgorithmConstraints( |
|
sslSocket, peerSupportedSignAlgs, true); |
|
} |
|
} |
|
|
|
return new SSLAlgorithmConstraints(sslSocket, true); |
|
} |
|
|
|
return new SSLAlgorithmConstraints((SSLSocket)null, true); |
|
} |
|
|
|
|
|
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) { |
|
ExtendedSSLSession extSession = |
|
(ExtendedSSLSession)session; |
|
peerSupportedSignAlgs = |
|
extSession.getPeerSupportedSignatureAlgorithms(); |
|
} |
|
|
|
return new SSLAlgorithmConstraints( |
|
engine, peerSupportedSignAlgs, true); |
|
} |
|
} |
|
} |
|
|
|
return new SSLAlgorithmConstraints(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() |
|
|
|
private String makeAlias(EntryStatus entry) { |
|
return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." |
|
+ entry.alias; |
|
} |
|
|
|
private PrivateKeyEntry getEntry(String alias) { |
|
|
|
if (alias == null) { |
|
return null; |
|
} |
|
|
|
|
|
Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias); |
|
PrivateKeyEntry entry = (ref != null) ? ref.get() : null; |
|
if (entry != null) { |
|
return entry; |
|
} |
|
|
|
|
|
int firstDot = alias.indexOf('.'); |
|
int secondDot = alias.indexOf('.', firstDot + 1); |
|
if ((firstDot == -1) || (secondDot == firstDot)) { |
|
|
|
return null; |
|
} |
|
try { |
|
int builderIndex = Integer.parseInt |
|
(alias.substring(firstDot + 1, secondDot)); |
|
String keyStoreAlias = alias.substring(secondDot + 1); |
|
Builder builder = builders.get(builderIndex); |
|
KeyStore ks = builder.getKeyStore(); |
|
Entry newEntry = ks.getEntry(keyStoreAlias, |
|
builder.getProtectionParameter(keyStoreAlias)); |
|
if (!(newEntry instanceof PrivateKeyEntry)) { |
|
|
|
return null; |
|
} |
|
entry = (PrivateKeyEntry)newEntry; |
|
entryCacheMap.put(alias, new SoftReference<PrivateKeyEntry>(entry)); |
|
return entry; |
|
} catch (Exception e) { |
|
|
|
return null; |
|
} |
|
} |
|
|
|
// Class to help verify that the public key algorithm (and optionally |
|
|
|
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 |
|
|
|
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) { |
|
|
|
return sigKeyAlgorithm.equals( |
|
chain[1].getPublicKey().getAlgorithm()); |
|
} else { |
|
// Check the signature algorithm of the certificate itself. |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
if (keyTypeList == null || keyTypeList.isEmpty()) { |
|
return null; |
|
} |
|
|
|
Set<Principal> issuerSet = getIssuerSet(issuers); |
|
List<EntryStatus> allResults = null; |
|
for (int i = 0, n = builders.size(); i < n; i++) { |
|
try { |
|
List<EntryStatus> results = getAliases(i, keyTypeList, |
|
issuerSet, false, checkType, constraints, |
|
requestedServerNames, idAlgorithm); |
|
if (results != null) { |
|
// the results will either be a single perfect match |
|
// or 1 or more imperfect matches |
|
|
|
EntryStatus status = results.get(0); |
|
if (status.checkResult == CheckResult.OK) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine("KeyMgr: choosing key: " + status); |
|
} |
|
return makeAlias(status); |
|
} |
|
if (allResults == null) { |
|
allResults = new ArrayList<EntryStatus>(); |
|
} |
|
allResults.addAll(results); |
|
} |
|
} catch (Exception e) { |
|
// ignore |
|
} |
|
} |
|
if (allResults == null) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine("KeyMgr: no matching key found"); |
|
} |
|
return null; |
|
} |
|
Collections.sort(allResults); |
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine( |
|
"KeyMgr: no good matching key found, " |
|
+ "returning best match out of", allResults); |
|
} |
|
return makeAlias(allResults.get(0)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String[] getAliases(String keyType, Principal[] issuers, |
|
CheckType checkType, AlgorithmConstraints constraints) { |
|
if (keyType == null) { |
|
return null; |
|
} |
|
|
|
Set<Principal> 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); |
|
if (results != null) { |
|
if (allResults == null) { |
|
allResults = new ArrayList<>(); |
|
} |
|
allResults.addAll(results); |
|
} |
|
} catch (Exception e) { |
|
// ignore |
|
} |
|
} |
|
if (allResults == null || allResults.isEmpty()) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine("KeyMgr: no matching alias found"); |
|
} |
|
return null; |
|
} |
|
Collections.sort(allResults); |
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine("KeyMgr: getting aliases", allResults); |
|
} |
|
return toAliases(allResults); |
|
} |
|
|
|
|
|
private String[] toAliases(List<EntryStatus> results) { |
|
String[] s = new String[results.size()]; |
|
int i = 0; |
|
for (EntryStatus result : results) { |
|
s[i++] = makeAlias(result); |
|
} |
|
return s; |
|
} |
|
|
|
|
|
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 |
|
|
|
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) |
|
|
|
private static enum CheckType { |
|
|
|
|
|
NONE(Collections.<String>emptySet()), |
|
|
|
// enum constant for "tls client" check |
|
|
|
CLIENT(new HashSet<String>(List.of( |
|
KnownOIDs.anyExtendedKeyUsage.value(), |
|
KnownOIDs.clientAuth.value() |
|
))), |
|
|
|
// enum constant for "tls server" check |
|
|
|
SERVER(new HashSet<String>(List.of( |
|
KnownOIDs.anyExtendedKeyUsage.value(), |
|
KnownOIDs.serverAuth.value(), |
|
KnownOIDs.NETSCAPE_ExportApproved.value(), |
|
KnownOIDs.MICROSOFT_ExportApproved.value() |
|
))); |
|
|
|
|
|
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 |
|
|
|
CheckResult check(X509Certificate cert, Date date, |
|
List<SNIServerName> serverNames, String idAlgorithm) { |
|
|
|
if (this == NONE) { |
|
return CheckResult.OK; |
|
} |
|
|
|
|
|
try { |
|
|
|
List<String> certEku = cert.getExtendedKeyUsage(); |
|
if ((certEku != null) && |
|
Collections.disjoint(validEku, certEku)) { |
|
// if extension present and it does not contain any of |
|
|
|
return CheckResult.EXTENSION_MISMATCH; |
|
} |
|
|
|
|
|
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 |
|
|
|
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": |
|
|
|
if (!supportsDigitalSignature) { |
|
return CheckResult.EXTENSION_MISMATCH; |
|
} |
|
break; |
|
case "DH": |
|
|
|
if (!getBit(ku, 4)) { |
|
return CheckResult.EXTENSION_MISMATCH; |
|
} |
|
break; |
|
case "EC": |
|
|
|
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 |
|
|
|
if (this == SERVER && !getBit(ku, 4)) { |
|
return CheckResult.EXTENSION_MISMATCH; |
|
} |
|
break; |
|
} |
|
} |
|
} catch (CertificateException e) { |
|
|
|
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) { |
|
|
|
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 Inidication (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 |
|
|
|
private static enum CheckResult { |
|
OK, |
|
INSENSITIVE, |
|
EXPIRED, |
|
EXTENSION_MISMATCH, |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private List<EntryStatus> getAliases(int builderIndex, |
|
List<KeyType> keyTypes, Set<Principal> issuerSet, |
|
boolean findAll, CheckType checkType, |
|
AlgorithmConstraints constraints, |
|
List<SNIServerName> requestedServerNames, |
|
String idAlgorithm) throws Exception { |
|
|
|
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(); |
|
|
|
if (!ks.isKeyEntry(alias)) { |
|
continue; |
|
} |
|
|
|
Certificate[] chain = ks.getCertificateChain(alias); |
|
if ((chain == null) || (chain.length == 0)) { |
|
|
|
continue; |
|
} |
|
|
|
boolean incompatible = false; |
|
for (Certificate cert : chain) { |
|
if (!(cert instanceof X509Certificate)) { |
|
|
|
incompatible = true; |
|
break; |
|
} |
|
} |
|
if (incompatible) { |
|
continue; |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
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) { |
|
preferred = true; |
|
} |
|
if (preferred && !findAll) { |
|
// if we have a good match and do not need all matches, |
|
|
|
return Collections.singletonList(status); |
|
} else { |
|
if (results == null) { |
|
results = new ArrayList<>(); |
|
} |
|
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) { |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) { |
|
SSLLogger.fine( |
|
"Cannot initialize algorithm constraints checker", cpve); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
for (int i = chain.length - 1; i >= 0; i--) { |
|
Certificate cert = chain[i]; |
|
try { |
|
|
|
checker.check(cert, Collections.<String>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; |
|
} |
|
} |