/* * Copyright (c) 2004, 2011, 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.lang.ref.*;import java.util.*;import static java.util.Locale.ENGLISH;import java.util.concurrent.atomic.AtomicLong;import java.net.Socket; import java.security.*;import java.security.KeyStore.*;import java.security.cert.*;import java.security.cert.Certificate; import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; /** * The new X509 key manager implementation. The main differences to the * old SunX509 key manager are: * . it is based around the KeyStore.Builder API. This allows it to use * other forms of KeyStore protection or password input (e.g. a * CallbackHandler) or to have keys within one KeyStore protected by * different keys. * . it can use multiple KeyStores at the same time. * . it is explicitly designed to accomodate KeyStores that change over * the lifetime of the process. * . it makes an effort to choose the key that matches best, i.e. one that * is not expired and has the appropriate certificate extensions. * * Note that this code is not explicitly performance optimzied yet. * * @author Andreas Sterbenz */final class X509KeyManagerImpl extends X509ExtendedKeyManager implements X509KeyManager { private static final Debug debug = Debug.getInstance("ssl"); private final static boolean useDebug = (debug != null) && Debug.isOn("keymanager"); // for unit testing only, set via privileged reflection private static Date verificationDate; // list of the builders private final List<Builder> builders; // counter to generate unique ids for the aliases private final AtomicLong uidCounter; // cached entries 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 // see LinkedHashMap JavaDocs private static class SizedMap<K,V> extends LinkedHashMap<K,V> { @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > 10; } } // // public methods // public X509Certificate[] getCertificateChain(String alias) { PrivateKeyEntry entry = getEntry(alias); return entry == null ? null : (X509Certificate[])entry.getCertificateChain(); } public PrivateKey getPrivateKey(String alias) { PrivateKeyEntry entry = getEntry(alias); return entry == null ? null : entry.getPrivateKey(); } public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, getAlgorithmConstraints(socket)); } public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, getAlgorithmConstraints(engine)); } public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, getAlgorithmConstraints(socket)); } public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, getAlgorithmConstraints(engine)); } public String[] getClientAliases(String keyType, Principal[] issuers) { return getAliases(keyType, issuers, CheckType.CLIENT, null); } public String[] getServerAliases(String keyType, Principal[] issuers) { return getAliases(keyType, issuers, CheckType.SERVER, null); } // // implementation private methods // // Gets algorithm constraints of the socket. private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket)socket; SSLSession session = sslSocket.getHandshakeSession(); if (session != null) { ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); if (protocolVersion.v >= ProtocolVersion.TLS12.v) { 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); } // Gets algorithm constraints of the engine. private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { if (engine != null) { SSLSession session = engine.getHandshakeSession(); if (session != null) { ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); if (protocolVersion.v >= ProtocolVersion.TLS12.v) { 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() // even if tokens are inserted or removed private String makeAlias(EntryStatus entry) { return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." + entry.alias; } private PrivateKeyEntry getEntry(String alias) { // if the alias is null, return immediately if (alias == null) { return null; } // try to get the entry from cache Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias); PrivateKeyEntry entry = (ref != null) ? ref.get() : null; if (entry != null) { return entry; } // parse the alias int firstDot = alias.indexOf('.'); int secondDot = alias.indexOf('.', firstDot + 1); if ((firstDot == -1) || (secondDot == firstDot)) { // invalid alias 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(alias)); if (newEntry instanceof PrivateKeyEntry == false) { // unexpected type of entry return null; } entry = (PrivateKeyEntry)newEntry; entryCacheMap.put(alias, new SoftReference(entry)); return entry; } catch (Exception e) { // ignore return null; } } // 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(ENGLISH); String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(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: * . scan through all the aliases in all builders in order * . as soon as we find a perfect match, return * (i.e. a match with a cert that has appropriate key usage * and is not expired). * . if we do not find a perfect match, keep looping and remember * the imperfect matches * . at the end, sort the imperfect matches. we prefer expired certs * 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) { if (keyTypeList == null || keyTypeList.size() == 0) { 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); if (results != null) { // the results will either be a single perfect match // or 1 or more imperfect matches // if it's a perfect match, return immediately EntryStatus status = results.get(0); if (status.checkResult == CheckResult.OK) { if (useDebug) { debug.println("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 (useDebug) { debug.println("KeyMgr: no matching key found"); } return null; } Collections.sort(allResults); if (useDebug) { debug.println("KeyMgr: no good matching key found, " + "returning best match out of:"); debug.println(allResults.toString()); } return makeAlias(allResults.get(0)); } /* * Return all aliases that (approximately) fit the parameters. * These are perfect matches plus imperfect matches (expired certificates * 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) { 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); if (results != null) { if (allResults == null) { allResults = new ArrayList<EntryStatus>(); } allResults.addAll(results); } } catch (Exception e) { // ignore } } if (allResults == null || allResults.size() == 0) { if (useDebug) { debug.println("KeyMgr: no matching alias found"); } return null; } Collections.sort(allResults); if (useDebug) { debug.println("KeyMgr: getting aliases: " + allResults); } return toAliases(allResults); } // turn candidate entries into unique aliases we can return to JSSE 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; } // 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; } public int compareTo(EntryStatus other) { int result = this.checkResult.compareTo(other.checkResult); return (result == 0) ? (this.keyIndex - other.keyIndex) : result; } 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 static enum CheckType { // enum constant for "no check" (currently not used) NONE(Collections.<String>emptySet()), // enum constant for "tls client" check // valid EKU for TLS client: any, tls_client CLIENT(new HashSet<String>(Arrays.asList(new String[] { "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), // enum constant for "tls server" check // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc SERVER(new HashSet<String>(Arrays.asList(new String[] { "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", "1.3.6.1.4.1.311.10.3.3" }))); // 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]; } Lines 1–500 of 741. Load more
/* * Copyright (c) 2004, 2011, 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.lang.ref.*; import java.util.*; import static java.util.Locale.ENGLISH; import java.util.concurrent.atomic.AtomicLong; import java.net.Socket; import java.security.*; import java.security.KeyStore.*; import java.security.cert.*; import java.security.cert.Certificate; import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; /** * The new X509 key manager implementation. The main differences to the * old SunX509 key manager are: * . it is based around the KeyStore.Builder API. This allows it to use * other forms of KeyStore protection or password input (e.g. a * CallbackHandler) or to have keys within one KeyStore protected by * different keys. * . it can use multiple KeyStores at the same time. * . it is explicitly designed to accomodate KeyStores that change over * the lifetime of the process. * . it makes an effort to choose the key that matches best, i.e. one that * is not expired and has the appropriate certificate extensions. * * Note that this code is not explicitly performance optimzied yet. * * @author Andreas Sterbenz */ final class X509KeyManagerImpl extends X509ExtendedKeyManager implements X509KeyManager { private static final Debug debug = Debug.getInstance("ssl"); private final static boolean useDebug = (debug != null) && Debug.isOn("keymanager"); // for unit testing only, set via privileged reflection private static Date verificationDate; // list of the builders private final List<Builder> builders; // counter to generate unique ids for the aliases private final AtomicLong uidCounter; // cached entries 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 // see LinkedHashMap JavaDocs private static class SizedMap<K,V> extends LinkedHashMap<K,V> { @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return size() > 10; } } // // public methods // public X509Certificate[] getCertificateChain(String alias) { PrivateKeyEntry entry = getEntry(alias); return entry == null ? null : (X509Certificate[])entry.getCertificateChain(); } public PrivateKey getPrivateKey(String alias) { PrivateKeyEntry entry = getEntry(alias); return entry == null ? null : entry.getPrivateKey(); } public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, getAlgorithmConstraints(socket)); } public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, getAlgorithmConstraints(engine)); } public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, getAlgorithmConstraints(socket)); } public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, getAlgorithmConstraints(engine)); } public String[] getClientAliases(String keyType, Principal[] issuers) { return getAliases(keyType, issuers, CheckType.CLIENT, null); } public String[] getServerAliases(String keyType, Principal[] issuers) { return getAliases(keyType, issuers, CheckType.SERVER, null); } // // implementation private methods // // Gets algorithm constraints of the socket. private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket)socket; SSLSession session = sslSocket.getHandshakeSession(); if (session != null) { ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); if (protocolVersion.v >= ProtocolVersion.TLS12.v) { 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); } // Gets algorithm constraints of the engine. private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { if (engine != null) { SSLSession session = engine.getHandshakeSession(); if (session != null) { ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); if (protocolVersion.v >= ProtocolVersion.TLS12.v) { 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() // even if tokens are inserted or removed private String makeAlias(EntryStatus entry) { return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." + entry.alias; } private PrivateKeyEntry getEntry(String alias) { // if the alias is null, return immediately if (alias == null) { return null; } // try to get the entry from cache Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias); PrivateKeyEntry entry = (ref != null) ? ref.get() : null; if (entry != null) { return entry; } // parse the alias int firstDot = alias.indexOf('.'); int secondDot = alias.indexOf('.', firstDot + 1); if ((firstDot == -1) || (secondDot == firstDot)) { // invalid alias 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(alias)); if (newEntry instanceof PrivateKeyEntry == false) { // unexpected type of entry return null; } entry = (PrivateKeyEntry)newEntry; entryCacheMap.put(alias, new SoftReference(entry)); return entry; } catch (Exception e) { // ignore return null; } } // 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(ENGLISH); String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(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: * . scan through all the aliases in all builders in order * . as soon as we find a perfect match, return * (i.e. a match with a cert that has appropriate key usage * and is not expired). * . if we do not find a perfect match, keep looping and remember * the imperfect matches * . at the end, sort the imperfect matches. we prefer expired certs * 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) { if (keyTypeList == null || keyTypeList.size() == 0) { 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); if (results != null) { // the results will either be a single perfect match // or 1 or more imperfect matches // if it's a perfect match, return immediately EntryStatus status = results.get(0); if (status.checkResult == CheckResult.OK) { if (useDebug) { debug.println("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 (useDebug) { debug.println("KeyMgr: no matching key found"); } return null; } Collections.sort(allResults); if (useDebug) { debug.println("KeyMgr: no good matching key found, " + "returning best match out of:"); debug.println(allResults.toString()); } return makeAlias(allResults.get(0)); } /* * Return all aliases that (approximately) fit the parameters. * These are perfect matches plus imperfect matches (expired certificates * 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) { 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); if (results != null) { if (allResults == null) { allResults = new ArrayList<EntryStatus>(); } allResults.addAll(results); } } catch (Exception e) { // ignore } } if (allResults == null || allResults.size() == 0) { if (useDebug) { debug.println("KeyMgr: no matching alias found"); } return null; } Collections.sort(allResults); if (useDebug) { debug.println("KeyMgr: getting aliases: " + allResults); } return toAliases(allResults); } // turn candidate entries into unique aliases we can return to JSSE 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; } // 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; } public int compareTo(EntryStatus other) { int result = this.checkResult.compareTo(other.checkResult); return (result == 0) ? (this.keyIndex - other.keyIndex) : result; } 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 static enum CheckType { // enum constant for "no check" (currently not used) NONE(Collections.<String>emptySet()), // enum constant for "tls client" check // valid EKU for TLS client: any, tls_client CLIENT(new HashSet<String>(Arrays.asList(new String[] { "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), // enum constant for "tls server" check // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc SERVER(new HashSet<String>(Arrays.asList(new String[] { "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", "1.3.6.1.4.1.311.10.3.3" }))); // 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]; }