1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.validate;
21
22 import java.time.Instant;
23 import java.util.List;
24
25 import org.apache.wss4j.common.cache.ReplayCache;
26 import org.apache.wss4j.common.ext.WSSecurityException;
27 import org.apache.wss4j.common.saml.OpenSAMLUtil;
28 import org.apache.wss4j.common.saml.SAMLKeyInfo;
29 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
30 import org.apache.wss4j.common.saml.builder.SAML1Constants;
31 import org.apache.wss4j.common.saml.builder.SAML2Constants;
32 import org.apache.wss4j.dom.handler.RequestData;
33 import org.opensaml.saml.common.SAMLVersion;
34
35
36
37
38
39
40
41
42 public class SamlAssertionValidator extends SignatureTrustValidator {
43
44 private static final org.slf4j.Logger LOG =
45 org.slf4j.LoggerFactory.getLogger(SamlAssertionValidator.class);
46
47
48
49
50
51 private int futureTTL = 60;
52
53
54
55
56
57 private int ttl = 60 * 30;
58
59
60
61
62
63 private boolean validateSignatureAgainstProfile = true;
64
65
66
67
68 private String requiredSubjectConfirmationMethod;
69
70
71
72
73
74 private boolean requireStandardSubjectConfirmationMethod = true;
75
76
77
78
79
80 private boolean requireBearerSignature = true;
81
82
83
84
85
86 public void setFutureTTL(int newFutureTTL) {
87 futureTTL = newFutureTTL;
88 }
89
90
91
92
93
94
95
96
97
98 public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
99 if (credential == null || credential.getSamlAssertion() == null) {
100 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noCredential");
101 }
102 SamlAssertionWrapper samlAssertion = credential.getSamlAssertion();
103
104
105 verifySubjectConfirmationMethod(samlAssertion);
106
107
108 checkConditions(samlAssertion, data.getAudienceRestrictions());
109
110
111 checkAuthnStatements(samlAssertion);
112
113
114 checkOneTimeUse(samlAssertion, data);
115
116
117 validateAssertion(samlAssertion);
118
119
120 if (samlAssertion.isSigned()) {
121 verifySignedAssertion(samlAssertion, data);
122 }
123 return credential;
124 }
125
126
127
128
129 protected void verifySubjectConfirmationMethod(
130 SamlAssertionWrapper samlAssertion
131 ) throws WSSecurityException {
132
133 List<String> methods = samlAssertion.getConfirmationMethods();
134 if (methods == null || methods.isEmpty()) {
135 if (requiredSubjectConfirmationMethod != null) {
136 LOG.warn("A required subject confirmation method was not present");
137 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
138 "invalidSAMLsecurity");
139 } else if (requireStandardSubjectConfirmationMethod) {
140 LOG.warn("A standard subject confirmation method was not present");
141 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
142 "invalidSAMLsecurity");
143 }
144 }
145
146 boolean signed = samlAssertion.isSigned();
147 boolean requiredMethodFound = false;
148 boolean standardMethodFound = false;
149 if (methods != null) {
150 for (String method : methods) {
151 if (OpenSAMLUtil.isMethodHolderOfKey(method)) {
152 if (samlAssertion.getSubjectKeyInfo() == null) {
153 LOG.warn("There is no Subject KeyInfo to match the holder-of-key subject conf method");
154 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noKeyInSAMLToken");
155 }
156
157
158 if (!signed) {
159 LOG.warn("A holder-of-key assertion must be signed");
160 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
161 }
162 standardMethodFound = true;
163 }
164
165 if (method != null) {
166 if (method.equals(requiredSubjectConfirmationMethod)) {
167 requiredMethodFound = true;
168 }
169 if (SAML2Constants.CONF_BEARER.equals(method)
170 || SAML1Constants.CONF_BEARER.equals(method)) {
171 standardMethodFound = true;
172 if (requireBearerSignature && !signed) {
173 LOG.warn("A Bearer Assertion was not signed");
174 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
175 "invalidSAMLsecurity");
176 }
177 } else if (SAML2Constants.CONF_SENDER_VOUCHES.equals(method)
178 || SAML1Constants.CONF_SENDER_VOUCHES.equals(method)) {
179 standardMethodFound = true;
180 }
181 }
182 }
183 }
184
185 if (!requiredMethodFound && requiredSubjectConfirmationMethod != null) {
186 LOG.warn("A required subject confirmation method was not present");
187 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
188 "invalidSAMLsecurity");
189 }
190
191 if (!standardMethodFound && requireStandardSubjectConfirmationMethod) {
192 LOG.warn("A standard subject confirmation method was not present");
193 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE,
194 "invalidSAMLsecurity");
195 }
196 }
197
198
199
200
201
202
203
204
205
206 protected Credential verifySignedAssertion(
207 SamlAssertionWrapper samlAssertion,
208 RequestData data
209 ) throws WSSecurityException {
210 Credential trustCredential = new Credential();
211 SAMLKeyInfo samlKeyInfo = samlAssertion.getSignatureKeyInfo();
212 trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
213 trustCredential.setCertificates(samlKeyInfo.getCerts());
214 return super.validate(trustCredential, data);
215 }
216
217
218
219
220 protected void checkConditions(
221 SamlAssertionWrapper samlAssertion, List<String> audienceRestrictions
222 ) throws WSSecurityException {
223 checkConditions(samlAssertion);
224 samlAssertion.checkAudienceRestrictions(audienceRestrictions);
225 }
226
227
228
229
230 protected void checkConditions(SamlAssertionWrapper samlAssertion) throws WSSecurityException {
231 samlAssertion.checkConditions(futureTTL);
232 samlAssertion.checkIssueInstant(futureTTL, ttl);
233 }
234
235
236
237
238 protected void checkAuthnStatements(SamlAssertionWrapper samlAssertion) throws WSSecurityException {
239 samlAssertion.checkAuthnStatements(futureTTL);
240 }
241
242
243
244
245
246 protected void checkOneTimeUse(
247 SamlAssertionWrapper samlAssertion, RequestData data
248 ) throws WSSecurityException {
249 if (samlAssertion.getSamlVersion().equals(SAMLVersion.VERSION_20)
250 && samlAssertion.getSaml2().getConditions() != null
251 && samlAssertion.getSaml2().getConditions().getOneTimeUse() != null
252 && data.getSamlOneTimeUseReplayCache() != null) {
253 String identifier = samlAssertion.getId();
254
255 ReplayCache replayCache = data.getSamlOneTimeUseReplayCache();
256 if (replayCache.contains(identifier)) {
257 throw new WSSecurityException(
258 WSSecurityException.ErrorCode.INVALID_SECURITY,
259 "badSamlToken",
260 new Object[] {"A replay attack has been detected"});
261 }
262
263 Instant expires = samlAssertion.getSaml2().getConditions().getNotOnOrAfter();
264 if (expires != null) {
265 replayCache.add(identifier, expires);
266 } else {
267 replayCache.add(identifier);
268 }
269
270 replayCache.add(identifier);
271 }
272 }
273
274
275
276
277 protected void validateAssertion(SamlAssertionWrapper samlAssertion) throws WSSecurityException {
278 if (validateSignatureAgainstProfile) {
279 samlAssertion.validateSignatureAgainstProfile();
280 }
281 }
282
283
284
285
286
287 public boolean isValidateSignatureAgainstProfile() {
288 return validateSignatureAgainstProfile;
289 }
290
291
292
293
294
295 public void setValidateSignatureAgainstProfile(boolean validateSignatureAgainstProfile) {
296 this.validateSignatureAgainstProfile = validateSignatureAgainstProfile;
297 }
298
299 public String getRequiredSubjectConfirmationMethod() {
300 return requiredSubjectConfirmationMethod;
301 }
302
303 public void setRequiredSubjectConfirmationMethod(String requiredSubjectConfirmationMethod) {
304 this.requiredSubjectConfirmationMethod = requiredSubjectConfirmationMethod;
305 }
306
307 public boolean isRequireStandardSubjectConfirmationMethod() {
308 return requireStandardSubjectConfirmationMethod;
309 }
310
311 public void setRequireStandardSubjectConfirmationMethod(boolean requireStandardSubjectConfirmationMethod) {
312 this.requireStandardSubjectConfirmationMethod = requireStandardSubjectConfirmationMethod;
313 }
314
315 public boolean isRequireBearerSignature() {
316 return requireBearerSignature;
317 }
318
319 public void setRequireBearerSignature(boolean requireBearerSignature) {
320 this.requireBearerSignature = requireBearerSignature;
321 }
322
323 public int getTtl() {
324 return ttl;
325 }
326
327 public void setTtl(int ttl) {
328 this.ttl = ttl;
329 }
330
331 }