1 /* |
1 /* |
2 * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. |
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. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
28 import sun.security.util.Debug; |
28 import sun.security.util.Debug; |
29 |
29 |
30 import java.util.Collections; |
30 import java.util.Collections; |
31 import java.util.List; |
31 import java.util.List; |
32 import java.util.Set; |
32 import java.util.Set; |
33 import java.security.cert.CertificateRevokedException; |
|
34 import java.security.cert.CertPath; |
33 import java.security.cert.CertPath; |
35 import java.security.cert.CertPathValidatorException; |
34 import java.security.cert.CertPathValidatorException; |
36 import java.security.cert.CertPathValidatorException.BasicReason; |
|
37 import java.security.cert.PKIXCertPathChecker; |
35 import java.security.cert.PKIXCertPathChecker; |
38 import java.security.cert.PKIXReason; |
36 import java.security.cert.PKIXReason; |
39 import java.security.cert.X509Certificate; |
37 import java.security.cert.X509Certificate; |
40 |
38 |
41 /** |
39 /** |
47 * @author Yassir Elley |
45 * @author Yassir Elley |
48 */ |
46 */ |
49 class PKIXMasterCertPathValidator { |
47 class PKIXMasterCertPathValidator { |
50 |
48 |
51 private static final Debug debug = Debug.getInstance("certpath"); |
49 private static final Debug debug = Debug.getInstance("certpath"); |
52 private List<PKIXCertPathChecker> certPathCheckers; |
|
53 |
|
54 /** |
|
55 * Initializes the list of PKIXCertPathCheckers whose checks |
|
56 * will be performed on each certificate in the certpath. |
|
57 * |
|
58 * @param certPathCheckers a List of checkers to use |
|
59 */ |
|
60 PKIXMasterCertPathValidator(List<PKIXCertPathChecker> certPathCheckers) { |
|
61 this.certPathCheckers = certPathCheckers; |
|
62 } |
|
63 |
50 |
64 /** |
51 /** |
65 * Validates a certification path consisting exclusively of |
52 * Validates a certification path consisting exclusively of |
66 * <code>X509Certificate</code>s using the |
53 * <code>X509Certificate</code>s using the specified |
67 * <code>PKIXCertPathChecker</code>s specified |
54 * <code>PKIXCertPathChecker</code>s. It is assumed that the |
68 * in the constructor. It is assumed that the |
|
69 * <code>PKIXCertPathChecker</code>s |
55 * <code>PKIXCertPathChecker</code>s |
70 * have been initialized with any input parameters they may need. |
56 * have been initialized with any input parameters they may need. |
71 * |
57 * |
72 * @param cpOriginal the original X509 CertPath passed in by the user |
58 * @param cpOriginal the original X509 CertPath passed in by the user |
73 * @param reversedCertList the reversed X509 CertPath (as a List) |
59 * @param reversedCertList the reversed X509 CertPath (as a List) |
74 * @exception CertPathValidatorException Exception thrown if cert |
60 * @param certPathCheckers the PKIXCertPathCheckers |
75 * path does not validate. |
61 * @throws CertPathValidatorException if cert path does not validate |
76 */ |
62 */ |
77 void validate(CertPath cpOriginal, List<X509Certificate> reversedCertList) |
63 static void validate(CertPath cpOriginal, |
|
64 List<X509Certificate> reversedCertList, |
|
65 List<PKIXCertPathChecker> certPathCheckers) |
78 throws CertPathValidatorException |
66 throws CertPathValidatorException |
79 { |
67 { |
80 // we actually process reversedCertList, but we keep cpOriginal because |
68 // we actually process reversedCertList, but we keep cpOriginal because |
81 // we need to return the original certPath when we throw an exception. |
69 // we need to return the original certPath when we throw an exception. |
82 // we will also need to modify the index appropriately when we |
70 // we will also need to modify the index appropriately when we |
102 */ |
90 */ |
103 if (debug != null) |
91 if (debug != null) |
104 debug.println("Checking cert" + (i+1) + " ..."); |
92 debug.println("Checking cert" + (i+1) + " ..."); |
105 |
93 |
106 X509Certificate currCert = reversedCertList.get(i); |
94 X509Certificate currCert = reversedCertList.get(i); |
107 Set<String> unresolvedCritExts = |
95 Set<String> unresCritExts = currCert.getCriticalExtensionOIDs(); |
108 currCert.getCriticalExtensionOIDs(); |
96 if (unresCritExts == null) { |
109 if (unresolvedCritExts == null) { |
97 unresCritExts = Collections.<String>emptySet(); |
110 unresolvedCritExts = Collections.<String>emptySet(); |
|
111 } |
98 } |
112 |
99 |
113 if (debug != null && !unresolvedCritExts.isEmpty()) { |
100 if (debug != null && !unresCritExts.isEmpty()) { |
114 debug.println("Set of critical extensions:"); |
101 debug.println("Set of critical extensions:"); |
115 for (String oid : unresolvedCritExts) { |
102 for (String oid : unresCritExts) { |
116 debug.println(oid); |
103 debug.println(oid); |
117 } |
104 } |
118 } |
105 } |
119 |
106 |
120 CertPathValidatorException ocspCause = null; |
|
121 for (int j = 0; j < certPathCheckers.size(); j++) { |
107 for (int j = 0; j < certPathCheckers.size(); j++) { |
122 |
108 |
123 PKIXCertPathChecker currChecker = certPathCheckers.get(j); |
109 PKIXCertPathChecker currChecker = certPathCheckers.get(j); |
124 if (debug != null) { |
110 if (debug != null) { |
125 debug.println("-Using checker" + (j + 1) + " ... [" + |
111 debug.println("-Using checker" + (j + 1) + " ... [" + |
128 |
114 |
129 if (i == 0) |
115 if (i == 0) |
130 currChecker.init(false); |
116 currChecker.init(false); |
131 |
117 |
132 try { |
118 try { |
133 currChecker.check(currCert, unresolvedCritExts); |
119 currChecker.check(currCert, unresCritExts); |
134 |
120 |
135 // OCSP has validated the cert so skip the CRL check |
121 if (debug != null) { |
136 if (isRevocationCheck(currChecker, j, certPathCheckers)) { |
122 debug.println("-checker" + (j + 1) + |
137 if (debug != null) { |
123 " validation succeeded"); |
138 debug.println("-checker" + (j + 1) + |
|
139 " validation succeeded"); |
|
140 } |
|
141 j++; |
|
142 continue; // skip |
|
143 } |
124 } |
144 |
125 |
145 } catch (CertPathValidatorException cpve) { |
126 } catch (CertPathValidatorException cpve) { |
146 // Throw the saved OCSP exception unless the CRL |
127 throw new CertPathValidatorException(cpve.getMessage(), |
147 // checker has determined that the cert is revoked |
128 cpve.getCause(), cpOriginal, cpSize - (i + 1), |
148 if (ocspCause != null && |
129 cpve.getReason()); |
149 currChecker instanceof CrlRevocationChecker) { |
|
150 if (cpve.getReason() == BasicReason.REVOKED) { |
|
151 throw cpve; |
|
152 } else { |
|
153 throw ocspCause; |
|
154 } |
|
155 } |
|
156 /* |
|
157 * Handle failover from OCSP to CRLs |
|
158 */ |
|
159 CertPathValidatorException currentCause = |
|
160 new CertPathValidatorException(cpve.getMessage(), |
|
161 cpve.getCause(), cpOriginal, cpSize - (i + 1), |
|
162 cpve.getReason()); |
|
163 |
|
164 // Check if OCSP has confirmed that the cert was revoked |
|
165 if (cpve.getReason() == BasicReason.REVOKED) { |
|
166 throw currentCause; |
|
167 } |
|
168 // Check if it is appropriate to failover |
|
169 if (! isRevocationCheck(currChecker, j, certPathCheckers)) { |
|
170 // no failover |
|
171 throw currentCause; |
|
172 } |
|
173 // Save the current exception |
|
174 // (in case the CRL check also fails) |
|
175 ocspCause = currentCause; |
|
176 |
|
177 // Otherwise, failover to CRLs |
|
178 if (debug != null) { |
|
179 debug.println(cpve.getMessage()); |
|
180 debug.println( |
|
181 "preparing to failover (from OCSP to CRLs)"); |
|
182 } |
|
183 } |
130 } |
184 |
|
185 if (debug != null) |
|
186 debug.println("-checker" + (j+1) + " validation succeeded"); |
|
187 } |
131 } |
188 |
132 |
189 if (debug != null) |
133 if (!unresCritExts.isEmpty()) { |
190 debug.println("checking for unresolvedCritExts"); |
|
191 if (!unresolvedCritExts.isEmpty()) { |
|
192 throw new CertPathValidatorException("unrecognized " + |
134 throw new CertPathValidatorException("unrecognized " + |
193 "critical extension(s)", null, cpOriginal, cpSize-(i+1), |
135 "critical extension(s)", null, cpOriginal, cpSize-(i+1), |
194 PKIXReason.UNRECOGNIZED_CRIT_EXT); |
136 PKIXReason.UNRECOGNIZED_CRIT_EXT); |
195 } |
137 } |
196 |
138 |
198 debug.println("\ncert" + (i+1) + " validation succeeded.\n"); |
140 debug.println("\ncert" + (i+1) + " validation succeeded.\n"); |
199 } |
141 } |
200 |
142 |
201 if (debug != null) { |
143 if (debug != null) { |
202 debug.println("Cert path validation succeeded. (PKIX validation " |
144 debug.println("Cert path validation succeeded. (PKIX validation " |
203 + "algorithm)"); |
145 + "algorithm)"); |
204 debug.println("-------------------------------------------------" |
146 debug.println("-------------------------------------------------" |
205 + "-------------"); |
147 + "-------------"); |
206 } |
148 } |
207 } |
149 } |
208 |
|
209 /* |
|
210 * Examines the list of PKIX cert path checkers to determine whether |
|
211 * both the current checker and the next checker are revocation checkers. |
|
212 * OCSPChecker and CrlRevocationChecker are both revocation checkers. |
|
213 */ |
|
214 private static boolean isRevocationCheck(PKIXCertPathChecker checker, |
|
215 int index, List<PKIXCertPathChecker> checkers) { |
|
216 |
|
217 if (checker instanceof OCSPChecker && index + 1 < checkers.size()) { |
|
218 PKIXCertPathChecker nextChecker = checkers.get(index + 1); |
|
219 if (nextChecker instanceof CrlRevocationChecker) { |
|
220 return true; |
|
221 } |
|
222 } |
|
223 return false; |
|
224 } |
|
225 } |
150 } |