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.saml;
21
22 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
23 import org.apache.wss4j.common.util.SOAPUtil;
24 import org.apache.wss4j.dom.WSConstants;
25 import org.apache.wss4j.dom.common.AbstractSAMLCallbackHandler;
26 import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
27 import org.apache.wss4j.dom.common.SAML1CallbackHandler;
28 import org.apache.wss4j.dom.common.SAML2CallbackHandler;
29
30 import org.apache.wss4j.dom.engine.WSSConfig;
31 import org.apache.wss4j.dom.engine.WSSecurityEngine;
32 import org.apache.wss4j.dom.handler.RequestData;
33 import org.apache.wss4j.dom.handler.WSHandlerResult;
34 import org.apache.wss4j.common.crypto.Crypto;
35 import org.apache.wss4j.common.crypto.CryptoFactory;
36 import org.apache.wss4j.common.crypto.CryptoType;
37 import org.apache.wss4j.common.crypto.Merlin;
38 import org.apache.wss4j.common.ext.WSSecurityException;
39 import org.apache.wss4j.common.saml.SAMLCallback;
40 import org.apache.wss4j.common.saml.SAMLUtil;
41 import org.apache.wss4j.common.saml.bean.SubjectBean;
42 import org.apache.wss4j.common.saml.bean.Version;
43 import org.apache.wss4j.common.saml.builder.SAML1Constants;
44 import org.apache.wss4j.common.saml.builder.SAML2Constants;
45 import org.apache.wss4j.common.util.Loader;
46 import org.apache.wss4j.common.util.XMLUtils;
47 import org.apache.wss4j.dom.message.WSSecHeader;
48 import org.apache.wss4j.dom.message.WSSecSAMLToken;
49
50 import org.junit.jupiter.api.Test;
51 import org.w3c.dom.Document;
52
53 import javax.security.auth.callback.Callback;
54 import javax.security.auth.callback.CallbackHandler;
55 import javax.security.auth.callback.UnsupportedCallbackException;
56
57 import java.io.IOException;
58 import java.io.InputStream;
59 import java.security.KeyStore;
60
61 import org.w3c.dom.Element;
62 import org.w3c.dom.NodeList;
63
64 import static org.junit.jupiter.api.Assertions.assertTrue;
65 import static org.junit.jupiter.api.Assertions.fail;
66
67
68
69
70
71 public class SamlNegativeTest {
72 private static final org.slf4j.Logger LOG =
73 org.slf4j.LoggerFactory.getLogger(SamlNegativeTest.class);
74 private WSSecurityEngine secEngine = new WSSecurityEngine();
75 private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
76 private Crypto trustCrypto;
77 private Crypto issuerCrypto;
78 private Crypto userCrypto = CryptoFactory.getInstance("wss40.properties");
79
80 public SamlNegativeTest() throws Exception {
81 WSSConfig config = WSSConfig.getNewInstance();
82 secEngine.setWssConfig(config);
83
84
85 issuerCrypto = new Merlin();
86 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
87 ClassLoader loader = Loader.getClassLoader(SamlNegativeTest.class);
88 InputStream input = Merlin.loadInputStream(loader, "keys/wss40_server.jks");
89 keyStore.load(input, "security".toCharArray());
90 input.close();
91 ((Merlin)issuerCrypto).setKeyStore(keyStore);
92
93
94 trustCrypto = new Merlin();
95 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
96 input = Merlin.loadInputStream(loader, "keys/wss40CA.jks");
97 trustStore.load(input, "security".toCharArray());
98 input.close();
99 ((Merlin)trustCrypto).setTrustStore(trustStore);
100 }
101
102
103
104
105
106
107 @Test
108 public void testSAML2AuthnAssertionModified() throws Exception {
109 SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
110 callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
111 callbackHandler.setConfirmationMethod(SAML2Constants.CONF_SENDER_VOUCHES);
112 callbackHandler.setIssuer("www.example.com");
113
114 SAMLCallback samlCallback = new SAMLCallback();
115 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
116 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
117
118 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
119 WSSecHeader secHeader = new WSSecHeader(doc);
120 secHeader.insertSecurityHeader();
121
122 WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
123 wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
124
125 Document signedDoc =
126 wsSign.build(
127 null, samlAssertion, userCrypto, "wss40", "security"
128 );
129
130
131
132
133 Element envelope = signedDoc.getDocumentElement();
134 NodeList list = envelope.getElementsByTagNameNS(WSConstants.SAML2_NS, "Assertion");
135 Element assertionElement = (Element)list.item(0);
136 list = assertionElement.getElementsByTagNameNS(WSConstants.SAML2_NS, "Issuer");
137 Element issuer = (Element)list.item(0);
138 issuer.setTextContent("www.example2.com");
139
140 if (LOG.isDebugEnabled()) {
141 LOG.debug("SAML 2 Authn Assertion (sender vouches):");
142 String outputString =
143 XMLUtils.prettyDocumentToString(signedDoc);
144 LOG.debug(outputString);
145 }
146
147 try {
148 verify(signedDoc, trustCrypto);
149 fail("Failure expected on a modified SAML Assertion");
150 } catch (WSSecurityException ex) {
151 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
152 }
153 }
154
155
156
157
158
159
160
161 @Test
162 public void testSAML1SignedKeyHolderSigModified() throws Exception {
163 SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
164 callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
165 callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
166 callbackHandler.setIssuer("www.example.com");
167
168 SAMLCallback samlCallback = new SAMLCallback();
169 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
170 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
171
172 samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
173
174 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
175 WSSecHeader secHeader = new WSSecHeader(doc);
176 secHeader.insertSecurityHeader();
177
178 WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
179
180 Document signedDoc = wsSign.build(samlAssertion);
181
182
183
184
185 Element envelope = signedDoc.getDocumentElement();
186 NodeList list = envelope.getElementsByTagNameNS(WSConstants.SAML_NS, "Assertion");
187 Element assertionElement = (Element)list.item(0);
188 list = assertionElement.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature");
189 Element sigElement = (Element)list.item(0);
190 list = sigElement.getElementsByTagNameNS(WSConstants.SIG_NS, "Transform");
191 Element transformElement = (Element)list.item(0);
192 transformElement.setAttributeNS(null, "Algorithm", WSConstants.C14N_EXCL_OMIT_COMMENTS);
193
194 if (LOG.isDebugEnabled()) {
195 LOG.debug("Signed (modified) SAML message (key holder):");
196 String outputString =
197 XMLUtils.prettyDocumentToString(signedDoc);
198 LOG.debug(outputString);
199 }
200
201 try {
202 verify(signedDoc, trustCrypto);
203 fail("Expected failure on a modified signature");
204 } catch (WSSecurityException ex) {
205 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILURE);
206 }
207 }
208
209
210
211
212
213 @Test
214 public void testSAML2SignedKeyHolderKeyModified() throws Exception {
215 SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
216 callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
217 callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
218 callbackHandler.setIssuer("www.example.com");
219
220 SAMLCallback samlCallback = new SAMLCallback();
221 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
222 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
223
224 samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
225
226 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
227 WSSecHeader secHeader = new WSSecHeader(doc);
228 secHeader.insertSecurityHeader();
229
230 WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
231
232 Document signedDoc = wsSign.build(samlAssertion);
233
234
235
236 Element envelope = signedDoc.getDocumentElement();
237 NodeList list = envelope.getElementsByTagNameNS(WSConstants.SAML2_NS, "Assertion");
238 Element assertionElement = (Element)list.item(0);
239 assertionElement.setAttributeNS(null, "MinorVersion", "5");
240
241 if (LOG.isDebugEnabled()) {
242 LOG.debug("Signed (modified) SAML message (key holder):");
243 String outputString =
244 XMLUtils.prettyDocumentToString(signedDoc);
245 LOG.debug(outputString);
246 }
247
248 try {
249 verify(signedDoc, trustCrypto);
250 fail("Expected failure on a modified signature");
251 } catch (WSSecurityException ex) {
252 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILURE);
253 }
254 }
255
256
257
258
259
260 @Test
261 public void testHOKNoKeyInfo() throws Exception {
262 SAML1HOKNoKeyInfoCallbackHandler callbackHandler =
263 new SAML1HOKNoKeyInfoCallbackHandler();
264 callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
265 callbackHandler.setIssuer("www.example.com");
266
267 SAMLCallback samlCallback = new SAMLCallback();
268 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
269 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
270
271 samlAssertion.signAssertion("wss40_server", "security", issuerCrypto, false);
272
273 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
274 WSSecHeader secHeader = new WSSecHeader(doc);
275 secHeader.insertSecurityHeader();
276
277 WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
278 Document signedDoc = wsSign.build(samlAssertion);
279
280 String outputString =
281 XMLUtils.prettyDocumentToString(signedDoc);
282 if (LOG.isDebugEnabled()) {
283 LOG.debug("SAML 1.1 Authn Assertion (key holder):");
284 LOG.debug(outputString);
285 }
286
287 try {
288 verify(signedDoc, trustCrypto);
289 fail("Expected failure on a holder-of-key confirmation method with no KeyInfo");
290 } catch (WSSecurityException ex) {
291 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILURE);
292 }
293 }
294
295
296
297
298
299 @Test
300 public void testHOKNotSigned() throws Exception {
301 SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
302 callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
303 callbackHandler.setConfirmationMethod(SAML1Constants.CONF_HOLDER_KEY);
304
305 SAMLCallback samlCallback = new SAMLCallback();
306 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
307
308 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
309
310 samlCallback.setIssuer("www.example.com");
311 samlCallback.setIssuerCrypto(issuerCrypto);
312 samlCallback.setIssuerKeyName("wss40_server");
313 samlCallback.setIssuerKeyPassword("security");
314
315
316 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
317 WSSecHeader secHeader = new WSSecHeader(doc);
318 secHeader.insertSecurityHeader();
319
320 WSSecSAMLToken wsSign = new WSSecSAMLToken(secHeader);
321 Document signedDoc = wsSign.build(samlAssertion);
322
323 String outputString =
324 XMLUtils.prettyDocumentToString(signedDoc);
325 if (LOG.isDebugEnabled()) {
326 LOG.debug("SAML 1.1 Authn Assertion (unsigned key holder):");
327 LOG.debug(outputString);
328 }
329
330 try {
331 verify(signedDoc, trustCrypto);
332 fail("Expected failure on an unsigned assertion with holder-of-key confirmation method");
333 } catch (WSSecurityException ex) {
334 assertTrue(ex.getMessage().contains("SAML token security failure"));
335 }
336 }
337
338
339
340
341
342 @Test
343 public void testSAML2TrustFailure() throws Exception {
344 SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
345 callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
346 callbackHandler.setConfirmationMethod(SAML2Constants.CONF_HOLDER_KEY);
347 callbackHandler.setIssuer("www.example.com");
348
349 SAMLCallback samlCallback = new SAMLCallback();
350 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
351 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
352
353 samlAssertion.signAssertion(
354 "16c73ab6-b892-458f-abf5-2f875f74882e", "security",
355 CryptoFactory.getInstance("crypto.properties"), false
356 );
357
358 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
359 WSSecHeader secHeader = new WSSecHeader(doc);
360 secHeader.insertSecurityHeader();
361
362 WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
363 wsSign.setUserInfo("wss40", "security");
364 wsSign.setDigestAlgo("http://www.w3.org/2001/04/xmlenc#sha256");
365 wsSign.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
366 wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
367
368 Document signedDoc =
369 wsSign.build(userCrypto, samlAssertion, null, null, null);
370
371 String outputString =
372 XMLUtils.prettyDocumentToString(signedDoc);
373 if (LOG.isDebugEnabled()) {
374 LOG.debug("Untrusted signed SAML 2 Authn Assertion (key holder):");
375 LOG.debug(outputString);
376 }
377
378 try {
379 verify(signedDoc, trustCrypto);
380 fail ("Failure expected on an untrusted signed assertion");
381 } catch (WSSecurityException ex) {
382 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILURE);
383 }
384 }
385
386
387
388
389
390
391
392 private WSHandlerResult verify(Document doc, Crypto sigCrypto) throws Exception {
393 RequestData requestData = new RequestData();
394 requestData.setCallbackHandler(callbackHandler);
395 requestData.setDecCrypto(userCrypto);
396 requestData.setSigVerCrypto(sigCrypto);
397 requestData.setValidateSamlSubjectConfirmation(false);
398
399 WSHandlerResult results = secEngine.processSecurityHeader(doc, requestData);
400
401 String outputString =
402 XMLUtils.prettyDocumentToString(doc);
403 assertTrue(outputString.indexOf("counter_port_type") > 0 ? true : false);
404 return results;
405 }
406
407
408
409
410
411 private static class SAML1HOKNoKeyInfoCallbackHandler extends AbstractSAMLCallbackHandler {
412
413 public SAML1HOKNoKeyInfoCallbackHandler() throws Exception {
414 Crypto crypto = CryptoFactory.getInstance("wss40.properties");
415 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
416 cryptoType.setAlias("wss40");
417 certs = crypto.getX509Certificates(cryptoType);
418
419 subjectName = "uid=joe,ou=people,ou=saml-demo,o=example.com";
420 subjectQualifier = "www.example.com";
421 confirmationMethod = SAML1Constants.CONF_HOLDER_KEY;
422 }
423
424 public void handle(Callback[] callbacks)
425 throws IOException, UnsupportedCallbackException {
426 for (Callback callback : callbacks) {
427 if (callback instanceof SAMLCallback) {
428 SAMLCallback samlCallback = (SAMLCallback) callback;
429 SubjectBean subjectBean =
430 new SubjectBean(
431 subjectName, subjectQualifier, confirmationMethod
432 );
433 createAndSetStatement(subjectBean, samlCallback);
434 samlCallback.setSamlVersion(Version.SAML_11);
435 } else {
436 throw new UnsupportedCallbackException(callback, "Unrecognized Callback");
437 }
438 }
439 }
440 }
441
442 }