X509KeyManagerImpl.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2004, 2013, 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 26package sun.security.ssl; 27 28import java.lang.ref.*; 29import java.util.*; 30import static java.util.Locale.ENGLISH; 31import java.util.concurrent.atomic.AtomicLong; 32import java.net.Socket; 33 34import java.security.*; 35import java.security.KeyStore.*; 36import java.security.cert.*; 37import java.security.cert.Certificate; 38 39import javax.net.ssl.*; 40 41import 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 accommodate 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 */ 60final class X509KeyManagerImpl extends X509ExtendedKeyManager 61 implements X509KeyManager { 62 63 private static final Debug debug = Debug.getInstance("ssl"); 64 65 private static final 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 private static final long serialVersionUID = -8211222668790986062L; 95 96 @Override protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { 97 return size() > 10; 98 } 99 } 100 101 // 102 // public methods 103 // 104 105 @Override 106 public X509Certificate[] getCertificateChain(String alias) { 107 PrivateKeyEntry entry = getEntry(alias); 108 return entry == null ? null : 109 (X509Certificate[])entry.getCertificateChain(); 110 } 111 112 @Override 113 public PrivateKey getPrivateKey(String alias) { 114 PrivateKeyEntry entry = getEntry(alias); 115 return entry == null ? null : entry.getPrivateKey(); 116 } 117 118 @Override 119 public String chooseClientAlias(String[] keyTypes, Principal[] issuers, 120 Socket socket) { 121 return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, 122 getAlgorithmConstraints(socket)); 123 } 124 125 @Override 126 public String chooseEngineClientAlias(String[] keyTypes, 127 Principal[] issuers, SSLEngine engine) { 128 return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, 129 getAlgorithmConstraints(engine)); 130 } 131 132 @Override 133 public String chooseServerAlias(String keyType, 134 Principal[] issuers, Socket socket) { 135 return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, 136 getAlgorithmConstraints(socket), 137 X509TrustManagerImpl.getRequestedServerNames(socket), 138 "HTTPS"); // The SNI HostName is a fully qualified domain name. 139 // The certificate selection scheme for SNI HostName 140 // is similar to HTTPS endpoint identification scheme 141 // implemented in this provider. 142 // 143 // Using HTTPS endpoint identification scheme to guide 144 // the selection of an appropriate authentication 145 // certificate according to requested SNI extension. 146 // 147 // It is not a really HTTPS endpoint identification. 148 } 149 150 @Override 151 public String chooseEngineServerAlias(String keyType, 152 Principal[] issuers, SSLEngine engine) { 153 return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, 154 getAlgorithmConstraints(engine), 155 X509TrustManagerImpl.getRequestedServerNames(engine), 156 "HTTPS"); // The SNI HostName is a fully qualified domain name. 157 // The certificate selection scheme for SNI HostName 158 // is similar to HTTPS endpoint identification scheme 159 // implemented in this provider. 160 // 161 // Using HTTPS endpoint identification scheme to guide 162 // the selection of an appropriate authentication 163 // certificate according to requested SNI extension. 164 // 165 // It is not a really HTTPS endpoint identification. 166 } 167 168 @Override 169 public String[] getClientAliases(String keyType, Principal[] issuers) { 170 return getAliases(keyType, issuers, CheckType.CLIENT, null); 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 ProtocolVersion protocolVersion = 192 ProtocolVersion.valueOf(session.getProtocol()); 193 if (protocolVersion.useTLS12PlusSpec()) { 194 String[] peerSupportedSignAlgs = null; 195 196 if (session instanceof ExtendedSSLSession) { 197 ExtendedSSLSession extSession = 198 (ExtendedSSLSession)session; 199 peerSupportedSignAlgs = 200 extSession.getPeerSupportedSignatureAlgorithms(); 201 } 202 203 return new SSLAlgorithmConstraints( 204 sslSocket, peerSupportedSignAlgs, true); 205 } 206 } 207 208 return new SSLAlgorithmConstraints(sslSocket, true); 209 } 210 211 return new SSLAlgorithmConstraints((SSLSocket)null, true); 212 } 213 214 // Gets algorithm constraints of the engine. 215 private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { 216 if (engine != null) { 217 SSLSession session = engine.getHandshakeSession(); 218 if (session != null) { 219 ProtocolVersion protocolVersion = 220 ProtocolVersion.valueOf(session.getProtocol()); 221 if (protocolVersion.useTLS12PlusSpec()) { 222 String[] peerSupportedSignAlgs = null; 223 224 if (session instanceof ExtendedSSLSession) { 225 ExtendedSSLSession extSession = 226 (ExtendedSSLSession)session; 227 peerSupportedSignAlgs = 228 extSession.getPeerSupportedSignatureAlgorithms(); 229 } 230 231 return new SSLAlgorithmConstraints( 232 engine, peerSupportedSignAlgs, true); 233 } 234 } 235 } 236 237 return new SSLAlgorithmConstraints(engine, true); 238 } 239 240 // we construct the alias we return to JSSE as seen in the code below 241 // a unique id is included to allow us to reliably cache entries 242 // between the calls to getCertificateChain() and getPrivateKey() 243 // even if tokens are inserted or removed 244 private String makeAlias(EntryStatus entry) { 245 return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." 246 + entry.alias; 247 } 248 249 private PrivateKeyEntry getEntry(String alias) { 250 // if the alias is null, return immediately 251 if (alias == null) { 252 return null; 253 } 254 255 // try to get the entry from cache 256 Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias); 257 PrivateKeyEntry entry = (ref != null) ? ref.get() : null; 258 if (entry != null) { 259 return entry; 260 } 261 262 // parse the alias 263 int firstDot = alias.indexOf('.'); 264 int secondDot = alias.indexOf('.', firstDot + 1); 265 if ((firstDot == -1) || (secondDot == firstDot)) { 266 // invalid alias 267 return null; 268 } 269 try { 270 int builderIndex = Integer.parseInt 271 (alias.substring(firstDot + 1, secondDot)); 272 String keyStoreAlias = alias.substring(secondDot + 1); 273 Builder builder = builders.get(builderIndex); 274 KeyStore ks = builder.getKeyStore(); 275 Entry newEntry = ks.getEntry 276 (keyStoreAlias, builder.getProtectionParameter(alias)); 277 if (newEntry instanceof PrivateKeyEntry == false) { 278 // unexpected type of entry 279 return null; 280 } 281 entry = (PrivateKeyEntry)newEntry; 282 entryCacheMap.put(alias, new SoftReference<PrivateKeyEntry>(entry)); 283 return entry; 284 } catch (Exception e) { 285 // ignore 286 return null; 287 } 288 } 289 290 // Class to help verify that the public key algorithm (and optionally 291 // the signature algorithm) of a certificate matches what we need. 292 private static class KeyType { 293 294 final String keyAlgorithm; 295 296 // In TLS 1.2, the signature algorithm has been obsoleted by the 297 // supported_signature_algorithms, and the certificate type no longer 298 // restricts the algorithm used to sign the certificate. 299 // However, because we don't support certificate type checking other 300 // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the 301 // protocol version here. 302 final String sigKeyAlgorithm; 303 304 KeyType(String algorithm) { 305 int k = algorithm.indexOf('_'); 306 if (k == -1) { 307 keyAlgorithm = algorithm; 308 sigKeyAlgorithm = null; 309 } else { 310 keyAlgorithm = algorithm.substring(0, k); 311 sigKeyAlgorithm = algorithm.substring(k + 1); 312 } 313 } 314 315 boolean matches(Certificate[] chain) { 316 if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) { 317 return false; 318 } 319 if (sigKeyAlgorithm == null) { 320 return true; 321 } 322 if (chain.length > 1) { 323 // if possible, check the public key in the issuer cert 324 return sigKeyAlgorithm.equals( 325 chain[1].getPublicKey().getAlgorithm()); 326 } else { 327 // Check the signature algorithm of the certificate itself. 328 // Look for the "withRSA" in "SHA1withRSA", etc. 329 X509Certificate issuer = (X509Certificate)chain[0]; 330 String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH); 331 String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH); 332 return sigAlgName.contains(pattern); 333 } 334 } 335 } 336 337 private static List<KeyType> getKeyTypes(String ... keyTypes) { 338 if ((keyTypes == null) || 339 (keyTypes.length == 0) || (keyTypes[0] == null)) { 340 return null; 341 } 342 List<KeyType> list = new ArrayList<>(keyTypes.length); 343 for (String keyType : keyTypes) { 344 list.add(new KeyType(keyType)); 345 } 346 return list; 347 } 348 349 /* 350 * Return the best alias that fits the given parameters. 351 * The algorithm we use is: 352 * . scan through all the aliases in all builders in order 353 * . as soon as we find a perfect match, return 354 * (i.e. a match with a cert that has appropriate key usage, 355 * qualified endpoint identity, and is not expired). 356 * . if we do not find a perfect match, keep looping and remember 357 * the imperfect matches 358 * . at the end, sort the imperfect matches. we prefer expired certs 359 * with appropriate key usage to certs with the wrong key usage. 360 * return the first one of them. 361 */ 362 private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers, 363 CheckType checkType, AlgorithmConstraints constraints) { 364 365 return chooseAlias(keyTypeList, issuers, 366 checkType, constraints, null, null); 367 } 368 369 private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers, 370 CheckType checkType, AlgorithmConstraints constraints, 371 List<SNIServerName> requestedServerNames, String idAlgorithm) { 372 373 if (keyTypeList == null || keyTypeList.isEmpty()) { 374 return null; 375 } 376 377 Set<Principal> issuerSet = getIssuerSet(issuers); 378 List<EntryStatus> allResults = null; 379 for (int i = 0, n = builders.size(); i < n; i++) { 380 try { 381 List<EntryStatus> results = getAliases(i, keyTypeList, 382 issuerSet, false, checkType, constraints, 383 requestedServerNames, idAlgorithm); 384 if (results != null) { 385 // the results will either be a single perfect match 386 // or 1 or more imperfect matches 387 // if it's a perfect match, return immediately 388 EntryStatus status = results.get(0); 389 if (status.checkResult == CheckResult.OK) { 390 if (useDebug) { 391 debug.println("KeyMgr: choosing key: " + status); 392 } 393 return makeAlias(status); 394 } 395 if (allResults == null) { 396 allResults = new ArrayList<EntryStatus>(); 397 } 398 allResults.addAll(results); 399 } 400 } catch (Exception e) { 401 // ignore 402 } 403 } 404 if (allResults == null) { 405 if (useDebug) { 406 debug.println("KeyMgr: no matching key found"); 407 } 408 return null; 409 } 410 Collections.sort(allResults); 411 if (useDebug) { 412 debug.println("KeyMgr: no good matching key found, " 413 + "returning best match out of:"); 414 debug.println(allResults.toString()); 415 } 416 return makeAlias(allResults.get(0)); 417 } 418 419 /* 420 * Return all aliases that (approximately) fit the parameters. 421 * These are perfect matches plus imperfect matches (expired certificates 422 * and certificates with the wrong extensions). 423 * The perfect matches will be first in the array. 424 */ 425 public String[] getAliases(String keyType, Principal[] issuers, 426 CheckType checkType, AlgorithmConstraints constraints) { 427 if (keyType == null) { 428 return null; 429 } 430 431 Set<Principal> issuerSet = getIssuerSet(issuers); 432 List<KeyType> keyTypeList = getKeyTypes(keyType); 433 List<EntryStatus> allResults = null; 434 for (int i = 0, n = builders.size(); i < n; i++) { 435 try { 436 List<EntryStatus> results = getAliases(i, keyTypeList, 437 issuerSet, true, checkType, constraints, 438 null, null); 439 if (results != null) { 440 if (allResults == null) { 441 allResults = new ArrayList<EntryStatus>(); 442 } 443 allResults.addAll(results); 444 } 445 } catch (Exception e) { 446 // ignore 447 } 448 } 449 if (allResults == null || allResults.isEmpty()) { 450 if (useDebug) { 451 debug.println("KeyMgr: no matching alias found"); 452 } 453 return null; 454 } 455 Collections.sort(allResults); 456 if (useDebug) { 457 debug.println("KeyMgr: getting aliases: " + allResults); 458 } 459 return toAliases(allResults); 460 } 461 462 // turn candidate entries into unique aliases we can return to JSSE 463 private String[] toAliases(List<EntryStatus> results) { 464 String[] s = new String[results.size()]; 465 int i = 0; 466 for (EntryStatus result : results) { 467 s[i++] = makeAlias(result); 468 } 469 return s; 470 } 471 472 // make a Set out of the array 473 private Set<Principal> getIssuerSet(Principal[] issuers) { 474 if ((issuers != null) && (issuers.length != 0)) { 475 return new HashSet<>(Arrays.asList(issuers)); 476 } else { 477 return null; 478 } 479 } 480 481 // a candidate match 482 // identifies the entry by builder and alias 483 // and includes the result of the certificate check 484 private static class EntryStatus implements Comparable<EntryStatus> { 485 486 final int builderIndex; 487 final int keyIndex; 488 final String alias; 489 final CheckResult checkResult; 490 491 EntryStatus(int builderIndex, int keyIndex, String alias, 492 Certificate[] chain, CheckResult checkResult) { 493 this.builderIndex = builderIndex; 494 this.keyIndex = keyIndex; 495 this.alias = alias; 496 this.checkResult = checkResult; 497 } 498 499 @Override 500 public int compareTo(EntryStatus other) { 501 int result = this.checkResult.compareTo(other.checkResult); 502 return (result == 0) ? (this.keyIndex - other.keyIndex) : result; 503 } 504 505 @Override 506 public String toString() { 507 String s = alias + " (verified: " + checkResult + ")"; 508 if (builderIndex == 0) { 509 return s; 510 } else { 511 return "Builder #" + builderIndex + ", alias: " + s; 512 } 513 } 514 } 515 516 // enum for the type of certificate check we want to perform 517 // (client or server) 518 // also includes the check code itself 519 private static enum CheckType { 520 521 // enum constant for "no check" (currently not used) 522 NONE(Collections.<String>emptySet()), 523 524 // enum constant for "tls client" check 525 // valid EKU for TLS client: any, tls_client 526 CLIENT(new HashSet<String>(Arrays.asList(new String[] { 527 "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), 528 529 // enum constant for "tls server" check 530 // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc 531 SERVER(new HashSet<String>(Arrays.asList(new String[] { 532 "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", 533 "1.3.6.1.4.1.311.10.3.3" }))); 534 535 // set of valid EKU values for this type 536 final Set<String> validEku; 537 538 CheckType(Set<String> validEku) { 539 this.validEku = validEku; 540 } 541 542 private static boolean getBit(boolean[] keyUsage, int bit) { 543 return (bit < keyUsage.length) && keyUsage[bit]; 544 } 545 546 // check if this certificate is appropriate for this type of use 547 // first check extensions, if they match, check expiration 548 // note: we may want to move this code into the sun.security.validator 549 // package 550 CheckResult check(X509Certificate cert, Date date, 551 List<SNIServerName> serverNames, String idAlgorithm) { 552 553 if (this == NONE) { 554 return CheckResult.OK; 555 } 556 557 // check extensions 558 try { 559 // check extended key usage 560 List<String> certEku = cert.getExtendedKeyUsage(); 561 if ((certEku != null) && 562 Collections.disjoint(validEku, certEku)) { 563 // if extension present and it does not contain any of 564 // the valid EKU OIDs, return extension_mismatch 565 return CheckResult.EXTENSION_MISMATCH; 566 } 567 568 // check key usage 569 boolean[] ku = cert.getKeyUsage(); 570 if (ku != null) { 571 String algorithm = cert.getPublicKey().getAlgorithm(); 572 boolean kuSignature = getBit(ku, 0); 573 switch (algorithm) { 574 case "RSA": 575 // require either signature bit 576 // or if server also allow key encipherment bit 577 if (kuSignature == false) { 578 if ((this == CLIENT) || (getBit(ku, 2) == false)) { 579 return CheckResult.EXTENSION_MISMATCH; 580 } 581 } 582 break; 583 case "DSA": 584 // require signature bit 585 if (kuSignature == false) { 586 return CheckResult.EXTENSION_MISMATCH; 587 } 588 break; 589 case "DH": 590 // require keyagreement bit 591 if (getBit(ku, 4) == false) { 592 return CheckResult.EXTENSION_MISMATCH; 593 } 594 break; 595 case "EC": 596 // require signature bit 597 if (kuSignature == false) { 598 return CheckResult.EXTENSION_MISMATCH; 599 } 600 // For servers, also require key agreement. 601 // This is not totally accurate as the keyAgreement 602 // bit is only necessary for static ECDH key 603 // exchange and not ephemeral ECDH. We leave it in 604 // for now until there are signs that this check 605 // causes problems for real world EC certificates. 606 if ((this == SERVER) && (getBit(ku, 4) == false)) { 607 return CheckResult.EXTENSION_MISMATCH; 608 } 609 break; 610 } 611 } 612 } catch (CertificateException e) { 613 // extensions unparseable, return failure 614 return CheckResult.EXTENSION_MISMATCH; 615 } 616 617 try { 618 cert.checkValidity(date); 619 } catch (CertificateException e) { 620 return CheckResult.EXPIRED; 621 } 622 623 if (serverNames != null && !serverNames.isEmpty()) { 624 for (SNIServerName serverName : serverNames) { 625 if (serverName.getType() == 626 StandardConstants.SNI_HOST_NAME) { 627 if (!(serverName instanceof SNIHostName)) { 628 try { 629 serverName = 630 new SNIHostName(serverName.getEncoded()); 631 } catch (IllegalArgumentException iae) { 632 // unlikely to happen, just in case ... 633 if (useDebug) { 634 debug.println( 635 "Illegal server name: " + serverName); 636 } 637 638 return CheckResult.INSENSITIVE; 639 } 640 } 641 String hostname = 642 ((SNIHostName)serverName).getAsciiName(); 643 644 try { 645 X509TrustManagerImpl.checkIdentity(hostname, 646 cert, idAlgorithm); 647 } catch (CertificateException e) { 648 if (useDebug) { 649 debug.println( 650 "Certificate identity does not match " + 651 "Server Name Inidication (SNI): " + 652 hostname); 653 } 654 return CheckResult.INSENSITIVE; 655 } 656 657 break; 658 } 659 } 660 } 661 662 return CheckResult.OK; 663 } 664 } 665 666 // enum for the result of the extension check 667 // NOTE: the order of the constants is important as they are used 668 // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH 669 private static enum CheckResult { 670 OK, // ok or not checked 671 INSENSITIVE, // server name indication insensitive 672 EXPIRED, // extensions valid but cert expired 673 EXTENSION_MISMATCH, // extensions invalid (expiration not checked) 674 } 675 676 /* 677 * Return a List of all candidate matches in the specified builder 678 * that fit the parameters. 679 * We exclude entries in the KeyStore if they are not: 680 * . private key entries 681 * . the certificates are not X509 certificates 682 * . the algorithm of the key in the EE cert doesn't match one of keyTypes 683 * . none of the certs is issued by a Principal in issuerSet 684 * Using those entries would not be possible or they would almost 685 * certainly be rejected by the peer. 686 * 687 * In addition to those checks, we also check the extensions in the EE 688 * cert and its expiration. Even if there is a mismatch, we include 689 * such certificates because they technically work and might be accepted 690 * by the peer. This leads to more graceful failure and better error 691 * messages if the cert expires from one day to the next. 692 * 693 * The return values are: 694 * . null, if there are no matching entries at all 695 * . if 'findAll' is 'false' and there is a perfect match, a List 696 * with a single element (early return) 697 * . if 'findAll' is 'false' and there is NO perfect match, a List 698 * with all the imperfect matches (expired, wrong extensions) 699 * . if 'findAll' is 'true', a List with all perfect and imperfect 700 * matches 701 */ 702 private List<EntryStatus> getAliases(int builderIndex, 703 List<KeyType> keyTypes, Set<Principal> issuerSet, 704 boolean findAll, CheckType checkType, 705 AlgorithmConstraints constraints, 706 List<SNIServerName> requestedServerNames, 707 String idAlgorithm) throws Exception { 708 709 Builder builder = builders.get(builderIndex); 710 KeyStore ks = builder.getKeyStore(); 711 List<EntryStatus> results = null; 712 Date date = verificationDate; 713 boolean preferred = false; 714 for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) { 715 String alias = e.nextElement(); 716 // check if it is a key entry (private key or secret key) 717 if (ks.isKeyEntry(alias) == false) { 718 continue; 719 } 720 721 Certificate[] chain = ks.getCertificateChain(alias); 722 if ((chain == null) || (chain.length == 0)) { 723 // must be secret key entry, ignore 724 continue; 725 } 726 727 boolean incompatible = false; 728 for (Certificate cert : chain) { 729 if (cert instanceof X509Certificate == false) { 730 // not an X509Certificate, ignore this alias 731 incompatible = true; 732 break; 733 } 734 } 735 if (incompatible) { 736 continue; 737 } 738 739 // check keytype 740 int keyIndex = -1; 741 int j = 0; 742 for (KeyType keyType : keyTypes) { 743 if (keyType.matches(chain)) { 744 keyIndex = j; 745 break; 746 } 747 j++; 748 } 749 if (keyIndex == -1) { 750 if (useDebug) { 751 debug.println("Ignoring alias " + alias 752 + ": key algorithm does not match"); 753 } 754 continue; 755 } 756 // check issuers 757 if (issuerSet != null) { 758 boolean found = false; 759 for (Certificate cert : chain) { 760 X509Certificate xcert = (X509Certificate)cert; 761 if (issuerSet.contains(xcert.getIssuerX500Principal())) { 762 found = true; 763 break; 764 } 765 } 766 if (found == false) { 767 if (useDebug) { 768 debug.println("Ignoring alias " + alias 769 + ": issuers do not match"); 770 } 771 continue; 772 } 773 } 774 775 // check the algorithm constraints 776 if (constraints != null && 777 !conformsToAlgorithmConstraints(constraints, chain)) { 778 779 if (useDebug) { 780 debug.println("Ignoring alias " + alias + 781 ": certificate list does not conform to " + 782 "algorithm constraints"); 783 } 784 continue; 785 } 786 787 if (date == null) { 788 date = new Date(); 789 } 790 CheckResult checkResult = 791 checkType.check((X509Certificate)chain[0], date, 792 requestedServerNames, idAlgorithm); 793 EntryStatus status = 794 new EntryStatus(builderIndex, keyIndex, 795 alias, chain, checkResult); 796 if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) { 797 preferred = true; 798 } 799 if (preferred && (findAll == false)) { 800 // if we have a good match and do not need all matches, 801 // return immediately 802 return Collections.singletonList(status); 803 } else { 804 if (results == null) { 805 results = new ArrayList<EntryStatus>(); 806 } 807 results.add(status); 808 } 809 } 810 return results; 811 } 812 813 private static boolean conformsToAlgorithmConstraints( 814 AlgorithmConstraints constraints, Certificate[] chain) { 815 816 AlgorithmChecker checker = new AlgorithmChecker(constraints); 817 try { 818 checker.init(false); 819 } catch (CertPathValidatorException cpve) { 820 // unlikely to happen 821 return false; 822 } 823 824 // It is a forward checker, so we need to check from trust to target. 825 for (int i = chain.length - 1; i >= 0; i--) { 826 Certificate cert = chain[i]; 827 try { 828 // We don't care about the unresolved critical extensions. 829 checker.check(cert, Collections.<String>emptySet()); 830 } catch (CertPathValidatorException cpve) { 831 return false; 832 } 833 } 834 835 return true; 836 } 837 838} 839