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