1 /*
2 * Copyright (c) 2004, 2017, 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 import sun.security.validator.Validator;
43
44 /**
45 * The new X509 key manager implementation. The main differences to the
46 * old SunX509 key manager are:
47 * . it is based around the KeyStore.Builder API. This allows it to use
48 * other forms of KeyStore protection or password input (e.g. a
49 * CallbackHandler) or to have keys within one KeyStore protected by
50 * different keys.
51 * . it can use multiple KeyStores at the same time.
52 * . it is explicitly designed to accommodate KeyStores that change over
53 * the lifetime of the process.
54 * . it makes an effort to choose the key that matches best, i.e. one that
55 * is not expired and has the appropriate certificate extensions.
56 *
57 * Note that this code is not explicitly performance optimzied yet.
58 *
59 * @author Andreas Sterbenz
60 */
61 final class X509KeyManagerImpl extends X509ExtendedKeyManager
62 implements X509KeyManager {
63
64 private static final Debug debug = Debug.getInstance("ssl");
65
66 private static final boolean useDebug =
67 (debug != null) && Debug.isOn("keymanager");
68
69 // for unit testing only, set via privileged reflection
70 private static Date verificationDate;
71
72 // list of the builders
73 private final List<Builder> builders;
74
75 // counter to generate unique ids for the aliases
76 private final AtomicLong uidCounter;
77
78 // cached entries
79 private final Map<String,Reference<PrivateKeyEntry>> entryCacheMap;
80
81 X509KeyManagerImpl(Builder builder) {
82 this(Collections.singletonList(builder));
83 }
84
85 X509KeyManagerImpl(List<Builder> builders) {
86 this.builders = builders;
87 uidCounter = new AtomicLong();
88 entryCacheMap = Collections.synchronizedMap
172 }
173
174 @Override
175 public String[] getServerAliases(String keyType, Principal[] issuers) {
176 return getAliases(keyType, issuers, CheckType.SERVER, null);
177 }
178
179 //
180 // implementation private methods
181 //
182
183 // Gets algorithm constraints of the socket.
184 private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
185 if (socket != null && socket.isConnected() &&
186 socket instanceof SSLSocket) {
187
188 SSLSocket sslSocket = (SSLSocket)socket;
189 SSLSession session = sslSocket.getHandshakeSession();
190
191 if (session != null) {
192 ProtocolVersion protocolVersion =
193 ProtocolVersion.valueOf(session.getProtocol());
194 if (protocolVersion.useTLS12PlusSpec()) {
195 String[] peerSupportedSignAlgs = null;
196
197 if (session instanceof ExtendedSSLSession) {
198 ExtendedSSLSession extSession =
199 (ExtendedSSLSession)session;
200 peerSupportedSignAlgs =
201 extSession.getPeerSupportedSignatureAlgorithms();
202 }
203
204 return new SSLAlgorithmConstraints(
205 sslSocket, peerSupportedSignAlgs, true);
206 }
207 }
208
209 return new SSLAlgorithmConstraints(sslSocket, true);
210 }
211
212 return new SSLAlgorithmConstraints((SSLSocket)null, true);
213 }
214
215 // Gets algorithm constraints of the engine.
216 private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
217 if (engine != null) {
218 SSLSession session = engine.getHandshakeSession();
219 if (session != null) {
220 ProtocolVersion protocolVersion =
221 ProtocolVersion.valueOf(session.getProtocol());
222 if (protocolVersion.useTLS12PlusSpec()) {
223 String[] peerSupportedSignAlgs = null;
224
225 if (session instanceof ExtendedSSLSession) {
226 ExtendedSSLSession extSession =
227 (ExtendedSSLSession)session;
228 peerSupportedSignAlgs =
229 extSession.getPeerSupportedSignatureAlgorithms();
230 }
231
232 return new SSLAlgorithmConstraints(
233 engine, peerSupportedSignAlgs, true);
234 }
235 }
236 }
237
238 return new SSLAlgorithmConstraints(engine, true);
239 }
240
241 // we construct the alias we return to JSSE as seen in the code below
242 // a unique id is included to allow us to reliably cache entries
371 CheckType checkType, AlgorithmConstraints constraints,
372 List<SNIServerName> requestedServerNames, String idAlgorithm) {
373
374 if (keyTypeList == null || keyTypeList.isEmpty()) {
375 return null;
376 }
377
378 Set<Principal> issuerSet = getIssuerSet(issuers);
379 List<EntryStatus> allResults = null;
380 for (int i = 0, n = builders.size(); i < n; i++) {
381 try {
382 List<EntryStatus> results = getAliases(i, keyTypeList,
383 issuerSet, false, checkType, constraints,
384 requestedServerNames, idAlgorithm);
385 if (results != null) {
386 // the results will either be a single perfect match
387 // or 1 or more imperfect matches
388 // if it's a perfect match, return immediately
389 EntryStatus status = results.get(0);
390 if (status.checkResult == CheckResult.OK) {
391 if (useDebug) {
392 debug.println("KeyMgr: choosing key: " + status);
393 }
394 return makeAlias(status);
395 }
396 if (allResults == null) {
397 allResults = new ArrayList<EntryStatus>();
398 }
399 allResults.addAll(results);
400 }
401 } catch (Exception e) {
402 // ignore
403 }
404 }
405 if (allResults == null) {
406 if (useDebug) {
407 debug.println("KeyMgr: no matching key found");
408 }
409 return null;
410 }
411 Collections.sort(allResults);
412 if (useDebug) {
413 debug.println("KeyMgr: no good matching key found, "
414 + "returning best match out of:");
415 debug.println(allResults.toString());
416 }
417 return makeAlias(allResults.get(0));
418 }
419
420 /*
421 * Return all aliases that (approximately) fit the parameters.
422 * These are perfect matches plus imperfect matches (expired certificates
423 * and certificates with the wrong extensions).
424 * The perfect matches will be first in the array.
425 */
426 public String[] getAliases(String keyType, Principal[] issuers,
427 CheckType checkType, AlgorithmConstraints constraints) {
428 if (keyType == null) {
429 return null;
430 }
431
432 Set<Principal> issuerSet = getIssuerSet(issuers);
433 List<KeyType> keyTypeList = getKeyTypes(keyType);
434 List<EntryStatus> allResults = null;
435 for (int i = 0, n = builders.size(); i < n; i++) {
436 try {
437 List<EntryStatus> results = getAliases(i, keyTypeList,
438 issuerSet, true, checkType, constraints,
439 null, null);
440 if (results != null) {
441 if (allResults == null) {
442 allResults = new ArrayList<EntryStatus>();
443 }
444 allResults.addAll(results);
445 }
446 } catch (Exception e) {
447 // ignore
448 }
449 }
450 if (allResults == null || allResults.isEmpty()) {
451 if (useDebug) {
452 debug.println("KeyMgr: no matching alias found");
453 }
454 return null;
455 }
456 Collections.sort(allResults);
457 if (useDebug) {
458 debug.println("KeyMgr: getting aliases: " + allResults);
459 }
460 return toAliases(allResults);
461 }
462
463 // turn candidate entries into unique aliases we can return to JSSE
464 private String[] toAliases(List<EntryStatus> results) {
465 String[] s = new String[results.size()];
466 int i = 0;
467 for (EntryStatus result : results) {
468 s[i++] = makeAlias(result);
469 }
470 return s;
471 }
472
473 // make a Set out of the array
474 private Set<Principal> getIssuerSet(Principal[] issuers) {
475 if ((issuers != null) && (issuers.length != 0)) {
476 return new HashSet<>(Arrays.asList(issuers));
477 } else {
478 return null;
559 try {
560 // check extended key usage
561 List<String> certEku = cert.getExtendedKeyUsage();
562 if ((certEku != null) &&
563 Collections.disjoint(validEku, certEku)) {
564 // if extension present and it does not contain any of
565 // the valid EKU OIDs, return extension_mismatch
566 return CheckResult.EXTENSION_MISMATCH;
567 }
568
569 // check key usage
570 boolean[] ku = cert.getKeyUsage();
571 if (ku != null) {
572 String algorithm = cert.getPublicKey().getAlgorithm();
573 boolean kuSignature = getBit(ku, 0);
574 switch (algorithm) {
575 case "RSA":
576 // require either signature bit
577 // or if server also allow key encipherment bit
578 if (kuSignature == false) {
579 if ((this == CLIENT) || (getBit(ku, 2) == false)) {
580 return CheckResult.EXTENSION_MISMATCH;
581 }
582 }
583 break;
584 case "DSA":
585 // require signature bit
586 if (kuSignature == false) {
587 return CheckResult.EXTENSION_MISMATCH;
588 }
589 break;
590 case "DH":
591 // require keyagreement bit
592 if (getBit(ku, 4) == false) {
593 return CheckResult.EXTENSION_MISMATCH;
594 }
595 break;
596 case "EC":
597 // require signature bit
598 if (kuSignature == false) {
599 return CheckResult.EXTENSION_MISMATCH;
614 // extensions unparseable, return failure
615 return CheckResult.EXTENSION_MISMATCH;
616 }
617
618 try {
619 cert.checkValidity(date);
620 } catch (CertificateException e) {
621 return CheckResult.EXPIRED;
622 }
623
624 if (serverNames != null && !serverNames.isEmpty()) {
625 for (SNIServerName serverName : serverNames) {
626 if (serverName.getType() ==
627 StandardConstants.SNI_HOST_NAME) {
628 if (!(serverName instanceof SNIHostName)) {
629 try {
630 serverName =
631 new SNIHostName(serverName.getEncoded());
632 } catch (IllegalArgumentException iae) {
633 // unlikely to happen, just in case ...
634 if (useDebug) {
635 debug.println(
636 "Illegal server name: " + serverName);
637 }
638
639 return CheckResult.INSENSITIVE;
640 }
641 }
642 String hostname =
643 ((SNIHostName)serverName).getAsciiName();
644
645 try {
646 X509TrustManagerImpl.checkIdentity(hostname,
647 cert, idAlgorithm);
648 } catch (CertificateException e) {
649 if (useDebug) {
650 debug.println(
651 "Certificate identity does not match " +
652 "Server Name Inidication (SNI): " +
653 hostname);
654 }
655 return CheckResult.INSENSITIVE;
656 }
657
658 break;
659 }
660 }
661 }
662
663 return CheckResult.OK;
664 }
665
666 public String getValidator() {
667 if (this == CLIENT) {
668 return Validator.VAR_TLS_CLIENT;
669 } else if (this == SERVER) {
670 return Validator.VAR_TLS_SERVER;
707 * . if 'findAll' is 'false' and there is NO perfect match, a List
708 * with all the imperfect matches (expired, wrong extensions)
709 * . if 'findAll' is 'true', a List with all perfect and imperfect
710 * matches
711 */
712 private List<EntryStatus> getAliases(int builderIndex,
713 List<KeyType> keyTypes, Set<Principal> issuerSet,
714 boolean findAll, CheckType checkType,
715 AlgorithmConstraints constraints,
716 List<SNIServerName> requestedServerNames,
717 String idAlgorithm) throws Exception {
718
719 Builder builder = builders.get(builderIndex);
720 KeyStore ks = builder.getKeyStore();
721 List<EntryStatus> results = null;
722 Date date = verificationDate;
723 boolean preferred = false;
724 for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
725 String alias = e.nextElement();
726 // check if it is a key entry (private key or secret key)
727 if (ks.isKeyEntry(alias) == false) {
728 continue;
729 }
730
731 Certificate[] chain = ks.getCertificateChain(alias);
732 if ((chain == null) || (chain.length == 0)) {
733 // must be secret key entry, ignore
734 continue;
735 }
736
737 boolean incompatible = false;
738 for (Certificate cert : chain) {
739 if (cert instanceof X509Certificate == false) {
740 // not an X509Certificate, ignore this alias
741 incompatible = true;
742 break;
743 }
744 }
745 if (incompatible) {
746 continue;
747 }
748
749 // check keytype
750 int keyIndex = -1;
751 int j = 0;
752 for (KeyType keyType : keyTypes) {
753 if (keyType.matches(chain)) {
754 keyIndex = j;
755 break;
756 }
757 j++;
758 }
759 if (keyIndex == -1) {
760 if (useDebug) {
761 debug.println("Ignoring alias " + alias
762 + ": key algorithm does not match");
763 }
764 continue;
765 }
766 // check issuers
767 if (issuerSet != null) {
768 boolean found = false;
769 for (Certificate cert : chain) {
770 X509Certificate xcert = (X509Certificate)cert;
771 if (issuerSet.contains(xcert.getIssuerX500Principal())) {
772 found = true;
773 break;
774 }
775 }
776 if (found == false) {
777 if (useDebug) {
778 debug.println("Ignoring alias " + alias
779 + ": issuers do not match");
780 }
781 continue;
782 }
783 }
784
785 // check the algorithm constraints
786 if (constraints != null &&
787 !conformsToAlgorithmConstraints(constraints, chain,
788 checkType.getValidator())) {
789
790 if (useDebug) {
791 debug.println("Ignoring alias " + alias +
792 ": certificate list does not conform to " +
793 "algorithm constraints");
794 }
795 continue;
796 }
797
798 if (date == null) {
799 date = new Date();
800 }
801 CheckResult checkResult =
802 checkType.check((X509Certificate)chain[0], date,
803 requestedServerNames, idAlgorithm);
804 EntryStatus status =
805 new EntryStatus(builderIndex, keyIndex,
806 alias, chain, checkResult);
807 if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
808 preferred = true;
809 }
810 if (preferred && (findAll == false)) {
811 // if we have a good match and do not need all matches,
812 // return immediately
813 return Collections.singletonList(status);
814 } else {
815 if (results == null) {
816 results = new ArrayList<EntryStatus>();
817 }
818 results.add(status);
819 }
820 }
821 return results;
822 }
823
824 private static boolean conformsToAlgorithmConstraints(
825 AlgorithmConstraints constraints, Certificate[] chain,
826 String variant) {
827
828 AlgorithmChecker checker = new AlgorithmChecker(constraints, null, variant);
829 try {
830 checker.init(false);
831 } catch (CertPathValidatorException cpve) {
832 // unlikely to happen
833 if (useDebug) {
834 debug.println(
835 "Cannot initialize algorithm constraints checker: " + cpve);
836 }
837
838 return false;
839 }
840
841 // It is a forward checker, so we need to check from trust to target.
842 for (int i = chain.length - 1; i >= 0; i--) {
843 Certificate cert = chain[i];
844 try {
845 // We don't care about the unresolved critical extensions.
846 checker.check(cert, Collections.<String>emptySet());
847 } catch (CertPathValidatorException cpve) {
848 if (useDebug) {
849 debug.println("Certificate (" + cert +
850 ") does not conform to algorithm constraints: " + cpve);
851 }
852
853 return false;
854 }
855 }
856
857 return true;
858 }
859
860 }
|
1 /*
2 * Copyright (c) 2004, 2018, 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.net.Socket;
30 import java.security.AlgorithmConstraints;
31 import java.security.KeyStore;
32 import java.security.KeyStore.Builder;
33 import java.security.KeyStore.Entry;
34 import java.security.KeyStore.PrivateKeyEntry;
35 import java.security.Principal;
36 import java.security.PrivateKey;
37 import java.security.cert.CertPathValidatorException;
38 import java.security.cert.Certificate;
39 import java.security.cert.CertificateException;
40 import java.security.cert.X509Certificate;
41 import java.util.*;
42 import static java.util.Locale.ENGLISH;
43 import java.util.concurrent.atomic.AtomicLong;
44 import javax.net.ssl.*;
45 import sun.security.provider.certpath.AlgorithmChecker;
46 import sun.security.validator.Validator;
47
48 /**
49 * The new X509 key manager implementation. The main differences to the
50 * old SunX509 key manager are:
51 * . it is based around the KeyStore.Builder API. This allows it to use
52 * other forms of KeyStore protection or password input (e.g. a
53 * CallbackHandler) or to have keys within one KeyStore protected by
54 * different keys.
55 * . it can use multiple KeyStores at the same time.
56 * . it is explicitly designed to accommodate KeyStores that change over
57 * the lifetime of the process.
58 * . it makes an effort to choose the key that matches best, i.e. one that
59 * is not expired and has the appropriate certificate extensions.
60 *
61 * Note that this code is not explicitly performance optimzied yet.
62 *
63 * @author Andreas Sterbenz
64 */
65 final class X509KeyManagerImpl extends X509ExtendedKeyManager
66 implements X509KeyManager {
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
171 }
172
173 @Override
174 public String[] getServerAliases(String keyType, Principal[] issuers) {
175 return getAliases(keyType, issuers, CheckType.SERVER, null);
176 }
177
178 //
179 // implementation private methods
180 //
181
182 // Gets algorithm constraints of the socket.
183 private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
184 if (socket != null && socket.isConnected() &&
185 socket instanceof SSLSocket) {
186
187 SSLSocket sslSocket = (SSLSocket)socket;
188 SSLSession session = sslSocket.getHandshakeSession();
189
190 if (session != null) {
191 if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
192 String[] peerSupportedSignAlgs = null;
193
194 if (session instanceof ExtendedSSLSession) {
195 ExtendedSSLSession extSession =
196 (ExtendedSSLSession)session;
197 peerSupportedSignAlgs =
198 extSession.getPeerSupportedSignatureAlgorithms();
199 }
200
201 return new SSLAlgorithmConstraints(
202 sslSocket, peerSupportedSignAlgs, true);
203 }
204 }
205
206 return new SSLAlgorithmConstraints(sslSocket, true);
207 }
208
209 return new SSLAlgorithmConstraints((SSLSocket)null, true);
210 }
211
212 // Gets algorithm constraints of the engine.
213 private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
214 if (engine != null) {
215 SSLSession session = engine.getHandshakeSession();
216 if (session != null) {
217 if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {
218 String[] peerSupportedSignAlgs = null;
219
220 if (session instanceof ExtendedSSLSession) {
221 ExtendedSSLSession extSession =
222 (ExtendedSSLSession)session;
223 peerSupportedSignAlgs =
224 extSession.getPeerSupportedSignatureAlgorithms();
225 }
226
227 return new SSLAlgorithmConstraints(
228 engine, peerSupportedSignAlgs, true);
229 }
230 }
231 }
232
233 return new SSLAlgorithmConstraints(engine, true);
234 }
235
236 // we construct the alias we return to JSSE as seen in the code below
237 // a unique id is included to allow us to reliably cache entries
366 CheckType checkType, AlgorithmConstraints constraints,
367 List<SNIServerName> requestedServerNames, String idAlgorithm) {
368
369 if (keyTypeList == null || keyTypeList.isEmpty()) {
370 return null;
371 }
372
373 Set<Principal> issuerSet = getIssuerSet(issuers);
374 List<EntryStatus> allResults = null;
375 for (int i = 0, n = builders.size(); i < n; i++) {
376 try {
377 List<EntryStatus> results = getAliases(i, keyTypeList,
378 issuerSet, false, checkType, constraints,
379 requestedServerNames, idAlgorithm);
380 if (results != null) {
381 // the results will either be a single perfect match
382 // or 1 or more imperfect matches
383 // if it's a perfect match, return immediately
384 EntryStatus status = results.get(0);
385 if (status.checkResult == CheckResult.OK) {
386 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
387 SSLLogger.fine("KeyMgr: choosing key: " + status);
388 }
389 return makeAlias(status);
390 }
391 if (allResults == null) {
392 allResults = new ArrayList<EntryStatus>();
393 }
394 allResults.addAll(results);
395 }
396 } catch (Exception e) {
397 // ignore
398 }
399 }
400 if (allResults == null) {
401 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
402 SSLLogger.fine("KeyMgr: no matching key found");
403 }
404 return null;
405 }
406 Collections.sort(allResults);
407 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
408 SSLLogger.fine(
409 "KeyMgr: no good matching key found, "
410 + "returning best match out of", allResults);
411 }
412 return makeAlias(allResults.get(0));
413 }
414
415 /*
416 * Return all aliases that (approximately) fit the parameters.
417 * These are perfect matches plus imperfect matches (expired certificates
418 * and certificates with the wrong extensions).
419 * The perfect matches will be first in the array.
420 */
421 public String[] getAliases(String keyType, Principal[] issuers,
422 CheckType checkType, AlgorithmConstraints constraints) {
423 if (keyType == null) {
424 return null;
425 }
426
427 Set<Principal> issuerSet = getIssuerSet(issuers);
428 List<KeyType> keyTypeList = getKeyTypes(keyType);
429 List<EntryStatus> allResults = null;
430 for (int i = 0, n = builders.size(); i < n; i++) {
431 try {
432 List<EntryStatus> results = getAliases(i, keyTypeList,
433 issuerSet, true, checkType, constraints,
434 null, null);
435 if (results != null) {
436 if (allResults == null) {
437 allResults = new ArrayList<EntryStatus>();
438 }
439 allResults.addAll(results);
440 }
441 } catch (Exception e) {
442 // ignore
443 }
444 }
445 if (allResults == null || allResults.isEmpty()) {
446 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
447 SSLLogger.fine("KeyMgr: no matching alias found");
448 }
449 return null;
450 }
451 Collections.sort(allResults);
452 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
453 SSLLogger.fine("KeyMgr: getting aliases", allResults);
454 }
455 return toAliases(allResults);
456 }
457
458 // turn candidate entries into unique aliases we can return to JSSE
459 private String[] toAliases(List<EntryStatus> results) {
460 String[] s = new String[results.size()];
461 int i = 0;
462 for (EntryStatus result : results) {
463 s[i++] = makeAlias(result);
464 }
465 return s;
466 }
467
468 // make a Set out of the array
469 private Set<Principal> getIssuerSet(Principal[] issuers) {
470 if ((issuers != null) && (issuers.length != 0)) {
471 return new HashSet<>(Arrays.asList(issuers));
472 } else {
473 return null;
554 try {
555 // check extended key usage
556 List<String> certEku = cert.getExtendedKeyUsage();
557 if ((certEku != null) &&
558 Collections.disjoint(validEku, certEku)) {
559 // if extension present and it does not contain any of
560 // the valid EKU OIDs, return extension_mismatch
561 return CheckResult.EXTENSION_MISMATCH;
562 }
563
564 // check key usage
565 boolean[] ku = cert.getKeyUsage();
566 if (ku != null) {
567 String algorithm = cert.getPublicKey().getAlgorithm();
568 boolean kuSignature = getBit(ku, 0);
569 switch (algorithm) {
570 case "RSA":
571 // require either signature bit
572 // or if server also allow key encipherment bit
573 if (kuSignature == false) {
574 if (this == CLIENT || getBit(ku, 2) == false) {
575 return CheckResult.EXTENSION_MISMATCH;
576 }
577 }
578 break;
579 case "DSA":
580 // require signature bit
581 if (kuSignature == false) {
582 return CheckResult.EXTENSION_MISMATCH;
583 }
584 break;
585 case "DH":
586 // require keyagreement bit
587 if (getBit(ku, 4) == false) {
588 return CheckResult.EXTENSION_MISMATCH;
589 }
590 break;
591 case "EC":
592 // require signature bit
593 if (kuSignature == false) {
594 return CheckResult.EXTENSION_MISMATCH;
609 // extensions unparseable, return failure
610 return CheckResult.EXTENSION_MISMATCH;
611 }
612
613 try {
614 cert.checkValidity(date);
615 } catch (CertificateException e) {
616 return CheckResult.EXPIRED;
617 }
618
619 if (serverNames != null && !serverNames.isEmpty()) {
620 for (SNIServerName serverName : serverNames) {
621 if (serverName.getType() ==
622 StandardConstants.SNI_HOST_NAME) {
623 if (!(serverName instanceof SNIHostName)) {
624 try {
625 serverName =
626 new SNIHostName(serverName.getEncoded());
627 } catch (IllegalArgumentException iae) {
628 // unlikely to happen, just in case ...
629 if (SSLLogger.isOn &&
630 SSLLogger.isOn("keymanager")) {
631 SSLLogger.fine(
632 "Illegal server name: " + serverName);
633 }
634
635 return CheckResult.INSENSITIVE;
636 }
637 }
638 String hostname =
639 ((SNIHostName)serverName).getAsciiName();
640
641 try {
642 X509TrustManagerImpl.checkIdentity(hostname,
643 cert, idAlgorithm);
644 } catch (CertificateException e) {
645 if (SSLLogger.isOn &&
646 SSLLogger.isOn("keymanager")) {
647 SSLLogger.fine(
648 "Certificate identity does not match " +
649 "Server Name Inidication (SNI): " +
650 hostname);
651 }
652 return CheckResult.INSENSITIVE;
653 }
654
655 break;
656 }
657 }
658 }
659
660 return CheckResult.OK;
661 }
662
663 public String getValidator() {
664 if (this == CLIENT) {
665 return Validator.VAR_TLS_CLIENT;
666 } else if (this == SERVER) {
667 return Validator.VAR_TLS_SERVER;
704 * . if 'findAll' is 'false' and there is NO perfect match, a List
705 * with all the imperfect matches (expired, wrong extensions)
706 * . if 'findAll' is 'true', a List with all perfect and imperfect
707 * matches
708 */
709 private List<EntryStatus> getAliases(int builderIndex,
710 List<KeyType> keyTypes, Set<Principal> issuerSet,
711 boolean findAll, CheckType checkType,
712 AlgorithmConstraints constraints,
713 List<SNIServerName> requestedServerNames,
714 String idAlgorithm) throws Exception {
715
716 Builder builder = builders.get(builderIndex);
717 KeyStore ks = builder.getKeyStore();
718 List<EntryStatus> results = null;
719 Date date = verificationDate;
720 boolean preferred = false;
721 for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
722 String alias = e.nextElement();
723 // check if it is a key entry (private key or secret key)
724 if (!ks.isKeyEntry(alias)) {
725 continue;
726 }
727
728 Certificate[] chain = ks.getCertificateChain(alias);
729 if ((chain == null) || (chain.length == 0)) {
730 // must be secret key entry, ignore
731 continue;
732 }
733
734 boolean incompatible = false;
735 for (Certificate cert : chain) {
736 if (cert instanceof X509Certificate == false) {
737 // not an X509Certificate, ignore this alias
738 incompatible = true;
739 break;
740 }
741 }
742 if (incompatible) {
743 continue;
744 }
745
746 // check keytype
747 int keyIndex = -1;
748 int j = 0;
749 for (KeyType keyType : keyTypes) {
750 if (keyType.matches(chain)) {
751 keyIndex = j;
752 break;
753 }
754 j++;
755 }
756 if (keyIndex == -1) {
757 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
758 SSLLogger.fine("Ignore alias " + alias
759 + ": key algorithm does not match");
760 }
761 continue;
762 }
763 // check issuers
764 if (issuerSet != null) {
765 boolean found = false;
766 for (Certificate cert : chain) {
767 X509Certificate xcert = (X509Certificate)cert;
768 if (issuerSet.contains(xcert.getIssuerX500Principal())) {
769 found = true;
770 break;
771 }
772 }
773 if (found == false) {
774 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
775 SSLLogger.fine(
776 "Ignore alias " + alias
777 + ": issuers do not match");
778 }
779 continue;
780 }
781 }
782
783 // check the algorithm constraints
784 if (constraints != null &&
785 !conformsToAlgorithmConstraints(constraints, chain,
786 checkType.getValidator())) {
787
788 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
789 SSLLogger.fine("Ignore alias " + alias +
790 ": certificate list does not conform to " +
791 "algorithm constraints");
792 }
793 continue;
794 }
795
796 if (date == null) {
797 date = new Date();
798 }
799 CheckResult checkResult =
800 checkType.check((X509Certificate)chain[0], date,
801 requestedServerNames, idAlgorithm);
802 EntryStatus status =
803 new EntryStatus(builderIndex, keyIndex,
804 alias, chain, checkResult);
805 if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
806 preferred = true;
807 }
808 if (preferred && (findAll == false)) {
809 // if we have a good match and do not need all matches,
810 // return immediately
811 return Collections.singletonList(status);
812 } else {
813 if (results == null) {
814 results = new ArrayList<EntryStatus>();
815 }
816 results.add(status);
817 }
818 }
819 return results;
820 }
821
822 private static boolean conformsToAlgorithmConstraints(
823 AlgorithmConstraints constraints, Certificate[] chain,
824 String variant) {
825
826 AlgorithmChecker checker =
827 new AlgorithmChecker(constraints, null, variant);
828 try {
829 checker.init(false);
830 } catch (CertPathValidatorException cpve) {
831 // unlikely to happen
832 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
833 SSLLogger.fine(
834 "Cannot initialize algorithm constraints checker", cpve);
835 }
836
837 return false;
838 }
839
840 // It is a forward checker, so we need to check from trust to target.
841 for (int i = chain.length - 1; i >= 0; i--) {
842 Certificate cert = chain[i];
843 try {
844 // We don't care about the unresolved critical extensions.
845 checker.check(cert, Collections.<String>emptySet());
846 } catch (CertPathValidatorException cpve) {
847 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
848 SSLLogger.fine("Certificate does not conform to " +
849 "algorithm constraints", cert, cpve);
850 }
851
852 return false;
853 }
854 }
855
856 return true;
857 }
858 }
|