jdk/src/share/classes/sun/security/provider/certpath/RevocationChecker.java
changeset 12860 9ffbd4e43413
child 13800 2fd4a82efe9c
equal deleted inserted replaced
11673:5fe35861f07e 12860:9ffbd4e43413
       
     1 /*
       
     2  * Copyright (c) 2012, 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.provider.certpath;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.math.BigInteger;
       
    30 import java.net.URI;
       
    31 import java.net.URISyntaxException;
       
    32 import java.security.AccessController;
       
    33 import java.security.InvalidAlgorithmParameterException;
       
    34 import java.security.NoSuchAlgorithmException;
       
    35 import java.security.PrivilegedAction;
       
    36 import java.security.PublicKey;
       
    37 import java.security.Security;
       
    38 import java.security.cert.CertPathValidatorException.BasicReason;
       
    39 import java.security.cert.Extension;
       
    40 import java.security.cert.*;
       
    41 import java.security.interfaces.DSAPublicKey;
       
    42 import java.util.Arrays;
       
    43 import java.util.ArrayList;
       
    44 import java.util.Collection;
       
    45 import java.util.Collections;
       
    46 import java.util.HashSet;
       
    47 import java.util.List;
       
    48 import java.util.Map;
       
    49 import java.util.Set;
       
    50 import javax.security.auth.x500.X500Principal;
       
    51 
       
    52 import static sun.security.provider.certpath.OCSP.*;
       
    53 import sun.security.provider.certpath.PKIX.ValidatorParams;
       
    54 import sun.security.action.GetPropertyAction;
       
    55 import sun.security.x509.*;
       
    56 import static sun.security.x509.PKIXExtensions.*;
       
    57 import sun.security.util.Debug;
       
    58 
       
    59 class RevocationChecker extends PKIXRevocationChecker {
       
    60 
       
    61     private static final Debug debug = Debug.getInstance("certpath");
       
    62 
       
    63     private TrustAnchor anchor;
       
    64     private ValidatorParams params;
       
    65     private boolean onlyEE;
       
    66     private boolean softFail;
       
    67     private boolean crlDP;
       
    68     private URI responderURI;
       
    69     private X509Certificate responderCert;
       
    70     private List<CertStore> certStores;
       
    71     private Map<X509Certificate, byte[]> ocspStapled;
       
    72     private List<Extension> ocspExtensions;
       
    73     private boolean legacy;
       
    74 
       
    75     // state variables
       
    76     private X509Certificate issuerCert;
       
    77     private PublicKey prevPubKey;
       
    78     private boolean crlSignFlag;
       
    79 
       
    80     private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS };
       
    81     private Mode mode = Mode.PREFER_OCSP;
       
    82 
       
    83     private static class RevocationProperties {
       
    84         boolean onlyEE;
       
    85         boolean ocspEnabled;
       
    86         boolean crlDPEnabled;
       
    87         String ocspUrl;
       
    88         String ocspSubject;
       
    89         String ocspIssuer;
       
    90         String ocspSerial;
       
    91     }
       
    92 
       
    93     RevocationChecker() {
       
    94         legacy = false;
       
    95     }
       
    96 
       
    97     RevocationChecker(TrustAnchor anchor, ValidatorParams params)
       
    98         throws CertPathValidatorException
       
    99     {
       
   100         legacy = true;
       
   101         init(anchor, params);
       
   102     }
       
   103 
       
   104     void init(TrustAnchor anchor, ValidatorParams params)
       
   105         throws CertPathValidatorException
       
   106     {
       
   107         RevocationProperties rp = getRevocationProperties();
       
   108         URI uri = getOCSPResponder();
       
   109         responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri;
       
   110         X509Certificate cert = getOCSPResponderCert();
       
   111         responderCert = (cert == null)
       
   112                         ? getResponderCert(rp, params.trustAnchors(),
       
   113                                            params.certStores())
       
   114                         : cert;
       
   115         Set<Option> options = getOptions();
       
   116         for (Option option : options) {
       
   117             switch (option) {
       
   118             case ONLY_END_ENTITY:
       
   119             case PREFER_CRLS:
       
   120             case SOFT_FAIL:
       
   121                 break;
       
   122             default:
       
   123                 throw new CertPathValidatorException(
       
   124                     "Unrecognized revocation parameter option: " + option);
       
   125             }
       
   126         }
       
   127 
       
   128         // set mode, only end entity flag
       
   129         if (legacy) {
       
   130             mode = (rp.ocspEnabled) ? Mode.PREFER_OCSP : Mode.ONLY_CRLS;
       
   131             onlyEE = rp.onlyEE;
       
   132         } else {
       
   133             if (options.contains(Option.PREFER_CRLS)) {
       
   134                 mode = Mode.PREFER_CRLS;
       
   135             }
       
   136             onlyEE = options.contains(Option.ONLY_END_ENTITY);
       
   137         }
       
   138         softFail = options.contains(Option.SOFT_FAIL);
       
   139         if (legacy) {
       
   140             crlDP = rp.crlDPEnabled;
       
   141         } else {
       
   142             crlDP = true;
       
   143         }
       
   144         ocspStapled = getOCSPStapledResponses();
       
   145         ocspExtensions = getOCSPExtensions();
       
   146 
       
   147         this.anchor = anchor;
       
   148         this.params = params;
       
   149         this.certStores = new ArrayList<>(params.certStores());
       
   150         try {
       
   151             this.certStores.add(CertStore.getInstance("Collection",
       
   152                 new CollectionCertStoreParameters(params.certificates())));
       
   153         } catch (InvalidAlgorithmParameterException |
       
   154                  NoSuchAlgorithmException e) {
       
   155             // should never occur but not necessarily fatal, so log it,
       
   156             // ignore and continue
       
   157             if (debug != null) {
       
   158                 debug.println("RevocationChecker: " +
       
   159                               "error creating Collection CertStore: " + e);
       
   160             }
       
   161         }
       
   162     }
       
   163 
       
   164     private static URI toURI(String uriString)
       
   165         throws CertPathValidatorException
       
   166     {
       
   167         try {
       
   168             if (uriString != null) {
       
   169                 return new URI(uriString);
       
   170             }
       
   171             return null;
       
   172         } catch (URISyntaxException e) {
       
   173             throw new CertPathValidatorException(
       
   174                 "cannot parse ocsp.responderURL property", e);
       
   175         }
       
   176     }
       
   177 
       
   178     private static RevocationProperties getRevocationProperties() {
       
   179         return AccessController.doPrivileged(
       
   180             new PrivilegedAction<RevocationProperties>() {
       
   181                 public RevocationProperties run() {
       
   182                     RevocationProperties rp = new RevocationProperties();
       
   183                     String onlyEE = Security.getProperty(
       
   184                         "com.sun.security.onlyCheckRevocationOfEECert");
       
   185                     rp.onlyEE = onlyEE != null
       
   186                                 && onlyEE.equalsIgnoreCase("true");
       
   187                     String ocspEnabled = Security.getProperty("ocsp.enable");
       
   188                     rp.ocspEnabled = ocspEnabled != null
       
   189                                      && ocspEnabled.equalsIgnoreCase("true");
       
   190                     rp.ocspUrl = Security.getProperty("ocsp.responderURL");
       
   191                     rp.ocspSubject
       
   192                         = Security.getProperty("ocsp.responderCertSubjectName");
       
   193                     rp.ocspIssuer
       
   194                         = Security.getProperty("ocsp.responderCertIssuerName");
       
   195                     rp.ocspSerial
       
   196                         = Security.getProperty("ocsp.responderCertSerialNumber");
       
   197                     rp.crlDPEnabled
       
   198                         = Boolean.getBoolean("com.sun.security.enableCRLDP");
       
   199                     return rp;
       
   200                 }
       
   201             }
       
   202         );
       
   203     }
       
   204 
       
   205     private static X509Certificate getResponderCert(RevocationProperties rp,
       
   206                                                     Set<TrustAnchor> anchors,
       
   207                                                     List<CertStore> stores)
       
   208         throws CertPathValidatorException
       
   209     {
       
   210         if (rp.ocspSubject != null) {
       
   211             return getResponderCert(rp.ocspSubject, anchors, stores);
       
   212         } else if (rp.ocspIssuer != null && rp.ocspSerial != null) {
       
   213             return getResponderCert(rp.ocspIssuer, rp.ocspSerial,
       
   214                                     anchors, stores);
       
   215         } else if (rp.ocspIssuer != null || rp.ocspSerial != null) {
       
   216             throw new CertPathValidatorException(
       
   217                 "Must specify both ocsp.responderCertIssuerName and " +
       
   218                 "ocsp.responderCertSerialNumber properties");
       
   219         }
       
   220         return null;
       
   221     }
       
   222 
       
   223     private static X509Certificate getResponderCert(String subject,
       
   224                                                     Set<TrustAnchor> anchors,
       
   225                                                     List<CertStore> stores)
       
   226         throws CertPathValidatorException
       
   227     {
       
   228         X509CertSelector sel = new X509CertSelector();
       
   229         try {
       
   230             sel.setSubject(new X500Principal(subject));
       
   231         } catch (IllegalArgumentException e) {
       
   232             throw new CertPathValidatorException(
       
   233                 "cannot parse ocsp.responderCertSubjectName property", e);
       
   234         }
       
   235         return getResponderCert(sel, anchors, stores);
       
   236     }
       
   237 
       
   238     private static X509Certificate getResponderCert(String issuer,
       
   239                                                     String serial,
       
   240                                                     Set<TrustAnchor> anchors,
       
   241                                                     List<CertStore> stores)
       
   242         throws CertPathValidatorException
       
   243     {
       
   244         X509CertSelector sel = new X509CertSelector();
       
   245         try {
       
   246             sel.setIssuer(new X500Principal(issuer));
       
   247         } catch (IllegalArgumentException e) {
       
   248             throw new CertPathValidatorException(
       
   249                 "cannot parse ocsp.responderCertIssuerName property", e);
       
   250         }
       
   251         try {
       
   252             sel.setSerialNumber(new BigInteger(stripOutSeparators(serial), 16));
       
   253         } catch (NumberFormatException e) {
       
   254             throw new CertPathValidatorException(
       
   255                 "cannot parse ocsp.responderCertSerialNumber property", e);
       
   256         }
       
   257         return getResponderCert(sel, anchors, stores);
       
   258     }
       
   259 
       
   260     private static X509Certificate getResponderCert(X509CertSelector sel,
       
   261                                                     Set<TrustAnchor> anchors,
       
   262                                                     List<CertStore> stores)
       
   263         throws CertPathValidatorException
       
   264     {
       
   265         // first check TrustAnchors
       
   266         for (TrustAnchor anchor : anchors) {
       
   267             X509Certificate cert = anchor.getTrustedCert();
       
   268             if (cert == null) {
       
   269                 continue;
       
   270             }
       
   271             if (sel.match(cert)) {
       
   272                 return cert;
       
   273             }
       
   274         }
       
   275         // now check CertStores
       
   276         for (CertStore store : stores) {
       
   277             try {
       
   278                 Collection<? extends Certificate> certs =
       
   279                     store.getCertificates(sel);
       
   280                 if (!certs.isEmpty()) {
       
   281                     return (X509Certificate)certs.iterator().next();
       
   282                 }
       
   283             } catch (CertStoreException e) {
       
   284                 // ignore and try next CertStore
       
   285                 if (debug != null) {
       
   286                     debug.println("CertStore exception:" + e);
       
   287                 }
       
   288                 continue;
       
   289             }
       
   290         }
       
   291         throw new CertPathValidatorException(
       
   292             "Cannot find the responder's certificate " +
       
   293             "(set using the OCSP security properties).");
       
   294     }
       
   295 
       
   296     @Override
       
   297     public void init(boolean forward) throws CertPathValidatorException {
       
   298         if (forward) {
       
   299             throw new
       
   300                 CertPathValidatorException("forward checking not supported");
       
   301         } else {
       
   302             if (anchor != null) {
       
   303                 issuerCert = anchor.getTrustedCert();
       
   304                 prevPubKey = (issuerCert != null) ? issuerCert.getPublicKey()
       
   305                                                   : anchor.getCAPublicKey();
       
   306             }
       
   307             crlSignFlag = true;
       
   308         }
       
   309     }
       
   310 
       
   311     @Override
       
   312     public boolean isForwardCheckingSupported() {
       
   313         return false;
       
   314     }
       
   315 
       
   316     @Override
       
   317     public Set<String> getSupportedExtensions() {
       
   318         return null;
       
   319     }
       
   320 
       
   321     @Override
       
   322     public void check(Certificate cert, Collection<String> unresolvedCritExts)
       
   323         throws CertPathValidatorException
       
   324     {
       
   325         X509Certificate xcert = (X509Certificate)cert;
       
   326         if (onlyEE && xcert.getBasicConstraints() != -1) {
       
   327             if (debug != null) {
       
   328                 debug.println("Skipping revocation check, not end entity cert");
       
   329             }
       
   330         } else {
       
   331             check(xcert, unresolvedCritExts, prevPubKey, crlSignFlag);
       
   332         }
       
   333         updateState(xcert);
       
   334     }
       
   335 
       
   336     void check(X509Certificate xcert, Collection<String> unresolvedCritExts,
       
   337                PublicKey pubKey, boolean crlSignFlag)
       
   338         throws CertPathValidatorException
       
   339     {
       
   340         try {
       
   341             switch (mode) {
       
   342                 case PREFER_OCSP:
       
   343                     checkOCSP(xcert, unresolvedCritExts);
       
   344                     break;
       
   345                 case PREFER_CRLS:
       
   346                 case ONLY_CRLS:
       
   347                     checkCRLs(xcert, unresolvedCritExts, null,
       
   348                               pubKey, crlSignFlag);
       
   349                     break;
       
   350             }
       
   351         } catch (CertPathValidatorException e) {
       
   352             if (e.getReason() == BasicReason.REVOKED) {
       
   353                 throw e;
       
   354             }
       
   355             CertPathValidatorException cause = e;
       
   356             if (softFail && e instanceof NetworkFailureException) {
       
   357                 if (mode == Mode.ONLY_CRLS) return;
       
   358             }
       
   359             // Rethrow the exception if ONLY_CRLS
       
   360             if (mode == Mode.ONLY_CRLS) {
       
   361                 throw e;
       
   362             }
       
   363             // Otherwise, failover
       
   364             if (debug != null) {
       
   365                 debug.println("RevocationChecker.check() " + e.getMessage());
       
   366                 debug.println("RevocationChecker.check() preparing to failover");
       
   367             }
       
   368             try {
       
   369                 switch (mode) {
       
   370                     case PREFER_OCSP:
       
   371                         checkCRLs(xcert, unresolvedCritExts, null,
       
   372                                   pubKey, crlSignFlag);
       
   373                         break;
       
   374                     case PREFER_CRLS:
       
   375                         checkOCSP(xcert, unresolvedCritExts);
       
   376                         break;
       
   377                 }
       
   378             } catch (CertPathValidatorException x) {
       
   379                 if (debug != null) {
       
   380                     debug.println("RevocationChecker.check() failover failed");
       
   381                     debug.println("RevocationChecker.check() " + x.getMessage());
       
   382                 }
       
   383                 if (x.getReason() == BasicReason.REVOKED) {
       
   384                     throw x;
       
   385                 }
       
   386                 if (cause != null) {
       
   387                     if (softFail && cause instanceof NetworkFailureException) {
       
   388                         return;
       
   389                     } else {
       
   390                         cause.addSuppressed(x);
       
   391                         throw cause;
       
   392                     }
       
   393                 }
       
   394                 if (softFail && x instanceof NetworkFailureException) {
       
   395                     return;
       
   396                 }
       
   397                 throw x;
       
   398             }
       
   399         }
       
   400     }
       
   401 
       
   402     private void updateState(X509Certificate cert)
       
   403         throws CertPathValidatorException
       
   404     {
       
   405         issuerCert = cert;
       
   406 
       
   407         // Make new public key if parameters are missing
       
   408         PublicKey pubKey = cert.getPublicKey();
       
   409         if (pubKey instanceof DSAPublicKey &&
       
   410             ((DSAPublicKey)pubKey).getParams() == null) {
       
   411             // pubKey needs to inherit DSA parameters from prev key
       
   412             pubKey = BasicChecker.makeInheritedParamsKey(pubKey, prevPubKey);
       
   413         }
       
   414         prevPubKey = pubKey;
       
   415         crlSignFlag = certCanSignCrl(cert);
       
   416     }
       
   417 
       
   418     // Maximum clock skew in milliseconds (15 minutes) allowed when checking
       
   419     // validity of CRLs
       
   420     private static final long MAX_CLOCK_SKEW = 900000;
       
   421     private void checkCRLs(X509Certificate cert,
       
   422                            Collection<String> unresolvedCritExts,
       
   423                            Set<X509Certificate> stackedCerts,
       
   424                            PublicKey pubKey, boolean signFlag)
       
   425         throws CertPathValidatorException
       
   426     {
       
   427         checkCRLs(cert, pubKey, signFlag, true,
       
   428                   stackedCerts, params.trustAnchors());
       
   429     }
       
   430 
       
   431     private void checkCRLs(X509Certificate cert, PublicKey prevKey,
       
   432                            boolean signFlag, boolean allowSeparateKey,
       
   433                            Set<X509Certificate> stackedCerts,
       
   434                            Set<TrustAnchor> anchors)
       
   435         throws CertPathValidatorException
       
   436     {
       
   437         if (debug != null) {
       
   438             debug.println("RevocationChecker.checkCRLs()" +
       
   439                           " ---checking revocation status ...");
       
   440         }
       
   441 
       
   442         // reject circular dependencies - RFC 3280 is not explicit on how
       
   443         // to handle this, so we feel it is safest to reject them until
       
   444         // the issue is resolved in the PKIX WG.
       
   445         if (stackedCerts != null && stackedCerts.contains(cert)) {
       
   446             if (debug != null) {
       
   447                 debug.println("RevocationChecker.checkCRLs()" +
       
   448                               " circular dependency");
       
   449             }
       
   450             throw new CertPathValidatorException
       
   451                 ("Could not determine revocation status", null, null, -1,
       
   452                  BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
   453         }
       
   454 
       
   455         Set<X509CRL> possibleCRLs = new HashSet<>();
       
   456         Set<X509CRL> approvedCRLs = new HashSet<>();
       
   457         X509CRLSelector sel = new X509CRLSelector();
       
   458         sel.setCertificateChecking(cert);
       
   459         CertPathHelper.setDateAndTime(sel, params.date(), MAX_CLOCK_SKEW);
       
   460 
       
   461         // First, check cached CRLs
       
   462         for (CertStore store : certStores) {
       
   463             try {
       
   464                 for (CRL crl : store.getCRLs(sel)) {
       
   465                     possibleCRLs.add((X509CRL)crl);
       
   466                 }
       
   467             } catch (CertStoreException e) {
       
   468                 // XXX ignore?
       
   469             }
       
   470         }
       
   471 
       
   472         if (debug != null) {
       
   473             debug.println("RevocationChecker.checkCRLs() " +
       
   474                           "possible crls.size() = " + possibleCRLs.size());
       
   475         }
       
   476         boolean[] reasonsMask = new boolean[9];
       
   477         if (!possibleCRLs.isEmpty()) {
       
   478             // Now that we have a list of possible CRLs, see which ones can
       
   479             // be approved
       
   480             approvedCRLs.addAll(verifyPossibleCRLs(possibleCRLs, cert, prevKey,
       
   481                                                    signFlag, reasonsMask,
       
   482                                                    anchors));
       
   483         }
       
   484 
       
   485         if (debug != null) {
       
   486             debug.println("RevocationChecker.checkCRLs() " +
       
   487                           "approved crls.size() = " + approvedCRLs.size());
       
   488         }
       
   489 
       
   490         // make sure that we have at least one CRL that _could_ cover
       
   491         // the certificate in question and all reasons are covered
       
   492         if (!approvedCRLs.isEmpty() &&
       
   493             Arrays.equals(reasonsMask, ALL_REASONS))
       
   494         {
       
   495             checkApprovedCRLs(cert, approvedCRLs);
       
   496         } else {
       
   497             // Check Distribution Points
       
   498             // all CRLs returned by the DP Fetcher have also been verified
       
   499             try {
       
   500                 if (crlDP) {
       
   501                     approvedCRLs.addAll(DistributionPointFetcher.getCRLs(
       
   502                                         sel, signFlag, prevKey,
       
   503                                         params.sigProvider(), certStores,
       
   504                                         reasonsMask, anchors, params.date()));
       
   505                 }
       
   506             } catch (CertStoreException e) {
       
   507                 if (debug != null) {
       
   508                     debug.println("RevocationChecker.checkCRLs() " +
       
   509                                   "unexpected exception: " + e.getMessage());
       
   510                 }
       
   511                 throw new CertPathValidatorException(e);
       
   512             }
       
   513             if (!approvedCRLs.isEmpty() &&
       
   514                 Arrays.equals(reasonsMask, ALL_REASONS))
       
   515             {
       
   516                 checkApprovedCRLs(cert, approvedCRLs);
       
   517             } else {
       
   518                 if (allowSeparateKey) {
       
   519                     verifyWithSeparateSigningKey(cert, prevKey, signFlag,
       
   520                                                  stackedCerts);
       
   521                     return;
       
   522                 } else {
       
   523                     throw new CertPathValidatorException
       
   524                     ("Could not determine revocation status", null, null, -1,
       
   525                      BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
   526                 }
       
   527             }
       
   528         }
       
   529     }
       
   530 
       
   531     private void checkApprovedCRLs(X509Certificate cert,
       
   532                                    Set<X509CRL> approvedCRLs)
       
   533         throws CertPathValidatorException
       
   534     {
       
   535         // See if the cert is in the set of approved crls.
       
   536         if (debug != null) {
       
   537             BigInteger sn = cert.getSerialNumber();
       
   538             debug.println("RevocationChecker.checkApprovedCRLs() " +
       
   539                           "starting the final sweep...");
       
   540             debug.println("RevocationChecker.checkApprovedCRLs()" +
       
   541                           " cert SN: " + sn.toString());
       
   542         }
       
   543 
       
   544         CRLReason reasonCode = CRLReason.UNSPECIFIED;
       
   545         X509CRLEntryImpl entry = null;
       
   546         for (X509CRL crl : approvedCRLs) {
       
   547             X509CRLEntry e = crl.getRevokedCertificate(cert);
       
   548             if (e != null) {
       
   549                 try {
       
   550                     entry = X509CRLEntryImpl.toImpl(e);
       
   551                 } catch (CRLException ce) {
       
   552                     throw new CertPathValidatorException(ce);
       
   553                 }
       
   554                 if (debug != null) {
       
   555                     debug.println("RevocationChecker.checkApprovedCRLs()"
       
   556                         + " CRL entry: " + entry.toString());
       
   557                 }
       
   558 
       
   559                 /*
       
   560                  * Abort CRL validation and throw exception if there are any
       
   561                  * unrecognized critical CRL entry extensions (see section
       
   562                  * 5.3 of RFC 3280).
       
   563                  */
       
   564                 Set<String> unresCritExts = entry.getCriticalExtensionOIDs();
       
   565                 if (unresCritExts != null && !unresCritExts.isEmpty()) {
       
   566                     /* remove any that we will process */
       
   567                     unresCritExts.remove(ReasonCode_Id.toString());
       
   568                     unresCritExts.remove(CertificateIssuer_Id.toString());
       
   569                     if (!unresCritExts.isEmpty()) {
       
   570                         if (debug != null) {
       
   571                             debug.println("Unrecognized "
       
   572                             + "critical extension(s) in revoked CRL entry: "
       
   573                             + unresCritExts);
       
   574                         }
       
   575                         throw new CertPathValidatorException
       
   576                         ("Could not determine revocation status", null, null,
       
   577                          -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
   578                     }
       
   579                 }
       
   580 
       
   581                 reasonCode = entry.getRevocationReason();
       
   582                 if (reasonCode == null) {
       
   583                     reasonCode = CRLReason.UNSPECIFIED;
       
   584                 }
       
   585                 Throwable t = new CertificateRevokedException
       
   586                     (entry.getRevocationDate(), reasonCode,
       
   587                      crl.getIssuerX500Principal(), entry.getExtensions());
       
   588                 throw new CertPathValidatorException(t.getMessage(), t,
       
   589                     null, -1, BasicReason.REVOKED);
       
   590             }
       
   591         }
       
   592     }
       
   593 
       
   594     private void checkOCSP(X509Certificate cert,
       
   595                            Collection<String> unresolvedCritExts)
       
   596         throws CertPathValidatorException
       
   597     {
       
   598         X509CertImpl currCert = null;
       
   599         try {
       
   600             currCert = X509CertImpl.toImpl(cert);
       
   601         } catch (CertificateException ce) {
       
   602             throw new CertPathValidatorException(ce);
       
   603         }
       
   604 
       
   605         URI responderURI = (this.responderURI != null)
       
   606                            ? this.responderURI : getOCSPServerURI(currCert);
       
   607 
       
   608         X509Certificate respCert = (responderCert == null) ? issuerCert
       
   609                                                            : responderCert;
       
   610 
       
   611         // The algorithm constraints of the OCSP trusted responder certificate
       
   612         // does not need to be checked in this code. The constraints will be
       
   613         // checked when the responder's certificate is validated.
       
   614 
       
   615         OCSPResponse response = null;
       
   616         CertId certId = null;
       
   617         try {
       
   618             certId = new CertId(issuerCert, currCert.getSerialNumberObject());
       
   619 
       
   620             // check if there is a stapled OCSP response available
       
   621             byte[] responseBytes = ocspStapled.get(cert);
       
   622             if (responseBytes != null) {
       
   623                 if (debug != null) {
       
   624                     debug.println("Found stapled OCSP response");
       
   625                 }
       
   626                 response = new OCSPResponse(responseBytes);
       
   627 
       
   628                 // verify the response
       
   629                 byte[] nonce = null;
       
   630                 for (Extension ext : ocspExtensions) {
       
   631                     if (ext.getId().equals("1.3.6.1.5.5.7.48.1.2")) {
       
   632                         nonce = ext.getValue();
       
   633                     }
       
   634                 }
       
   635                 response.verify(Collections.singletonList(certId), respCert,
       
   636                                 params.date(), nonce);
       
   637 
       
   638             } else {
       
   639                 response = OCSP.check(Collections.singletonList(certId),
       
   640                                       responderURI, respCert, params.date(),
       
   641                                       ocspExtensions);
       
   642             }
       
   643         } catch (IOException e) {
       
   644             throw new CertPathValidatorException(e);
       
   645         }
       
   646 
       
   647         RevocationStatus rs =
       
   648             (RevocationStatus)response.getSingleResponse(certId);
       
   649         RevocationStatus.CertStatus certStatus = rs.getCertStatus();
       
   650         if (certStatus == RevocationStatus.CertStatus.REVOKED) {
       
   651             Throwable t = new CertificateRevokedException(
       
   652                 rs.getRevocationTime(), rs.getRevocationReason(),
       
   653                 respCert.getSubjectX500Principal(), rs.getSingleExtensions());
       
   654             throw new CertPathValidatorException(t.getMessage(), t, null,
       
   655                                                  -1, BasicReason.REVOKED);
       
   656         } else if (certStatus == RevocationStatus.CertStatus.UNKNOWN) {
       
   657             throw new CertPathValidatorException(
       
   658                 "Certificate's revocation status is unknown", null,
       
   659                 params.certPath(), -1,
       
   660                 BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
   661         }
       
   662     }
       
   663 
       
   664     /*
       
   665      * Removes any non-hexadecimal characters from a string.
       
   666      */
       
   667     private static final String HEX_DIGITS = "0123456789ABCDEFabcdef";
       
   668     private static String stripOutSeparators(String value) {
       
   669         char[] chars = value.toCharArray();
       
   670         StringBuilder hexNumber = new StringBuilder();
       
   671         for (int i = 0; i < chars.length; i++) {
       
   672             if (HEX_DIGITS.indexOf(chars[i]) != -1) {
       
   673                 hexNumber.append(chars[i]);
       
   674             }
       
   675         }
       
   676         return hexNumber.toString();
       
   677     }
       
   678 
       
   679     private static URI getOCSPServerURI(X509CertImpl cert)
       
   680         throws CertPathValidatorException
       
   681     {
       
   682         // Examine the certificate's AuthorityInfoAccess extension
       
   683         AuthorityInfoAccessExtension aia =
       
   684             cert.getAuthorityInfoAccessExtension();
       
   685         if (aia == null) {
       
   686             throw new CertPathValidatorException(
       
   687                 "Must specify the location of an OCSP Responder");
       
   688         }
       
   689 
       
   690         List<AccessDescription> descriptions = aia.getAccessDescriptions();
       
   691         for (AccessDescription description : descriptions) {
       
   692             if (description.getAccessMethod().equals((Object)
       
   693                 AccessDescription.Ad_OCSP_Id)) {
       
   694 
       
   695                 GeneralName generalName = description.getAccessLocation();
       
   696                 if (generalName.getType() == GeneralNameInterface.NAME_URI) {
       
   697                     URIName uri = (URIName)generalName.getName();
       
   698                     return uri.getURI();
       
   699                 }
       
   700             }
       
   701         }
       
   702 
       
   703         throw new CertPathValidatorException(
       
   704             "Cannot find the location of the OCSP Responder");
       
   705     }
       
   706 
       
   707     /**
       
   708      * Checks that a cert can be used to verify a CRL.
       
   709      *
       
   710      * @param cert an X509Certificate to check
       
   711      * @return a boolean specifying if the cert is allowed to vouch for the
       
   712      *         validity of a CRL
       
   713      */
       
   714     static boolean certCanSignCrl(X509Certificate cert) {
       
   715         // if the cert doesn't include the key usage ext, or
       
   716         // the key usage ext asserts cRLSigning, return true,
       
   717         // otherwise return false.
       
   718         boolean[] keyUsage = cert.getKeyUsage();
       
   719         if (keyUsage != null) {
       
   720             return keyUsage[6];
       
   721         }
       
   722         return false;
       
   723     }
       
   724 
       
   725     /**
       
   726      * Internal method that verifies a set of possible_crls,
       
   727      * and sees if each is approved, based on the cert.
       
   728      *
       
   729      * @param crls a set of possible CRLs to test for acceptability
       
   730      * @param cert the certificate whose revocation status is being checked
       
   731      * @param signFlag <code>true</code> if prevKey was trusted to sign CRLs
       
   732      * @param prevKey the public key of the issuer of cert
       
   733      * @param reasonsMask the reason code mask
       
   734      * @param trustAnchors a <code>Set</code> of <code>TrustAnchor</code>s>
       
   735      * @return a collection of approved crls (or an empty collection)
       
   736      */
       
   737     private static final boolean[] ALL_REASONS =
       
   738         {true, true, true, true, true, true, true, true, true};
       
   739     private Collection<X509CRL> verifyPossibleCRLs(Set<X509CRL> crls,
       
   740                                                    X509Certificate cert,
       
   741                                                    PublicKey prevKey,
       
   742                                                    boolean signFlag,
       
   743                                                    boolean[] reasonsMask,
       
   744                                                    Set<TrustAnchor> anchors)
       
   745         throws CertPathValidatorException
       
   746     {
       
   747         try {
       
   748             X509CertImpl certImpl = X509CertImpl.toImpl(cert);
       
   749             if (debug != null) {
       
   750                 debug.println("RevocationChecker.verifyPossibleCRLs: " +
       
   751                               "Checking CRLDPs for "
       
   752                               + certImpl.getSubjectX500Principal());
       
   753             }
       
   754             CRLDistributionPointsExtension ext =
       
   755                 certImpl.getCRLDistributionPointsExtension();
       
   756             List<DistributionPoint> points = null;
       
   757             if (ext == null) {
       
   758                 // assume a DP with reasons and CRLIssuer fields omitted
       
   759                 // and a DP name of the cert issuer.
       
   760                 // TODO add issuerAltName too
       
   761                 X500Name certIssuer = (X500Name)certImpl.getIssuerDN();
       
   762                 DistributionPoint point = new DistributionPoint(
       
   763                      new GeneralNames().add(new GeneralName(certIssuer)),
       
   764                      null, null);
       
   765                 points = Collections.singletonList(point);
       
   766             } else {
       
   767                 points = ext.get(CRLDistributionPointsExtension.POINTS);
       
   768             }
       
   769             Set<X509CRL> results = new HashSet<>();
       
   770             for (DistributionPoint point : points) {
       
   771                 for (X509CRL crl : crls) {
       
   772                     if (DistributionPointFetcher.verifyCRL(
       
   773                             certImpl, point, crl, reasonsMask, signFlag,
       
   774                             prevKey, params.sigProvider(), anchors,
       
   775                             certStores, params.date()))
       
   776                     {
       
   777                         results.add(crl);
       
   778                     }
       
   779                 }
       
   780                 if (Arrays.equals(reasonsMask, ALL_REASONS))
       
   781                     break;
       
   782             }
       
   783             return results;
       
   784         } catch (CertificateException | CRLException | IOException e) {
       
   785             if (debug != null) {
       
   786                 debug.println("Exception while verifying CRL: "+e.getMessage());
       
   787                 e.printStackTrace();
       
   788             }
       
   789             return Collections.emptySet();
       
   790         }
       
   791     }
       
   792 
       
   793     /**
       
   794      * We have a cert whose revocation status couldn't be verified by
       
   795      * a CRL issued by the cert that issued the CRL. See if we can
       
   796      * find a valid CRL issued by a separate key that can verify the
       
   797      * revocation status of this certificate.
       
   798      * <p>
       
   799      * Note that this does not provide support for indirect CRLs,
       
   800      * only CRLs signed with a different key (but the same issuer
       
   801      * name) as the certificate being checked.
       
   802      *
       
   803      * @param currCert the <code>X509Certificate</code> to be checked
       
   804      * @param prevKey the <code>PublicKey</code> that failed
       
   805      * @param signFlag <code>true</code> if that key was trusted to sign CRLs
       
   806      * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
       
   807      *                     whose revocation status depends on the
       
   808      *                     non-revoked status of this cert. To avoid
       
   809      *                     circular dependencies, we assume they're
       
   810      *                     revoked while checking the revocation
       
   811      *                     status of this cert.
       
   812      * @throws CertPathValidatorException if the cert's revocation status
       
   813      *         cannot be verified successfully with another key
       
   814      */
       
   815     private void verifyWithSeparateSigningKey(X509Certificate cert,
       
   816                                               PublicKey prevKey,
       
   817                                               boolean signFlag,
       
   818                                               Set<X509Certificate> stackedCerts)
       
   819         throws CertPathValidatorException
       
   820     {
       
   821         String msg = "revocation status";
       
   822         if (debug != null) {
       
   823             debug.println(
       
   824                 "RevocationChecker.verifyWithSeparateSigningKey()" +
       
   825                 " ---checking " + msg + "...");
       
   826         }
       
   827 
       
   828         // reject circular dependencies - RFC 3280 is not explicit on how
       
   829         // to handle this, so we feel it is safest to reject them until
       
   830         // the issue is resolved in the PKIX WG.
       
   831         if ((stackedCerts != null) && stackedCerts.contains(cert)) {
       
   832             if (debug != null) {
       
   833                 debug.println(
       
   834                     "RevocationChecker.verifyWithSeparateSigningKey()" +
       
   835                     " circular dependency");
       
   836             }
       
   837             throw new CertPathValidatorException
       
   838                 ("Could not determine revocation status", null, null,
       
   839                  -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
   840         }
       
   841 
       
   842         // Try to find another key that might be able to sign
       
   843         // CRLs vouching for this cert.
       
   844         // If prevKey wasn't trusted, maybe we just didn't have the right
       
   845         // path to it. Don't rule that key out.
       
   846         if (!signFlag) {
       
   847             buildToNewKey(cert, null, stackedCerts);
       
   848         } else {
       
   849             buildToNewKey(cert, prevKey, stackedCerts);
       
   850         }
       
   851     }
       
   852 
       
   853     /**
       
   854      * Tries to find a CertPath that establishes a key that can be
       
   855      * used to verify the revocation status of a given certificate.
       
   856      * Ignores keys that have previously been tried. Throws a
       
   857      * CertPathValidatorException if no such key could be found.
       
   858      *
       
   859      * @param currCert the <code>X509Certificate</code> to be checked
       
   860      * @param prevKey the <code>PublicKey</code> of the certificate whose key
       
   861      *    cannot be used to vouch for the CRL and should be ignored
       
   862      * @param stackedCerts a <code>Set</code> of <code>X509Certificate</code>s>
       
   863      *                     whose revocation status depends on the
       
   864      *                     establishment of this path.
       
   865      * @throws CertPathValidatorException on failure
       
   866      */
       
   867     private static final boolean [] CRL_SIGN_USAGE =
       
   868         { false, false, false, false, false, false, true };
       
   869     private void buildToNewKey(X509Certificate currCert,
       
   870                                PublicKey prevKey,
       
   871                                Set<X509Certificate> stackedCerts)
       
   872         throws CertPathValidatorException
       
   873     {
       
   874 
       
   875         if (debug != null) {
       
   876             debug.println("RevocationChecker.buildToNewKey()" +
       
   877                           " starting work");
       
   878         }
       
   879         Set<PublicKey> badKeys = new HashSet<>();
       
   880         if (prevKey != null) {
       
   881             badKeys.add(prevKey);
       
   882         }
       
   883         X509CertSelector certSel = new RejectKeySelector(badKeys);
       
   884         certSel.setSubject(currCert.getIssuerX500Principal());
       
   885         certSel.setKeyUsage(CRL_SIGN_USAGE);
       
   886 
       
   887         Set<TrustAnchor> newAnchors = anchor == null ?
       
   888                                       params.trustAnchors() :
       
   889                                       Collections.singleton(anchor);
       
   890 
       
   891         PKIXBuilderParameters builderParams;
       
   892         try {
       
   893             builderParams = new PKIXBuilderParameters(newAnchors, certSel);
       
   894         } catch (InvalidAlgorithmParameterException iape) {
       
   895             throw new RuntimeException(iape); // should never occur
       
   896         }
       
   897         builderParams.setInitialPolicies(params.initialPolicies());
       
   898         builderParams.setCertStores(certStores);
       
   899         builderParams.setExplicitPolicyRequired
       
   900             (params.explicitPolicyRequired());
       
   901         builderParams.setPolicyMappingInhibited
       
   902             (params.policyMappingInhibited());
       
   903         builderParams.setAnyPolicyInhibited(params.anyPolicyInhibited());
       
   904         // Policy qualifiers must be rejected, since we don't have
       
   905         // any way to convey them back to the application.
       
   906         // That's the default, so no need to write code.
       
   907         builderParams.setDate(params.date());
       
   908         // CertPathCheckers need to be cloned to start from fresh state
       
   909         builderParams.setCertPathCheckers(
       
   910             params.getPKIXParameters().getCertPathCheckers());
       
   911         builderParams.setSigProvider(params.sigProvider());
       
   912 
       
   913         // Skip revocation during this build to detect circular
       
   914         // references. But check revocation afterwards, using the
       
   915         // key (or any other that works).
       
   916         builderParams.setRevocationEnabled(false);
       
   917 
       
   918         // check for AuthorityInformationAccess extension
       
   919         if (Builder.USE_AIA == true) {
       
   920             X509CertImpl currCertImpl = null;
       
   921             try {
       
   922                 currCertImpl = X509CertImpl.toImpl(currCert);
       
   923             } catch (CertificateException ce) {
       
   924                 // ignore but log it
       
   925                 if (debug != null) {
       
   926                     debug.println("RevocationChecker.buildToNewKey: " +
       
   927                                   "error decoding cert: " + ce);
       
   928                 }
       
   929             }
       
   930             AuthorityInfoAccessExtension aiaExt = null;
       
   931             if (currCertImpl != null) {
       
   932                 aiaExt = currCertImpl.getAuthorityInfoAccessExtension();
       
   933             }
       
   934             if (aiaExt != null) {
       
   935                 List<AccessDescription> adList = aiaExt.getAccessDescriptions();
       
   936                 if (adList != null) {
       
   937                     for (AccessDescription ad : adList) {
       
   938                         CertStore cs = URICertStore.getInstance(ad);
       
   939                         if (cs != null) {
       
   940                             if (debug != null) {
       
   941                                 debug.println("adding AIAext CertStore");
       
   942                             }
       
   943                             builderParams.addCertStore(cs);
       
   944                         }
       
   945                     }
       
   946                 }
       
   947             }
       
   948         }
       
   949 
       
   950         CertPathBuilder builder = null;
       
   951         try {
       
   952             builder = CertPathBuilder.getInstance("PKIX");
       
   953         } catch (NoSuchAlgorithmException nsae) {
       
   954             throw new CertPathValidatorException(nsae);
       
   955         }
       
   956         while (true) {
       
   957             try {
       
   958                 if (debug != null) {
       
   959                     debug.println("RevocationChecker.buildToNewKey()" +
       
   960                                   " about to try build ...");
       
   961                 }
       
   962                 PKIXCertPathBuilderResult cpbr =
       
   963                     (PKIXCertPathBuilderResult)builder.build(builderParams);
       
   964 
       
   965                 if (debug != null) {
       
   966                     debug.println("RevocationChecker.buildToNewKey()" +
       
   967                                   " about to check revocation ...");
       
   968                 }
       
   969                 // Now check revocation of all certs in path, assuming that
       
   970                 // the stackedCerts are revoked.
       
   971                 if (stackedCerts == null) {
       
   972                     stackedCerts = new HashSet<X509Certificate>();
       
   973                 }
       
   974                 stackedCerts.add(currCert);
       
   975                 TrustAnchor ta = cpbr.getTrustAnchor();
       
   976                 PublicKey prevKey2 = ta.getCAPublicKey();
       
   977                 if (prevKey2 == null) {
       
   978                     prevKey2 = ta.getTrustedCert().getPublicKey();
       
   979                 }
       
   980                 boolean signFlag = true;
       
   981                 List<? extends Certificate> cpList =
       
   982                     cpbr.getCertPath().getCertificates();
       
   983                 try {
       
   984                     for (int i = cpList.size()-1; i >= 0; i-- ) {
       
   985                         X509Certificate cert = (X509Certificate)cpList.get(i);
       
   986 
       
   987                         if (debug != null) {
       
   988                             debug.println("RevocationChecker.buildToNewKey()"
       
   989                                           + " index " + i + " checking "
       
   990                                           + cert);
       
   991                         }
       
   992                         checkCRLs(cert, prevKey2, signFlag, true,
       
   993                                   stackedCerts, newAnchors);
       
   994                         signFlag = certCanSignCrl(cert);
       
   995                         prevKey2 = cert.getPublicKey();
       
   996                     }
       
   997                 } catch (CertPathValidatorException cpve) {
       
   998                     // ignore it and try to get another key
       
   999                     badKeys.add(cpbr.getPublicKey());
       
  1000                     continue;
       
  1001                 }
       
  1002 
       
  1003                 if (debug != null) {
       
  1004                     debug.println("RevocationChecker.buildToNewKey()" +
       
  1005                                   " got key " + cpbr.getPublicKey());
       
  1006                 }
       
  1007                 // Now check revocation on the current cert using that key.
       
  1008                 // If it doesn't check out, try to find a different key.
       
  1009                 // And if we can't find a key, then return false.
       
  1010                 PublicKey newKey = cpbr.getPublicKey();
       
  1011                 try {
       
  1012                     checkCRLs(currCert, newKey, true, false, null,
       
  1013                               params.trustAnchors());
       
  1014                     // If that passed, the cert is OK!
       
  1015                     return;
       
  1016                 } catch (CertPathValidatorException cpve) {
       
  1017                     // If it is revoked, rethrow exception
       
  1018                     if (cpve.getReason() == BasicReason.REVOKED) {
       
  1019                         throw cpve;
       
  1020                     }
       
  1021                     // Otherwise, ignore the exception and
       
  1022                     // try to get another key.
       
  1023                 }
       
  1024                 badKeys.add(newKey);
       
  1025             } catch (InvalidAlgorithmParameterException iape) {
       
  1026                 throw new CertPathValidatorException(iape);
       
  1027             } catch (CertPathBuilderException cpbe) {
       
  1028                 throw new CertPathValidatorException
       
  1029                     ("Could not determine revocation status", null, null,
       
  1030                      -1, BasicReason.UNDETERMINED_REVOCATION_STATUS);
       
  1031             }
       
  1032         }
       
  1033     }
       
  1034 
       
  1035     /*
       
  1036      * This inner class extends the X509CertSelector to add an additional
       
  1037      * check to make sure the subject public key isn't on a particular list.
       
  1038      * This class is used by buildToNewKey() to make sure the builder doesn't
       
  1039      * end up with a CertPath to a public key that has already been rejected.
       
  1040      */
       
  1041     private static class RejectKeySelector extends X509CertSelector {
       
  1042         private final Set<PublicKey> badKeySet;
       
  1043 
       
  1044         /**
       
  1045          * Creates a new <code>RejectKeySelector</code>.
       
  1046          *
       
  1047          * @param badPublicKeys a <code>Set</code> of
       
  1048          *                      <code>PublicKey</code>s that
       
  1049          *                      should be rejected (or <code>null</code>
       
  1050          *                      if no such check should be done)
       
  1051          */
       
  1052         RejectKeySelector(Set<PublicKey> badPublicKeys) {
       
  1053             this.badKeySet = badPublicKeys;
       
  1054         }
       
  1055 
       
  1056         /**
       
  1057          * Decides whether a <code>Certificate</code> should be selected.
       
  1058          *
       
  1059          * @param cert the <code>Certificate</code> to be checked
       
  1060          * @return <code>true</code> if the <code>Certificate</code> should be
       
  1061          *         selected, <code>false</code> otherwise
       
  1062          */
       
  1063         @Override
       
  1064         public boolean match(Certificate cert) {
       
  1065             if (!super.match(cert))
       
  1066                 return(false);
       
  1067 
       
  1068             if (badKeySet.contains(cert.getPublicKey())) {
       
  1069                 if (debug != null)
       
  1070                     debug.println("RejectKeySelector.match: bad key");
       
  1071                 return false;
       
  1072             }
       
  1073 
       
  1074             if (debug != null)
       
  1075                 debug.println("RejectKeySelector.match: returning true");
       
  1076             return true;
       
  1077         }
       
  1078 
       
  1079         /**
       
  1080          * Return a printable representation of the <code>CertSelector</code>.
       
  1081          *
       
  1082          * @return a <code>String</code> describing the contents of the
       
  1083          *         <code>CertSelector</code>
       
  1084          */
       
  1085         @Override
       
  1086         public String toString() {
       
  1087             StringBuilder sb = new StringBuilder();
       
  1088             sb.append("RejectKeySelector: [\n");
       
  1089             sb.append(super.toString());
       
  1090             sb.append(badKeySet);
       
  1091             sb.append("]");
       
  1092             return sb.toString();
       
  1093         }
       
  1094     }
       
  1095 }