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 }