29 |
29 |
30 import java.security.*; |
30 import java.security.*; |
31 import java.security.cert.*; |
31 import java.security.cert.*; |
32 |
32 |
33 import javax.security.auth.x500.X500Principal; |
33 import javax.security.auth.x500.X500Principal; |
|
34 import sun.security.action.GetBooleanAction; |
|
35 import sun.security.provider.certpath.AlgorithmChecker; |
34 |
36 |
35 /** |
37 /** |
36 * Validator implementation built on the PKIX CertPath API. This |
38 * Validator implementation built on the PKIX CertPath API. This |
37 * implementation will be emphasized going forward.<p> |
39 * implementation will be emphasized going forward.<p> |
38 * |
40 * <p> |
39 * Note that the validate() implementation tries to use a PKIX validator |
41 * Note that the validate() implementation tries to use a PKIX validator |
40 * if that appears possible and a PKIX builder otherwise. This increases |
42 * if that appears possible and a PKIX builder otherwise. This increases |
41 * performance and currently also leads to better exception messages |
43 * performance and currently also leads to better exception messages |
42 * in case of failures. |
44 * in case of failures. |
|
45 * <p> |
|
46 * {@code PKIXValidator} objects are immutable once they have been created. |
|
47 * Please DO NOT add methods that can change the state of an instance once |
|
48 * it has been created. |
43 * |
49 * |
44 * @author Andreas Sterbenz |
50 * @author Andreas Sterbenz |
45 */ |
51 */ |
46 public final class PKIXValidator extends Validator { |
52 public final class PKIXValidator extends Validator { |
47 |
53 |
|
54 /** |
|
55 * Flag indicating whether to enable revocation check for the PKIX trust |
|
56 * manager. Typically, this will only work if the PKIX implementation |
|
57 * supports CRL distribution points as we do not manually setup CertStores. |
|
58 */ |
|
59 private final static boolean checkTLSRevocation = |
|
60 AccessController.doPrivileged |
|
61 (new GetBooleanAction("com.sun.net.ssl.checkRevocation")); |
|
62 |
48 // enable use of the validator if possible |
63 // enable use of the validator if possible |
49 private final static boolean TRY_VALIDATOR = true; |
64 private final static boolean TRY_VALIDATOR = true; |
50 |
65 |
51 private final Set<X509Certificate> trustedCerts; |
66 private final Set<X509Certificate> trustedCerts; |
52 private final PKIXBuilderParameters parameterTemplate; |
67 private final PKIXBuilderParameters parameterTemplate; |
53 private int certPathLength = -1; |
68 private int certPathLength = -1; |
54 |
69 |
55 // needed only for the validator |
70 // needed only for the validator |
56 private Map<X500Principal, List<PublicKey>> trustedSubjects; |
71 private final Map<X500Principal, List<PublicKey>> trustedSubjects; |
57 private CertificateFactory factory; |
72 private final CertificateFactory factory; |
58 |
73 |
59 private boolean plugin = false; |
74 private final boolean plugin; |
60 |
75 |
61 PKIXValidator(String variant, Collection<X509Certificate> trustedCerts) { |
76 PKIXValidator(String variant, Collection<X509Certificate> trustedCerts) { |
62 super(TYPE_PKIX, variant); |
77 super(TYPE_PKIX, variant); |
63 if (trustedCerts instanceof Set) { |
78 if (trustedCerts instanceof Set) { |
64 this.trustedCerts = (Set<X509Certificate>)trustedCerts; |
79 this.trustedCerts = (Set<X509Certificate>)trustedCerts; |
73 parameterTemplate = new PKIXBuilderParameters(trustAnchors, null); |
88 parameterTemplate = new PKIXBuilderParameters(trustAnchors, null); |
74 } catch (InvalidAlgorithmParameterException e) { |
89 } catch (InvalidAlgorithmParameterException e) { |
75 throw new RuntimeException("Unexpected error: " + e.toString(), e); |
90 throw new RuntimeException("Unexpected error: " + e.toString(), e); |
76 } |
91 } |
77 setDefaultParameters(variant); |
92 setDefaultParameters(variant); |
78 initCommon(); |
93 |
|
94 // initCommon(); |
|
95 if (TRY_VALIDATOR) { |
|
96 if (TRY_VALIDATOR == false) { |
|
97 return; |
|
98 } |
|
99 trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); |
|
100 for (X509Certificate cert : trustedCerts) { |
|
101 X500Principal dn = cert.getSubjectX500Principal(); |
|
102 List<PublicKey> keys; |
|
103 if (trustedSubjects.containsKey(dn)) { |
|
104 keys = trustedSubjects.get(dn); |
|
105 } else { |
|
106 keys = new ArrayList<PublicKey>(); |
|
107 trustedSubjects.put(dn, keys); |
|
108 } |
|
109 keys.add(cert.getPublicKey()); |
|
110 } |
|
111 try { |
|
112 factory = CertificateFactory.getInstance("X.509"); |
|
113 } catch (CertificateException e) { |
|
114 throw new RuntimeException("Internal error", e); |
|
115 } |
|
116 plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); |
|
117 } else { |
|
118 plugin = false; |
|
119 } |
79 } |
120 } |
80 |
121 |
81 PKIXValidator(String variant, PKIXBuilderParameters params) { |
122 PKIXValidator(String variant, PKIXBuilderParameters params) { |
82 super(TYPE_PKIX, variant); |
123 super(TYPE_PKIX, variant); |
83 trustedCerts = new HashSet<X509Certificate>(); |
124 trustedCerts = new HashSet<X509Certificate>(); |
86 if (cert != null) { |
127 if (cert != null) { |
87 trustedCerts.add(cert); |
128 trustedCerts.add(cert); |
88 } |
129 } |
89 } |
130 } |
90 parameterTemplate = params; |
131 parameterTemplate = params; |
91 initCommon(); |
132 |
92 } |
133 // initCommon(); |
93 |
134 if (TRY_VALIDATOR) { |
94 private void initCommon() { |
135 if (TRY_VALIDATOR == false) { |
95 if (TRY_VALIDATOR == false) { |
136 return; |
96 return; |
137 } |
97 } |
138 trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); |
98 trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); |
139 for (X509Certificate cert : trustedCerts) { |
99 for (X509Certificate cert : trustedCerts) { |
140 X500Principal dn = cert.getSubjectX500Principal(); |
100 X500Principal dn = cert.getSubjectX500Principal(); |
141 List<PublicKey> keys; |
101 List<PublicKey> keys; |
142 if (trustedSubjects.containsKey(dn)) { |
102 if (trustedSubjects.containsKey(dn)) { |
143 keys = trustedSubjects.get(dn); |
103 keys = trustedSubjects.get(dn); |
144 } else { |
104 } else { |
145 keys = new ArrayList<PublicKey>(); |
105 keys = new ArrayList<PublicKey>(); |
146 trustedSubjects.put(dn, keys); |
106 trustedSubjects.put(dn, keys); |
147 } |
107 } |
148 keys.add(cert.getPublicKey()); |
108 keys.add(cert.getPublicKey()); |
149 } |
109 } |
150 try { |
110 try { |
151 factory = CertificateFactory.getInstance("X.509"); |
111 factory = CertificateFactory.getInstance("X.509"); |
152 } catch (CertificateException e) { |
112 } catch (CertificateException e) { |
153 throw new RuntimeException("Internal error", e); |
113 throw new RuntimeException("Internal error", e); |
154 } |
114 } |
155 plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); |
115 plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); |
156 } else { |
|
157 plugin = false; |
|
158 } |
116 } |
159 } |
117 |
160 |
118 public Collection<X509Certificate> getTrustedCertificates() { |
161 public Collection<X509Certificate> getTrustedCertificates() { |
119 return trustedCerts; |
162 return trustedCerts; |
120 } |
163 } |
127 * may modify the length of the path. |
170 * may modify the length of the path. |
128 * |
171 * |
129 * @return the length of the last certification path passed to |
172 * @return the length of the last certification path passed to |
130 * CertPathValidator.validate, or -1 if it has not been invoked yet |
173 * CertPathValidator.validate, or -1 if it has not been invoked yet |
131 */ |
174 */ |
132 public int getCertPathLength() { |
175 public int getCertPathLength() { // mutable, should be private |
133 return certPathLength; |
176 return certPathLength; |
134 } |
177 } |
135 |
178 |
136 /** |
179 /** |
137 * Set J2SE global default PKIX parameters. Currently, hardcoded to disable |
180 * Set J2SE global default PKIX parameters. Currently, hardcoded to disable |
138 * revocation checking. In the future, this should be configurable. |
181 * revocation checking. In the future, this should be configurable. |
139 */ |
182 */ |
140 private void setDefaultParameters(String variant) { |
183 private void setDefaultParameters(String variant) { |
141 parameterTemplate.setRevocationEnabled(false); |
184 if ((variant == Validator.VAR_TLS_SERVER) || |
|
185 (variant == Validator.VAR_TLS_CLIENT)) { |
|
186 parameterTemplate.setRevocationEnabled(checkTLSRevocation); |
|
187 } else { |
|
188 parameterTemplate.setRevocationEnabled(false); |
|
189 } |
142 } |
190 } |
143 |
191 |
144 /** |
192 /** |
145 * Return the PKIX parameters used by this instance. An application may |
193 * Return the PKIX parameters used by this instance. An application may |
146 * modify the parameters but must make sure not to perform any concurrent |
194 * modify the parameters but must make sure not to perform any concurrent |
147 * validations. |
195 * validations. |
148 */ |
196 */ |
149 public PKIXBuilderParameters getParameters() { |
197 public PKIXBuilderParameters getParameters() { // mutable, should be private |
150 return parameterTemplate; |
198 return parameterTemplate; |
151 } |
199 } |
152 |
200 |
|
201 @Override |
153 X509Certificate[] engineValidate(X509Certificate[] chain, |
202 X509Certificate[] engineValidate(X509Certificate[] chain, |
154 Collection<X509Certificate> otherCerts, Object parameter) |
203 Collection<X509Certificate> otherCerts, |
155 throws CertificateException { |
204 AlgorithmConstraints constraints, |
|
205 Object parameter) throws CertificateException { |
156 if ((chain == null) || (chain.length == 0)) { |
206 if ((chain == null) || (chain.length == 0)) { |
157 throw new CertificateException |
207 throw new CertificateException |
158 ("null or zero-length certificate chain"); |
208 ("null or zero-length certificate chain"); |
159 } |
209 } |
|
210 |
|
211 // add new algorithm constraints checker |
|
212 PKIXBuilderParameters pkixParameters = |
|
213 (PKIXBuilderParameters) parameterTemplate.clone(); |
|
214 AlgorithmChecker algorithmChecker = null; |
|
215 if (constraints != null) { |
|
216 algorithmChecker = new AlgorithmChecker(constraints); |
|
217 pkixParameters.addCertPathChecker(algorithmChecker); |
|
218 } |
|
219 |
160 if (TRY_VALIDATOR) { |
220 if (TRY_VALIDATOR) { |
161 // check that chain is in correct order and check if chain contains |
221 // check that chain is in correct order and check if chain contains |
162 // trust anchor |
222 // trust anchor |
163 X500Principal prevIssuer = null; |
223 X500Principal prevIssuer = null; |
164 for (int i = 0; i < chain.length; i++) { |
224 for (int i = 0; i < chain.length; i++) { |
165 X509Certificate cert = chain[i]; |
225 X509Certificate cert = chain[i]; |
166 X500Principal dn = cert.getSubjectX500Principal(); |
226 X500Principal dn = cert.getSubjectX500Principal(); |
167 if (i != 0 && |
227 if (i != 0 && |
168 !dn.equals(prevIssuer)) { |
228 !dn.equals(prevIssuer)) { |
169 // chain is not ordered correctly, call builder instead |
229 // chain is not ordered correctly, call builder instead |
170 return doBuild(chain, otherCerts); |
230 return doBuild(chain, otherCerts, pkixParameters); |
171 } |
231 } |
172 |
232 |
173 // Check if chain[i] is already trusted. It may be inside |
233 // Check if chain[i] is already trusted. It may be inside |
174 // trustedCerts, or has the same dn and public key as a cert |
234 // trustedCerts, or has the same dn and public key as a cert |
175 // inside trustedCerts. The latter happens when a CA has |
235 // inside trustedCerts. The latter happens when a CA has |
184 return new X509Certificate[] {chain[0]}; |
244 return new X509Certificate[] {chain[0]}; |
185 } |
245 } |
186 // Remove and call validator on partial chain [0 .. i-1] |
246 // Remove and call validator on partial chain [0 .. i-1] |
187 X509Certificate[] newChain = new X509Certificate[i]; |
247 X509Certificate[] newChain = new X509Certificate[i]; |
188 System.arraycopy(chain, 0, newChain, 0, i); |
248 System.arraycopy(chain, 0, newChain, 0, i); |
189 return doValidate(newChain); |
249 return doValidate(newChain, pkixParameters); |
190 } |
250 } |
191 prevIssuer = cert.getIssuerX500Principal(); |
251 prevIssuer = cert.getIssuerX500Principal(); |
192 } |
252 } |
193 |
253 |
194 // apparently issued by trust anchor? |
254 // apparently issued by trust anchor? |
195 X509Certificate last = chain[chain.length - 1]; |
255 X509Certificate last = chain[chain.length - 1]; |
196 X500Principal issuer = last.getIssuerX500Principal(); |
256 X500Principal issuer = last.getIssuerX500Principal(); |
197 X500Principal subject = last.getSubjectX500Principal(); |
257 X500Principal subject = last.getSubjectX500Principal(); |
198 if (trustedSubjects.containsKey(issuer) && |
258 if (trustedSubjects.containsKey(issuer) && |
199 isSignatureValid(trustedSubjects.get(issuer), last)) { |
259 isSignatureValid(trustedSubjects.get(issuer), last)) { |
200 return doValidate(chain); |
260 return doValidate(chain, pkixParameters); |
201 } |
261 } |
202 |
262 |
203 // don't fallback to builder if called from plugin/webstart |
263 // don't fallback to builder if called from plugin/webstart |
204 if (plugin) { |
264 if (plugin) { |
205 // Validate chain even if no trust anchor is found. This |
265 // Validate chain even if no trust anchor is found. This |
207 // otherwise valid |
267 // otherwise valid |
208 if (chain.length > 1) { |
268 if (chain.length > 1) { |
209 X509Certificate[] newChain = |
269 X509Certificate[] newChain = |
210 new X509Certificate[chain.length-1]; |
270 new X509Certificate[chain.length-1]; |
211 System.arraycopy(chain, 0, newChain, 0, newChain.length); |
271 System.arraycopy(chain, 0, newChain, 0, newChain.length); |
|
272 |
212 // temporarily set last cert as sole trust anchor |
273 // temporarily set last cert as sole trust anchor |
213 PKIXBuilderParameters params = |
|
214 (PKIXBuilderParameters) parameterTemplate.clone(); |
|
215 try { |
274 try { |
216 params.setTrustAnchors |
275 pkixParameters.setTrustAnchors |
217 (Collections.singleton(new TrustAnchor |
276 (Collections.singleton(new TrustAnchor |
218 (chain[chain.length-1], null))); |
277 (chain[chain.length-1], null))); |
219 } catch (InvalidAlgorithmParameterException iape) { |
278 } catch (InvalidAlgorithmParameterException iape) { |
220 // should never occur, but ... |
279 // should never occur, but ... |
221 throw new CertificateException(iape); |
280 throw new CertificateException(iape); |
222 } |
281 } |
223 doValidate(newChain, params); |
282 doValidate(newChain, pkixParameters); |
224 } |
283 } |
225 // if the rest of the chain is valid, throw exception |
284 // if the rest of the chain is valid, throw exception |
226 // indicating no trust anchor was found |
285 // indicating no trust anchor was found |
227 throw new ValidatorException |
286 throw new ValidatorException |
228 (ValidatorException.T_NO_TRUST_ANCHOR); |
287 (ValidatorException.T_NO_TRUST_ANCHOR); |
229 } |
288 } |
230 // otherwise, fall back to builder |
289 // otherwise, fall back to builder |
231 } |
290 } |
232 |
291 |
233 return doBuild(chain, otherCerts); |
292 return doBuild(chain, otherCerts, pkixParameters); |
234 } |
293 } |
235 |
294 |
236 private boolean isSignatureValid(List<PublicKey> keys, X509Certificate sub) { |
295 private boolean isSignatureValid(List<PublicKey> keys, |
|
296 X509Certificate sub) { |
237 if (plugin) { |
297 if (plugin) { |
238 for (PublicKey key: keys) { |
298 for (PublicKey key: keys) { |
239 try { |
299 try { |
240 sub.verify(key); |
300 sub.verify(key); |
241 return true; |
301 return true; |
298 ("PKIX path validation failed: " + e.toString(), e); |
351 ("PKIX path validation failed: " + e.toString(), e); |
299 } |
352 } |
300 } |
353 } |
301 |
354 |
302 private X509Certificate[] doBuild(X509Certificate[] chain, |
355 private X509Certificate[] doBuild(X509Certificate[] chain, |
303 Collection<X509Certificate> otherCerts) throws CertificateException { |
356 Collection<X509Certificate> otherCerts, |
|
357 PKIXBuilderParameters params) throws CertificateException { |
304 |
358 |
305 try { |
359 try { |
306 PKIXBuilderParameters params = |
|
307 (PKIXBuilderParameters)parameterTemplate.clone(); |
|
308 setDate(params); |
360 setDate(params); |
309 |
361 |
310 // setup target constraints |
362 // setup target constraints |
311 X509CertSelector selector = new X509CertSelector(); |
363 X509CertSelector selector = new X509CertSelector(); |
312 selector.setCertificate(chain[0]); |
364 selector.setCertificate(chain[0]); |