< prev index next >

src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java

Print this page


   1 /*
   2  * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.ssl;
  27 
  28 import java.lang.ref.*;












  29 import java.util.*;
  30 import static java.util.Locale.ENGLISH;
  31 import java.util.concurrent.atomic.AtomicLong;
  32 import java.net.Socket;
  33 
  34 import java.security.*;
  35 import java.security.KeyStore.*;
  36 import java.security.cert.*;
  37 import java.security.cert.Certificate;
  38 
  39 import javax.net.ssl.*;
  40 
  41 import sun.security.provider.certpath.AlgorithmChecker;
  42 import sun.security.validator.Validator;
  43 
  44 /**
  45  * The new X509 key manager implementation. The main differences to the
  46  * old SunX509 key manager are:
  47  *  . it is based around the KeyStore.Builder API. This allows it to use
  48  *    other forms of KeyStore protection or password input (e.g. a
  49  *    CallbackHandler) or to have keys within one KeyStore protected by
  50  *    different keys.
  51  *  . it can use multiple KeyStores at the same time.
  52  *  . it is explicitly designed to accommodate KeyStores that change over
  53  *    the lifetime of the process.
  54  *  . it makes an effort to choose the key that matches best, i.e. one that
  55  *    is not expired and has the appropriate certificate extensions.
  56  *
  57  * Note that this code is not explicitly performance optimzied yet.
  58  *
  59  * @author  Andreas Sterbenz
  60  */
  61 final class X509KeyManagerImpl extends X509ExtendedKeyManager
  62         implements X509KeyManager {
  63 
  64     private static final Debug debug = Debug.getInstance("ssl");
  65 
  66     private static final boolean useDebug =
  67                             (debug != null) && Debug.isOn("keymanager");
  68 
  69     // for unit testing only, set via privileged reflection
  70     private static Date verificationDate;
  71 
  72     // list of the builders
  73     private final List<Builder> builders;
  74 
  75     // counter to generate unique ids for the aliases
  76     private final AtomicLong uidCounter;
  77 
  78     // cached entries
  79     private final Map<String,Reference<PrivateKeyEntry>> entryCacheMap;
  80 
  81     X509KeyManagerImpl(Builder builder) {
  82         this(Collections.singletonList(builder));
  83     }
  84 
  85     X509KeyManagerImpl(List<Builder> builders) {
  86         this.builders = builders;
  87         uidCounter = new AtomicLong();
  88         entryCacheMap = Collections.synchronizedMap


 172     }
 173 
 174     @Override
 175     public String[] getServerAliases(String keyType, Principal[] issuers) {
 176         return getAliases(keyType, issuers, CheckType.SERVER, null);
 177     }
 178 
 179     //
 180     // implementation private methods
 181     //
 182 
 183     // Gets algorithm constraints of the socket.
 184     private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
 185         if (socket != null && socket.isConnected() &&
 186                                         socket instanceof SSLSocket) {
 187 
 188             SSLSocket sslSocket = (SSLSocket)socket;
 189             SSLSession session = sslSocket.getHandshakeSession();
 190 
 191             if (session != null) {
 192                 ProtocolVersion protocolVersion =
 193                     ProtocolVersion.valueOf(session.getProtocol());
 194                 if (protocolVersion.useTLS12PlusSpec()) {
 195                     String[] peerSupportedSignAlgs = null;
 196 
 197                     if (session instanceof ExtendedSSLSession) {
 198                         ExtendedSSLSession extSession =
 199                             (ExtendedSSLSession)session;
 200                         peerSupportedSignAlgs =
 201                             extSession.getPeerSupportedSignatureAlgorithms();
 202                     }
 203 
 204                     return new SSLAlgorithmConstraints(
 205                         sslSocket, peerSupportedSignAlgs, true);
 206                 }
 207             }
 208 
 209             return new SSLAlgorithmConstraints(sslSocket, true);
 210         }
 211 
 212         return new SSLAlgorithmConstraints((SSLSocket)null, true);
 213     }
 214 
 215     // Gets algorithm constraints of the engine.
 216     private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
 217         if (engine != null) {
 218             SSLSession session = engine.getHandshakeSession();
 219             if (session != null) {
 220                 ProtocolVersion protocolVersion =
 221                     ProtocolVersion.valueOf(session.getProtocol());
 222                 if (protocolVersion.useTLS12PlusSpec()) {
 223                     String[] peerSupportedSignAlgs = null;
 224 
 225                     if (session instanceof ExtendedSSLSession) {
 226                         ExtendedSSLSession extSession =
 227                             (ExtendedSSLSession)session;
 228                         peerSupportedSignAlgs =
 229                             extSession.getPeerSupportedSignatureAlgorithms();
 230                     }
 231 
 232                     return new SSLAlgorithmConstraints(
 233                         engine, peerSupportedSignAlgs, true);
 234                 }
 235             }
 236         }
 237 
 238         return new SSLAlgorithmConstraints(engine, true);
 239     }
 240 
 241     // we construct the alias we return to JSSE as seen in the code below
 242     // a unique id is included to allow us to reliably cache entries


 371             CheckType checkType, AlgorithmConstraints constraints,
 372             List<SNIServerName> requestedServerNames, String idAlgorithm) {
 373 
 374         if (keyTypeList == null || keyTypeList.isEmpty()) {
 375             return null;
 376         }
 377 
 378         Set<Principal> issuerSet = getIssuerSet(issuers);
 379         List<EntryStatus> allResults = null;
 380         for (int i = 0, n = builders.size(); i < n; i++) {
 381             try {
 382                 List<EntryStatus> results = getAliases(i, keyTypeList,
 383                             issuerSet, false, checkType, constraints,
 384                             requestedServerNames, idAlgorithm);
 385                 if (results != null) {
 386                     // the results will either be a single perfect match
 387                     // or 1 or more imperfect matches
 388                     // if it's a perfect match, return immediately
 389                     EntryStatus status = results.get(0);
 390                     if (status.checkResult == CheckResult.OK) {
 391                         if (useDebug) {
 392                             debug.println("KeyMgr: choosing key: " + status);
 393                         }
 394                         return makeAlias(status);
 395                     }
 396                     if (allResults == null) {
 397                         allResults = new ArrayList<EntryStatus>();
 398                     }
 399                     allResults.addAll(results);
 400                 }
 401             } catch (Exception e) {
 402                 // ignore
 403             }
 404         }
 405         if (allResults == null) {
 406             if (useDebug) {
 407                 debug.println("KeyMgr: no matching key found");
 408             }
 409             return null;
 410         }
 411         Collections.sort(allResults);
 412         if (useDebug) {
 413             debug.println("KeyMgr: no good matching key found, "
 414                         + "returning best match out of:");
 415             debug.println(allResults.toString());
 416         }
 417         return makeAlias(allResults.get(0));
 418     }
 419 
 420     /*
 421      * Return all aliases that (approximately) fit the parameters.
 422      * These are perfect matches plus imperfect matches (expired certificates
 423      * and certificates with the wrong extensions).
 424      * The perfect matches will be first in the array.
 425      */
 426     public String[] getAliases(String keyType, Principal[] issuers,
 427             CheckType checkType, AlgorithmConstraints constraints) {
 428         if (keyType == null) {
 429             return null;
 430         }
 431 
 432         Set<Principal> issuerSet = getIssuerSet(issuers);
 433         List<KeyType> keyTypeList = getKeyTypes(keyType);
 434         List<EntryStatus> allResults = null;
 435         for (int i = 0, n = builders.size(); i < n; i++) {
 436             try {
 437                 List<EntryStatus> results = getAliases(i, keyTypeList,
 438                                     issuerSet, true, checkType, constraints,
 439                                     null, null);
 440                 if (results != null) {
 441                     if (allResults == null) {
 442                         allResults = new ArrayList<EntryStatus>();
 443                     }
 444                     allResults.addAll(results);
 445                 }
 446             } catch (Exception e) {
 447                 // ignore
 448             }
 449         }
 450         if (allResults == null || allResults.isEmpty()) {
 451             if (useDebug) {
 452                 debug.println("KeyMgr: no matching alias found");
 453             }
 454             return null;
 455         }
 456         Collections.sort(allResults);
 457         if (useDebug) {
 458             debug.println("KeyMgr: getting aliases: " + allResults);
 459         }
 460         return toAliases(allResults);
 461     }
 462 
 463     // turn candidate entries into unique aliases we can return to JSSE
 464     private String[] toAliases(List<EntryStatus> results) {
 465         String[] s = new String[results.size()];
 466         int i = 0;
 467         for (EntryStatus result : results) {
 468             s[i++] = makeAlias(result);
 469         }
 470         return s;
 471     }
 472 
 473     // make a Set out of the array
 474     private Set<Principal> getIssuerSet(Principal[] issuers) {
 475         if ((issuers != null) && (issuers.length != 0)) {
 476             return new HashSet<>(Arrays.asList(issuers));
 477         } else {
 478             return null;


 559             try {
 560                 // check extended key usage
 561                 List<String> certEku = cert.getExtendedKeyUsage();
 562                 if ((certEku != null) &&
 563                         Collections.disjoint(validEku, certEku)) {
 564                     // if extension present and it does not contain any of
 565                     // the valid EKU OIDs, return extension_mismatch
 566                     return CheckResult.EXTENSION_MISMATCH;
 567                 }
 568 
 569                 // check key usage
 570                 boolean[] ku = cert.getKeyUsage();
 571                 if (ku != null) {
 572                     String algorithm = cert.getPublicKey().getAlgorithm();
 573                     boolean kuSignature = getBit(ku, 0);
 574                     switch (algorithm) {
 575                         case "RSA":
 576                             // require either signature bit
 577                             // or if server also allow key encipherment bit
 578                             if (kuSignature == false) {
 579                                 if ((this == CLIENT) || (getBit(ku, 2) == false)) {
 580                                     return CheckResult.EXTENSION_MISMATCH;
 581                                 }
 582                             }
 583                             break;
 584                         case "DSA":
 585                             // require signature bit
 586                             if (kuSignature == false) {
 587                                 return CheckResult.EXTENSION_MISMATCH;
 588                             }
 589                             break;
 590                         case "DH":
 591                             // require keyagreement bit
 592                             if (getBit(ku, 4) == false) {
 593                                 return CheckResult.EXTENSION_MISMATCH;
 594                             }
 595                             break;
 596                         case "EC":
 597                             // require signature bit
 598                             if (kuSignature == false) {
 599                                 return CheckResult.EXTENSION_MISMATCH;


 614                 // extensions unparseable, return failure
 615                 return CheckResult.EXTENSION_MISMATCH;
 616             }
 617 
 618             try {
 619                 cert.checkValidity(date);
 620             } catch (CertificateException e) {
 621                 return CheckResult.EXPIRED;
 622             }
 623 
 624             if (serverNames != null && !serverNames.isEmpty()) {
 625                 for (SNIServerName serverName : serverNames) {
 626                     if (serverName.getType() ==
 627                                 StandardConstants.SNI_HOST_NAME) {
 628                         if (!(serverName instanceof SNIHostName)) {
 629                             try {
 630                                 serverName =
 631                                     new SNIHostName(serverName.getEncoded());
 632                             } catch (IllegalArgumentException iae) {
 633                                 // unlikely to happen, just in case ...
 634                                 if (useDebug) {
 635                                     debug.println(

 636                                        "Illegal server name: " + serverName);
 637                                 }
 638 
 639                                 return CheckResult.INSENSITIVE;
 640                             }
 641                         }
 642                         String hostname =
 643                                 ((SNIHostName)serverName).getAsciiName();
 644 
 645                         try {
 646                             X509TrustManagerImpl.checkIdentity(hostname,
 647                                                         cert, idAlgorithm);
 648                         } catch (CertificateException e) {
 649                             if (useDebug) {
 650                                 debug.println(

 651                                    "Certificate identity does not match " +
 652                                    "Server Name Inidication (SNI): " +
 653                                    hostname);
 654                             }
 655                             return CheckResult.INSENSITIVE;
 656                         }
 657 
 658                         break;
 659                     }
 660                 }
 661             }
 662 
 663             return CheckResult.OK;
 664         }
 665 
 666         public String getValidator() {
 667             if (this == CLIENT) {
 668                 return Validator.VAR_TLS_CLIENT;
 669             } else if (this == SERVER) {
 670                 return Validator.VAR_TLS_SERVER;


 707      *   . if 'findAll' is 'false' and there is NO perfect match, a List
 708      *     with all the imperfect matches (expired, wrong extensions)
 709      *   . if 'findAll' is 'true', a List with all perfect and imperfect
 710      *     matches
 711      */
 712     private List<EntryStatus> getAliases(int builderIndex,
 713             List<KeyType> keyTypes, Set<Principal> issuerSet,
 714             boolean findAll, CheckType checkType,
 715             AlgorithmConstraints constraints,
 716             List<SNIServerName> requestedServerNames,
 717             String idAlgorithm) throws Exception {
 718 
 719         Builder builder = builders.get(builderIndex);
 720         KeyStore ks = builder.getKeyStore();
 721         List<EntryStatus> results = null;
 722         Date date = verificationDate;
 723         boolean preferred = false;
 724         for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
 725             String alias = e.nextElement();
 726             // check if it is a key entry (private key or secret key)
 727             if (ks.isKeyEntry(alias) == false) {
 728                 continue;
 729             }
 730 
 731             Certificate[] chain = ks.getCertificateChain(alias);
 732             if ((chain == null) || (chain.length == 0)) {
 733                 // must be secret key entry, ignore
 734                 continue;
 735             }
 736 
 737             boolean incompatible = false;
 738             for (Certificate cert : chain) {
 739                 if (cert instanceof X509Certificate == false) {
 740                     // not an X509Certificate, ignore this alias
 741                     incompatible = true;
 742                     break;
 743                 }
 744             }
 745             if (incompatible) {
 746                 continue;
 747             }
 748 
 749             // check keytype
 750             int keyIndex = -1;
 751             int j = 0;
 752             for (KeyType keyType : keyTypes) {
 753                 if (keyType.matches(chain)) {
 754                     keyIndex = j;
 755                     break;
 756                 }
 757                 j++;
 758             }
 759             if (keyIndex == -1) {
 760                 if (useDebug) {
 761                     debug.println("Ignoring alias " + alias
 762                                 + ": key algorithm does not match");
 763                 }
 764                 continue;
 765             }
 766             // check issuers
 767             if (issuerSet != null) {
 768                 boolean found = false;
 769                 for (Certificate cert : chain) {
 770                     X509Certificate xcert = (X509Certificate)cert;
 771                     if (issuerSet.contains(xcert.getIssuerX500Principal())) {
 772                         found = true;
 773                         break;
 774                     }
 775                 }
 776                 if (found == false) {
 777                     if (useDebug) {
 778                         debug.println("Ignoring alias " + alias

 779                                     + ": issuers do not match");
 780                     }
 781                     continue;
 782                 }
 783             }
 784 
 785             // check the algorithm constraints
 786             if (constraints != null &&
 787                     !conformsToAlgorithmConstraints(constraints, chain,
 788                             checkType.getValidator())) {
 789 
 790                 if (useDebug) {
 791                     debug.println("Ignoring alias " + alias +
 792                             ": certificate list does not conform to " +
 793                             "algorithm constraints");
 794                 }
 795                 continue;
 796             }
 797 
 798             if (date == null) {
 799                 date = new Date();
 800             }
 801             CheckResult checkResult =
 802                     checkType.check((X509Certificate)chain[0], date,
 803                                     requestedServerNames, idAlgorithm);
 804             EntryStatus status =
 805                     new EntryStatus(builderIndex, keyIndex,
 806                                         alias, chain, checkResult);
 807             if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
 808                 preferred = true;
 809             }
 810             if (preferred && (findAll == false)) {
 811                 // if we have a good match and do not need all matches,
 812                 // return immediately
 813                 return Collections.singletonList(status);
 814             } else {
 815                 if (results == null) {
 816                     results = new ArrayList<EntryStatus>();
 817                 }
 818                 results.add(status);
 819             }
 820         }
 821         return results;
 822     }
 823 
 824     private static boolean conformsToAlgorithmConstraints(
 825             AlgorithmConstraints constraints, Certificate[] chain,
 826             String variant) {
 827 
 828         AlgorithmChecker checker = new AlgorithmChecker(constraints, null, variant);

 829         try {
 830             checker.init(false);
 831         } catch (CertPathValidatorException cpve) {
 832             // unlikely to happen
 833             if (useDebug) {
 834                 debug.println(
 835                     "Cannot initialize algorithm constraints checker: " + cpve);
 836             }
 837 
 838             return false;
 839         }
 840 
 841         // It is a forward checker, so we need to check from trust to target.
 842         for (int i = chain.length - 1; i >= 0; i--) {
 843             Certificate cert = chain[i];
 844             try {
 845                 // We don't care about the unresolved critical extensions.
 846                 checker.check(cert, Collections.<String>emptySet());
 847             } catch (CertPathValidatorException cpve) {
 848                 if (useDebug) {
 849                     debug.println("Certificate (" + cert +
 850                         ") does not conform to algorithm constraints: " + cpve);
 851                 }
 852 
 853                 return false;
 854             }
 855         }
 856 
 857         return true;
 858     }
 859 
 860 }
   1 /*
   2  * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.ssl;
  27 
  28 import java.lang.ref.*;
  29 import java.net.Socket;
  30 import java.security.AlgorithmConstraints;
  31 import java.security.KeyStore;
  32 import java.security.KeyStore.Builder;
  33 import java.security.KeyStore.Entry;
  34 import java.security.KeyStore.PrivateKeyEntry;
  35 import java.security.Principal;
  36 import java.security.PrivateKey;
  37 import java.security.cert.CertPathValidatorException;
  38 import java.security.cert.Certificate;
  39 import java.security.cert.CertificateException;
  40 import java.security.cert.X509Certificate;
  41 import java.util.*;
  42 import static java.util.Locale.ENGLISH;
  43 import java.util.concurrent.atomic.AtomicLong;







  44 import javax.net.ssl.*;

  45 import sun.security.provider.certpath.AlgorithmChecker;
  46 import sun.security.validator.Validator;
  47 
  48 /**
  49  * The new X509 key manager implementation. The main differences to the
  50  * old SunX509 key manager are:
  51  *  . it is based around the KeyStore.Builder API. This allows it to use
  52  *    other forms of KeyStore protection or password input (e.g. a
  53  *    CallbackHandler) or to have keys within one KeyStore protected by
  54  *    different keys.
  55  *  . it can use multiple KeyStores at the same time.
  56  *  . it is explicitly designed to accommodate KeyStores that change over
  57  *    the lifetime of the process.
  58  *  . it makes an effort to choose the key that matches best, i.e. one that
  59  *    is not expired and has the appropriate certificate extensions.
  60  *
  61  * Note that this code is not explicitly performance optimzied yet.
  62  *
  63  * @author  Andreas Sterbenz
  64  */
  65 final class X509KeyManagerImpl extends X509ExtendedKeyManager
  66         implements X509KeyManager {
  67 





  68     // for unit testing only, set via privileged reflection
  69     private static Date verificationDate;
  70 
  71     // list of the builders
  72     private final List<Builder> builders;
  73 
  74     // counter to generate unique ids for the aliases
  75     private final AtomicLong uidCounter;
  76 
  77     // cached entries
  78     private final Map<String,Reference<PrivateKeyEntry>> entryCacheMap;
  79 
  80     X509KeyManagerImpl(Builder builder) {
  81         this(Collections.singletonList(builder));
  82     }
  83 
  84     X509KeyManagerImpl(List<Builder> builders) {
  85         this.builders = builders;
  86         uidCounter = new AtomicLong();
  87         entryCacheMap = Collections.synchronizedMap


 171     }
 172 
 173     @Override
 174     public String[] getServerAliases(String keyType, Principal[] issuers) {
 175         return getAliases(keyType, issuers, CheckType.SERVER, null);
 176     }
 177 
 178     //
 179     // implementation private methods
 180     //
 181 
 182     // Gets algorithm constraints of the socket.
 183     private AlgorithmConstraints getAlgorithmConstraints(Socket socket) {
 184         if (socket != null && socket.isConnected() &&
 185                                         socket instanceof SSLSocket) {
 186 
 187             SSLSocket sslSocket = (SSLSocket)socket;
 188             SSLSession session = sslSocket.getHandshakeSession();
 189 
 190             if (session != null) {
 191                 if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {


 192                     String[] peerSupportedSignAlgs = null;
 193 
 194                     if (session instanceof ExtendedSSLSession) {
 195                         ExtendedSSLSession extSession =
 196                             (ExtendedSSLSession)session;
 197                         peerSupportedSignAlgs =
 198                             extSession.getPeerSupportedSignatureAlgorithms();
 199                     }
 200 
 201                     return new SSLAlgorithmConstraints(
 202                         sslSocket, peerSupportedSignAlgs, true);
 203                 }
 204             }
 205 
 206             return new SSLAlgorithmConstraints(sslSocket, true);
 207         }
 208 
 209         return new SSLAlgorithmConstraints((SSLSocket)null, true);
 210     }
 211 
 212     // Gets algorithm constraints of the engine.
 213     private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) {
 214         if (engine != null) {
 215             SSLSession session = engine.getHandshakeSession();
 216             if (session != null) {
 217                 if (ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) {


 218                     String[] peerSupportedSignAlgs = null;
 219 
 220                     if (session instanceof ExtendedSSLSession) {
 221                         ExtendedSSLSession extSession =
 222                             (ExtendedSSLSession)session;
 223                         peerSupportedSignAlgs =
 224                             extSession.getPeerSupportedSignatureAlgorithms();
 225                     }
 226 
 227                     return new SSLAlgorithmConstraints(
 228                         engine, peerSupportedSignAlgs, true);
 229                 }
 230             }
 231         }
 232 
 233         return new SSLAlgorithmConstraints(engine, true);
 234     }
 235 
 236     // we construct the alias we return to JSSE as seen in the code below
 237     // a unique id is included to allow us to reliably cache entries


 366             CheckType checkType, AlgorithmConstraints constraints,
 367             List<SNIServerName> requestedServerNames, String idAlgorithm) {
 368 
 369         if (keyTypeList == null || keyTypeList.isEmpty()) {
 370             return null;
 371         }
 372 
 373         Set<Principal> issuerSet = getIssuerSet(issuers);
 374         List<EntryStatus> allResults = null;
 375         for (int i = 0, n = builders.size(); i < n; i++) {
 376             try {
 377                 List<EntryStatus> results = getAliases(i, keyTypeList,
 378                             issuerSet, false, checkType, constraints,
 379                             requestedServerNames, idAlgorithm);
 380                 if (results != null) {
 381                     // the results will either be a single perfect match
 382                     // or 1 or more imperfect matches
 383                     // if it's a perfect match, return immediately
 384                     EntryStatus status = results.get(0);
 385                     if (status.checkResult == CheckResult.OK) {
 386                         if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 387                             SSLLogger.fine("KeyMgr: choosing key: " + status);
 388                         }
 389                         return makeAlias(status);
 390                     }
 391                     if (allResults == null) {
 392                         allResults = new ArrayList<EntryStatus>();
 393                     }
 394                     allResults.addAll(results);
 395                 }
 396             } catch (Exception e) {
 397                 // ignore
 398             }
 399         }
 400         if (allResults == null) {
 401             if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 402                 SSLLogger.fine("KeyMgr: no matching key found");
 403             }
 404             return null;
 405         }
 406         Collections.sort(allResults);
 407         if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 408             SSLLogger.fine(
 409                     "KeyMgr: no good matching key found, "
 410                     + "returning best match out of", allResults);
 411         }
 412         return makeAlias(allResults.get(0));
 413     }
 414 
 415     /*
 416      * Return all aliases that (approximately) fit the parameters.
 417      * These are perfect matches plus imperfect matches (expired certificates
 418      * and certificates with the wrong extensions).
 419      * The perfect matches will be first in the array.
 420      */
 421     public String[] getAliases(String keyType, Principal[] issuers,
 422             CheckType checkType, AlgorithmConstraints constraints) {
 423         if (keyType == null) {
 424             return null;
 425         }
 426 
 427         Set<Principal> issuerSet = getIssuerSet(issuers);
 428         List<KeyType> keyTypeList = getKeyTypes(keyType);
 429         List<EntryStatus> allResults = null;
 430         for (int i = 0, n = builders.size(); i < n; i++) {
 431             try {
 432                 List<EntryStatus> results = getAliases(i, keyTypeList,
 433                                     issuerSet, true, checkType, constraints,
 434                                     null, null);
 435                 if (results != null) {
 436                     if (allResults == null) {
 437                         allResults = new ArrayList<EntryStatus>();
 438                     }
 439                     allResults.addAll(results);
 440                 }
 441             } catch (Exception e) {
 442                 // ignore
 443             }
 444         }
 445         if (allResults == null || allResults.isEmpty()) {
 446             if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 447                 SSLLogger.fine("KeyMgr: no matching alias found");
 448             }
 449             return null;
 450         }
 451         Collections.sort(allResults);
 452         if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 453             SSLLogger.fine("KeyMgr: getting aliases", allResults);
 454         }
 455         return toAliases(allResults);
 456     }
 457 
 458     // turn candidate entries into unique aliases we can return to JSSE
 459     private String[] toAliases(List<EntryStatus> results) {
 460         String[] s = new String[results.size()];
 461         int i = 0;
 462         for (EntryStatus result : results) {
 463             s[i++] = makeAlias(result);
 464         }
 465         return s;
 466     }
 467 
 468     // make a Set out of the array
 469     private Set<Principal> getIssuerSet(Principal[] issuers) {
 470         if ((issuers != null) && (issuers.length != 0)) {
 471             return new HashSet<>(Arrays.asList(issuers));
 472         } else {
 473             return null;


 554             try {
 555                 // check extended key usage
 556                 List<String> certEku = cert.getExtendedKeyUsage();
 557                 if ((certEku != null) &&
 558                         Collections.disjoint(validEku, certEku)) {
 559                     // if extension present and it does not contain any of
 560                     // the valid EKU OIDs, return extension_mismatch
 561                     return CheckResult.EXTENSION_MISMATCH;
 562                 }
 563 
 564                 // check key usage
 565                 boolean[] ku = cert.getKeyUsage();
 566                 if (ku != null) {
 567                     String algorithm = cert.getPublicKey().getAlgorithm();
 568                     boolean kuSignature = getBit(ku, 0);
 569                     switch (algorithm) {
 570                         case "RSA":
 571                             // require either signature bit
 572                             // or if server also allow key encipherment bit
 573                             if (kuSignature == false) {
 574                                 if (this == CLIENT || getBit(ku, 2) == false) {
 575                                     return CheckResult.EXTENSION_MISMATCH;
 576                                 }
 577                             }
 578                             break;
 579                         case "DSA":
 580                             // require signature bit
 581                             if (kuSignature == false) {
 582                                 return CheckResult.EXTENSION_MISMATCH;
 583                             }
 584                             break;
 585                         case "DH":
 586                             // require keyagreement bit
 587                             if (getBit(ku, 4) == false) {
 588                                 return CheckResult.EXTENSION_MISMATCH;
 589                             }
 590                             break;
 591                         case "EC":
 592                             // require signature bit
 593                             if (kuSignature == false) {
 594                                 return CheckResult.EXTENSION_MISMATCH;


 609                 // extensions unparseable, return failure
 610                 return CheckResult.EXTENSION_MISMATCH;
 611             }
 612 
 613             try {
 614                 cert.checkValidity(date);
 615             } catch (CertificateException e) {
 616                 return CheckResult.EXPIRED;
 617             }
 618 
 619             if (serverNames != null && !serverNames.isEmpty()) {
 620                 for (SNIServerName serverName : serverNames) {
 621                     if (serverName.getType() ==
 622                                 StandardConstants.SNI_HOST_NAME) {
 623                         if (!(serverName instanceof SNIHostName)) {
 624                             try {
 625                                 serverName =
 626                                     new SNIHostName(serverName.getEncoded());
 627                             } catch (IllegalArgumentException iae) {
 628                                 // unlikely to happen, just in case ...
 629                                 if (SSLLogger.isOn &&
 630                                         SSLLogger.isOn("keymanager")) {
 631                                     SSLLogger.fine(
 632                                        "Illegal server name: " + serverName);
 633                                 }
 634 
 635                                 return CheckResult.INSENSITIVE;
 636                             }
 637                         }
 638                         String hostname =
 639                                 ((SNIHostName)serverName).getAsciiName();
 640 
 641                         try {
 642                             X509TrustManagerImpl.checkIdentity(hostname,
 643                                                         cert, idAlgorithm);
 644                         } catch (CertificateException e) {
 645                             if (SSLLogger.isOn &&
 646                                     SSLLogger.isOn("keymanager")) {
 647                                 SSLLogger.fine(
 648                                     "Certificate identity does not match " +
 649                                     "Server Name Inidication (SNI): " +
 650                                     hostname);
 651                             }
 652                             return CheckResult.INSENSITIVE;
 653                         }
 654 
 655                         break;
 656                     }
 657                 }
 658             }
 659 
 660             return CheckResult.OK;
 661         }
 662 
 663         public String getValidator() {
 664             if (this == CLIENT) {
 665                 return Validator.VAR_TLS_CLIENT;
 666             } else if (this == SERVER) {
 667                 return Validator.VAR_TLS_SERVER;


 704      *   . if 'findAll' is 'false' and there is NO perfect match, a List
 705      *     with all the imperfect matches (expired, wrong extensions)
 706      *   . if 'findAll' is 'true', a List with all perfect and imperfect
 707      *     matches
 708      */
 709     private List<EntryStatus> getAliases(int builderIndex,
 710             List<KeyType> keyTypes, Set<Principal> issuerSet,
 711             boolean findAll, CheckType checkType,
 712             AlgorithmConstraints constraints,
 713             List<SNIServerName> requestedServerNames,
 714             String idAlgorithm) throws Exception {
 715 
 716         Builder builder = builders.get(builderIndex);
 717         KeyStore ks = builder.getKeyStore();
 718         List<EntryStatus> results = null;
 719         Date date = verificationDate;
 720         boolean preferred = false;
 721         for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
 722             String alias = e.nextElement();
 723             // check if it is a key entry (private key or secret key)
 724             if (!ks.isKeyEntry(alias)) {
 725                 continue;
 726             }
 727 
 728             Certificate[] chain = ks.getCertificateChain(alias);
 729             if ((chain == null) || (chain.length == 0)) {
 730                 // must be secret key entry, ignore
 731                 continue;
 732             }
 733 
 734             boolean incompatible = false;
 735             for (Certificate cert : chain) {
 736                 if (cert instanceof X509Certificate == false) {
 737                     // not an X509Certificate, ignore this alias
 738                     incompatible = true;
 739                     break;
 740                 }
 741             }
 742             if (incompatible) {
 743                 continue;
 744             }
 745 
 746             // check keytype
 747             int keyIndex = -1;
 748             int j = 0;
 749             for (KeyType keyType : keyTypes) {
 750                 if (keyType.matches(chain)) {
 751                     keyIndex = j;
 752                     break;
 753                 }
 754                 j++;
 755             }
 756             if (keyIndex == -1) {
 757                 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 758                     SSLLogger.fine("Ignore alias " + alias
 759                                 + ": key algorithm does not match");
 760                 }
 761                 continue;
 762             }
 763             // check issuers
 764             if (issuerSet != null) {
 765                 boolean found = false;
 766                 for (Certificate cert : chain) {
 767                     X509Certificate xcert = (X509Certificate)cert;
 768                     if (issuerSet.contains(xcert.getIssuerX500Principal())) {
 769                         found = true;
 770                         break;
 771                     }
 772                 }
 773                 if (found == false) {
 774                     if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 775                         SSLLogger.fine(
 776                                 "Ignore alias " + alias
 777                                 + ": issuers do not match");
 778                     }
 779                     continue;
 780                 }
 781             }
 782 
 783             // check the algorithm constraints
 784             if (constraints != null &&
 785                     !conformsToAlgorithmConstraints(constraints, chain,
 786                             checkType.getValidator())) {
 787 
 788                 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 789                     SSLLogger.fine("Ignore alias " + alias +
 790                             ": certificate list does not conform to " +
 791                             "algorithm constraints");
 792                 }
 793                 continue;
 794             }
 795 
 796             if (date == null) {
 797                 date = new Date();
 798             }
 799             CheckResult checkResult =
 800                     checkType.check((X509Certificate)chain[0], date,
 801                                     requestedServerNames, idAlgorithm);
 802             EntryStatus status =
 803                     new EntryStatus(builderIndex, keyIndex,
 804                                         alias, chain, checkResult);
 805             if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) {
 806                 preferred = true;
 807             }
 808             if (preferred && (findAll == false)) {
 809                 // if we have a good match and do not need all matches,
 810                 // return immediately
 811                 return Collections.singletonList(status);
 812             } else {
 813                 if (results == null) {
 814                     results = new ArrayList<EntryStatus>();
 815                 }
 816                 results.add(status);
 817             }
 818         }
 819         return results;
 820     }
 821 
 822     private static boolean conformsToAlgorithmConstraints(
 823             AlgorithmConstraints constraints, Certificate[] chain,
 824             String variant) {
 825 
 826         AlgorithmChecker checker =
 827                 new AlgorithmChecker(constraints, null, variant);
 828         try {
 829             checker.init(false);
 830         } catch (CertPathValidatorException cpve) {
 831             // unlikely to happen
 832             if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 833                 SSLLogger.fine(
 834                     "Cannot initialize algorithm constraints checker", cpve);
 835             }
 836 
 837             return false;
 838         }
 839 
 840         // It is a forward checker, so we need to check from trust to target.
 841         for (int i = chain.length - 1; i >= 0; i--) {
 842             Certificate cert = chain[i];
 843             try {
 844                 // We don't care about the unresolved critical extensions.
 845                 checker.check(cert, Collections.<String>emptySet());
 846             } catch (CertPathValidatorException cpve) {
 847                 if (SSLLogger.isOn && SSLLogger.isOn("keymanager")) {
 848                     SSLLogger.fine("Certificate does not conform to " +
 849                             "algorithm constraints", cert, cpve);
 850                 }
 851 
 852                 return false;
 853             }
 854         }
 855 
 856         return true;
 857     }

 858 }
< prev index next >