jdk/src/java.base/share/classes/sun/security/provider/certpath/ReverseBuilder.java
changeset 29991 de68c8dd1de2
parent 29990 27f6a088fa6a
parent 29989 b427ddb99dba
child 29997 dd08b1d9f43f
equal deleted inserted replaced
29990:27f6a088fa6a 29991:de68c8dd1de2
     1 /*
       
     2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.provider.certpath;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.security.GeneralSecurityException;
       
    30 import java.security.Principal;
       
    31 import java.security.cert.CertificateException;
       
    32 import java.security.cert.CertPathValidatorException;
       
    33 import java.security.cert.CertStore;
       
    34 import java.security.cert.CertStoreException;
       
    35 import java.security.cert.PKIXBuilderParameters;
       
    36 import java.security.cert.PKIXCertPathChecker;
       
    37 import java.security.cert.PKIXParameters;
       
    38 import java.security.cert.PKIXReason;
       
    39 import java.security.cert.TrustAnchor;
       
    40 import java.security.cert.X509Certificate;
       
    41 import java.security.cert.X509CertSelector;
       
    42 import java.util.ArrayList;
       
    43 import java.util.Collection;
       
    44 import java.util.Collections;
       
    45 import java.util.Comparator;
       
    46 import java.util.HashSet;
       
    47 import java.util.Iterator;
       
    48 import java.util.List;
       
    49 import java.util.LinkedList;
       
    50 import java.util.Set;
       
    51 
       
    52 import javax.security.auth.x500.X500Principal;
       
    53 
       
    54 import sun.security.provider.certpath.PKIX.BuilderParams;
       
    55 import sun.security.util.Debug;
       
    56 import sun.security.x509.Extension;
       
    57 import static sun.security.x509.PKIXExtensions.*;
       
    58 import sun.security.x509.X500Name;
       
    59 import sun.security.x509.X509CertImpl;
       
    60 import sun.security.x509.PolicyMappingsExtension;
       
    61 
       
    62 /**
       
    63  * This class represents a reverse builder, which is able to retrieve
       
    64  * matching certificates from CertStores and verify a particular certificate
       
    65  * against a ReverseState.
       
    66  *
       
    67  * @since       1.4
       
    68  * @author      Sean Mullan
       
    69  * @author      Yassir Elley
       
    70  */
       
    71 
       
    72 class ReverseBuilder extends Builder {
       
    73 
       
    74     private Debug debug = Debug.getInstance("certpath");
       
    75 
       
    76     private final Set<String> initPolicies;
       
    77 
       
    78     /**
       
    79      * Initialize the builder with the input parameters.
       
    80      *
       
    81      * @param params the parameter set used to build a certification path
       
    82      */
       
    83     ReverseBuilder(BuilderParams buildParams) {
       
    84         super(buildParams);
       
    85 
       
    86         Set<String> initialPolicies = buildParams.initialPolicies();
       
    87         initPolicies = new HashSet<String>();
       
    88         if (initialPolicies.isEmpty()) {
       
    89             // if no initialPolicies are specified by user, set
       
    90             // initPolicies to be anyPolicy by default
       
    91             initPolicies.add(PolicyChecker.ANY_POLICY);
       
    92         } else {
       
    93             initPolicies.addAll(initialPolicies);
       
    94         }
       
    95     }
       
    96 
       
    97     /**
       
    98      * Retrieves all certs from the specified CertStores that satisfy the
       
    99      * requirements specified in the parameters and the current
       
   100      * PKIX state (name constraints, policy constraints, etc).
       
   101      *
       
   102      * @param currentState the current state.
       
   103      *        Must be an instance of <code>ReverseState</code>
       
   104      * @param certStores list of CertStores
       
   105      */
       
   106     @Override
       
   107     Collection<X509Certificate> getMatchingCerts
       
   108         (State currState, List<CertStore> certStores)
       
   109         throws CertStoreException, CertificateException, IOException
       
   110     {
       
   111         ReverseState currentState = (ReverseState) currState;
       
   112 
       
   113         if (debug != null)
       
   114             debug.println("In ReverseBuilder.getMatchingCerts.");
       
   115 
       
   116         /*
       
   117          * The last certificate could be an EE or a CA certificate
       
   118          * (we may be building a partial certification path or
       
   119          * establishing trust in a CA).
       
   120          *
       
   121          * Try the EE certs before the CA certs. It will be more
       
   122          * common to build a path to an end entity.
       
   123          */
       
   124         Collection<X509Certificate> certs =
       
   125             getMatchingEECerts(currentState, certStores);
       
   126         certs.addAll(getMatchingCACerts(currentState, certStores));
       
   127 
       
   128         return certs;
       
   129     }
       
   130 
       
   131     /*
       
   132      * Retrieves all end-entity certificates which satisfy constraints
       
   133      * and requirements specified in the parameters and PKIX state.
       
   134      */
       
   135     private Collection<X509Certificate> getMatchingEECerts
       
   136         (ReverseState currentState, List<CertStore> certStores)
       
   137         throws CertStoreException, CertificateException, IOException {
       
   138 
       
   139         /*
       
   140          * Compose a CertSelector to filter out
       
   141          * certs which do not satisfy requirements.
       
   142          *
       
   143          * First, retrieve clone of current target cert constraints, and
       
   144          * then add more selection criteria based on current validation state.
       
   145          */
       
   146         X509CertSelector sel = (X509CertSelector) targetCertConstraints.clone();
       
   147 
       
   148         /*
       
   149          * Match on issuer (subject of previous cert)
       
   150          */
       
   151         sel.setIssuer(currentState.subjectDN);
       
   152 
       
   153         /*
       
   154          * Match on certificate validity date.
       
   155          */
       
   156         sel.setCertificateValid(buildParams.date());
       
   157 
       
   158         /*
       
   159          * Policy processing optimizations
       
   160          */
       
   161         if (currentState.explicitPolicy == 0)
       
   162             sel.setPolicy(getMatchingPolicies());
       
   163 
       
   164         /*
       
   165          * If previous cert has a subject key identifier extension,
       
   166          * use it to match on authority key identifier extension.
       
   167          */
       
   168         /*if (currentState.subjKeyId != null) {
       
   169           AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
       
   170                 (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
       
   171                 null, null);
       
   172         sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
       
   173         }*/
       
   174 
       
   175         /*
       
   176          * Require EE certs
       
   177          */
       
   178         sel.setBasicConstraints(-2);
       
   179 
       
   180         /* Retrieve matching certs from CertStores */
       
   181         HashSet<X509Certificate> eeCerts = new HashSet<>();
       
   182         addMatchingCerts(sel, certStores, eeCerts, true);
       
   183 
       
   184         if (debug != null) {
       
   185             debug.println("ReverseBuilder.getMatchingEECerts got "
       
   186                           + eeCerts.size() + " certs.");
       
   187         }
       
   188         return eeCerts;
       
   189     }
       
   190 
       
   191     /*
       
   192      * Retrieves all CA certificates which satisfy constraints
       
   193      * and requirements specified in the parameters and PKIX state.
       
   194      */
       
   195     private Collection<X509Certificate> getMatchingCACerts
       
   196         (ReverseState currentState, List<CertStore> certStores)
       
   197         throws CertificateException, CertStoreException, IOException {
       
   198 
       
   199         /*
       
   200          * Compose a CertSelector to filter out
       
   201          * certs which do not satisfy requirements.
       
   202          */
       
   203         X509CertSelector sel = new X509CertSelector();
       
   204 
       
   205         /*
       
   206          * Match on issuer (subject of previous cert)
       
   207          */
       
   208         sel.setIssuer(currentState.subjectDN);
       
   209 
       
   210         /*
       
   211          * Match on certificate validity date.
       
   212          */
       
   213         sel.setCertificateValid(buildParams.date());
       
   214 
       
   215         /*
       
   216          * Match on target subject name (checks that current cert's
       
   217          * name constraints permit it to certify target).
       
   218          * (4 is the integer type for DIRECTORY name).
       
   219          */
       
   220         byte[] subject = targetCertConstraints.getSubjectAsBytes();
       
   221         if (subject != null) {
       
   222             sel.addPathToName(4, subject);
       
   223         } else {
       
   224             X509Certificate cert = targetCertConstraints.getCertificate();
       
   225             if (cert != null) {
       
   226                 sel.addPathToName(4,
       
   227                                   cert.getSubjectX500Principal().getEncoded());
       
   228             }
       
   229         }
       
   230 
       
   231         /*
       
   232          * Policy processing optimizations
       
   233          */
       
   234         if (currentState.explicitPolicy == 0)
       
   235             sel.setPolicy(getMatchingPolicies());
       
   236 
       
   237         /*
       
   238          * If previous cert has a subject key identifier extension,
       
   239          * use it to match on authority key identifier extension.
       
   240          */
       
   241         /*if (currentState.subjKeyId != null) {
       
   242           AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension(
       
   243                 (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID),
       
   244                                 null, null);
       
   245           sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue());
       
   246         }*/
       
   247 
       
   248         /*
       
   249          * Require CA certs
       
   250          */
       
   251         sel.setBasicConstraints(0);
       
   252 
       
   253         /* Retrieve matching certs from CertStores */
       
   254         ArrayList<X509Certificate> reverseCerts = new ArrayList<>();
       
   255         addMatchingCerts(sel, certStores, reverseCerts, true);
       
   256 
       
   257         /* Sort remaining certs using name constraints */
       
   258         Collections.sort(reverseCerts, new PKIXCertComparator());
       
   259 
       
   260         if (debug != null)
       
   261             debug.println("ReverseBuilder.getMatchingCACerts got " +
       
   262                           reverseCerts.size() + " certs.");
       
   263         return reverseCerts;
       
   264     }
       
   265 
       
   266     /*
       
   267      * This inner class compares 2 PKIX certificates according to which
       
   268      * should be tried first when building a path to the target. For
       
   269      * now, the algorithm is to look at name constraints in each cert and those
       
   270      * which constrain the path closer to the target should be
       
   271      * ranked higher. Later, we may want to consider other components,
       
   272      * such as key identifiers.
       
   273      */
       
   274     class PKIXCertComparator implements Comparator<X509Certificate> {
       
   275 
       
   276         private Debug debug = Debug.getInstance("certpath");
       
   277 
       
   278         @Override
       
   279         public int compare(X509Certificate cert1, X509Certificate cert2) {
       
   280 
       
   281             /*
       
   282              * if either cert certifies the target, always
       
   283              * put at head of list.
       
   284              */
       
   285             X500Principal targetSubject = buildParams.targetSubject();
       
   286             if (cert1.getSubjectX500Principal().equals(targetSubject)) {
       
   287                 return -1;
       
   288             }
       
   289             if (cert2.getSubjectX500Principal().equals(targetSubject)) {
       
   290                 return 1;
       
   291             }
       
   292 
       
   293             int targetDist1;
       
   294             int targetDist2;
       
   295             try {
       
   296                 X500Name targetSubjectName = X500Name.asX500Name(targetSubject);
       
   297                 targetDist1 = Builder.targetDistance(
       
   298                     null, cert1, targetSubjectName);
       
   299                 targetDist2 = Builder.targetDistance(
       
   300                     null, cert2, targetSubjectName);
       
   301             } catch (IOException e) {
       
   302                 if (debug != null) {
       
   303                     debug.println("IOException in call to Builder.targetDistance");
       
   304                     e.printStackTrace();
       
   305                 }
       
   306                 throw new ClassCastException
       
   307                     ("Invalid target subject distinguished name");
       
   308             }
       
   309 
       
   310             if (targetDist1 == targetDist2)
       
   311                 return 0;
       
   312 
       
   313             if (targetDist1 == -1)
       
   314                 return 1;
       
   315 
       
   316             if (targetDist1 < targetDist2)
       
   317                 return -1;
       
   318 
       
   319             return 1;
       
   320         }
       
   321     }
       
   322 
       
   323     /**
       
   324      * Verifies a matching certificate.
       
   325      *
       
   326      * This method executes any of the validation steps in the PKIX path validation
       
   327      * algorithm which were not satisfied via filtering out non-compliant
       
   328      * certificates with certificate matching rules.
       
   329      *
       
   330      * If the last certificate is being verified (the one whose subject
       
   331      * matches the target subject, then the steps in Section 6.1.4 of the
       
   332      * Certification Path Validation algorithm are NOT executed,
       
   333      * regardless of whether or not the last cert is an end-entity
       
   334      * cert or not. This allows callers to certify CA certs as
       
   335      * well as EE certs.
       
   336      *
       
   337      * @param cert the certificate to be verified
       
   338      * @param currentState the current state against which the cert is verified
       
   339      * @param certPathList the certPathList generated thus far
       
   340      */
       
   341     @Override
       
   342     void verifyCert(X509Certificate cert, State currState,
       
   343         List<X509Certificate> certPathList)
       
   344         throws GeneralSecurityException
       
   345     {
       
   346         if (debug != null) {
       
   347             debug.println("ReverseBuilder.verifyCert(SN: "
       
   348                 + Debug.toHexString(cert.getSerialNumber())
       
   349                 + "\n  Subject: " + cert.getSubjectX500Principal() + ")");
       
   350         }
       
   351 
       
   352         ReverseState currentState = (ReverseState) currState;
       
   353 
       
   354         /* we don't perform any validation of the trusted cert */
       
   355         if (currentState.isInitial()) {
       
   356             return;
       
   357         }
       
   358 
       
   359         // Don't bother to verify untrusted certificate more.
       
   360         currentState.untrustedChecker.check(cert,
       
   361                                     Collections.<String>emptySet());
       
   362 
       
   363         /*
       
   364          * check for looping - abort a loop if
       
   365          * ((we encounter the same certificate twice) AND
       
   366          * ((policyMappingInhibited = true) OR (no policy mapping
       
   367          * extensions can be found between the occurrences of the same
       
   368          * certificate)))
       
   369          * in order to facilitate the check to see if there are
       
   370          * any policy mapping extensions found between the occurrences
       
   371          * of the same certificate, we reverse the certpathlist first
       
   372          */
       
   373         if ((certPathList != null) && (!certPathList.isEmpty())) {
       
   374             List<X509Certificate> reverseCertList = new ArrayList<>();
       
   375             for (X509Certificate c : certPathList) {
       
   376                 reverseCertList.add(0, c);
       
   377             }
       
   378 
       
   379             boolean policyMappingFound = false;
       
   380             for (X509Certificate cpListCert : reverseCertList) {
       
   381                 X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert);
       
   382                 PolicyMappingsExtension policyMappingsExt =
       
   383                         cpListCertImpl.getPolicyMappingsExtension();
       
   384                 if (policyMappingsExt != null) {
       
   385                     policyMappingFound = true;
       
   386                 }
       
   387                 if (debug != null)
       
   388                     debug.println("policyMappingFound = " + policyMappingFound);
       
   389                 if (cert.equals(cpListCert)) {
       
   390                     if ((buildParams.policyMappingInhibited()) ||
       
   391                         (!policyMappingFound)){
       
   392                         if (debug != null)
       
   393                             debug.println("loop detected!!");
       
   394                         throw new CertPathValidatorException("loop detected");
       
   395                     }
       
   396                 }
       
   397             }
       
   398         }
       
   399 
       
   400         /* check if target cert */
       
   401         boolean finalCert = cert.getSubjectX500Principal().equals(buildParams.targetSubject());
       
   402 
       
   403         /* check if CA cert */
       
   404         boolean caCert = (cert.getBasicConstraints() != -1 ? true : false);
       
   405 
       
   406         /* if there are more certs to follow, verify certain constraints */
       
   407         if (!finalCert) {
       
   408 
       
   409             /* check if CA cert */
       
   410             if (!caCert)
       
   411                 throw new CertPathValidatorException("cert is NOT a CA cert");
       
   412 
       
   413             /* If the certificate was not self-issued, verify that
       
   414              * remainingCerts is greater than zero
       
   415              */
       
   416             if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) {
       
   417                     throw new CertPathValidatorException
       
   418                         ("pathLenConstraint violated, path too long", null,
       
   419                          null, -1, PKIXReason.PATH_TOO_LONG);
       
   420             }
       
   421 
       
   422             /*
       
   423              * Check keyUsage extension (only if CA cert and not final cert)
       
   424              */
       
   425             KeyChecker.verifyCAKeyUsage(cert);
       
   426 
       
   427         } else {
       
   428 
       
   429             /*
       
   430              * If final cert, check that it satisfies specified target
       
   431              * constraints
       
   432              */
       
   433             if (targetCertConstraints.match(cert) == false) {
       
   434                 throw new CertPathValidatorException("target certificate " +
       
   435                     "constraints check failed");
       
   436             }
       
   437         }
       
   438 
       
   439         /*
       
   440          * Check revocation.
       
   441          */
       
   442         if (buildParams.revocationEnabled() && currentState.revChecker != null) {
       
   443             currentState.revChecker.check(cert, Collections.<String>emptySet());
       
   444         }
       
   445 
       
   446         /* Check name constraints if this is not a self-issued cert */
       
   447         if (finalCert || !X509CertImpl.isSelfIssued(cert)){
       
   448             if (currentState.nc != null) {
       
   449                 try {
       
   450                     if (!currentState.nc.verify(cert)){
       
   451                         throw new CertPathValidatorException
       
   452                             ("name constraints check failed", null, null, -1,
       
   453                              PKIXReason.INVALID_NAME);
       
   454                     }
       
   455                 } catch (IOException ioe) {
       
   456                     throw new CertPathValidatorException(ioe);
       
   457                 }
       
   458             }
       
   459         }
       
   460 
       
   461         /*
       
   462          * Check policy
       
   463          */
       
   464         X509CertImpl certImpl = X509CertImpl.toImpl(cert);
       
   465         currentState.rootNode = PolicyChecker.processPolicies
       
   466             (currentState.certIndex, initPolicies,
       
   467             currentState.explicitPolicy, currentState.policyMapping,
       
   468             currentState.inhibitAnyPolicy,
       
   469             buildParams.policyQualifiersRejected(), currentState.rootNode,
       
   470             certImpl, finalCert);
       
   471 
       
   472         /*
       
   473          * Check CRITICAL private extensions
       
   474          */
       
   475         Set<String> unresolvedCritExts = cert.getCriticalExtensionOIDs();
       
   476         if (unresolvedCritExts == null) {
       
   477             unresolvedCritExts = Collections.<String>emptySet();
       
   478         }
       
   479 
       
   480         /*
       
   481          * Check that the signature algorithm is not disabled.
       
   482          */
       
   483         currentState.algorithmChecker.check(cert, unresolvedCritExts);
       
   484 
       
   485         for (PKIXCertPathChecker checker : currentState.userCheckers) {
       
   486             checker.check(cert, unresolvedCritExts);
       
   487         }
       
   488 
       
   489         /*
       
   490          * Look at the remaining extensions and remove any ones we have
       
   491          * already checked. If there are any left, throw an exception!
       
   492          */
       
   493         if (!unresolvedCritExts.isEmpty()) {
       
   494             unresolvedCritExts.remove(BasicConstraints_Id.toString());
       
   495             unresolvedCritExts.remove(NameConstraints_Id.toString());
       
   496             unresolvedCritExts.remove(CertificatePolicies_Id.toString());
       
   497             unresolvedCritExts.remove(PolicyMappings_Id.toString());
       
   498             unresolvedCritExts.remove(PolicyConstraints_Id.toString());
       
   499             unresolvedCritExts.remove(InhibitAnyPolicy_Id.toString());
       
   500             unresolvedCritExts.remove(SubjectAlternativeName_Id.toString());
       
   501             unresolvedCritExts.remove(KeyUsage_Id.toString());
       
   502             unresolvedCritExts.remove(ExtendedKeyUsage_Id.toString());
       
   503 
       
   504             if (!unresolvedCritExts.isEmpty())
       
   505                 throw new CertPathValidatorException
       
   506                     ("Unrecognized critical extension(s)", null, null, -1,
       
   507                      PKIXReason.UNRECOGNIZED_CRIT_EXT);
       
   508         }
       
   509 
       
   510         /*
       
   511          * Check signature.
       
   512          */
       
   513         if (buildParams.sigProvider() != null) {
       
   514             cert.verify(currentState.pubKey, buildParams.sigProvider());
       
   515         } else {
       
   516             cert.verify(currentState.pubKey);
       
   517         }
       
   518     }
       
   519 
       
   520     /**
       
   521      * Verifies whether the input certificate completes the path.
       
   522      * This checks whether the cert is the target certificate.
       
   523      *
       
   524      * @param cert the certificate to test
       
   525      * @return a boolean value indicating whether the cert completes the path.
       
   526      */
       
   527     @Override
       
   528     boolean isPathCompleted(X509Certificate cert) {
       
   529         return cert.getSubjectX500Principal().equals(buildParams.targetSubject());
       
   530     }
       
   531 
       
   532     /** Adds the certificate to the certPathList
       
   533      *
       
   534      * @param cert the certificate to be added
       
   535      * @param certPathList the certification path list
       
   536      */
       
   537     @Override
       
   538     void addCertToPath(X509Certificate cert,
       
   539         LinkedList<X509Certificate> certPathList) {
       
   540         certPathList.addLast(cert);
       
   541     }
       
   542 
       
   543     /** Removes final certificate from the certPathList
       
   544      *
       
   545      * @param certPathList the certification path list
       
   546      */
       
   547     @Override
       
   548     void removeFinalCertFromPath(LinkedList<X509Certificate> certPathList) {
       
   549         certPathList.removeLast();
       
   550     }
       
   551 }