1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.common.binding.security;
18
19 import java.util.List;
20
21 import javax.servlet.http.HttpServletRequest;
22
23 import org.opensaml.common.binding.SAMLMessageContext;
24 import org.opensaml.security.MetadataCriteria;
25 import org.opensaml.ws.message.MessageContext;
26 import org.opensaml.ws.security.SecurityPolicyException;
27 import org.opensaml.ws.security.SecurityPolicyRule;
28 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
29 import org.opensaml.xml.security.CriteriaSet;
30 import org.opensaml.xml.security.SecurityException;
31 import org.opensaml.xml.security.credential.Credential;
32 import org.opensaml.xml.security.credential.UsageType;
33 import org.opensaml.xml.security.criteria.EntityIDCriteria;
34 import org.opensaml.xml.security.criteria.UsageCriteria;
35 import org.opensaml.xml.signature.SignatureTrustEngine;
36 import org.opensaml.xml.util.Base64;
37 import org.opensaml.xml.util.DatatypeHelper;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44 public abstract class BaseSAMLSimpleSignatureSecurityPolicyRule implements SecurityPolicyRule {
45
46
47 private final Logger log = LoggerFactory.getLogger(BaseSAMLSimpleSignatureSecurityPolicyRule.class);
48
49
50 private SignatureTrustEngine trustEngine;
51
52
53
54
55
56
57 protected BaseSAMLSimpleSignatureSecurityPolicyRule(SignatureTrustEngine engine) {
58 trustEngine = engine;
59 }
60
61
62 public void evaluate(MessageContext messageContext) throws SecurityPolicyException {
63 log.debug("Evaluating simple signature rule of type: {}", getClass().getName());
64 if (!(messageContext instanceof SAMLMessageContext)) {
65 log.debug("Invalid message context type, this policy rule only supports SAMLMessageContext");
66 return;
67 }
68
69 if (!(messageContext.getInboundMessageTransport() instanceof HttpServletRequestAdapter)) {
70 log.debug("Invalid inbound message transport type, this rule only supports HttpServletRequestAdapter");
71 return;
72 }
73
74 SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
75 HttpServletRequestAdapter requestAdapter = (HttpServletRequestAdapter) messageContext
76 .getInboundMessageTransport();
77 HttpServletRequest request = requestAdapter.getWrappedRequest();
78
79 if (!ruleHandles(request, samlMsgCtx)) {
80 log.debug("Rule can not handle this request, skipping processing");
81 return;
82 }
83
84 byte[] signature = getSignature(request);
85 if (signature == null || signature.length == 0) {
86 log.debug("HTTP request was not signed via simple signature mechanism, skipping");
87 return;
88 }
89
90 String sigAlg = getSignatureAlgorithm(request);
91 if (DatatypeHelper.isEmpty(sigAlg)) {
92 log.warn("Signature algorithm could not be extracted from request, can not validate simple signature");
93 return;
94 }
95
96 byte[] signedContent = getSignedContent(request);
97 if (signedContent == null || signedContent.length == 0) {
98 log.warn("Signed content could not be extracted from HTTP request, can not validate");
99 return;
100 }
101
102 doEvaluate(signature, signedContent, sigAlg, request, samlMsgCtx);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117 private void doEvaluate(byte[] signature, byte[] signedContent, String algorithmURI, HttpServletRequest request,
118 SAMLMessageContext samlMsgCtx) throws SecurityPolicyException {
119
120 List<Credential> candidateCredentials = getRequestCredentials(request, samlMsgCtx);
121
122 String contextIssuer = samlMsgCtx.getInboundMessageIssuer();
123
124 if (contextIssuer != null) {
125 log.debug("Attempting to validate SAML protocol message simple signature using context issuer: {}",
126 contextIssuer);
127 CriteriaSet criteriaSet = buildCriteriaSet(contextIssuer, samlMsgCtx);
128 if (validateSignature(signature, signedContent, algorithmURI, criteriaSet, candidateCredentials)) {
129 log.info("Validation of request simple signature succeeded");
130 if (!samlMsgCtx.isInboundSAMLMessageAuthenticated()) {
131 log.info("Authentication via request simple signature succeeded for context issuer entity ID {}",
132 contextIssuer);
133 samlMsgCtx.setInboundSAMLMessageAuthenticated(true);
134 }
135 return;
136 } else {
137 log.warn("Validation of request simple signature failed for context issuer: {}", contextIssuer);
138 throw new SecurityPolicyException("Validation of request simple signature failed for context issuer");
139 }
140 }
141
142 String derivedIssuer = deriveSignerEntityID(samlMsgCtx);
143 if (derivedIssuer != null) {
144 log.debug("Attempting to validate SAML protocol message simple signature using derived issuer: {}",
145 derivedIssuer);
146 CriteriaSet criteriaSet = buildCriteriaSet(derivedIssuer, samlMsgCtx);
147 if (validateSignature(signature, signedContent, algorithmURI, criteriaSet, candidateCredentials)) {
148 log.info("Validation of request simple signature succeeded");
149 if (!samlMsgCtx.isInboundSAMLMessageAuthenticated()) {
150 log.info("Authentication via request simple signature succeeded for derived issuer {}",
151 derivedIssuer);
152 samlMsgCtx.setInboundMessageIssuer(derivedIssuer);
153 samlMsgCtx.setInboundSAMLMessageAuthenticated(true);
154 }
155 return;
156 } else {
157 log.warn("Validation of request simple signature failed for derived issuer: {}", derivedIssuer);
158 throw new SecurityPolicyException("Validation of request simple signature failed for derived issuer");
159 }
160 }
161
162 log.warn("Neither context nor derived issuer available, can not attempt SAML simple signature validation");
163 throw new SecurityPolicyException("No message issuer available, can not attempt simple signature validation");
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 protected boolean validateSignature(byte[] signature, byte[] signedContent, String algorithmURI,
182 CriteriaSet criteriaSet, List<Credential> candidateCredentials) throws SecurityPolicyException {
183
184 SignatureTrustEngine engine = getTrustEngine();
185
186
187
188 try {
189 if (candidateCredentials == null || candidateCredentials.isEmpty()) {
190 if (engine.validate(signature, signedContent, algorithmURI, criteriaSet, null)) {
191 log.debug("Simple signature validation (with no request-derived credentials) was successful");
192 return true;
193 } else {
194 log.warn("Simple signature validation (with no request-derived credentials) failed");
195 return false;
196 }
197 } else {
198 for (Credential cred : candidateCredentials) {
199 if (engine.validate(signature, signedContent, algorithmURI, criteriaSet, cred)) {
200 log.debug("Simple signature validation succeeded with a request-derived credential");
201 return true;
202 }
203 }
204 log.warn("Signature validation using request-derived credentials failed");
205 return false;
206 }
207 } catch (SecurityException e) {
208 log.warn("There was an error evaluating the request's simple signature using the trust engine", e);
209 throw new SecurityPolicyException("Error during trust engine evaluation of the simple signature", e);
210 }
211 }
212
213
214
215
216
217
218
219
220
221
222
223 protected List<Credential> getRequestCredentials(HttpServletRequest request, SAMLMessageContext samlContext)
224 throws SecurityPolicyException {
225
226 return null;
227 }
228
229
230
231
232
233
234 protected SignatureTrustEngine getTrustEngine() {
235 return trustEngine;
236 }
237
238
239
240
241
242
243
244
245
246
247
248 protected byte[] getSignature(HttpServletRequest request) throws SecurityPolicyException {
249 String signature = request.getParameter("Signature");
250 if (DatatypeHelper.isEmpty(signature)) {
251 return null;
252 }
253 return Base64.decode(signature);
254 }
255
256
257
258
259
260
261
262
263
264
265 protected String getSignatureAlgorithm(HttpServletRequest request) throws SecurityPolicyException {
266 return request.getParameter("SigAlg");
267 }
268
269
270
271
272
273
274
275
276
277
278 protected String deriveSignerEntityID(SAMLMessageContext samlContext) throws SecurityPolicyException {
279
280 return null;
281 }
282
283
284
285
286
287
288
289
290
291 protected CriteriaSet buildCriteriaSet(String entityID, SAMLMessageContext samlContext)
292 throws SecurityPolicyException {
293
294 CriteriaSet criteriaSet = new CriteriaSet();
295 if (!DatatypeHelper.isEmpty(entityID)) {
296 criteriaSet.add(new EntityIDCriteria(entityID));
297 }
298
299 MetadataCriteria mdCriteria = new MetadataCriteria(samlContext.getPeerEntityRole(), samlContext
300 .getInboundSAMLProtocol());
301 criteriaSet.add(mdCriteria);
302
303 criteriaSet.add(new UsageCriteria(UsageType.SIGNING));
304
305 return criteriaSet;
306 }
307
308
309
310
311
312
313
314
315
316 protected abstract byte[] getSignedContent(HttpServletRequest request) throws SecurityPolicyException;
317
318
319
320
321
322
323
324
325
326
327 protected abstract boolean ruleHandles(HttpServletRequest request, SAMLMessageContext samlMsgCtx)
328 throws SecurityPolicyException;
329
330 }