1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.security.x509;
18
19 import java.security.GeneralSecurityException;
20 import java.security.cert.CRL;
21 import java.security.cert.CertPathBuilder;
22 import java.security.cert.CertPathBuilderException;
23 import java.security.cert.CertStore;
24 import java.security.cert.CertStoreException;
25 import java.security.cert.Certificate;
26 import java.security.cert.CollectionCertStoreParameters;
27 import java.security.cert.PKIXBuilderParameters;
28 import java.security.cert.PKIXCertPathBuilderResult;
29 import java.security.cert.TrustAnchor;
30 import java.security.cert.X509CRL;
31 import java.security.cert.X509CertSelector;
32 import java.security.cert.X509Certificate;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Date;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39
40 import org.opensaml.xml.security.SecurityException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47 public class CertPathPKIXTrustEvaluator implements PKIXTrustEvaluator {
48
49
50 public static final Integer DEFAULT_VERIFY_DEPTH = 1;
51
52
53 private final Logger log = LoggerFactory.getLogger(CertPathPKIXTrustEvaluator.class);
54
55
56 private X500DNHandler x500DNHandler;
57
58
59 public CertPathPKIXTrustEvaluator() {
60 x500DNHandler = new InternalX500DNHandler();
61 }
62
63
64
65
66
67
68
69
70 public X500DNHandler getX500DNHandler() {
71 return x500DNHandler;
72 }
73
74
75
76
77
78
79
80
81 public void setX500DNHandler(X500DNHandler handler) {
82 if (handler == null) {
83 throw new IllegalArgumentException("X500DNHandler may not be null");
84 }
85 x500DNHandler = handler;
86 }
87
88
89 public boolean validate(PKIXValidationInformation validationInfo, X509Credential untrustedCredential)
90 throws SecurityException {
91
92 if (log.isDebugEnabled()) {
93 log.debug("Attempting PKIX path validation on untrusted credential: {}",
94 X509Util.getIdentifiersToken(untrustedCredential, x500DNHandler));
95 }
96
97 try {
98 PKIXBuilderParameters params = getPKIXBuilderParameters(validationInfo, untrustedCredential);
99
100 log.trace("Building certificate validation path");
101
102 CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
103 PKIXCertPathBuilderResult buildResult = (PKIXCertPathBuilderResult) builder.build(params);
104 if (log.isDebugEnabled()) {
105 logCertPathDebug(buildResult, untrustedCredential.getEntityCertificate());
106 log.debug("PKIX validation succeeded for untrusted credential: {}",
107 X509Util.getIdentifiersToken(untrustedCredential, x500DNHandler));
108 }
109 return true;
110
111 } catch (CertPathBuilderException e) {
112 if (log.isTraceEnabled()) {
113 log.trace("PKIX path construction failed for untrusted credential: "
114 + X509Util.getIdentifiersToken(untrustedCredential, x500DNHandler), e);
115 } else {
116 log.error("PKIX path construction failed for untrusted credential: "
117 + X509Util.getIdentifiersToken(untrustedCredential, x500DNHandler) + ": " + e.getMessage());
118 }
119 return false;
120 } catch (GeneralSecurityException e) {
121 log.error("PKIX validation failure", e);
122 throw new SecurityException("PKIX validation failure", e);
123 }
124 }
125
126
127
128
129
130
131
132
133
134
135
136 protected PKIXBuilderParameters getPKIXBuilderParameters(PKIXValidationInformation validationInfo,
137 X509Credential untrustedCredential) throws GeneralSecurityException {
138 Set<TrustAnchor> trustAnchors = getTrustAnchors(validationInfo);
139 if (trustAnchors == null || trustAnchors.isEmpty()) {
140 throw new GeneralSecurityException(
141 "Unable to validate X509 certificate, no trust anchors found in the PKIX validation information");
142 }
143
144 X509CertSelector selector = new X509CertSelector();
145 selector.setCertificate(untrustedCredential.getEntityCertificate());
146
147 log.trace("Adding trust anchors to PKIX validator parameters");
148 PKIXBuilderParameters params = new PKIXBuilderParameters(trustAnchors, selector);
149
150 Integer effectiveVerifyDepth = getEffectiveVerificationDepth(validationInfo);
151 log.trace("Setting max verification depth to: {} ", effectiveVerifyDepth);
152 params.setMaxPathLength(effectiveVerifyDepth);
153
154 CertStore certStore = buildCertStore(validationInfo, untrustedCredential);
155 params.addCertStore(certStore);
156
157 if (storeContainsCRLs(certStore)) {
158 log.trace("At least one CRL was present in cert store, enabling revocation checking");
159 params.setRevocationEnabled(true);
160 } else {
161 log.trace("No CRLs present in cert store, disabling revocation checking");
162 params.setRevocationEnabled(false);
163 }
164
165 return params;
166 }
167
168
169
170
171
172
173
174 protected boolean storeContainsCRLs(CertStore certStore) {
175 Collection<? extends CRL> crls = null;
176 try {
177
178
179 crls = certStore.getCRLs(null);
180 } catch (CertStoreException e) {
181 log.error("Error examining cert store for CRL's, treating as if no CRL's present", e);
182 return false;
183 }
184 if (crls != null && !crls.isEmpty()) {
185 return true;
186 }
187 return false;
188 }
189
190
191
192
193
194
195
196 protected Integer getEffectiveVerificationDepth(PKIXValidationInformation validationInfo) {
197 Integer effectiveVerifyDepth = validationInfo.getVerificationDepth();
198 if (effectiveVerifyDepth == null) {
199 effectiveVerifyDepth = DEFAULT_VERIFY_DEPTH;
200 }
201 return effectiveVerifyDepth;
202 }
203
204
205
206
207
208
209
210
211 protected Set<TrustAnchor> getTrustAnchors(PKIXValidationInformation validationInfo) {
212 Collection<X509Certificate> validationCertificates = validationInfo.getCertificates();
213
214 log.trace("Constructing trust anchors for PKIX validation");
215 Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
216 for (X509Certificate cert : validationCertificates) {
217 trustAnchors.add(buildTrustAnchor(cert));
218 }
219
220 if (log.isTraceEnabled()) {
221 for (TrustAnchor anchor : trustAnchors) {
222 log.trace("TrustAnchor: {}", anchor.toString());
223 }
224 }
225
226 return trustAnchors;
227 }
228
229
230
231
232
233
234
235
236
237 protected TrustAnchor buildTrustAnchor(X509Certificate cert) {
238 return new TrustAnchor(cert, null);
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252 protected CertStore buildCertStore(PKIXValidationInformation validationInfo, X509Credential untrustedCredential)
253 throws GeneralSecurityException {
254
255 log.trace("Creating cert store to use during path validation");
256
257 log.trace("Adding entity certificate chain to cert store");
258 List<Object> storeMaterial = new ArrayList<Object>(untrustedCredential.getEntityCertificateChain());
259 if (log.isTraceEnabled()) {
260 for (X509Certificate cert : untrustedCredential.getEntityCertificateChain()) {
261 log.trace(String.format("Added X509Certificate from entity cert chain to cert store "
262 + "with subject name '%s' issued by '%s' with serial number '%s'",
263 x500DNHandler.getName(cert.getSubjectX500Principal()),
264 x500DNHandler.getName(cert.getIssuerX500Principal()),
265 cert.getSerialNumber().toString()));
266 }
267 }
268
269 Date now = new Date();
270 for (X509CRL crl : validationInfo.getCRLs()) {
271 if (crl.getRevokedCertificates() != null && !crl.getRevokedCertificates().isEmpty()) {
272 storeMaterial.add(crl);
273 if (log.isTraceEnabled()) {
274 log.trace("Added X509CRL to cert store from issuer {} dated {}",
275 x500DNHandler.getName(crl.getIssuerX500Principal()), crl.getThisUpdate());
276 }
277 if (crl.getNextUpdate().before(now)) {
278 log.warn("Using X509CRL from issuer {} with a nextUpdate in the past: {}",
279 x500DNHandler.getName(crl.getIssuerX500Principal()), crl.getNextUpdate());
280 }
281 } else {
282 if (log.isTraceEnabled()) {
283 log.trace("Empty X509CRL not added to cert store, from issuer {} dated {}",
284 x500DNHandler.getName(crl.getIssuerX500Principal()), crl.getThisUpdate());
285 }
286 }
287 }
288
289 return CertStore.getInstance("Collection", new CollectionCertStoreParameters(storeMaterial));
290 }
291
292
293
294
295
296
297
298 private void logCertPathDebug(PKIXCertPathBuilderResult buildResult, X509Certificate targetCert) {
299 log.debug("Built valid PKIX cert path");
300 log.debug("Target certificate: {}", x500DNHandler.getName(targetCert.getSubjectX500Principal()));
301 for (Certificate cert : buildResult.getCertPath().getCertificates()) {
302 log.debug("CertPath certificate: {}", x500DNHandler.getName(((X509Certificate) cert)
303 .getSubjectX500Principal()));
304 }
305 TrustAnchor ta = buildResult.getTrustAnchor();
306 if (ta.getTrustedCert() != null) {
307 log.debug("TrustAnchor: {}", x500DNHandler.getName(ta.getTrustedCert().getSubjectX500Principal()));
308 } else if (ta.getCA() != null) {
309 log.debug("TrustAnchor: {}", x500DNHandler.getName(ta.getCA()));
310 } else {
311 log.debug("TrustAnchor: {}", ta.getCAName());
312 }
313 }
314
315 }