1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.security;
18
19 import org.apache.xml.security.exceptions.XMLSecurityException;
20 import org.apache.xml.security.signature.Reference;
21 import org.apache.xml.security.signature.XMLSignature;
22 import org.apache.xml.security.transforms.Transform;
23 import org.apache.xml.security.transforms.TransformationException;
24 import org.apache.xml.security.transforms.Transforms;
25 import org.opensaml.common.SignableSAMLObject;
26 import org.opensaml.xml.signature.Signature;
27 import org.opensaml.xml.signature.impl.SignatureImpl;
28 import org.opensaml.xml.util.DatatypeHelper;
29 import org.opensaml.xml.validation.ValidationException;
30 import org.opensaml.xml.validation.Validator;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34
35
36
37
38 public class SAMLSignatureProfileValidator implements Validator<Signature> {
39
40
41 private final Logger log = LoggerFactory.getLogger(SAMLSignatureProfileValidator.class);
42
43
44 public void validate(Signature signature) throws ValidationException {
45
46 if (!(signature instanceof SignatureImpl)) {
47 log.info("Signature was not an instance of SignatureImpl, was {} validation not supported", signature
48 .getClass().getName());
49 return;
50 }
51
52 validateSignatureImpl((SignatureImpl) signature);
53 }
54
55
56
57
58
59
60
61
62 protected void validateSignatureImpl(SignatureImpl sigImpl) throws ValidationException {
63
64 if (sigImpl.getXMLSignature() == null) {
65 log.error("SignatureImpl did not contain the an Apache XMLSignature child");
66 throw new ValidationException("Apache XMLSignature does not exist on SignatureImpl");
67 }
68 XMLSignature apacheSig = sigImpl.getXMLSignature();
69
70 if (!(sigImpl.getParent() instanceof SignableSAMLObject)) {
71 log.error("Signature is not an immedidate child of a SignableSAMLObject");
72 throw new ValidationException("Signature is not an immediate child of a SignableSAMLObject.");
73 }
74 SignableSAMLObject signableObject = (SignableSAMLObject) sigImpl.getParent();
75
76 Reference ref = validateReference(apacheSig);
77
78 String uri = ref.getURI();
79 String id = signableObject.getSignatureReferenceID();
80
81 validateReferenceURI(uri, id);
82
83 validateTransforms(ref);
84 }
85
86
87
88
89
90
91
92
93
94
95
96 protected Reference validateReference(XMLSignature apacheSig) throws ValidationException {
97 int numReferences = apacheSig.getSignedInfo().getLength();
98 if (numReferences != 1) {
99 log.error("Signature SignedInfo had invalid number of References: " + numReferences);
100 throw new ValidationException("Signature SignedInfo must have exactly 1 Reference element");
101 }
102
103 Reference ref = null;
104 try {
105 ref = apacheSig.getSignedInfo().item(0);
106 } catch (XMLSecurityException e) {
107 log.error("Apache XML Security exception obtaining Reference", e);
108 throw new ValidationException("Could not obtain Reference from Signature/SignedInfo", e);
109 }
110 if (ref == null) {
111 log.error("Signature Reference was null");
112 throw new ValidationException("Signature Reference was null");
113 }
114 return ref;
115 }
116
117
118
119
120
121
122
123
124
125
126
127 protected void validateReferenceURI(String uri, String id) throws ValidationException {
128 if (!DatatypeHelper.isEmpty(uri)) {
129 if (!uri.startsWith("#")) {
130 log.error("Signature Reference URI was not a document fragment reference: " + uri);
131 throw new ValidationException("Signature Reference URI was not a document fragment reference");
132 } else if (DatatypeHelper.isEmpty(id)) {
133 log.error("SignableSAMLObject did not contain an ID attribute");
134 throw new ValidationException("SignableSAMLObject did not contain an ID attribute");
135 } else if (uri.length() < 2 || !id.equals(uri.substring(1))) {
136 log
137 .error(String.format("Reference URI '%s' did not point to SignableSAMLObject with ID '%s'",
138 uri, id));
139 throw new ValidationException("Reference URI did not point to parent ID");
140 }
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154 protected void validateTransforms(Reference reference) throws ValidationException {
155 Transforms transforms = null;
156 try {
157 transforms = reference.getTransforms();
158 } catch (XMLSecurityException e) {
159 log.error("Apache XML Security error obtaining Transforms instance", e);
160 throw new ValidationException("Apache XML Security error obtaining Transforms instance", e);
161 }
162
163 if (transforms == null) {
164 log.error("Error obtaining Transforms instance, null was returned");
165 throw new ValidationException("Transforms instance was null");
166 }
167
168 int numTransforms = transforms.getLength();
169 if (numTransforms > 2) {
170 log.error("Invalid number of Transforms was present: " + numTransforms);
171 throw new ValidationException("Invalid number of transforms");
172 }
173
174 boolean sawEnveloped = false;
175 for (int i = 0; i < numTransforms; i++) {
176 Transform transform = null;
177 try {
178 transform = transforms.item(i);
179 } catch (TransformationException e) {
180 log.error("Error obtaining transform instance", e);
181 throw new ValidationException("Error obtaining transform instance", e);
182 }
183 String uri = transform.getURI();
184 if (Transforms.TRANSFORM_ENVELOPED_SIGNATURE.equals(uri)) {
185 log.debug("Saw Enveloped signature transform");
186 sawEnveloped = true;
187 } else if (Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS.equals(uri)
188 || Transforms.TRANSFORM_C14N_EXCL_WITH_COMMENTS.equals(uri)) {
189 log.debug("Saw Exclusive C14N signature transform");
190 } else {
191 log.error("Saw invalid signature transform: " + uri);
192 throw new ValidationException("Signature contained an invalid transform");
193 }
194 }
195
196 if (!sawEnveloped) {
197 log.error("Signature was missing the required Enveloped signature transform");
198 throw new ValidationException("Transforms did not contain the required enveloped transform");
199 }
200 }
201
202 }