blob: faa472f84d1f120997f1f5a3dd78ecba0790f573 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.provider.certpath;
27
28import sun.security.util.Debug;
29
30import java.util.Collections;
31import java.util.List;
32import java.util.Set;
33import java.util.Iterator;
34import java.security.cert.CertPath;
35import java.security.cert.CertPathValidatorException;
36import java.security.cert.CertificateRevokedException;
37import java.security.cert.PKIXCertPathChecker;
38import java.security.cert.X509Certificate;
39
40/**
41 * This class is initialized with a list of <code>PKIXCertPathChecker</code>s
42 * and is used to verify the certificates in a <code>CertPath</code> by
43 * feeding each certificate to each <code>PKIXCertPathChecker</code>.
44 *
45 * @since 1.4
46 * @author Yassir Elley
47 */
48class PKIXMasterCertPathValidator {
49
50 private static final Debug debug = Debug.getInstance("certpath");
51 private List<PKIXCertPathChecker> certPathCheckers;
52
53 /**
54 * Initializes the list of PKIXCertPathCheckers whose checks
55 * will be performed on each certificate in the certpath.
56 *
57 * @param certPathCheckers a List of checkers to use
58 */
59 PKIXMasterCertPathValidator(List<PKIXCertPathChecker> certPathCheckers) {
60 this.certPathCheckers = certPathCheckers;
61 }
62
63 /**
64 * Validates a certification path consisting exclusively of
65 * <code>X509Certificate</code>s using the
66 * <code>PKIXCertPathChecker</code>s specified
67 * in the constructor. It is assumed that the
68 * <code>PKIXCertPathChecker</code>s
69 * have been initialized with any input parameters they may need.
70 *
71 * @param cpOriginal the original X509 CertPath passed in by the user
72 * @param reversedCertList the reversed X509 CertPath (as a List)
73 * @exception CertPathValidatorException Exception thrown if cert
74 * path does not validate.
75 */
76 void validate(CertPath cpOriginal, List<X509Certificate> reversedCertList)
77 throws CertPathValidatorException
78 {
79 // we actually process reversedCertList, but we keep cpOriginal because
80 // we need to return the original certPath when we throw an exception.
81 // we will also need to modify the index appropriately when we
82 // throw an exception.
83
84 int cpSize = reversedCertList.size();
85
86 if (debug != null) {
87 debug.println("--------------------------------------------------"
88 + "------------");
89 debug.println("Executing PKIX certification path validation "
90 + "algorithm.");
91 }
92
93 for (int i = 0; i < cpSize; i++) {
94
95 /* The basic loop algorithm is that we get the
96 * current certificate, we verify the current certificate using
97 * information from the previous certificate and from the state,
98 * and we modify the state for the next loop by setting the
99 * current certificate of this loop to be the previous certificate
100 * of the next loop. The state is initialized during first loop.
101 */
102 if (debug != null)
103 debug.println("Checking cert" + (i+1) + " ...");
104
105 X509Certificate currCert = reversedCertList.get(i);
106 Set<String> unresolvedCritExts =
107 currCert.getCriticalExtensionOIDs();
108 if (unresolvedCritExts == null) {
109 unresolvedCritExts = Collections.<String>emptySet();
110 }
111
112 if (debug != null && !unresolvedCritExts.isEmpty()) {
113 debug.println("Set of critical extensions:");
114 for (String oid : unresolvedCritExts) {
115 debug.println(oid);
116 }
117 }
118
119 CertPathValidatorException ocspCause = null;
120 for (int j = 0; j < certPathCheckers.size(); j++) {
121
122 PKIXCertPathChecker currChecker = certPathCheckers.get(j);
123 if (debug != null) {
124 debug.println("-Using checker" + (j + 1) + " ... [" +
125 currChecker.getClass().getName() + "]");
126 }
127
128 if (i == 0)
129 currChecker.init(false);
130
131 try {
132 currChecker.check(currCert, unresolvedCritExts);
133
134 // OCSP has validated the cert so skip the CRL check
135 if (isRevocationCheck(currChecker, j, certPathCheckers)) {
136 if (debug != null) {
137 debug.println("-checker" + (j + 1) +
138 " validation succeeded");
139 }
140 j++;
141 continue; // skip
142 }
143
144 } catch (CertPathValidatorException cpve) {
145 // Throw the saved OCSP exception
146 // (when the CRL check has also failed)
147 if (ocspCause != null &&
148 currChecker instanceof CrlRevocationChecker) {
149 throw ocspCause;
150 }
151 /*
152 * Handle failover from OCSP to CRLs
153 */
154 CertPathValidatorException currentCause =
155 new CertPathValidatorException(cpve.getMessage(),
156 cpve.getCause(), cpOriginal, cpSize - (i + 1));
157
158 // Check if OCSP has confirmed that the cert was revoked
159 if (cpve.getCause() instanceof CertificateRevokedException) {
160 throw currentCause;
161 }
162 // Check if it is appropriate to failover
163 if (! isRevocationCheck(currChecker, j, certPathCheckers)) {
164 // no failover
165 throw currentCause;
166 }
167 // Save the current exception
168 // (in case the CRL check also fails)
169 ocspCause = currentCause;
170
171 // Otherwise, failover to CRLs
172 if (debug != null) {
173 debug.println(cpve.getMessage());
174 debug.println(
175 "preparing to failover (from OCSP to CRLs)");
176 }
177 }
178
179 if (debug != null)
180 debug.println("-checker" + (j+1) + " validation succeeded");
181 }
182
183 if (debug != null)
184 debug.println("checking for unresolvedCritExts");
185 if (!unresolvedCritExts.isEmpty()) {
186 throw new CertPathValidatorException("unrecognized " +
187 "critical extension(s)", null, cpOriginal, cpSize-(i+1));
188 }
189
190 if (debug != null)
191 debug.println("\ncert" + (i+1) + " validation succeeded.\n");
192 }
193
194 if (debug != null) {
195 debug.println("Cert path validation succeeded. (PKIX validation "
196 + "algorithm)");
197 debug.println("-------------------------------------------------"
198 + "-------------");
199 }
200 }
201
202 /*
203 * Examines the list of PKIX cert path checkers to determine whether
204 * both the current checker and the next checker are revocation checkers.
205 * OCSPChecker and CrlRevocationChecker are both revocation checkers.
206 */
207 private static boolean isRevocationCheck(PKIXCertPathChecker checker,
208 int index, List<PKIXCertPathChecker> checkers) {
209
210 if (checker instanceof OCSPChecker && index + 1 < checkers.size()) {
211 PKIXCertPathChecker nextChecker = checkers.get(index + 1);
212 if (nextChecker instanceof CrlRevocationChecker) {
213 return true;
214 }
215 }
216 return false;
217 }
218}