1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.validate;
21
22 import java.math.BigInteger;
23 import java.security.PublicKey;
24 import java.security.cert.CertificateExpiredException;
25 import java.security.cert.CertificateNotYetValidException;
26 import java.security.cert.X509Certificate;
27 import java.util.Collection;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import org.apache.ws.security.WSSecurityException;
32 import org.apache.ws.security.components.crypto.Crypto;
33 import org.apache.ws.security.components.crypto.CryptoType;
34 import org.apache.ws.security.handler.RequestData;
35
36
37
38
39
40 public class SignatureTrustValidator implements Validator {
41
42 private static final org.apache.commons.logging.Log LOG =
43 org.apache.commons.logging.LogFactory.getLog(SignatureTrustValidator.class);
44
45
46
47
48
49
50
51
52
53
54
55
56 public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
57 if (credential == null) {
58 throw new WSSecurityException(WSSecurityException.FAILURE, "noCredential");
59 }
60 X509Certificate[] certs = credential.getCertificates();
61 PublicKey publicKey = credential.getPublicKey();
62 Crypto crypto = getCrypto(data);
63 if (crypto == null) {
64 throw new WSSecurityException(WSSecurityException.FAILURE, "noSigCryptoFile");
65 }
66
67 if (certs != null && certs.length > 0) {
68 validateCertificates(certs);
69 boolean trust = false;
70 boolean enableRevocation = data.isRevocationEnabled();
71 if (certs.length == 1) {
72 trust = verifyTrustInCert(certs[0], crypto, data, enableRevocation);
73 } else {
74 trust = verifyTrustInCerts(certs, crypto, data, enableRevocation);
75 }
76 if (trust) {
77 return credential;
78 }
79 }
80 if (publicKey != null) {
81 boolean trust = validatePublicKey(publicKey, crypto);
82 if (trust) {
83 return credential;
84 }
85 }
86 throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
87 }
88
89
90 protected Crypto getCrypto(RequestData data) {
91 return data.getSigCrypto();
92 }
93
94
95
96
97
98
99 protected void validateCertificates(X509Certificate[] certificates)
100 throws WSSecurityException {
101 try {
102 for (int i = 0; i < certificates.length; i++) {
103 certificates[i].checkValidity();
104 }
105 } catch (CertificateExpiredException e) {
106 throw new WSSecurityException(
107 WSSecurityException.FAILED_CHECK, "invalidCert", null, e
108 );
109 } catch (CertificateNotYetValidException e) {
110 throw new WSSecurityException(
111 WSSecurityException.FAILED_CHECK, "invalidCert", null, e
112 );
113 }
114 }
115
116 @Deprecated
117 protected boolean verifyTrustInCert(X509Certificate cert, Crypto crypto)
118 throws WSSecurityException {
119 return verifyTrustInCert(cert, crypto, new RequestData(), false);
120 }
121
122 @Deprecated
123 protected boolean verifyTrustInCert(X509Certificate cert, Crypto crypto, boolean enableRevocation)
124 throws WSSecurityException {
125 return verifyTrustInCert(cert, crypto, new RequestData(), enableRevocation);
126 }
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 protected boolean verifyTrustInCert(
146 X509Certificate cert,
147 Crypto crypto,
148 RequestData data,
149 boolean enableRevocation
150 ) throws WSSecurityException {
151 String subjectString = cert.getSubjectX500Principal().getName();
152 String issuerString = cert.getIssuerX500Principal().getName();
153 BigInteger issuerSerial = cert.getSerialNumber();
154
155 if (LOG.isDebugEnabled()) {
156 LOG.debug("Transmitted certificate has subject " + subjectString);
157 LOG.debug(
158 "Transmitted certificate has issuer " + issuerString + " (serial "
159 + issuerSerial + ")"
160 );
161 }
162
163
164
165
166 if (!enableRevocation && isCertificateInKeyStore(crypto, cert)) {
167 return true;
168 }
169
170
171
172
173
174 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.SUBJECT_DN);
175 cryptoType.setSubjectDN(issuerString);
176 X509Certificate[] foundCerts = crypto.getX509Certificates(cryptoType);
177
178
179
180 if (foundCerts == null || foundCerts.length < 1) {
181 if (LOG.isDebugEnabled()) {
182 LOG.debug(
183 "No certs found in keystore for issuer " + issuerString
184 + " of certificate for " + subjectString
185 );
186 }
187 return false;
188 }
189
190
191
192
193
194 if (LOG.isDebugEnabled()) {
195 LOG.debug(
196 "Preparing to validate certificate path for issuer " + issuerString
197 );
198 }
199
200
201
202
203 X509Certificate[] x509certs = new X509Certificate[foundCerts.length + 1];
204 x509certs[0] = cert;
205 for (int j = 0; j < foundCerts.length; j++) {
206 x509certs[j + 1] = (X509Certificate)foundCerts[j];
207 }
208
209
210
211
212
213 if (crypto.verifyTrust(x509certs, enableRevocation)) {
214 if (LOG.isDebugEnabled()) {
215 LOG.debug(
216 "Certificate path has been verified for certificate with subject "
217 + subjectString
218 );
219 }
220 Collection<Pattern> subjectCertConstraints = data.getSubjectCertConstraints();
221 if (matches(cert, subjectCertConstraints)) {
222 return true;
223 }
224 }
225
226 if (LOG.isDebugEnabled()) {
227 LOG.debug(
228 "Certificate path could not be verified for certificate with subject "
229 + subjectString
230 );
231 }
232 return false;
233 }
234
235
236
237
238
239
240
241
242 protected boolean isCertificateInKeyStore(
243 Crypto crypto,
244 X509Certificate cert
245 ) throws WSSecurityException {
246 String issuerString = cert.getIssuerX500Principal().getName();
247 BigInteger issuerSerial = cert.getSerialNumber();
248
249 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
250 cryptoType.setIssuerSerial(issuerString, issuerSerial);
251 X509Certificate[] foundCerts = crypto.getX509Certificates(cryptoType);
252
253
254
255
256
257 if (foundCerts != null && foundCerts[0] != null && foundCerts[0].equals(cert)) {
258 if (LOG.isDebugEnabled()) {
259 LOG.debug(
260 "Direct trust for certificate with " + cert.getSubjectX500Principal().getName()
261 );
262 }
263 return true;
264 }
265 if (LOG.isDebugEnabled()) {
266 LOG.debug(
267 "No certificate found for subject from issuer with " + issuerString
268 + " (serial " + issuerSerial + ")"
269 );
270 }
271 return false;
272 }
273
274 @Deprecated
275 protected boolean verifyTrustInCerts(
276 X509Certificate[] certificates,
277 Crypto crypto
278 ) throws WSSecurityException {
279 return verifyTrustInCerts(certificates, crypto, new RequestData(), false);
280 }
281
282 @Deprecated
283 protected boolean verifyTrustInCerts(
284 X509Certificate[] certificates,
285 Crypto crypto,
286 boolean enableRevocation
287 ) throws WSSecurityException {
288 return verifyTrustInCerts(certificates, crypto, new RequestData(), enableRevocation);
289 }
290
291
292
293
294
295
296
297
298
299
300
301 protected boolean verifyTrustInCerts(
302 X509Certificate[] certificates,
303 Crypto crypto,
304 RequestData data,
305 boolean enableRevocation
306 ) throws WSSecurityException {
307 if (certificates == null || certificates.length < 2) {
308 return false;
309 }
310
311 String subjectString = certificates[0].getSubjectX500Principal().getName();
312
313
314
315
316 if (crypto.verifyTrust(certificates, enableRevocation)) {
317 if (LOG.isDebugEnabled()) {
318 LOG.debug(
319 "Certificate path has been verified for certificate with subject "
320 + subjectString
321 );
322 }
323 Collection<Pattern> subjectCertConstraints = data.getSubjectCertConstraints();
324 if (matches(certificates[0], subjectCertConstraints)) {
325 return true;
326 }
327 }
328
329 if (LOG.isDebugEnabled()) {
330 LOG.debug(
331 "Certificate path could not be verified for certificate with subject "
332 + subjectString
333 );
334 }
335
336 return false;
337 }
338
339
340
341
342
343 protected boolean validatePublicKey(PublicKey publicKey, Crypto crypto)
344 throws WSSecurityException {
345 return crypto.verifyTrust(publicKey);
346 }
347
348
349
350
351
352
353 protected boolean
354 matches(
355 final java.security.cert.X509Certificate cert,
356 final Collection<Pattern> subjectDNPatterns
357 ) {
358 if (subjectDNPatterns.isEmpty()) {
359 LOG.warn("No Subject DN Certificate Constraints were defined. This could be a security issue");
360 }
361 if (!subjectDNPatterns.isEmpty()) {
362 if (cert == null) {
363 LOG.debug("The certificate is null so no constraints matching was possible");
364 return false;
365 }
366 String subjectName = cert.getSubjectX500Principal().getName();
367 boolean subjectMatch = false;
368 for (Pattern subjectDNPattern : subjectDNPatterns) {
369 final Matcher matcher = subjectDNPattern.matcher(subjectName);
370 if (matcher.matches()) {
371 LOG.debug("Subject DN " + subjectName + " matches with pattern " + subjectDNPattern);
372 subjectMatch = true;
373 break;
374 }
375 }
376 if (!subjectMatch) {
377 return false;
378 }
379 }
380
381 return true;
382 }
383
384 }