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 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 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 238 // between the calls to getCertificateChain() and getPrivateKey() 239 // even if tokens are inserted or removed 240 private String makeAlias(EntryStatus entry) { 241 return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." 242 + entry.alias; 243 } 244 245 private PrivateKeyEntry getEntry(String alias) { 246 // if the alias is null, return immediately 247 if (alias == null) { 248 return null; 249 } 250 251 // try to get the entry from cache 252 Reference<PrivateKeyEntry> ref = entryCacheMap.get(alias); 253 PrivateKeyEntry entry = (ref != null) ? ref.get() : null; 254 if (entry != null) { 255 return entry; 256 } 257 258 // parse the alias 259 int firstDot = alias.indexOf('.'); 260 int secondDot = alias.indexOf('.', firstDot + 1); 261 if ((firstDot == -1) || (secondDot == firstDot)) { 262 // invalid alias 263 return null; 264 } 265 try { 266 int builderIndex = Integer.parseInt 267 (alias.substring(firstDot + 1, secondDot)); 268 String keyStoreAlias = alias.substring(secondDot + 1); 269 Builder builder = builders.get(builderIndex); 270 KeyStore ks = builder.getKeyStore(); 271 Entry newEntry = ks.getEntry 272 (keyStoreAlias, builder.getProtectionParameter(alias)); 273 if (newEntry instanceof PrivateKeyEntry == false) { 274 // unexpected type of entry 275 return null; 276 } 277 entry = (PrivateKeyEntry)newEntry; 278 entryCacheMap.put(alias, new SoftReference<PrivateKeyEntry>(entry)); 279 return entry; 280 } catch (Exception e) { 281 // ignore 282 return null; 283 } 284 } 285 286 // Class to help verify that the public key algorithm (and optionally 287 // the signature algorithm) of a certificate matches what we need. 288 private static class KeyType { 289 290 final String keyAlgorithm; 291 292 // In TLS 1.2, the signature algorithm has been obsoleted by the 293 // supported_signature_algorithms, and the certificate type no longer 294 // restricts the algorithm used to sign the certificate. 295 // However, because we don't support certificate type checking other 296 // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the 297 // protocol version here. 298 final String sigKeyAlgorithm; 299 300 KeyType(String algorithm) { 301 int k = algorithm.indexOf('_'); 302 if (k == -1) { 303 keyAlgorithm = algorithm; 304 sigKeyAlgorithm = null; 305 } else { 306 keyAlgorithm = algorithm.substring(0, k); 307 sigKeyAlgorithm = algorithm.substring(k + 1); 308 } 309 } 310 311 boolean matches(Certificate[] chain) { 312 if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) { 313 return false; 314 } 315 if (sigKeyAlgorithm == null) { 316 return true; 317 } 318 if (chain.length > 1) { 319 // if possible, check the public key in the issuer cert 320 return sigKeyAlgorithm.equals( 321 chain[1].getPublicKey().getAlgorithm()); 322 } else { 323 // Check the signature algorithm of the certificate itself. 324 // Look for the "withRSA" in "SHA1withRSA", etc. 325 X509Certificate issuer = (X509Certificate)chain[0]; 326 String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH); 327 String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH); 328 return sigAlgName.contains(pattern); 329 } 330 } 331 } 332 333 private static List<KeyType> getKeyTypes(String ... keyTypes) { 334 if ((keyTypes == null) || 335 (keyTypes.length == 0) || (keyTypes[0] == null)) { 336 return null; 337 } 338 List<KeyType> list = new ArrayList<>(keyTypes.length); 339 for (String keyType : keyTypes) { 340 list.add(new KeyType(keyType)); 341 } 342 return list; 343 } 344 345 /* 346 * Return the best alias that fits the given parameters. 347 * The algorithm we use is: 348 * . scan through all the aliases in all builders in order 349 * . as soon as we find a perfect match, return 350 * (i.e. a match with a cert that has appropriate key usage, 351 * qualified endpoint identity, and is not expired). 352 * . if we do not find a perfect match, keep looping and remember 353 * the imperfect matches 354 * . at the end, sort the imperfect matches. we prefer expired certs 355 * with appropriate key usage to certs with the wrong key usage. 356 * return the first one of them. 357 */ 358 private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers, 359 CheckType checkType, AlgorithmConstraints constraints) { 360 361 return chooseAlias(keyTypeList, issuers, 362 checkType, constraints, null, null); 363 } 364 365 private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers, 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; 474 } 475 } 476 477 // a candidate match 478 // identifies the entry by builder and alias 479 // and includes the result of the certificate check 480 private static class EntryStatus implements Comparable<EntryStatus> { 481 482 final int builderIndex; 483 final int keyIndex; 484 final String alias; 485 final CheckResult checkResult; 486 487 EntryStatus(int builderIndex, int keyIndex, String alias, 488 Certificate[] chain, CheckResult checkResult) { 489 this.builderIndex = builderIndex; 490 this.keyIndex = keyIndex; 491 this.alias = alias; 492 this.checkResult = checkResult; 493 } 494 495 @Override 496 public int compareTo(EntryStatus other) { 497 int result = this.checkResult.compareTo(other.checkResult); 498 return (result == 0) ? (this.keyIndex - other.keyIndex) : result; 499 } 500 501 @Override 502 public String toString() { 503 String s = alias + " (verified: " + checkResult + ")"; 504 if (builderIndex == 0) { 505 return s; 506 } else { 507 return "Builder #" + builderIndex + ", alias: " + s; 508 } 509 } 510 } 511 512 // enum for the type of certificate check we want to perform 513 // (client or server) 514 // also includes the check code itself 515 private static enum CheckType { 516 517 // enum constant for "no check" (currently not used) 518 NONE(Collections.<String>emptySet()), 519 520 // enum constant for "tls client" check 521 // valid EKU for TLS client: any, tls_client 522 CLIENT(new HashSet<String>(Arrays.asList(new String[] { 523 "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), 524 525 // enum constant for "tls server" check 526 // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc 527 SERVER(new HashSet<String>(Arrays.asList(new String[] { 528 "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", 529 "1.3.6.1.4.1.311.10.3.3" }))); 530 531 // set of valid EKU values for this type 532 final Set<String> validEku; 533 534 CheckType(Set<String> validEku) { 535 this.validEku = validEku; 536 } 537 538 private static boolean getBit(boolean[] keyUsage, int bit) { 539 return (bit < keyUsage.length) && keyUsage[bit]; 540 } 541 542 // check if this certificate is appropriate for this type of use 543 // first check extensions, if they match, check expiration 544 // note: we may want to move this code into the sun.security.validator 545 // package 546 CheckResult check(X509Certificate cert, Date date, 547 List<SNIServerName> serverNames, String idAlgorithm) { 548 549 if (this == NONE) { 550 return CheckResult.OK; 551 } 552 553 // check extensions 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; 595 } 596 // For servers, also require key agreement. 597 // This is not totally accurate as the keyAgreement 598 // bit is only necessary for static ECDH key 599 // exchange and not ephemeral ECDH. We leave it in 600 // for now until there are signs that this check 601 // causes problems for real world EC certificates. 602 if ((this == SERVER) && (getBit(ku, 4) == false)) { 603 return CheckResult.EXTENSION_MISMATCH; 604 } 605 break; 606 } 607 } 608 } catch (CertificateException e) { 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; 668 } 669 return Validator.VAR_GENERIC; 670 } 671 } 672 673 // enum for the result of the extension check 674 // NOTE: the order of the constants is important as they are used 675 // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH 676 private static enum CheckResult { 677 OK, // ok or not checked 678 INSENSITIVE, // server name indication insensitive 679 EXPIRED, // extensions valid but cert expired 680 EXTENSION_MISMATCH, // extensions invalid (expiration not checked) 681 } 682 683 /* 684 * Return a List of all candidate matches in the specified builder 685 * that fit the parameters. 686 * We exclude entries in the KeyStore if they are not: 687 * . private key entries 688 * . the certificates are not X509 certificates 689 * . the algorithm of the key in the EE cert doesn't match one of keyTypes 690 * . none of the certs is issued by a Principal in issuerSet 691 * Using those entries would not be possible or they would almost 692 * certainly be rejected by the peer. 693 * 694 * In addition to those checks, we also check the extensions in the EE 695 * cert and its expiration. Even if there is a mismatch, we include 696 * such certificates because they technically work and might be accepted 697 * by the peer. This leads to more graceful failure and better error 698 * messages if the cert expires from one day to the next. 699 * 700 * The return values are: 701 * . null, if there are no matching entries at all 702 * . if 'findAll' is 'false' and there is a perfect match, a List 703 * with a single element (early return) 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 }