aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java
blob: d5f12168ddab2dbca3df7099544d7a538bd8297d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
 * Copyright 2000-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.security.provider.certpath;

import sun.security.util.Debug;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.security.cert.CertificateRevokedException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXReason;
import java.security.cert.X509Certificate;

/**
 * This class is initialized with a list of <code>PKIXCertPathChecker</code>s
 * and is used to verify the certificates in a <code>CertPath</code> by
 * feeding each certificate to each <code>PKIXCertPathChecker</code>.
 *
 * @since       1.4
 * @author      Yassir Elley
 */
class PKIXMasterCertPathValidator {

    private static final Debug debug = Debug.getInstance("certpath");
    private List<PKIXCertPathChecker> certPathCheckers;

    /**
     * Initializes the list of PKIXCertPathCheckers whose checks
     * will be performed on each certificate in the certpath.
     *
     * @param certPathCheckers a List of checkers to use
     */
    PKIXMasterCertPathValidator(List<PKIXCertPathChecker> certPathCheckers) {
        this.certPathCheckers = certPathCheckers;
    }

    /**
     * Validates a certification path consisting exclusively of
     * <code>X509Certificate</code>s using the
     * <code>PKIXCertPathChecker</code>s specified
     * in the constructor. It is assumed that the
     * <code>PKIXCertPathChecker</code>s
     * have been initialized with any input parameters they may need.
     *
     * @param cpOriginal the original X509 CertPath passed in by the user
     * @param reversedCertList the reversed X509 CertPath (as a List)
     * @exception CertPathValidatorException Exception thrown if cert
     * path does not validate.
     */
    void validate(CertPath cpOriginal, List<X509Certificate> reversedCertList)
        throws CertPathValidatorException
    {
        // we actually process reversedCertList, but we keep cpOriginal because
        // we need to return the original certPath when we throw an exception.
        // we will also need to modify the index appropriately when we
        // throw an exception.

        int cpSize = reversedCertList.size();

        if (debug != null) {
            debug.println("--------------------------------------------------"
                  + "------------");
            debug.println("Executing PKIX certification path validation "
                  + "algorithm.");
        }

        for (int i = 0; i < cpSize; i++) {

            /* The basic loop algorithm is that we get the
             * current certificate, we verify the current certificate using
             * information from the previous certificate and from the state,
             * and we modify the state for the next loop by setting the
             * current certificate of this loop to be the previous certificate
             * of the next loop. The state is initialized during first loop.
             */
            if (debug != null)
                debug.println("Checking cert" + (i+1) + " ...");

            X509Certificate currCert = reversedCertList.get(i);
            Set<String> unresolvedCritExts =
                                        currCert.getCriticalExtensionOIDs();
            if (unresolvedCritExts == null) {
                unresolvedCritExts = Collections.<String>emptySet();
            }

            if (debug != null && !unresolvedCritExts.isEmpty()) {
                debug.println("Set of critical extensions:");
                for (String oid : unresolvedCritExts) {
                    debug.println(oid);
                }
            }

            CertPathValidatorException ocspCause = null;
            for (int j = 0; j < certPathCheckers.size(); j++) {

                PKIXCertPathChecker currChecker = certPathCheckers.get(j);
                if (debug != null) {
                    debug.println("-Using checker" + (j + 1) + " ... [" +
                        currChecker.getClass().getName() + "]");
                }

                if (i == 0)
                    currChecker.init(false);

                try {
                    currChecker.check(currCert, unresolvedCritExts);

                    // OCSP has validated the cert so skip the CRL check
                    if (isRevocationCheck(currChecker, j, certPathCheckers)) {
                        if (debug != null) {
                            debug.println("-checker" + (j + 1) +
                                " validation succeeded");
                        }
                        j++;
                        continue; // skip
                    }

                } catch (CertPathValidatorException cpve) {
                    // Throw the saved OCSP exception
                    // (when the CRL check has also failed)
                    if (ocspCause != null &&
                        currChecker instanceof CrlRevocationChecker) {
                        throw ocspCause;
                    }
                    /*
                     * Handle failover from OCSP to CRLs
                     */
                    CertPathValidatorException currentCause =
                        new CertPathValidatorException(cpve.getMessage(),
                            cpve.getCause(), cpOriginal, cpSize - (i + 1),
                            cpve.getReason());

                    // Check if OCSP has confirmed that the cert was revoked
                    if (cpve.getReason() == BasicReason.REVOKED) {
                        throw currentCause;
                    }
                    // Check if it is appropriate to failover
                    if (! isRevocationCheck(currChecker, j, certPathCheckers)) {
                        // no failover
                        throw currentCause;
                    }
                    // Save the current exception
                    // (in case the CRL check also fails)
                    ocspCause = currentCause;

                    // Otherwise, failover to CRLs
                    if (debug != null) {
                        debug.println(cpve.getMessage());
                        debug.println(
                            "preparing to failover (from OCSP to CRLs)");
                    }
                }

                if (debug != null)
                    debug.println("-checker" + (j+1) + " validation succeeded");
            }

            if (debug != null)
                debug.println("checking for unresolvedCritExts");
            if (!unresolvedCritExts.isEmpty()) {
                throw new CertPathValidatorException("unrecognized " +
                    "critical extension(s)", null, cpOriginal, cpSize-(i+1),
                    PKIXReason.UNRECOGNIZED_CRIT_EXT);
            }

            if (debug != null)
                debug.println("\ncert" + (i+1) + " validation succeeded.\n");
        }

        if (debug != null) {
            debug.println("Cert path validation succeeded. (PKIX validation "
                    + "algorithm)");
            debug.println("-------------------------------------------------"
                    + "-------------");
        }
    }

    /*
     * Examines the list of PKIX cert path checkers to determine whether
     * both the current checker and the next checker are revocation checkers.
     * OCSPChecker and CrlRevocationChecker are both revocation checkers.
     */
    private static boolean isRevocationCheck(PKIXCertPathChecker checker,
        int index, List<PKIXCertPathChecker> checkers) {

        if (checker instanceof OCSPChecker && index + 1 < checkers.size()) {
            PKIXCertPathChecker nextChecker = checkers.get(index + 1);
            if (nextChecker instanceof CrlRevocationChecker) {
                return true;
            }
        }
        return false;
    }
}