Home | History | Annotate | Download | only in certpath
      1 /*
      2  * Copyright (c) 2000, 2015, 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.cert.Certificate;
     30 import java.security.cert.CertificateException;
     31 import java.security.cert.CertPathValidatorException;
     32 import java.security.cert.PKIXCertPathChecker;
     33 import java.security.cert.PKIXReason;
     34 import java.security.cert.X509Certificate;
     35 import java.util.Collection;
     36 import java.util.Collections;
     37 import java.util.HashSet;
     38 import java.util.Set;
     39 
     40 import sun.security.util.Debug;
     41 import static sun.security.x509.PKIXExtensions.*;
     42 import sun.security.x509.NameConstraintsExtension;
     43 import sun.security.x509.X509CertImpl;
     44 
     45 /**
     46  * ConstraintsChecker is a <code>PKIXCertPathChecker</code> that checks
     47  * constraints information on a PKIX certificate, namely basic constraints
     48  * and name constraints.
     49  *
     50  * @since       1.4
     51  * @author      Yassir Elley
     52  */
     53 class ConstraintsChecker extends PKIXCertPathChecker {
     54 
     55     private static final Debug debug = Debug.getInstance("certpath");
     56     /* length of cert path */
     57     private final int certPathLength;
     58     /* current maximum path length (as defined in PKIX) */
     59     private int maxPathLength;
     60     /* current index of cert */
     61     private int i;
     62     private NameConstraintsExtension prevNC;
     63 
     64     private Set<String> supportedExts;
     65 
     66     /**
     67      * Creates a ConstraintsChecker.
     68      *
     69      * @param certPathLength the length of the certification path
     70      */
     71     ConstraintsChecker(int certPathLength) {
     72         this.certPathLength = certPathLength;
     73     }
     74 
     75     @Override
     76     public void init(boolean forward) throws CertPathValidatorException {
     77         if (!forward) {
     78             i = 0;
     79             maxPathLength = certPathLength;
     80             prevNC = null;
     81         } else {
     82             throw new CertPathValidatorException
     83                 ("forward checking not supported");
     84         }
     85     }
     86 
     87     @Override
     88     public boolean isForwardCheckingSupported() {
     89         return false;
     90     }
     91 
     92     @Override
     93     public Set<String> getSupportedExtensions() {
     94         if (supportedExts == null) {
     95             supportedExts = new HashSet<String>(2);
     96             supportedExts.add(BasicConstraints_Id.toString());
     97             supportedExts.add(NameConstraints_Id.toString());
     98             supportedExts = Collections.unmodifiableSet(supportedExts);
     99         }
    100         return supportedExts;
    101     }
    102 
    103     /**
    104      * Performs the basic constraints and name constraints
    105      * checks on the certificate using its internal state.
    106      *
    107      * @param cert the <code>Certificate</code> to be checked
    108      * @param unresCritExts a <code>Collection</code> of OID strings
    109      *        representing the current set of unresolved critical extensions
    110      * @throws CertPathValidatorException if the specified certificate
    111      *         does not pass the check
    112      */
    113     @Override
    114     public void check(Certificate cert, Collection<String> unresCritExts)
    115         throws CertPathValidatorException
    116     {
    117         X509Certificate currCert = (X509Certificate)cert;
    118 
    119         i++;
    120         // MUST run NC check second, since it depends on BC check to
    121         // update remainingCerts
    122         checkBasicConstraints(currCert);
    123         verifyNameConstraints(currCert);
    124 
    125         if (unresCritExts != null && !unresCritExts.isEmpty()) {
    126             unresCritExts.remove(BasicConstraints_Id.toString());
    127             unresCritExts.remove(NameConstraints_Id.toString());
    128         }
    129     }
    130 
    131     /**
    132      * Internal method to check the name constraints against a cert
    133      */
    134     private void verifyNameConstraints(X509Certificate currCert)
    135         throws CertPathValidatorException
    136     {
    137         String msg = "name constraints";
    138         if (debug != null) {
    139             debug.println("---checking " + msg + "...");
    140         }
    141 
    142         // check name constraints only if there is a previous name constraint
    143         // and either the currCert is the final cert or the currCert is not
    144         // self-issued
    145         if (prevNC != null && ((i == certPathLength) ||
    146                 !X509CertImpl.isSelfIssued(currCert))) {
    147             if (debug != null) {
    148                 debug.println("prevNC = " + prevNC +
    149                     ", currDN = " + currCert.getSubjectX500Principal());
    150             }
    151 
    152             try {
    153                 if (!prevNC.verify(currCert)) {
    154                     throw new CertPathValidatorException(msg + " check failed",
    155                         null, null, -1, PKIXReason.INVALID_NAME);
    156                 }
    157             } catch (IOException ioe) {
    158                 throw new CertPathValidatorException(ioe);
    159             }
    160         }
    161 
    162         // merge name constraints regardless of whether cert is self-issued
    163         prevNC = mergeNameConstraints(currCert, prevNC);
    164 
    165         if (debug != null)
    166             debug.println(msg + " verified.");
    167     }
    168 
    169     /**
    170      * Helper to fold sets of name constraints together
    171      */
    172     static NameConstraintsExtension mergeNameConstraints(
    173         X509Certificate currCert, NameConstraintsExtension prevNC)
    174         throws CertPathValidatorException
    175     {
    176         X509CertImpl currCertImpl;
    177         try {
    178             currCertImpl = X509CertImpl.toImpl(currCert);
    179         } catch (CertificateException ce) {
    180             throw new CertPathValidatorException(ce);
    181         }
    182 
    183         NameConstraintsExtension newConstraints =
    184             currCertImpl.getNameConstraintsExtension();
    185 
    186         if (debug != null) {
    187             debug.println("prevNC = " + prevNC +
    188                         ", newNC = " + String.valueOf(newConstraints));
    189         }
    190 
    191         // if there are no previous name constraints, we just return the
    192         // new name constraints.
    193         if (prevNC == null) {
    194             if (debug != null) {
    195                 debug.println("mergedNC = " + String.valueOf(newConstraints));
    196             }
    197             if (newConstraints == null) {
    198                 return newConstraints;
    199             } else {
    200                 // Make sure we do a clone here, because we're probably
    201                 // going to modify this object later and we don't want to
    202                 // be sharing it with a Certificate object!
    203                 return (NameConstraintsExtension)newConstraints.clone();
    204             }
    205         } else {
    206             try {
    207                 // after merge, prevNC should contain the merged constraints
    208                 prevNC.merge(newConstraints);
    209             } catch (IOException ioe) {
    210                 throw new CertPathValidatorException(ioe);
    211             }
    212             if (debug != null) {
    213                 debug.println("mergedNC = " + prevNC);
    214             }
    215             return prevNC;
    216         }
    217     }
    218 
    219     /**
    220      * Internal method to check that a given cert meets basic constraints.
    221      */
    222     private void checkBasicConstraints(X509Certificate currCert)
    223         throws CertPathValidatorException
    224     {
    225         String msg = "basic constraints";
    226         if (debug != null) {
    227             debug.println("---checking " + msg + "...");
    228             debug.println("i = " + i +
    229                         ", maxPathLength = " + maxPathLength);
    230         }
    231 
    232         /* check if intermediate cert */
    233         if (i < certPathLength) {
    234             // RFC5280: If certificate i is a version 3 certificate, verify
    235             // that the basicConstraints extension is present and that cA is
    236             // set to TRUE.  (If certificate i is a version 1 or version 2
    237             // certificate, then the application MUST either verify that
    238             // certificate i is a CA certificate through out-of-band means
    239             // or reject the certificate.  Conforming implementations may
    240             // choose to reject all version 1 and version 2 intermediate
    241             // certificates.)
    242             //
    243             // We choose to reject all version 1 and version 2 intermediate
    244             // certificates except that it is self issued by the trust
    245             // anchor in order to support key rollover or changes in
    246             // certificate policies.
    247             int pathLenConstraint = -1;
    248             if (currCert.getVersion() < 3) {    // version 1 or version 2
    249                 if (i == 1) {                   // issued by a trust anchor
    250                     if (X509CertImpl.isSelfIssued(currCert)) {
    251                         pathLenConstraint = Integer.MAX_VALUE;
    252                     }
    253                 }
    254             } else {
    255                 pathLenConstraint = currCert.getBasicConstraints();
    256             }
    257 
    258             if (pathLenConstraint == -1) {
    259                 throw new CertPathValidatorException
    260                     (msg + " check failed: this is not a CA certificate",
    261                      null, null, -1, PKIXReason.NOT_CA_CERT);
    262             }
    263 
    264             if (!X509CertImpl.isSelfIssued(currCert)) {
    265                 if (maxPathLength <= 0) {
    266                    throw new CertPathValidatorException
    267                         (msg + " check failed: pathLenConstraint violated - "
    268                          + "this cert must be the last cert in the "
    269                          + "certification path", null, null, -1,
    270                          PKIXReason.PATH_TOO_LONG);
    271                 }
    272                 maxPathLength--;
    273             }
    274             if (pathLenConstraint < maxPathLength)
    275                 maxPathLength = pathLenConstraint;
    276         }
    277 
    278         if (debug != null) {
    279             debug.println("after processing, maxPathLength = " + maxPathLength);
    280             debug.println(msg + " verified.");
    281         }
    282     }
    283 
    284     /**
    285      * Merges the specified maxPathLength with the pathLenConstraint
    286      * obtained from the certificate.
    287      *
    288      * @param cert the <code>X509Certificate</code>
    289      * @param maxPathLength the previous maximum path length
    290      * @return the new maximum path length constraint (-1 means no more
    291      * certificates can follow, Integer.MAX_VALUE means path length is
    292      * unconstrained)
    293      */
    294     static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) {
    295 
    296         int pathLenConstraint = cert.getBasicConstraints();
    297 
    298         if (!X509CertImpl.isSelfIssued(cert)) {
    299             maxPathLength--;
    300         }
    301 
    302         if (pathLenConstraint < maxPathLength) {
    303             maxPathLength = pathLenConstraint;
    304         }
    305 
    306         return maxPathLength;
    307     }
    308 }
    309