History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/ssl/X509KeyManagerImpl.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/ssl/X509KeyManagerImpl.java+741−0
@@ -0,0 +1,741 @@
1+/*
2+ * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.security.ssl;
27+
28+import java.lang.ref.*;
29+import java.util.*;
30+import static java.util.Locale.ENGLISH;
31+import java.util.concurrent.atomic.AtomicLong;
32+import java.net.Socket;
33+
34+import java.security.*;
35+import java.security.KeyStore.*;
36+import java.security.cert.*;
37+import java.security.cert.Certificate;
38+
39+import javax.net.ssl.*;
40+
41+import sun.security.provider.certpath.AlgorithmChecker;
42+
43+/**
44+ * The new X509 key manager implementation. The main differences to the
45+ * old SunX509 key manager are:
46+ * . it is based around the KeyStore.Builder API. This allows it to use
47+ * other forms of KeyStore protection or password input (e.g. a
48+ * CallbackHandler) or to have keys within one KeyStore protected by
49+ * different keys.
50+ * . it can use multiple KeyStores at the same time.
51+ * . it is explicitly designed to accomodate KeyStores that change over
52+ * the lifetime of the process.
53+ * . it makes an effort to choose the key that matches best, i.e. one that
54+ * is not expired and has the appropriate certificate extensions.
55+ *
56+ * Note that this code is not explicitly performance optimzied yet.
57+ *
58+ * @author Andreas Sterbenz
59+ */
60+final class X509KeyManagerImpl extends X509ExtendedKeyManager
61+ implements X509KeyManager {
62+
63+ private static final Debug debug = Debug.getInstance("ssl");
64+
65+ private final static boolean useDebug =
66+ (debug != null) && Debug.isOn("keymanager");
67+
68+ // for unit testing only, set via privileged reflection
69+ private static Date verificationDate;
70+
71+ // list of the builders
72+ private final List<Builder> builders;
73+
74+ // counter to generate unique ids for the aliases
75+ private final AtomicLong uidCounter;
76+
77+ // cached entries
78+ private final Map<String,Reference<PrivateKeyEntry>> entryCacheMap;
79+
80+ X509KeyManagerImpl(Builder builder) {
81+ this(Collections.singletonList(builder));
82+ }
83+
84+ X509KeyManagerImpl(List<Builder> builders) {
85+ this.builders = builders;
86+ uidCounter = new AtomicLong();
87+ entryCacheMap = Collections.synchronizedMap
88+ (new SizedMap<String,Reference<PrivateKeyEntry>>());
89+ }
90+
91+ // LinkedHashMap with a max size of 10
92+ // see LinkedHashMap JavaDocs
93+ private static class SizedMap<K,V> extends LinkedHashMap<K,V> {
94+ @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
95+ return size() > 10;
96+ }
97+ }
98+
99+ //
100+ // public methods
101+ //
102+
103+ public X509Certificate[] getCertificateChain(String alias) {
104+ PrivateKeyEntry entry = getEntry(alias);
105+ return entry == null ? null :
106+ (X509Certificate[])entry.getCertificateChain();
107+ }
108+
109+ public PrivateKey getPrivateKey(String alias) {
110+ PrivateKeyEntry entry = getEntry(alias);
111+ return entry == null ? null : entry.getPrivateKey();
112+ }
113+
114+ public String chooseClientAlias(String[] keyTypes, Principal[] issuers,
115+ Socket socket) {
116+ return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
117+ getAlgorithmConstraints(socket));
118+ }
119+
120+ public String chooseEngineClientAlias(String[] keyTypes,
121+ Principal[] issuers, SSLEngine engine) {
122+ return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT,
123+ getAlgorithmConstraints(engine));
124+ }
125+
126+ public String chooseServerAlias(String keyType,
127+ Principal[] issuers, Socket socket) {
128+ return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
129+ getAlgorithmConstraints(socket));
130+ }
131+
132+ public String chooseEngineServerAlias(String keyType,
133+ Principal[] issuers, SSLEngine engine) {
134+ return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER,
135+ getAlgorithmConstraints(engine));
136+ }
137+
138+ public String[] getClientAliases(String keyType, Principal[] issuers) {
139+ return getAliases(keyType, issuers, CheckType.CLIENT, null);
140+ }
141+
142+ public String[] getServerAliases(String keyType, Principal[] issuers) {
143+ return getAliases(keyType, issuers, CheckType.SERVER, null);
144+ }
145+
146+ //
147+ // implementation private methods
148+ //
149+
150+ // Gets algorithm constraints of the socket.
151+ private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
152+ if (socket != null && socket.isConnected() &&
153+ socket instanceof SSLSocket) {
154+
155+ SSLSocket sslSocket = (SSLSocket)socket;
156+ SSLSession session = sslSocket.getHandshakeSession();
157+
158+ if (session != null) {
159+ ProtocolVersion protocolVersion =
160+ ProtocolVersion.valueOf(session.getProtocol());
161+ if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
162+ String[] peerSupportedSignAlgs = null;
163+
164+ if (session instanceof ExtendedSSLSession) {
165+ ExtendedSSLSession extSession =
166+ (ExtendedSSLSession)session;
167+ peerSupportedSignAlgs =
168+ extSession.getPeerSupportedSignatureAlgorithms();
169+ }
170+
171+ return new SSLAlgorithmConstraints(
172+ sslSocket, peerSupportedSignAlgs, true);
173+ }
174+ }
175+
176+ return new SSLAlgorithmConstraints(sslSocket, true);
177+ }
178+
179+ return new SSLAlgorithmConstraints((SSLSocket)null, true);
180+ }
181+
182+ // Gets algorithm constraints of the engine.
183+ private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
184+ if (engine != null) {
185+ SSLSession session = engine.getHandshakeSession();
186+ if (session != null) {
187+ ProtocolVersion protocolVersion =
188+ ProtocolVersion.valueOf(session.getProtocol());
189+ if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
190+ String[] peerSupportedSignAlgs = null;
191+
192+ if (session instanceof ExtendedSSLSession) {
193+ ExtendedSSLSession extSession =
194+ (ExtendedSSLSession)session;
195+ peerSupportedSignAlgs =
196+ extSession.getPeerSupportedSignatureAlgorithms();
197+ }
198+
199+ return new SSLAlgorithmConstraints(
200+ engine, peerSupportedSignAlgs, true);
201+ }
202+ }
203+ }
204+
205+ return new SSLAlgorithmConstraints(engine, true);
206+ }
207+
208+ // we construct the alias we return to JSSE as seen in the code below
209+ // a unique id is included to allow us to reliably cache entries
210+ // between the calls to getCertificateChain() and getPrivateKey()
211+ // even if tokens are inserted or removed
212+ private String makeAlias(EntryStatus entry) {
213+ return uidCounter.incrementAndGet() + "." + entry.builderIndex + "."
214+ + entry.alias;
215+ }
216+
217+ private PrivateKeyEntry getEntry(String alias) {
218+ // if the alias is null, return immediately
219+ if (alias == null) {
220+ return null;
221+ }
222+
223+ // try to get the entry from cache
224+ Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias);
225+ PrivateKeyEntry entry = (ref != null) ? ref.get() : null;
226+ if (entry != null) {
227+ return entry;
228+ }
229+
230+ // parse the alias
231+ int firstDot = alias.indexOf('.');
232+ int secondDot = alias.indexOf('.', firstDot + 1);
233+ if ((firstDot == -1) || (secondDot == firstDot)) {
234+ // invalid alias
235+ return null;
236+ }
237+ try {
238+ int builderIndex = Integer.parseInt
239+ (alias.substring(firstDot + 1, secondDot));
240+ String keyStoreAlias = alias.substring(secondDot + 1);
241+ Builder builder = builders.get(builderIndex);
242+ KeyStore ks = builder.getKeyStore();
243+ Entry newEntry = ks.getEntry
244+ (keyStoreAlias, builder.getProtectionParameter(alias));
245+ if (newEntry instanceof PrivateKeyEntry == false) {
246+ // unexpected type of entry
247+ return null;
248+ }
249+ entry = (PrivateKeyEntry)newEntry;
250+ entryCacheMap.put(alias, new SoftReference(entry));
251+ return entry;
252+ } catch (Exception e) {
253+ // ignore
254+ return null;
255+ }
256+ }
257+
258+ // Class to help verify that the public key algorithm (and optionally
259+ // the signature algorithm) of a certificate matches what we need.
260+ private static class KeyType {
261+
262+ final String keyAlgorithm;
263+
264+ // In TLS 1.2, the signature algorithm has been obsoleted by the
265+ // supported_signature_algorithms, and the certificate type no longer
266+ // restricts the algorithm used to sign the certificate.
267+ // However, because we don't support certificate type checking other
268+ // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the
269+ // protocol version here.
270+ final String sigKeyAlgorithm;
271+
272+ KeyType(String algorithm) {
273+ int k = algorithm.indexOf("_");
274+ if (k == -1) {
275+ keyAlgorithm = algorithm;
276+ sigKeyAlgorithm = null;
277+ } else {
278+ keyAlgorithm = algorithm.substring(0, k);
279+ sigKeyAlgorithm = algorithm.substring(k + 1);
280+ }
281+ }
282+
283+ boolean matches(Certificate[] chain) {
284+ if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) {
285+ return false;
286+ }
287+ if (sigKeyAlgorithm == null) {
288+ return true;
289+ }
290+ if (chain.length > 1) {
291+ // if possible, check the public key in the issuer cert
292+ return sigKeyAlgorithm.equals(
293+ chain[1].getPublicKey().getAlgorithm());
294+ } else {
295+ // Check the signature algorithm of the certificate itself.
296+ // Look for the "withRSA" in "SHA1withRSA", etc.
297+ X509Certificate issuer = (X509Certificate)chain[0];
298+ String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH);
299+ String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH);
300+ return sigAlgName.contains(pattern);
301+ }
302+ }
303+ }
304+
305+ private static List<KeyType> getKeyTypes(String ... keyTypes) {
306+ if ((keyTypes == null) ||
307+ (keyTypes.length == 0) || (keyTypes[0] == null)) {
308+ return null;
309+ }
310+ List<KeyType> list = new ArrayList<>(keyTypes.length);
311+ for (String keyType : keyTypes) {
312+ list.add(new KeyType(keyType));
313+ }
314+ return list;
315+ }
316+
317+ /*
318+ * Return the best alias that fits the given parameters.
319+ * The algorithm we use is:
320+ * . scan through all the aliases in all builders in order
321+ * . as soon as we find a perfect match, return
322+ * (i.e. a match with a cert that has appropriate key usage
323+ * and is not expired).
324+ * . if we do not find a perfect match, keep looping and remember
325+ * the imperfect matches
326+ * . at the end, sort the imperfect matches. we prefer expired certs
327+ * with appropriate key usage to certs with the wrong key usage.
328+ * return the first one of them.
329+ */
330+ private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers,
331+ CheckType checkType, AlgorithmConstraints constraints) {
332+ if (keyTypeList == null || keyTypeList.size() == 0) {
333+ return null;
334+ }
335+
336+ Set<Principal> issuerSet = getIssuerSet(issuers);
337+ List<EntryStatus> allResults = null;
338+ for (int i = 0, n = builders.size(); i < n; i++) {
339+ try {
340+ List<EntryStatus> results = getAliases(i, keyTypeList,
341+ issuerSet, false, checkType, constraints);
342+ if (results != null) {
343+ // the results will either be a single perfect match
344+ // or 1 or more imperfect matches
345+ // if it's a perfect match, return immediately
346+ EntryStatus status = results.get(0);
347+ if (status.checkResult == CheckResult.OK) {
348+ if (useDebug) {
349+ debug.println("KeyMgr: choosing key: " + status);
350+ }
351+ return makeAlias(status);
352+ }
353+ if (allResults == null) {
354+ allResults = new ArrayList<EntryStatus>();
355+ }
356+ allResults.addAll(results);
357+ }
358+ } catch (Exception e) {
359+ // ignore
360+ }
361+ }
362+ if (allResults == null) {
363+ if (useDebug) {
364+ debug.println("KeyMgr: no matching key found");
365+ }
366+ return null;
367+ }
368+ Collections.sort(allResults);
369+ if (useDebug) {
370+ debug.println("KeyMgr: no good matching key found, "
371+ + "returning best match out of:");
372+ debug.println(allResults.toString());
373+ }
374+ return makeAlias(allResults.get(0));
375+ }
376+
377+ /*
378+ * Return all aliases that (approximately) fit the parameters.
379+ * These are perfect matches plus imperfect matches (expired certificates
380+ * and certificates with the wrong extensions).
381+ * The perfect matches will be first in the array.
382+ */
383+ public String[] getAliases(String keyType, Principal[] issuers,
384+ CheckType checkType, AlgorithmConstraints constraints) {
385+ if (keyType == null) {
386+ return null;
387+ }
388+
389+ Set<Principal> issuerSet = getIssuerSet(issuers);
390+ List<KeyType> keyTypeList = getKeyTypes(keyType);
391+ List<EntryStatus> allResults = null;
392+ for (int i = 0, n = builders.size(); i < n; i++) {
393+ try {
394+ List<EntryStatus> results = getAliases(i, keyTypeList,
395+ issuerSet, true, checkType, constraints);
396+ if (results != null) {
397+ if (allResults == null) {
398+ allResults = new ArrayList<EntryStatus>();
399+ }
400+ allResults.addAll(results);
401+ }
402+ } catch (Exception e) {
403+ // ignore
404+ }
405+ }
406+ if (allResults == null || allResults.size() == 0) {
407+ if (useDebug) {
408+ debug.println("KeyMgr: no matching alias found");
409+ }
410+ return null;
411+ }
412+ Collections.sort(allResults);
413+ if (useDebug) {
414+ debug.println("KeyMgr: getting aliases: " + allResults);
415+ }
416+ return toAliases(allResults);
417+ }
418+
419+ // turn candidate entries into unique aliases we can return to JSSE
420+ private String[] toAliases(List<EntryStatus> results) {
421+ String[] s = new String[results.size()];
422+ int i = 0;
423+ for (EntryStatus result : results) {
424+ s[i++] = makeAlias(result);
425+ }
426+ return s;
427+ }
428+
429+ // make a Set out of the array
430+ private Set<Principal> getIssuerSet(Principal[] issuers) {
431+ if ((issuers != null) && (issuers.length != 0)) {
432+ return new HashSet<>(Arrays.asList(issuers));
433+ } else {
434+ return null;
435+ }
436+ }
437+
438+ // a candidate match
439+ // identifies the entry by builder and alias
440+ // and includes the result of the certificate check
441+ private static class EntryStatus implements Comparable<EntryStatus> {
442+
443+ final int builderIndex;
444+ final int keyIndex;
445+ final String alias;
446+ final CheckResult checkResult;
447+
448+ EntryStatus(int builderIndex, int keyIndex, String alias,
449+ Certificate[] chain, CheckResult checkResult) {
450+ this.builderIndex = builderIndex;
451+ this.keyIndex = keyIndex;
452+ this.alias = alias;
453+ this.checkResult = checkResult;
454+ }
455+
456+ public int compareTo(EntryStatus other) {
457+ int result = this.checkResult.compareTo(other.checkResult);
458+ return (result == 0) ? (this.keyIndex - other.keyIndex) : result;
459+ }
460+
461+ public String toString() {
462+ String s = alias + " (verified: " + checkResult + ")";
463+ if (builderIndex == 0) {
464+ return s;
465+ } else {
466+ return "Builder #" + builderIndex + ", alias: " + s;
467+ }
468+ }
469+ }
470+
471+ // enum for the type of certificate check we want to perform
472+ // (client or server)
473+ // also includes the check code itself
474+ private static enum CheckType {
475+
476+ // enum constant for "no check" (currently not used)
477+ NONE(Collections.<String>emptySet()),
478+
479+ // enum constant for "tls client" check
480+ // valid EKU for TLS client: any, tls_client
481+ CLIENT(new HashSet<String>(Arrays.asList(new String[] {
482+ "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))),
483+
484+ // enum constant for "tls server" check
485+ // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc
486+ SERVER(new HashSet<String>(Arrays.asList(new String[] {
487+ "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1",
488+ "1.3.6.1.4.1.311.10.3.3" })));
489+
490+ // set of valid EKU values for this type
491+ final Set<String> validEku;
492+
493+ CheckType(Set<String> validEku) {
494+ this.validEku = validEku;
495+ }
496+
497+ private static boolean getBit(boolean[] keyUsage, int bit) {
498+ return (bit < keyUsage.length) && keyUsage[bit];
499+ }
500+
501+ // check if this certificate is appropriate for this type of use
502+ // first check extensions, if they match, check expiration
503+ // note: we may want to move this code into the sun.security.validator
504+ // package
505+ CheckResult check(X509Certificate cert, Date date) {
506+ if (this == NONE) {
507+ return CheckResult.OK;
508+ }
509+
510+ // check extensions
511+ try {
512+ // check extended key usage
513+ List<String> certEku = cert.getExtendedKeyUsage();
514+ if ((certEku != null) &&
515+ Collections.disjoint(validEku, certEku)) {
516+ // if extension present and it does not contain any of
517+ // the valid EKU OIDs, return extension_mismatch
518+ return CheckResult.EXTENSION_MISMATCH;
519+ }
520+
521+ // check key usage
522+ boolean[] ku = cert.getKeyUsage();
523+ if (ku != null) {
524+ String algorithm = cert.getPublicKey().getAlgorithm();
525+ boolean kuSignature = getBit(ku, 0);
526+ if (algorithm.equals("RSA")) {
527+ // require either signature bit
528+ // or if server also allow key encipherment bit
529+ if (kuSignature == false) {
530+ if ((this == CLIENT) || (getBit(ku, 2) == false)) {
531+ return CheckResult.EXTENSION_MISMATCH;
532+ }
533+ }
534+ } else if (algorithm.equals("DSA")) {
535+ // require signature bit
536+ if (kuSignature == false) {
537+ return CheckResult.EXTENSION_MISMATCH;
538+ }
539+ } else if (algorithm.equals("DH")) {
540+ // require keyagreement bit
541+ if (getBit(ku, 4) == false) {
542+ return CheckResult.EXTENSION_MISMATCH;
543+ }
544+ } else if (algorithm.equals("EC")) {
545+ // require signature bit
546+ if (kuSignature == false) {
547+ return CheckResult.EXTENSION_MISMATCH;
548+ }
549+ // For servers, also require key agreement.
550+ // This is not totally accurate as the keyAgreement bit
551+ // is only necessary for static ECDH key exchange and
552+ // not ephemeral ECDH. We leave it in for now until
553+ // there are signs that this check causes problems
554+ // for real world EC certificates.
555+ if ((this == SERVER) && (getBit(ku, 4) == false)) {
556+ return CheckResult.EXTENSION_MISMATCH;
557+ }
558+ }
559+ }
560+ } catch (CertificateException e) {
561+ // extensions unparseable, return failure
562+ return CheckResult.EXTENSION_MISMATCH;
563+ }
564+
565+ try {
566+ cert.checkValidity(date);
567+ return CheckResult.OK;
568+ } catch (CertificateException e) {
569+ return CheckResult.EXPIRED;
570+ }
571+ }
572+ }
573+
574+ // enum for the result of the extension check
575+ // NOTE: the order of the constants is important as they are used
576+ // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
577+ private static enum CheckResult {
578+ OK, // ok or not checked
579+ EXPIRED, // extensions valid but cert expired
580+ EXTENSION_MISMATCH, // extensions invalid (expiration not checked)
581+ }
582+
583+ /*
584+ * Return a List of all candidate matches in the specified builder
585+ * that fit the parameters.
586+ * We exclude entries in the KeyStore if they are not:
587+ * . private key entries
588+ * . the certificates are not X509 certificates
589+ * . the algorithm of the key in the EE cert doesn't match one of keyTypes
590+ * . none of the certs is issued by a Principal in issuerSet
591+ * Using those entries would not be possible or they would almost
592+ * certainly be rejected by the peer.
593+ *
594+ * In addition to those checks, we also check the extensions in the EE
595+ * cert and its expiration. Even if there is a mismatch, we include
596+ * such certificates because they technically work and might be accepted
597+ * by the peer. This leads to more graceful failure and better error
598+ * messages if the cert expires from one day to the next.
599+ *
600+ * The return values are:
601+ * . null, if there are no matching entries at all
602+ * . if 'findAll' is 'false' and there is a perfect match, a List
603+ * with a single element (early return)
604+ * . if 'findAll' is 'false' and there is NO perfect match, a List
605+ * with all the imperfect matches (expired, wrong extensions)
606+ * . if 'findAll' is 'true', a List with all perfect and imperfect
607+ * matches
608+ */
609+ private List<EntryStatus> getAliases(int builderIndex,
610+ List<KeyType> keyTypes, Set<Principal> issuerSet,
611+ boolean findAll, CheckType checkType,
612+ AlgorithmConstraints constraints) throws Exception {
613+ Builder builder = builders.get(builderIndex);
614+ KeyStore ks = builder.getKeyStore();
615+ List<EntryStatus> results = null;
616+ Date date = verificationDate;
617+ boolean preferred = false;
618+ for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
619+ String alias = e.nextElement();
620+ // check if it is a key entry (private key or secret key)
621+ if (ks.isKeyEntry(alias) == false) {
622+ continue;
623+ }
624+
625+ Certificate[] chain = ks.getCertificateChain(alias);
626+ if ((chain == null) || (chain.length == 0)) {
627+ // must be secret key entry, ignore
628+ continue;
629+ }
630+
631+ boolean incompatible = false;
632+ for (Certificate cert : chain) {
633+ if (cert instanceof X509Certificate == false) {
634+ // not an X509Certificate, ignore this alias
635+ incompatible = true;
636+ break;
637+ }
638+ }
639+ if (incompatible) {
640+ continue;
641+ }
642+
643+ // check keytype
644+ int keyIndex = -1;
645+ int j = 0;
646+ for (KeyType keyType : keyTypes) {
647+ if (keyType.matches(chain)) {
648+ keyIndex = j;
649+ break;
650+ }
651+ j++;
652+ }
653+ if (keyIndex == -1) {
654+ if (useDebug) {
655+ debug.println("Ignoring alias " + alias
656+ + ": key algorithm does not match");
657+ }
658+ continue;
659+ }
660+ // check issuers
661+ if (issuerSet != null) {
662+ boolean found = false;
663+ for (Certificate cert : chain) {
664+ X509Certificate xcert = (X509Certificate)cert;
665+ if (issuerSet.contains(xcert.getIssuerX500Principal())) {
666+ found = true;
667+ break;
668+ }
669+ }
670+ if (found == false) {
671+ if (useDebug) {
672+ debug.println("Ignoring alias " + alias
673+ + ": issuers do not match");
674+ }
675+ continue;
676+ }
677+ }
678+
679+ // check the algorithm constraints
680+ if (constraints != null &&
681+ !conformsToAlgorithmConstraints(constraints, chain)) {
682+
683+ if (useDebug) {
684+ debug.println("Ignoring alias " + alias +
685+ ": certificate list does not conform to " +
686+ "algorithm constraints");
687+ }
688+ continue;
689+ }
690+
691+ if (date == null) {
692+ date = new Date();
693+ }
694+ CheckResult checkResult =
695+ checkType.check((X509Certificate)chain[0], date);
696+ EntryStatus status =
697+ new EntryStatus(builderIndex, keyIndex,
698+ alias, chain, checkResult);
699+ if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
700+ preferred = true;
701+ }
702+ if (preferred && (findAll == false)) {
703+ // if we have a good match and do not need all matches,
704+ // return immediately
705+ return Collections.singletonList(status);
706+ } else {
707+ if (results == null) {
708+ results = new ArrayList<EntryStatus>();
709+ }
710+ results.add(status);
711+ }
712+ }
713+ return results;
714+ }
715+
716+ private static boolean conformsToAlgorithmConstraints(
717+ AlgorithmConstraints constraints, Certificate[] chain) {
718+
719+ AlgorithmChecker checker = new AlgorithmChecker(constraints);
720+ try {
721+ checker.init(false);
722+ } catch (CertPathValidatorException cpve) {
723+ // unlikely to happen
724+ return false;
725+ }
726+
727+ // It is a forward checker, so we need to check from trust to target.
728+ for (int i = chain.length - 1; i >= 0; i--) {
729+ Certificate cert = chain[i];
730+ try {
731+ // We don't care about the unresolved critical extensions.
732+ checker.check(cert, Collections.<String>emptySet());
733+ } catch (CertPathValidatorException cpve) {
734+ return false;
735+ }
736+ }
737+
738+ return true;
739+ }
740+
741+}