Home | History | Annotate | Download | only in certpath
      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.PublicKey;
     30 import java.security.cert.CertificateException;
     31 import java.security.cert.CertPathValidatorException;
     32 import java.security.cert.PKIXCertPathChecker;
     33 import java.security.cert.PKIXRevocationChecker;
     34 import java.security.cert.TrustAnchor;
     35 import java.security.cert.X509Certificate;
     36 import java.util.ArrayList;
     37 import java.util.HashSet;
     38 import java.util.List;
     39 import java.util.ListIterator;
     40 import java.util.Set;
     41 import javax.security.auth.x500.X500Principal;
     42 
     43 import sun.security.provider.certpath.PKIX.BuilderParams;
     44 import sun.security.util.Debug;
     45 import sun.security.x509.NameConstraintsExtension;
     46 import sun.security.x509.SubjectKeyIdentifierExtension;
     47 import sun.security.x509.X509CertImpl;
     48 
     49 /**
     50  * A specification of a reverse PKIX validation state
     51  * which is initialized by each build and updated each time a
     52  * certificate is added to the current path.
     53  * @since       1.4
     54  * @author      Sean Mullan
     55  * @author      Yassir Elley
     56  */
     57 
     58 class ReverseState implements State {
     59 
     60     private static final Debug debug = Debug.getInstance("certpath");
     61 
     62     /* The subject DN of the last cert in the path */
     63     X500Principal subjectDN;
     64 
     65     /* The subject public key of the last cert */
     66     PublicKey pubKey;
     67 
     68     /* The subject key identifier extension (if any) of the last cert */
     69     SubjectKeyIdentifierExtension subjKeyId;
     70 
     71     /* The PKIX constrained/excluded subtrees state variable */
     72     NameConstraintsExtension nc;
     73 
     74     /* The PKIX explicit policy, policy mapping, and inhibit_any-policy
     75        state variables */
     76     int explicitPolicy;
     77     int policyMapping;
     78     int inhibitAnyPolicy;
     79     int certIndex;
     80     PolicyNodeImpl rootNode;
     81 
     82     /* The number of remaining CA certs which may follow in the path.
     83      * -1: previous cert was an EE cert
     84      * 0: only EE certs may follow.
     85      * >0 and <Integer.MAX_VALUE:no more than this number of CA certs may follow
     86      * Integer.MAX_VALUE: unlimited
     87      */
     88     int remainingCACerts;
     89 
     90     /* The list of user-defined checkers retrieved from the PKIXParameters
     91      * instance */
     92     ArrayList<PKIXCertPathChecker> userCheckers;
     93 
     94     /* Flag indicating if state is initial (path is just starting) */
     95     private boolean init = true;
     96 
     97     /* the checker used for revocation status */
     98     RevocationChecker revChecker;
     99 
    100     /* the algorithm checker */
    101     AlgorithmChecker algorithmChecker;
    102 
    103     /* the untrusted certificates checker */
    104     UntrustedChecker untrustedChecker;
    105 
    106     /* the trust anchor used to validate the path */
    107     TrustAnchor trustAnchor;
    108 
    109     /* Flag indicating if current cert can vouch for the CRL for
    110      * the next cert
    111      */
    112     boolean crlSign = true;
    113 
    114     /**
    115      * Returns a boolean flag indicating if the state is initial
    116      * (just starting)
    117      *
    118      * @return boolean flag indicating if the state is initial (just starting)
    119      */
    120     @Override
    121     public boolean isInitial() {
    122         return init;
    123     }
    124 
    125     /**
    126      * Display state for debugging purposes
    127      */
    128     @Override
    129     public String toString() {
    130         StringBuilder sb = new StringBuilder();
    131         sb.append("State [");
    132         sb.append("\n  subjectDN of last cert: ").append(subjectDN);
    133         sb.append("\n  subjectKeyIdentifier: ").append
    134                  (String.valueOf(subjKeyId));
    135         sb.append("\n  nameConstraints: ").append(String.valueOf(nc));
    136         sb.append("\n  certIndex: ").append(certIndex);
    137         sb.append("\n  explicitPolicy: ").append(explicitPolicy);
    138         sb.append("\n  policyMapping:  ").append(policyMapping);
    139         sb.append("\n  inhibitAnyPolicy:  ").append(inhibitAnyPolicy);
    140         sb.append("\n  rootNode: ").append(rootNode);
    141         sb.append("\n  remainingCACerts: ").append(remainingCACerts);
    142         sb.append("\n  crlSign: ").append(crlSign);
    143         sb.append("\n  init: ").append(init);
    144         sb.append("\n]\n");
    145         return sb.toString();
    146     }
    147 
    148     /**
    149      * Initialize the state.
    150      *
    151      * @param buildParams builder parameters
    152      */
    153     public void initState(BuilderParams buildParams)
    154         throws CertPathValidatorException
    155     {
    156         /*
    157          * Initialize number of remainingCACerts.
    158          * Note that -1 maxPathLen implies unlimited.
    159          * 0 implies only an EE cert is acceptable.
    160          */
    161         int maxPathLen = buildParams.maxPathLength();
    162         remainingCACerts = (maxPathLen == -1) ? Integer.MAX_VALUE
    163                                               : maxPathLen;
    164 
    165         /* Initialize explicit policy state variable */
    166         if (buildParams.explicitPolicyRequired()) {
    167             explicitPolicy = 0;
    168         } else {
    169             // unconstrained if maxPathLen is -1,
    170             // otherwise, we want to initialize this to the value of the
    171             // longest possible path + 1 (i.e. maxpathlen + finalcert + 1)
    172             explicitPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
    173         }
    174 
    175         /* Initialize policy mapping state variable */
    176         if (buildParams.policyMappingInhibited()) {
    177             policyMapping = 0;
    178         } else {
    179             policyMapping = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
    180         }
    181 
    182         /* Initialize inhibit any policy state variable */
    183         if (buildParams.anyPolicyInhibited()) {
    184             inhibitAnyPolicy = 0;
    185         } else {
    186             inhibitAnyPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2;
    187         }
    188 
    189         /* Initialize certIndex */
    190         certIndex = 1;
    191 
    192         /* Initialize policy tree */
    193         Set<String> initExpPolSet = new HashSet<>(1);
    194         initExpPolSet.add(PolicyChecker.ANY_POLICY);
    195 
    196         rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null,
    197                                       false, initExpPolSet, false);
    198 
    199         /*
    200          * Initialize each user-defined checker
    201          * Shallow copy the checkers
    202          */
    203         userCheckers = new ArrayList<>(buildParams.certPathCheckers());
    204         /* initialize each checker (just in case) */
    205         for (PKIXCertPathChecker checker : userCheckers) {
    206             checker.init(false);
    207         }
    208 
    209         /* Start by trusting the cert to sign CRLs */
    210         crlSign = true;
    211 
    212         init = true;
    213     }
    214 
    215     /**
    216      * Update the state with the specified trust anchor.
    217      *
    218      * @param anchor the most-trusted CA
    219      * @param buildParams builder parameters
    220      */
    221     public void updateState(TrustAnchor anchor, BuilderParams buildParams)
    222         throws CertificateException, IOException, CertPathValidatorException
    223     {
    224         trustAnchor = anchor;
    225         X509Certificate trustedCert = anchor.getTrustedCert();
    226         if (trustedCert != null) {
    227             updateState(trustedCert);
    228         } else {
    229             X500Principal caName = anchor.getCA();
    230             updateState(anchor.getCAPublicKey(), caName);
    231         }
    232 
    233         // The user specified AlgorithmChecker and RevocationChecker may not be
    234         // able to set the trust anchor until now.
    235         boolean revCheckerAdded = false;
    236         for (PKIXCertPathChecker checker : userCheckers) {
    237             if (checker instanceof AlgorithmChecker) {
    238                 ((AlgorithmChecker)checker).trySetTrustAnchor(anchor);
    239             } else if (checker instanceof PKIXRevocationChecker) {
    240                 if (revCheckerAdded) {
    241                     throw new CertPathValidatorException(
    242                         "Only one PKIXRevocationChecker can be specified");
    243                 }
    244                 // if it's our own, initialize it
    245                 if (checker instanceof RevocationChecker) {
    246                     ((RevocationChecker)checker).init(anchor, buildParams);
    247                 }
    248                 ((PKIXRevocationChecker)checker).init(false);
    249                 revCheckerAdded = true;
    250             }
    251         }
    252 
    253         // only create a RevocationChecker if revocation is enabled and
    254         // a PKIXRevocationChecker has not already been added
    255         if (buildParams.revocationEnabled() && !revCheckerAdded) {
    256             revChecker = new RevocationChecker(anchor, buildParams);
    257             revChecker.init(false);
    258         }
    259 
    260         init = false;
    261     }
    262 
    263     /**
    264      * Update the state. This method is used when the most-trusted CA is
    265      * a trusted public-key and caName, instead of a trusted cert.
    266      *
    267      * @param pubKey the public key of the trusted CA
    268      * @param subjectDN the subject distinguished name of the trusted CA
    269      */
    270     private void updateState(PublicKey pubKey, X500Principal subjectDN) {
    271 
    272         /* update subject DN */
    273         this.subjectDN = subjectDN;
    274 
    275         /* update subject public key */
    276         this.pubKey = pubKey;
    277     }
    278 
    279     /**
    280      * Update the state with the next certificate added to the path.
    281      *
    282      * @param cert the certificate which is used to update the state
    283      */
    284     public void updateState(X509Certificate cert)
    285         throws CertificateException, IOException, CertPathValidatorException {
    286 
    287         if (cert == null) {
    288             return;
    289         }
    290 
    291         /* update subject DN */
    292         subjectDN = cert.getSubjectX500Principal();
    293 
    294         /* check for key needing to inherit alg parameters */
    295         X509CertImpl icert = X509CertImpl.toImpl(cert);
    296         PublicKey newKey = cert.getPublicKey();
    297         if (PKIX.isDSAPublicKeyWithoutParams(newKey)) {
    298             newKey = BasicChecker.makeInheritedParamsKey(newKey, pubKey);
    299         }
    300 
    301         /* update subject public key */
    302         pubKey = newKey;
    303 
    304         /*
    305          * if this is a trusted cert (init == true), then we
    306          * don't update any of the remaining fields
    307          */
    308         if (init) {
    309             init = false;
    310             return;
    311         }
    312 
    313         /* update subject key identifier */
    314         subjKeyId = icert.getSubjectKeyIdentifierExtension();
    315 
    316         /* update crlSign */
    317         crlSign = RevocationChecker.certCanSignCrl(cert);
    318 
    319         /* update current name constraints */
    320         if (nc != null) {
    321             nc.merge(icert.getNameConstraintsExtension());
    322         } else {
    323             nc = icert.getNameConstraintsExtension();
    324             if (nc != null) {
    325                 // Make sure we do a clone here, because we're probably
    326                 // going to modify this object later and we don't want to
    327                 // be sharing it with a Certificate object!
    328                 nc = (NameConstraintsExtension) nc.clone();
    329             }
    330         }
    331 
    332         /* update policy state variables */
    333         explicitPolicy =
    334             PolicyChecker.mergeExplicitPolicy(explicitPolicy, icert, false);
    335         policyMapping =
    336             PolicyChecker.mergePolicyMapping(policyMapping, icert);
    337         inhibitAnyPolicy =
    338             PolicyChecker.mergeInhibitAnyPolicy(inhibitAnyPolicy, icert);
    339         certIndex++;
    340 
    341         /*
    342          * Update remaining CA certs
    343          */
    344         remainingCACerts =
    345             ConstraintsChecker.mergeBasicConstraints(cert, remainingCACerts);
    346 
    347         init = false;
    348     }
    349 
    350     /**
    351      * Returns a boolean flag indicating if a key lacking necessary key
    352      * algorithm parameters has been encountered.
    353      *
    354      * @return boolean flag indicating if key lacking parameters encountered.
    355      */
    356     @Override
    357     public boolean keyParamsNeeded() {
    358         /* when building in reverse, we immediately get parameters needed
    359          * or else throw an exception
    360          */
    361         return false;
    362     }
    363 
    364     /*
    365      * Clone current state. The state is cloned as each cert is
    366      * added to the path. This is necessary if backtracking occurs,
    367      * and a prior state needs to be restored.
    368      *
    369      * Note that this is a SMART clone. Not all fields are fully copied,
    370      * because some of them (e.g., subjKeyId) will
    371      * not have their contents modified by subsequent calls to updateState.
    372      */
    373     @Override
    374     @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly
    375     public Object clone() {
    376         try {
    377             ReverseState clonedState = (ReverseState) super.clone();
    378 
    379             /* clone checkers, if cloneable */
    380             clonedState.userCheckers =
    381                         (ArrayList<PKIXCertPathChecker>)userCheckers.clone();
    382             ListIterator<PKIXCertPathChecker> li =
    383                         clonedState.userCheckers.listIterator();
    384             while (li.hasNext()) {
    385                 PKIXCertPathChecker checker = li.next();
    386                 if (checker instanceof Cloneable) {
    387                     li.set((PKIXCertPathChecker)checker.clone());
    388                 }
    389             }
    390 
    391             /* make copy of name constraints */
    392             if (nc != null) {
    393                 clonedState.nc = (NameConstraintsExtension) nc.clone();
    394             }
    395 
    396             /* make copy of policy tree */
    397             if (rootNode != null) {
    398                 clonedState.rootNode = rootNode.copyTree();
    399             }
    400 
    401             return clonedState;
    402         } catch (CloneNotSupportedException e) {
    403             throw new InternalError(e.toString(), e);
    404         }
    405     }
    406 }
    407