Home | History | Annotate | Download | only in certpath
      1 /*
      2  * Copyright (c) 2000, 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.security.GeneralSecurityException;
     30 import java.security.cert.Certificate;
     31 import java.security.cert.CertificateException;
     32 import java.security.cert.CertPathValidatorException;
     33 import java.security.cert.PKIXCertPathChecker;
     34 import java.security.cert.PKIXReason;
     35 import java.security.cert.PolicyNode;
     36 import java.security.cert.PolicyQualifierInfo;
     37 import java.security.cert.X509Certificate;
     38 import java.util.*;
     39 
     40 import sun.security.util.Debug;
     41 import sun.security.x509.CertificatePoliciesExtension;
     42 import sun.security.x509.PolicyConstraintsExtension;
     43 import sun.security.x509.PolicyMappingsExtension;
     44 import sun.security.x509.CertificatePolicyMap;
     45 import static sun.security.x509.PKIXExtensions.*;
     46 import sun.security.x509.PolicyInformation;
     47 import sun.security.x509.X509CertImpl;
     48 import sun.security.x509.InhibitAnyPolicyExtension;
     49 
     50 /**
     51  * PolicyChecker is a <code>PKIXCertPathChecker</code> that checks policy
     52  * information on a PKIX certificate, namely certificate policies, policy
     53  * mappings, policy constraints and policy qualifiers.
     54  *
     55  * @since       1.4
     56  * @author      Yassir Elley
     57  */
     58 class PolicyChecker extends PKIXCertPathChecker {
     59 
     60     private final Set<String> initPolicies;
     61     private final int certPathLen;
     62     private final boolean expPolicyRequired;
     63     private final boolean polMappingInhibited;
     64     private final boolean anyPolicyInhibited;
     65     private final boolean rejectPolicyQualifiers;
     66     private PolicyNodeImpl rootNode;
     67     private int explicitPolicy;
     68     private int policyMapping;
     69     private int inhibitAnyPolicy;
     70     private int certIndex;
     71 
     72     private Set<String> supportedExts;
     73 
     74     private static final Debug debug = Debug.getInstance("certpath");
     75     static final String ANY_POLICY = "2.5.29.32.0";
     76 
     77     /**
     78      * Constructs a Policy Checker.
     79      *
     80      * @param initialPolicies Set of initial policies
     81      * @param certPathLen length of the certification path to be checked
     82      * @param expPolicyRequired true if explicit policy is required
     83      * @param polMappingInhibited true if policy mapping is inhibited
     84      * @param anyPolicyInhibited true if the ANY_POLICY OID should be inhibited
     85      * @param rejectPolicyQualifiers true if pol qualifiers are to be rejected
     86      * @param rootNode the initial root node of the valid policy tree
     87      */
     88     PolicyChecker(Set<String> initialPolicies, int certPathLen,
     89         boolean expPolicyRequired, boolean polMappingInhibited,
     90         boolean anyPolicyInhibited, boolean rejectPolicyQualifiers,
     91         PolicyNodeImpl rootNode)
     92     {
     93         if (initialPolicies.isEmpty()) {
     94             // if no initialPolicies are specified by user, set
     95             // initPolicies to be anyPolicy by default
     96             this.initPolicies = new HashSet<String>(1);
     97             this.initPolicies.add(ANY_POLICY);
     98         } else {
     99             this.initPolicies = new HashSet<String>(initialPolicies);
    100         }
    101         this.certPathLen = certPathLen;
    102         this.expPolicyRequired = expPolicyRequired;
    103         this.polMappingInhibited = polMappingInhibited;
    104         this.anyPolicyInhibited = anyPolicyInhibited;
    105         this.rejectPolicyQualifiers = rejectPolicyQualifiers;
    106         this.rootNode = rootNode;
    107     }
    108 
    109     /**
    110      * Initializes the internal state of the checker from parameters
    111      * specified in the constructor
    112      *
    113      * @param forward a boolean indicating whether this checker should be
    114      *        initialized capable of building in the forward direction
    115      * @throws CertPathValidatorException if user wants to enable forward
    116      *         checking and forward checking is not supported.
    117      */
    118     @Override
    119     public void init(boolean forward) throws CertPathValidatorException {
    120         if (forward) {
    121             throw new CertPathValidatorException
    122                                         ("forward checking not supported");
    123         }
    124 
    125         certIndex = 1;
    126         explicitPolicy = (expPolicyRequired ? 0 : certPathLen + 1);
    127         policyMapping = (polMappingInhibited ? 0 : certPathLen + 1);
    128         inhibitAnyPolicy = (anyPolicyInhibited ? 0 : certPathLen + 1);
    129     }
    130 
    131     /**
    132      * Checks if forward checking is supported. Forward checking refers
    133      * to the ability of the PKIXCertPathChecker to perform its checks
    134      * when presented with certificates in the forward direction (from
    135      * target to anchor).
    136      *
    137      * @return true if forward checking is supported, false otherwise
    138      */
    139     @Override
    140     public boolean isForwardCheckingSupported() {
    141         return false;
    142     }
    143 
    144     /**
    145      * Gets an immutable Set of the OID strings for the extensions that
    146      * the PKIXCertPathChecker supports (i.e. recognizes, is able to
    147      * process), or null if no extensions are
    148      * supported. All OID strings that a PKIXCertPathChecker might
    149      * possibly be able to process should be included.
    150      *
    151      * @return the Set of extensions supported by this PKIXCertPathChecker,
    152      * or null if no extensions are supported
    153      */
    154     @Override
    155     public Set<String> getSupportedExtensions() {
    156         if (supportedExts == null) {
    157             supportedExts = new HashSet<String>(4);
    158             supportedExts.add(CertificatePolicies_Id.toString());
    159             supportedExts.add(PolicyMappings_Id.toString());
    160             supportedExts.add(PolicyConstraints_Id.toString());
    161             supportedExts.add(InhibitAnyPolicy_Id.toString());
    162             supportedExts = Collections.unmodifiableSet(supportedExts);
    163         }
    164         return supportedExts;
    165     }
    166 
    167     /**
    168      * Performs the policy processing checks on the certificate using its
    169      * internal state.
    170      *
    171      * @param cert the Certificate to be processed
    172      * @param unresCritExts the unresolved critical extensions
    173      * @throws CertPathValidatorException if the certificate does not verify
    174      */
    175     @Override
    176     public void check(Certificate cert, Collection<String> unresCritExts)
    177         throws CertPathValidatorException
    178     {
    179         // now do the policy checks
    180         checkPolicy((X509Certificate) cert);
    181 
    182         if (unresCritExts != null && !unresCritExts.isEmpty()) {
    183             unresCritExts.remove(CertificatePolicies_Id.toString());
    184             unresCritExts.remove(PolicyMappings_Id.toString());
    185             unresCritExts.remove(PolicyConstraints_Id.toString());
    186             unresCritExts.remove(InhibitAnyPolicy_Id.toString());
    187         }
    188     }
    189 
    190     /**
    191      * Internal method to run through all the checks.
    192      *
    193      * @param currCert the certificate to be processed
    194      * @exception CertPathValidatorException Exception thrown if
    195      * the certificate does not verify
    196      */
    197     private void checkPolicy(X509Certificate currCert)
    198         throws CertPathValidatorException
    199     {
    200         String msg = "certificate policies";
    201         if (debug != null) {
    202             debug.println("PolicyChecker.checkPolicy() ---checking " + msg
    203                 + "...");
    204             debug.println("PolicyChecker.checkPolicy() certIndex = "
    205                 + certIndex);
    206             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
    207                 + "explicitPolicy = " + explicitPolicy);
    208             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
    209                 + "policyMapping = " + policyMapping);
    210             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
    211                 + "inhibitAnyPolicy = " + inhibitAnyPolicy);
    212             debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: "
    213                 + "policyTree = " + rootNode);
    214         }
    215 
    216         X509CertImpl currCertImpl = null;
    217         try {
    218             currCertImpl = X509CertImpl.toImpl(currCert);
    219         } catch (CertificateException ce) {
    220             throw new CertPathValidatorException(ce);
    221         }
    222 
    223         boolean finalCert = (certIndex == certPathLen);
    224 
    225         rootNode = processPolicies(certIndex, initPolicies, explicitPolicy,
    226             policyMapping, inhibitAnyPolicy, rejectPolicyQualifiers, rootNode,
    227             currCertImpl, finalCert);
    228 
    229         if (!finalCert) {
    230             explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCertImpl,
    231                                                  finalCert);
    232             policyMapping = mergePolicyMapping(policyMapping, currCertImpl);
    233             inhibitAnyPolicy = mergeInhibitAnyPolicy(inhibitAnyPolicy,
    234                                                      currCertImpl);
    235         }
    236 
    237         certIndex++;
    238 
    239         if (debug != null) {
    240             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
    241                 + "explicitPolicy = " + explicitPolicy);
    242             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
    243                 + "policyMapping = " + policyMapping);
    244             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
    245                 + "inhibitAnyPolicy = " + inhibitAnyPolicy);
    246             debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: "
    247                 + "policyTree = " + rootNode);
    248             debug.println("PolicyChecker.checkPolicy() " + msg + " verified");
    249         }
    250     }
    251 
    252     /**
    253      * Merges the specified explicitPolicy value with the
    254      * requireExplicitPolicy field of the <code>PolicyConstraints</code>
    255      * extension obtained from the certificate. An explicitPolicy
    256      * value of -1 implies no constraint.
    257      *
    258      * @param explicitPolicy an integer which indicates if a non-null
    259      * valid policy tree is required
    260      * @param currCert the Certificate to be processed
    261      * @param finalCert a boolean indicating whether currCert is
    262      * the final cert in the cert path
    263      * @return returns the new explicitPolicy value
    264      * @exception CertPathValidatorException Exception thrown if an error
    265      * occurs
    266      */
    267     static int mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert,
    268         boolean finalCert) throws CertPathValidatorException
    269     {
    270         if ((explicitPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
    271             explicitPolicy--;
    272         }
    273 
    274         try {
    275             PolicyConstraintsExtension polConstExt
    276                 = currCert.getPolicyConstraintsExtension();
    277             if (polConstExt == null)
    278                 return explicitPolicy;
    279             int require =
    280                 polConstExt.get(PolicyConstraintsExtension.REQUIRE).intValue();
    281             if (debug != null) {
    282                 debug.println("PolicyChecker.mergeExplicitPolicy() "
    283                    + "require Index from cert = " + require);
    284             }
    285             if (!finalCert) {
    286                 if (require != -1) {
    287                     if ((explicitPolicy == -1) || (require < explicitPolicy)) {
    288                         explicitPolicy = require;
    289                     }
    290                 }
    291             } else {
    292                 if (require == 0)
    293                     explicitPolicy = require;
    294             }
    295         } catch (IOException e) {
    296             if (debug != null) {
    297                 debug.println("PolicyChecker.mergeExplicitPolicy "
    298                               + "unexpected exception");
    299                 e.printStackTrace();
    300             }
    301             throw new CertPathValidatorException(e);
    302         }
    303 
    304         return explicitPolicy;
    305     }
    306 
    307     /**
    308      * Merges the specified policyMapping value with the
    309      * inhibitPolicyMapping field of the <code>PolicyConstraints</code>
    310      * extension obtained from the certificate. A policyMapping
    311      * value of -1 implies no constraint.
    312      *
    313      * @param policyMapping an integer which indicates if policy mapping
    314      * is inhibited
    315      * @param currCert the Certificate to be processed
    316      * @return returns the new policyMapping value
    317      * @exception CertPathValidatorException Exception thrown if an error
    318      * occurs
    319      */
    320     static int mergePolicyMapping(int policyMapping, X509CertImpl currCert)
    321         throws CertPathValidatorException
    322     {
    323         if ((policyMapping > 0) && !X509CertImpl.isSelfIssued(currCert)) {
    324             policyMapping--;
    325         }
    326 
    327         try {
    328             PolicyConstraintsExtension polConstExt
    329                 = currCert.getPolicyConstraintsExtension();
    330             if (polConstExt == null)
    331                 return policyMapping;
    332 
    333             int inhibit =
    334                 polConstExt.get(PolicyConstraintsExtension.INHIBIT).intValue();
    335             if (debug != null)
    336                 debug.println("PolicyChecker.mergePolicyMapping() "
    337                     + "inhibit Index from cert = " + inhibit);
    338 
    339             if (inhibit != -1) {
    340                 if ((policyMapping == -1) || (inhibit < policyMapping)) {
    341                     policyMapping = inhibit;
    342                 }
    343             }
    344         } catch (IOException e) {
    345             if (debug != null) {
    346                 debug.println("PolicyChecker.mergePolicyMapping "
    347                               + "unexpected exception");
    348                 e.printStackTrace();
    349             }
    350             throw new CertPathValidatorException(e);
    351         }
    352 
    353         return policyMapping;
    354     }
    355 
    356     /**
    357      * Merges the specified inhibitAnyPolicy value with the
    358      * SkipCerts value of the InhibitAnyPolicy
    359      * extension obtained from the certificate.
    360      *
    361      * @param inhibitAnyPolicy an integer which indicates whether
    362      * "any-policy" is considered a match
    363      * @param currCert the Certificate to be processed
    364      * @return returns the new inhibitAnyPolicy value
    365      * @exception CertPathValidatorException Exception thrown if an error
    366      * occurs
    367      */
    368     static int mergeInhibitAnyPolicy(int inhibitAnyPolicy,
    369         X509CertImpl currCert) throws CertPathValidatorException
    370     {
    371         if ((inhibitAnyPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) {
    372             inhibitAnyPolicy--;
    373         }
    374 
    375         try {
    376             InhibitAnyPolicyExtension inhAnyPolExt = (InhibitAnyPolicyExtension)
    377                 currCert.getExtension(InhibitAnyPolicy_Id);
    378             if (inhAnyPolExt == null)
    379                 return inhibitAnyPolicy;
    380 
    381             int skipCerts =
    382                 inhAnyPolExt.get(InhibitAnyPolicyExtension.SKIP_CERTS).intValue();
    383             if (debug != null)
    384                 debug.println("PolicyChecker.mergeInhibitAnyPolicy() "
    385                     + "skipCerts Index from cert = " + skipCerts);
    386 
    387             if (skipCerts != -1) {
    388                 if (skipCerts < inhibitAnyPolicy) {
    389                     inhibitAnyPolicy = skipCerts;
    390                 }
    391             }
    392         } catch (IOException e) {
    393             if (debug != null) {
    394                 debug.println("PolicyChecker.mergeInhibitAnyPolicy "
    395                               + "unexpected exception");
    396                 e.printStackTrace();
    397             }
    398             throw new CertPathValidatorException(e);
    399         }
    400 
    401         return inhibitAnyPolicy;
    402     }
    403 
    404     /**
    405      * Processes certificate policies in the certificate.
    406      *
    407      * @param certIndex the index of the certificate
    408      * @param initPolicies the initial policies required by the user
    409      * @param explicitPolicy an integer which indicates if a non-null
    410      * valid policy tree is required
    411      * @param policyMapping an integer which indicates if policy
    412      * mapping is inhibited
    413      * @param inhibitAnyPolicy an integer which indicates whether
    414      * "any-policy" is considered a match
    415      * @param rejectPolicyQualifiers a boolean indicating whether the
    416      * user wants to reject policies that have qualifiers
    417      * @param origRootNode the root node of the valid policy tree
    418      * @param currCert the Certificate to be processed
    419      * @param finalCert a boolean indicating whether currCert is the final
    420      * cert in the cert path
    421      * @return the root node of the valid policy tree after modification
    422      * @exception CertPathValidatorException Exception thrown if an
    423      * error occurs while processing policies.
    424      */
    425     static PolicyNodeImpl processPolicies(int certIndex, Set<String> initPolicies,
    426         int explicitPolicy, int policyMapping, int inhibitAnyPolicy,
    427         boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode,
    428         X509CertImpl currCert, boolean finalCert)
    429         throws CertPathValidatorException
    430     {
    431         boolean policiesCritical = false;
    432         List<PolicyInformation> policyInfo;
    433         PolicyNodeImpl rootNode = null;
    434         Set<PolicyQualifierInfo> anyQuals = new HashSet<>();
    435 
    436         if (origRootNode == null)
    437             rootNode = null;
    438         else
    439             rootNode = origRootNode.copyTree();
    440 
    441         // retrieve policyOIDs from currCert
    442         CertificatePoliciesExtension currCertPolicies
    443             = currCert.getCertificatePoliciesExtension();
    444 
    445         // PKIX: Section 6.1.3: Step (d)
    446         if ((currCertPolicies != null) && (rootNode != null)) {
    447             policiesCritical = currCertPolicies.isCritical();
    448             if (debug != null)
    449                 debug.println("PolicyChecker.processPolicies() "
    450                     + "policiesCritical = " + policiesCritical);
    451 
    452             try {
    453                 policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
    454             } catch (IOException ioe) {
    455                 throw new CertPathValidatorException("Exception while "
    456                     + "retrieving policyOIDs", ioe);
    457             }
    458 
    459             if (debug != null)
    460                 debug.println("PolicyChecker.processPolicies() "
    461                     + "rejectPolicyQualifiers = " + rejectPolicyQualifiers);
    462 
    463             boolean foundAnyPolicy = false;
    464 
    465             // process each policy in cert
    466             for (PolicyInformation curPolInfo : policyInfo) {
    467                 String curPolicy =
    468                     curPolInfo.getPolicyIdentifier().getIdentifier().toString();
    469 
    470                 if (curPolicy.equals(ANY_POLICY)) {
    471                     foundAnyPolicy = true;
    472                     anyQuals = curPolInfo.getPolicyQualifiers();
    473                 } else {
    474                     // PKIX: Section 6.1.3: Step (d)(1)
    475                     if (debug != null)
    476                         debug.println("PolicyChecker.processPolicies() "
    477                                       + "processing policy: " + curPolicy);
    478 
    479                     // retrieve policy qualifiers from cert
    480                     Set<PolicyQualifierInfo> pQuals =
    481                                         curPolInfo.getPolicyQualifiers();
    482 
    483                     // reject cert if we find critical policy qualifiers and
    484                     // the policyQualifiersRejected flag is set in the params
    485                     if (!pQuals.isEmpty() && rejectPolicyQualifiers &&
    486                         policiesCritical) {
    487                         throw new CertPathValidatorException(
    488                             "critical policy qualifiers present in certificate",
    489                             null, null, -1, PKIXReason.INVALID_POLICY);
    490                     }
    491 
    492                     // PKIX: Section 6.1.3: Step (d)(1)(i)
    493                     boolean foundMatch = processParents(certIndex,
    494                         policiesCritical, rejectPolicyQualifiers, rootNode,
    495                         curPolicy, pQuals, false);
    496 
    497                     if (!foundMatch) {
    498                         // PKIX: Section 6.1.3: Step (d)(1)(ii)
    499                         processParents(certIndex, policiesCritical,
    500                             rejectPolicyQualifiers, rootNode, curPolicy,
    501                             pQuals, true);
    502                     }
    503                 }
    504             }
    505 
    506             // PKIX: Section 6.1.3: Step (d)(2)
    507             if (foundAnyPolicy) {
    508                 if ((inhibitAnyPolicy > 0) ||
    509                         (!finalCert && X509CertImpl.isSelfIssued(currCert))) {
    510                     if (debug != null) {
    511                         debug.println("PolicyChecker.processPolicies() "
    512                             + "processing policy: " + ANY_POLICY);
    513                     }
    514                     processParents(certIndex, policiesCritical,
    515                         rejectPolicyQualifiers, rootNode, ANY_POLICY, anyQuals,
    516                         true);
    517                 }
    518             }
    519 
    520             // PKIX: Section 6.1.3: Step (d)(3)
    521             rootNode.prune(certIndex);
    522             if (!rootNode.getChildren().hasNext()) {
    523                 rootNode = null;
    524             }
    525         } else if (currCertPolicies == null) {
    526             if (debug != null)
    527                 debug.println("PolicyChecker.processPolicies() "
    528                               + "no policies present in cert");
    529             // PKIX: Section 6.1.3: Step (e)
    530             rootNode = null;
    531         }
    532 
    533         // We delay PKIX: Section 6.1.3: Step (f) to the end
    534         // because the code that follows may delete some nodes
    535         // resulting in a null tree
    536         if (rootNode != null) {
    537             if (!finalCert) {
    538                 // PKIX: Section 6.1.4: Steps (a)-(b)
    539                 rootNode = processPolicyMappings(currCert, certIndex,
    540                     policyMapping, rootNode, policiesCritical, anyQuals);
    541             }
    542         }
    543 
    544         // At this point, we optimize the PKIX algorithm by
    545         // removing those nodes which would later have
    546         // been removed by PKIX: Section 6.1.5: Step (g)(iii)
    547 
    548         if ((rootNode != null) && (!initPolicies.contains(ANY_POLICY))
    549             && (currCertPolicies != null)) {
    550             rootNode = removeInvalidNodes(rootNode, certIndex,
    551                                           initPolicies, currCertPolicies);
    552 
    553             // PKIX: Section 6.1.5: Step (g)(iii)
    554             if ((rootNode != null) && finalCert) {
    555                 // rewrite anyPolicy leaf nodes (see method comments)
    556                 rootNode = rewriteLeafNodes(certIndex, initPolicies, rootNode);
    557             }
    558         }
    559 
    560 
    561         if (finalCert) {
    562             // PKIX: Section 6.1.5: Steps (a) and (b)
    563             explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCert,
    564                                              finalCert);
    565         }
    566 
    567         // PKIX: Section 6.1.3: Step (f)
    568         // verify that either explicit policy is greater than 0 or
    569         // the valid_policy_tree is not equal to NULL
    570 
    571         if ((explicitPolicy == 0) && (rootNode == null)) {
    572             throw new CertPathValidatorException
    573                 ("non-null policy tree required and policy tree is null",
    574                  null, null, -1, PKIXReason.INVALID_POLICY);
    575         }
    576 
    577         return rootNode;
    578     }
    579 
    580     /**
    581      * Rewrite leaf nodes at the end of validation as described in RFC 3280
    582      * section 6.1.5: Step (g)(iii). Leaf nodes with anyPolicy are replaced
    583      * by nodes explicitly representing initial policies not already
    584      * represented by leaf nodes.
    585      *
    586      * This method should only be called when processing the final cert
    587      * and if the policy tree is not null and initial policies is not
    588      * anyPolicy.
    589      *
    590      * @param certIndex the depth of the tree
    591      * @param initPolicies Set of user specified initial policies
    592      * @param rootNode the root of the policy tree
    593      */
    594     private static PolicyNodeImpl rewriteLeafNodes(int certIndex,
    595             Set<String> initPolicies, PolicyNodeImpl rootNode) {
    596         Set<PolicyNodeImpl> anyNodes =
    597                         rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
    598         if (anyNodes.isEmpty()) {
    599             return rootNode;
    600         }
    601         PolicyNodeImpl anyNode = anyNodes.iterator().next();
    602         PolicyNodeImpl parentNode = (PolicyNodeImpl)anyNode.getParent();
    603         parentNode.deleteChild(anyNode);
    604         // see if there are any initialPolicies not represented by leaf nodes
    605         Set<String> initial = new HashSet<>(initPolicies);
    606         for (PolicyNodeImpl node : rootNode.getPolicyNodes(certIndex)) {
    607             initial.remove(node.getValidPolicy());
    608         }
    609         if (initial.isEmpty()) {
    610             // we deleted the anyPolicy node and have nothing to re-add,
    611             // so we need to prune the tree
    612             rootNode.prune(certIndex);
    613             if (rootNode.getChildren().hasNext() == false) {
    614                 rootNode = null;
    615             }
    616         } else {
    617             boolean anyCritical = anyNode.isCritical();
    618             Set<PolicyQualifierInfo> anyQualifiers =
    619                                                 anyNode.getPolicyQualifiers();
    620             for (String policy : initial) {
    621                 Set<String> expectedPolicies = Collections.singleton(policy);
    622                 PolicyNodeImpl node = new PolicyNodeImpl(parentNode, policy,
    623                     anyQualifiers, anyCritical, expectedPolicies, false);
    624             }
    625         }
    626         return rootNode;
    627     }
    628 
    629     /**
    630      * Finds the policy nodes of depth (certIndex-1) where curPolicy
    631      * is in the expected policy set and creates a new child node
    632      * appropriately. If matchAny is true, then a value of ANY_POLICY
    633      * in the expected policy set will match any curPolicy. If matchAny
    634      * is false, then the expected policy set must exactly contain the
    635      * curPolicy to be considered a match. This method returns a boolean
    636      * value indicating whether a match was found.
    637      *
    638      * @param certIndex the index of the certificate whose policy is
    639      * being processed
    640      * @param policiesCritical a boolean indicating whether the certificate
    641      * policies extension is critical
    642      * @param rejectPolicyQualifiers a boolean indicating whether the
    643      * user wants to reject policies that have qualifiers
    644      * @param rootNode the root node of the valid policy tree
    645      * @param curPolicy a String representing the policy being processed
    646      * @param pQuals the policy qualifiers of the policy being processed or an
    647      * empty Set if there are no qualifiers
    648      * @param matchAny a boolean indicating whether a value of ANY_POLICY
    649      * in the expected policy set will be considered a match
    650      * @return a boolean indicating whether a match was found
    651      * @exception CertPathValidatorException Exception thrown if error occurs.
    652      */
    653     private static boolean processParents(int certIndex,
    654         boolean policiesCritical, boolean rejectPolicyQualifiers,
    655         PolicyNodeImpl rootNode, String curPolicy,
    656         Set<PolicyQualifierInfo> pQuals,
    657         boolean matchAny) throws CertPathValidatorException
    658     {
    659         boolean foundMatch = false;
    660 
    661         if (debug != null)
    662             debug.println("PolicyChecker.processParents(): matchAny = "
    663                 + matchAny);
    664 
    665         // find matching parents
    666         Set<PolicyNodeImpl> parentNodes =
    667                 rootNode.getPolicyNodesExpected(certIndex - 1,
    668                                                 curPolicy, matchAny);
    669 
    670         // for each matching parent, extend policy tree
    671         for (PolicyNodeImpl curParent : parentNodes) {
    672             if (debug != null)
    673                 debug.println("PolicyChecker.processParents() "
    674                               + "found parent:\n" + curParent.asString());
    675 
    676             foundMatch = true;
    677             String curParPolicy = curParent.getValidPolicy();
    678 
    679             PolicyNodeImpl curNode = null;
    680             Set<String> curExpPols = null;
    681 
    682             if (curPolicy.equals(ANY_POLICY)) {
    683                 // do step 2
    684                 Set<String> parExpPols = curParent.getExpectedPolicies();
    685             parentExplicitPolicies:
    686                 for (String curParExpPol : parExpPols) {
    687 
    688                     Iterator<PolicyNodeImpl> childIter =
    689                                         curParent.getChildren();
    690                     while (childIter.hasNext()) {
    691                         PolicyNodeImpl childNode = childIter.next();
    692                         String childPolicy = childNode.getValidPolicy();
    693                         if (curParExpPol.equals(childPolicy)) {
    694                             if (debug != null)
    695                                 debug.println(childPolicy + " in parent's "
    696                                     + "expected policy set already appears in "
    697                                     + "child node");
    698                             continue parentExplicitPolicies;
    699                         }
    700                     }
    701 
    702                     Set<String> expPols = new HashSet<>();
    703                     expPols.add(curParExpPol);
    704 
    705                     curNode = new PolicyNodeImpl
    706                         (curParent, curParExpPol, pQuals,
    707                          policiesCritical, expPols, false);
    708                 }
    709             } else {
    710                 curExpPols = new HashSet<String>();
    711                 curExpPols.add(curPolicy);
    712 
    713                 curNode = new PolicyNodeImpl
    714                     (curParent, curPolicy, pQuals,
    715                      policiesCritical, curExpPols, false);
    716             }
    717         }
    718 
    719         return foundMatch;
    720     }
    721 
    722     /**
    723      * Processes policy mappings in the certificate.
    724      *
    725      * @param currCert the Certificate to be processed
    726      * @param certIndex the index of the current certificate
    727      * @param policyMapping an integer which indicates if policy
    728      * mapping is inhibited
    729      * @param rootNode the root node of the valid policy tree
    730      * @param policiesCritical a boolean indicating if the certificate policies
    731      * extension is critical
    732      * @param anyQuals the qualifiers associated with ANY-POLICY, or an empty
    733      * Set if there are no qualifiers associated with ANY-POLICY
    734      * @return the root node of the valid policy tree after modification
    735      * @exception CertPathValidatorException exception thrown if an error
    736      * occurs while processing policy mappings
    737      */
    738     private static PolicyNodeImpl processPolicyMappings(X509CertImpl currCert,
    739         int certIndex, int policyMapping, PolicyNodeImpl rootNode,
    740         boolean policiesCritical, Set<PolicyQualifierInfo> anyQuals)
    741         throws CertPathValidatorException
    742     {
    743         PolicyMappingsExtension polMappingsExt
    744             = currCert.getPolicyMappingsExtension();
    745 
    746         if (polMappingsExt == null)
    747             return rootNode;
    748 
    749         if (debug != null)
    750             debug.println("PolicyChecker.processPolicyMappings() "
    751                 + "inside policyMapping check");
    752 
    753         List<CertificatePolicyMap> maps = null;
    754         try {
    755             maps = polMappingsExt.get(PolicyMappingsExtension.MAP);
    756         } catch (IOException e) {
    757             if (debug != null) {
    758                 debug.println("PolicyChecker.processPolicyMappings() "
    759                     + "mapping exception");
    760                 e.printStackTrace();
    761             }
    762             throw new CertPathValidatorException("Exception while checking "
    763                                                  + "mapping", e);
    764         }
    765 
    766         boolean childDeleted = false;
    767         for (CertificatePolicyMap polMap : maps) {
    768             String issuerDomain
    769                 = polMap.getIssuerIdentifier().getIdentifier().toString();
    770             String subjectDomain
    771                 = polMap.getSubjectIdentifier().getIdentifier().toString();
    772             if (debug != null) {
    773                 debug.println("PolicyChecker.processPolicyMappings() "
    774                               + "issuerDomain = " + issuerDomain);
    775                 debug.println("PolicyChecker.processPolicyMappings() "
    776                               + "subjectDomain = " + subjectDomain);
    777             }
    778 
    779             if (issuerDomain.equals(ANY_POLICY)) {
    780                 throw new CertPathValidatorException
    781                     ("encountered an issuerDomainPolicy of ANY_POLICY",
    782                      null, null, -1, PKIXReason.INVALID_POLICY);
    783             }
    784 
    785             if (subjectDomain.equals(ANY_POLICY)) {
    786                 throw new CertPathValidatorException
    787                     ("encountered a subjectDomainPolicy of ANY_POLICY",
    788                      null, null, -1, PKIXReason.INVALID_POLICY);
    789             }
    790 
    791             Set<PolicyNodeImpl> validNodes =
    792                 rootNode.getPolicyNodesValid(certIndex, issuerDomain);
    793             if (!validNodes.isEmpty()) {
    794                 for (PolicyNodeImpl curNode : validNodes) {
    795                     if ((policyMapping > 0) || (policyMapping == -1)) {
    796                         curNode.addExpectedPolicy(subjectDomain);
    797                     } else if (policyMapping == 0) {
    798                         PolicyNodeImpl parentNode =
    799                             (PolicyNodeImpl) curNode.getParent();
    800                         if (debug != null)
    801                             debug.println("PolicyChecker.processPolicyMappings"
    802                                 + "() before deleting: policy tree = "
    803                                 + rootNode);
    804                         parentNode.deleteChild(curNode);
    805                         childDeleted = true;
    806                         if (debug != null)
    807                             debug.println("PolicyChecker.processPolicyMappings"
    808                                 + "() after deleting: policy tree = "
    809                                 + rootNode);
    810                     }
    811                 }
    812             } else { // no node of depth i has a valid policy
    813                 if ((policyMapping > 0) || (policyMapping == -1)) {
    814                     Set<PolicyNodeImpl> validAnyNodes =
    815                         rootNode.getPolicyNodesValid(certIndex, ANY_POLICY);
    816                     for (PolicyNodeImpl curAnyNode : validAnyNodes) {
    817                         PolicyNodeImpl curAnyNodeParent =
    818                             (PolicyNodeImpl) curAnyNode.getParent();
    819 
    820                         Set<String> expPols = new HashSet<>();
    821                         expPols.add(subjectDomain);
    822 
    823                         PolicyNodeImpl curNode = new PolicyNodeImpl
    824                             (curAnyNodeParent, issuerDomain, anyQuals,
    825                              policiesCritical, expPols, true);
    826                     }
    827                 }
    828             }
    829         }
    830 
    831         if (childDeleted) {
    832             rootNode.prune(certIndex);
    833             if (!rootNode.getChildren().hasNext()) {
    834                 if (debug != null)
    835                     debug.println("setting rootNode to null");
    836                 rootNode = null;
    837             }
    838         }
    839 
    840         return rootNode;
    841     }
    842 
    843     /**
    844      * Removes those nodes which do not intersect with the initial policies
    845      * specified by the user.
    846      *
    847      * @param rootNode the root node of the valid policy tree
    848      * @param certIndex the index of the certificate being processed
    849      * @param initPolicies the Set of policies required by the user
    850      * @param currCertPolicies the CertificatePoliciesExtension of the
    851      * certificate being processed
    852      * @returns the root node of the valid policy tree after modification
    853      * @exception CertPathValidatorException Exception thrown if error occurs.
    854      */
    855     private static PolicyNodeImpl removeInvalidNodes(PolicyNodeImpl rootNode,
    856         int certIndex, Set<String> initPolicies,
    857         CertificatePoliciesExtension currCertPolicies)
    858         throws CertPathValidatorException
    859     {
    860         List<PolicyInformation> policyInfo = null;
    861         try {
    862             policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES);
    863         } catch (IOException ioe) {
    864             throw new CertPathValidatorException("Exception while "
    865                 + "retrieving policyOIDs", ioe);
    866         }
    867 
    868         boolean childDeleted = false;
    869         for (PolicyInformation curPolInfo : policyInfo) {
    870             String curPolicy =
    871                 curPolInfo.getPolicyIdentifier().getIdentifier().toString();
    872 
    873             if (debug != null)
    874                 debug.println("PolicyChecker.processPolicies() "
    875                               + "processing policy second time: " + curPolicy);
    876 
    877             Set<PolicyNodeImpl> validNodes =
    878                         rootNode.getPolicyNodesValid(certIndex, curPolicy);
    879             for (PolicyNodeImpl curNode : validNodes) {
    880                 PolicyNodeImpl parentNode = (PolicyNodeImpl)curNode.getParent();
    881                 if (parentNode.getValidPolicy().equals(ANY_POLICY)) {
    882                     if ((!initPolicies.contains(curPolicy)) &&
    883                         (!curPolicy.equals(ANY_POLICY))) {
    884                         if (debug != null)
    885                             debug.println("PolicyChecker.processPolicies() "
    886                                 + "before deleting: policy tree = " + rootNode);
    887                         parentNode.deleteChild(curNode);
    888                         childDeleted = true;
    889                         if (debug != null)
    890                             debug.println("PolicyChecker.processPolicies() "
    891                                 + "after deleting: policy tree = " + rootNode);
    892                     }
    893                 }
    894             }
    895         }
    896 
    897         if (childDeleted) {
    898             rootNode.prune(certIndex);
    899             if (!rootNode.getChildren().hasNext()) {
    900                 rootNode = null;
    901             }
    902         }
    903 
    904         return rootNode;
    905     }
    906 
    907     /**
    908      * Gets the root node of the valid policy tree, or null if the
    909      * valid policy tree is null. Marks each node of the returned tree
    910      * immutable and thread-safe.
    911      *
    912      * @returns the root node of the valid policy tree, or null if
    913      * the valid policy tree is null
    914      */
    915     PolicyNode getPolicyTree() {
    916         if (rootNode == null)
    917             return null;
    918         else {
    919             PolicyNodeImpl policyTree = rootNode.copyTree();
    920             policyTree.setImmutable();
    921             return policyTree;
    922         }
    923     }
    924 }
    925