1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.metadata.provider;
18
19 import java.util.Iterator;
20
21 import org.opensaml.saml2.metadata.AffiliationDescriptor;
22 import org.opensaml.saml2.metadata.EntitiesDescriptor;
23 import org.opensaml.saml2.metadata.EntityDescriptor;
24 import org.opensaml.saml2.metadata.RoleDescriptor;
25 import org.opensaml.security.SAMLSignatureProfileValidator;
26 import org.opensaml.xml.XMLObject;
27 import org.opensaml.xml.security.CriteriaSet;
28 import org.opensaml.xml.security.SecurityException;
29 import org.opensaml.xml.security.credential.UsageType;
30 import org.opensaml.xml.security.criteria.UsageCriteria;
31 import org.opensaml.xml.signature.SignableXMLObject;
32 import org.opensaml.xml.signature.Signature;
33 import org.opensaml.xml.signature.SignatureTrustEngine;
34 import org.opensaml.xml.validation.ValidationException;
35 import org.opensaml.xml.validation.Validator;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39
40
41
42 public class SignatureValidationFilter implements MetadataFilter {
43
44
45 private final Logger log = LoggerFactory.getLogger(SignatureValidationFilter.class);
46
47
48 private SignatureTrustEngine signatureTrustEngine;
49
50
51 private boolean requireSignature;
52
53
54 private CriteriaSet defaultCriteria;
55
56
57 private Validator<Signature> sigValidator;
58
59
60
61
62
63
64 public SignatureValidationFilter(SignatureTrustEngine engine) {
65 if (engine == null) {
66 throw new IllegalArgumentException("Signature trust engine may not be null");
67 }
68
69 signatureTrustEngine = engine;
70 sigValidator = new SAMLSignatureProfileValidator();
71 }
72
73
74
75
76
77
78
79
80 public SignatureValidationFilter(SignatureTrustEngine engine, Validator<Signature> signatureValidator) {
81 if (engine == null) {
82 throw new IllegalArgumentException("Signature trust engine may not be null");
83 }
84
85 signatureTrustEngine = engine;
86 sigValidator = signatureValidator;
87 }
88
89
90
91
92
93
94 public SignatureTrustEngine getSignatureTrustEngine() {
95 return signatureTrustEngine;
96 }
97
98
99
100
101
102
103 public Validator<Signature> getSignaturePrevalidator() {
104 return sigValidator;
105 }
106
107
108
109
110
111
112 public boolean getRequireSignature() {
113 return requireSignature;
114 }
115
116
117
118
119
120
121 public void setRequireSignature(boolean require) {
122 requireSignature = require;
123 }
124
125
126
127
128
129
130 public CriteriaSet getDefaultCriteria() {
131 return defaultCriteria;
132 }
133
134
135
136
137
138
139 public void setDefaultCriteria(CriteriaSet newCriteria) {
140 defaultCriteria = newCriteria;
141 }
142
143
144 public void doFilter(XMLObject metadata) throws FilterException {
145 SignableXMLObject signableMetadata = (SignableXMLObject) metadata;
146
147 if (!signableMetadata.isSigned()){
148 if (getRequireSignature()) {
149 throw new FilterException("Metadata root element was unsigned and signatures are required.");
150 }
151 }
152
153 if (signableMetadata instanceof EntityDescriptor) {
154 processEntityDescriptor((EntityDescriptor) signableMetadata);
155 } else if (signableMetadata instanceof EntitiesDescriptor) {
156 processEntityGroup((EntitiesDescriptor) signableMetadata);
157 } else {
158 log.error("Internal error, metadata object was of an unsupported type: {}", metadata.getClass().getName());
159 }
160 }
161
162
163
164
165
166
167
168
169
170
171 protected void processEntityDescriptor(EntityDescriptor entityDescriptor) throws FilterException {
172 String entityID = entityDescriptor.getEntityID();
173 log.trace("Processing EntityDescriptor: {}", entityID);
174
175 if (entityDescriptor.isSigned()) {
176 verifySignature(entityDescriptor, entityID, false);
177 }
178
179 Iterator<RoleDescriptor> roleIter = entityDescriptor.getRoleDescriptors().iterator();
180 while (roleIter.hasNext()) {
181 RoleDescriptor roleChild = roleIter.next();
182 if (!roleChild.isSigned()) {
183 log.trace("RoleDescriptor member '{}' was not signed, skipping signature processing...",
184 roleChild.getElementQName());
185 continue;
186 } else {
187 log.trace("Processing signed RoleDescriptor member: {}", roleChild.getElementQName());
188 }
189
190 try {
191 String roleID = getRoleIDToken(entityID, roleChild);
192 verifySignature(roleChild, roleID, false);
193 } catch (FilterException e) {
194 log.error("RoleDescriptor '{}' subordinate to entity '{}' failed signature verification, "
195 + "removing from metadata provider",
196 roleChild.getElementQName(), entityID);
197 roleIter.remove();
198 }
199 }
200
201 if (entityDescriptor.getAffiliationDescriptor() != null) {
202 AffiliationDescriptor affiliationDescriptor = entityDescriptor.getAffiliationDescriptor();
203 if (!affiliationDescriptor.isSigned()) {
204 log.trace("AffiliationDescriptor member was not signed, skipping signature processing...");
205 } else {
206 log.trace("Processing signed AffiliationDescriptor member with owner ID: {}",
207 affiliationDescriptor.getOwnerID());
208
209 try {
210 verifySignature(affiliationDescriptor, affiliationDescriptor.getOwnerID(), false);
211 } catch (FilterException e) {
212 log.error("AffiliationDescriptor with owner ID '{}' subordinate to entity '{}' " +
213 "failed signature verification, removing from metadata provider",
214 affiliationDescriptor.getOwnerID(), entityID);
215 entityDescriptor.setAffiliationDescriptor(null);
216 }
217
218 }
219 }
220 }
221
222
223
224
225
226
227
228
229
230
231
232 protected void processEntityGroup(EntitiesDescriptor entitiesDescriptor) throws FilterException {
233 log.trace("Processing EntitiesDescriptor group: {}", entitiesDescriptor.getName());
234
235 if (entitiesDescriptor.isSigned()) {
236 verifySignature(entitiesDescriptor, entitiesDescriptor.getName(), true);
237 }
238
239 Iterator<EntityDescriptor> entityIter = entitiesDescriptor.getEntityDescriptors().iterator();
240 while (entityIter.hasNext()) {
241 EntityDescriptor entityChild = entityIter.next();
242 if (!entityChild.isSigned()) {
243 log.trace("EntityDescriptor member '{}' was not signed, skipping signature processing...",
244 entityChild.getEntityID());
245 continue;
246 } else {
247 log.trace("Processing signed EntityDescriptor member: {}", entityChild.getEntityID());
248 }
249
250 try {
251 processEntityDescriptor(entityChild);
252 } catch (FilterException e) {
253 log.error("EntityDescriptor '{}' failed signature verification, removing from metadata provider",
254 entityChild.getEntityID());
255 entityIter.remove();
256 }
257 }
258
259 Iterator<EntitiesDescriptor> entitiesIter = entitiesDescriptor.getEntitiesDescriptors().iterator();
260 while(entitiesIter.hasNext()) {
261 EntitiesDescriptor entitiesChild = entitiesIter.next();
262 log.trace("Processing EntitiesDescriptor member: {}", entitiesChild.getName());
263 try {
264 processEntityGroup(entitiesChild);
265 } catch (FilterException e) {
266 log.error("EntitiesDescriptor '{}' failed signature verification, removing from metadata provider",
267 entitiesChild.getName());
268 entitiesIter.remove();
269 }
270 }
271
272 }
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 protected void verifySignature(SignableXMLObject signedMetadata, String metadataEntryName,
290 boolean isEntityGroup) throws FilterException {
291
292 log.debug("Verifying signature on metadata entry: {}", metadataEntryName);
293
294 Signature signature = signedMetadata.getSignature();
295 if (signature == null) {
296
297 log.warn("Signature was null, skipping processing on metadata entry: {}", metadataEntryName);
298 return;
299 }
300
301 performPreValidation(signature, metadataEntryName);
302
303 CriteriaSet criteriaSet = buildCriteriaSet(signedMetadata, metadataEntryName, isEntityGroup);
304
305 try {
306 if ( getSignatureTrustEngine().validate(signature, criteriaSet) ) {
307 log.trace("Signature trust establishment succeeded for metadata entry {}", metadataEntryName);
308 return;
309 } else {
310 log.error("Signature trust establishment failed for metadata entry {}", metadataEntryName);
311 throw new FilterException("Signature trust establishment failed for metadata entry");
312 }
313 } catch (SecurityException e) {
314
315 log.error("Error processing signature verification for metadata entry '{}': {} ",
316 metadataEntryName, e.getMessage());
317 throw new FilterException("Error processing signature verification for metadata entry", e);
318 }
319 }
320
321
322
323
324
325
326
327
328
329
330
331
332
333 protected void performPreValidation(Signature signature, String metadataEntryName) throws FilterException {
334 if (getSignaturePrevalidator() != null) {
335 try {
336 getSignaturePrevalidator().validate(signature);
337 } catch (ValidationException e) {
338 log.error("Signature on metadata entry '{}' failed signature pre-validation", metadataEntryName);
339 throw new FilterException("Metadata instance signature failed signature pre-validation", e);
340 }
341 }
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357 protected CriteriaSet buildCriteriaSet(SignableXMLObject signedMetadata,
358 String metadataEntryName, boolean isEntityGroup) {
359
360 CriteriaSet newCriteriaSet = new CriteriaSet();
361
362 if (getDefaultCriteria() != null) {
363 newCriteriaSet.addAll( getDefaultCriteria() );
364 }
365
366 if (!newCriteriaSet.contains(UsageCriteria.class)) {
367 newCriteriaSet.add( new UsageCriteria(UsageType.SIGNING) );
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382 return newCriteriaSet;
383 }
384
385
386
387
388
389
390
391
392
393 protected String getRoleIDToken(String entityID, RoleDescriptor role) {
394 String roleName = role.getElementQName().getLocalPart();
395 return "[Role: " + entityID + "::" + roleName + "]";
396 }
397 }