Author: coheigea
Date: Fri Sep 20 11:08:39 2013
New Revision: 1524976
URL: http://svn.apache.org/r1524976
Log:
[WSS-478] - Support the ability to cache SAML2 Assertions with "OneTimeUse"
Conditions
Conflicts:
src/main/java/org/apache/ws/security/handler/RequestData.java
src/main/java/org/apache/ws/security/handler/WSHandlerConstants.java
src/main/java/org/apache/ws/security/processor/SignatureProcessor.java
src/test/java/org/apache/ws/security/message/ReplayTest.java
ws-security-common/src/main/resources/messages/wss4j_errors.properties
ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java
ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java
ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java
ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java
ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/handler/RequestData.java
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/SignatureProcessor.java
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
webservices/wss4j/branches/1_6_x-fixes/src/main/resources/org/apache/ws/security/errors.properties
webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/message/ReplayTest.java
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/handler/RequestData.java
URL:
http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/handler/RequestData.java?rev=1524976&r1=1524975&r2=1524976&view=diff
==============================================================================
---
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/handler/RequestData.java
(original)
+++
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/handler/RequestData.java
Fri Sep 20 11:08:39 2013
@@ -85,6 +85,7 @@ public class RequestData {
protected boolean requireSignedEncryptedDataElements = false;
private ReplayCache timestampReplayCache;
private ReplayCache nonceReplayCache;
+ private ReplayCache samlOneTimeUseReplayCache;
private Collection<Pattern> subjectDNPatterns = new ArrayList<Pattern>();
private boolean appendSignatureAfterTimestamp;
private int originalSignatureActionPosition;
@@ -115,6 +116,7 @@ public class RequestData {
enableRevocation = false;
timestampReplayCache = null;
nonceReplayCache = null;
+ samlOneTimeUseReplayCache = null;
subjectDNPatterns.clear();
appendSignatureAfterTimestamp = false;
algorithmSuite = null;
@@ -516,6 +518,21 @@ public class RequestData {
}
/**
+ * Set the replay cache for SAML2 OneTimeUse Assertions
+ */
+ public void setSamlOneTimeUseReplayCache(ReplayCache newCache) {
+ samlOneTimeUseReplayCache = newCache;
+ }
+
+ /**
+ * Get the replay cache for SAML2 OneTimeUse Assertions
+ * @throws WSSecurityException
+ */
+ public ReplayCache getSamlOneTimeUseReplayCache() throws
WSSecurityException {
+ return samlOneTimeUseReplayCache;
+ }
+
+ /**
* Set the Signature Subject Cert Constraints
*/
public void setSubjectCertConstraints(Collection<Pattern>
subjectCertConstraints) {
@@ -562,5 +579,5 @@ public class RequestData {
public void setOriginalSignatureActionPosition(int
originalSignatureActionPosition) {
this.originalSignatureActionPosition = originalSignatureActionPosition;
}
-
+
}
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/SignatureProcessor.java
URL:
http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/SignatureProcessor.java?rev=1524976&r1=1524975&r2=1524976&view=diff
==============================================================================
---
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/SignatureProcessor.java
(original)
+++
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/processor/SignatureProcessor.java
Fri Sep 20 11:08:39 2013
@@ -686,7 +686,7 @@ public class SignatureProcessor implemen
Date rightNow = new Date();
long currentTime = rightNow.getTime();
long expiresTime = expires.getTime();
- replayCache.add(identifier, ((expiresTime - currentTime) / 1000L));
+ replayCache.add(identifier, 1L + (expiresTime - currentTime) /
1000L);
} else {
replayCache.add(identifier);
}
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
URL:
http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java?rev=1524976&r1=1524975&r2=1524976&view=diff
==============================================================================
---
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
(original)
+++
webservices/wss4j/branches/1_6_x-fixes/src/main/java/org/apache/ws/security/validate/SamlAssertionValidator.java
Fri Sep 20 11:08:39 2013
@@ -19,9 +19,11 @@
package org.apache.ws.security.validate;
+import java.util.Date;
import java.util.List;
import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.cache.ReplayCache;
import org.apache.ws.security.handler.RequestData;
import org.apache.ws.security.saml.SAMLKeyInfo;
import org.apache.ws.security.saml.ext.AssertionWrapper;
@@ -97,6 +99,9 @@ public class SamlAssertionValidator exte
// Check conditions
checkConditions(assertion);
+
+ // Check OneTimeUse Condition
+ checkOneTimeUse(assertion, data);
// Validate the assertion against schemas/profiles
validateAssertion(assertion);
@@ -157,7 +162,42 @@ public class SamlAssertionValidator exte
throw new WSSecurityException(WSSecurityException.FAILURE,
"invalidSAMLsecurity");
}
}
-
+
+ /**
+ * Check the "OneTimeUse" Condition of the Assertion. If this is set then
the Assertion
+ * is cached (if a cache is defined), and must not have been previously
cached
+ */
+ protected void checkOneTimeUse(
+ AssertionWrapper samlAssertion, RequestData data
+ ) throws WSSecurityException {
+ if (data.getSamlOneTimeUseReplayCache() != null
+ && samlAssertion.getSamlVersion().equals(SAMLVersion.VERSION_20)
+ && samlAssertion.getSaml2().getConditions() != null
+ && samlAssertion.getSaml2().getConditions().getOneTimeUse() !=
null) {
+ String identifier = samlAssertion.getId();
+
+ ReplayCache replayCache = data.getSamlOneTimeUseReplayCache();
+ if (replayCache.contains(identifier)) {
+ throw new WSSecurityException(
+ WSSecurityException.INVALID_SECURITY,
+ "badSamlToken",
+ new Object[] {"A replay attack has been detected"});
+ }
+
+ DateTime expires =
samlAssertion.getSaml2().getConditions().getNotOnOrAfter();
+ if (expires != null) {
+ Date rightNow = new Date();
+ long currentTime = rightNow.getTime();
+ long expiresTime = expires.getMillis();
+ replayCache.add(identifier, 1L + (expiresTime - currentTime) /
1000L);
+ } else {
+ replayCache.add(identifier);
+ }
+
+ replayCache.add(identifier);
+ }
+ }
+
/**
* Validate the assertion against schemas/profiles
*/
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/main/resources/org/apache/ws/security/errors.properties
URL:
http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/main/resources/org/apache/ws/security/errors.properties?rev=1524976&r1=1524975&r2=1524976&view=diff
==============================================================================
---
webservices/wss4j/branches/1_6_x-fixes/src/main/resources/org/apache/ws/security/errors.properties
(original)
+++
webservices/wss4j/branches/1_6_x-fixes/src/main/resources/org/apache/ws/security/errors.properties
Fri Sep 20 11:08:39 2013
@@ -37,6 +37,7 @@ invalidDataRef = Cannot handle multiple
invalidEmbeddedRef = The embedded Reference is invalid
noEncryptedData = Referenced encrypted data could not be retrieved. Reference
\"{0}\"
badElement = Bad element, expected \"{0}\" while got \"{1}\"
+badSamlToken = An error happened processing a SAML Token: \"{0}\"
badUsernameToken = An error happened processing a Username Token \"{0}\"
badC14nAlgo = A bad canonicalization algorithm was specified
failedAuthentication = User ({0}) not authenticated
@@ -111,4 +112,4 @@ R5406="Any CANONICALIZATION_METHOD MUST
R5416=Any SIG_REFERENCE MUST contain a SIG_TRANSFORMS child element
R5423=The SIG_TRANSFORM Algorithm attribute has an incorrect value
R5412=The last SIG_TRANSFORM child of SIG_TRANSFORMS has an incorrect
Algorithm attribute
-R5407= Algorithm attribute with a value of
\"http://www.w3.org/2001/10/xml-exc-c14n#\" MUST contain an
INCLUSIVE_NAMESPACES with an PrefixList attribute unless the PrefixList is empty
\ No newline at end of file
+R5407= Algorithm attribute with a value of
\"http://www.w3.org/2001/10/xml-exc-c14n#\" MUST contain an
INCLUSIVE_NAMESPACES with an PrefixList attribute unless the PrefixList is empty
Modified:
webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/message/ReplayTest.java
URL:
http://svn.apache.org/viewvc/webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/message/ReplayTest.java?rev=1524976&r1=1524975&r2=1524976&view=diff
==============================================================================
---
webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/message/ReplayTest.java
(original)
+++
webservices/wss4j/branches/1_6_x-fixes/src/test/java/org/apache/ws/security/message/ReplayTest.java
Fri Sep 20 11:08:39 2013
@@ -32,12 +32,18 @@ import org.apache.ws.security.WSSecurity
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.cache.MemoryReplayCache;
import org.apache.ws.security.common.KeystoreCallbackHandler;
+import org.apache.ws.security.common.SAML2CallbackHandler;
import org.apache.ws.security.common.SOAPUtil;
import org.apache.ws.security.common.UsernamePasswordCallbackHandler;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.SAMLParms;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.builder.SAML2Constants;
import org.apache.ws.security.util.WSSecurityUtil;
+import org.apache.ws.security.util.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -244,6 +250,108 @@ public class ReplayTest extends org.juni
}
/**
+ * Test that creates, sends and processes an unsigned SAML 2
authentication assertion. This
+ * is just a sanity test to make sure that it is possible to send the SAML
token twice, as
+ * no "OneTimeUse" Element is defined there is no problem with replaying
it.
+ * with a OneTimeUse Element
+ */
+ @org.junit.Test
+ public void testReplayedSAML2() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("www.example.com");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setTokenPeriodMinutes(5);
+
+ callbackHandler.setConditions(conditions);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper samlAssertion = new AssertionWrapper(samlParms);
+
+ WSSecSAMLToken wsSign = new WSSecSAMLToken();
+
+ Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+ WSSecHeader secHeader = new WSSecHeader();
+ secHeader.insertSecurityHeader(doc);
+
+ Document unsignedDoc = wsSign.build(doc, samlAssertion, secHeader);
+
+ if (LOG.isDebugEnabled()) {
+ String outputString = XMLUtils.PrettyDocumentToString(unsignedDoc);
+ LOG.debug(outputString);
+ }
+
+ WSSConfig wssConfig = WSSConfig.getNewInstance();
+ RequestData data = new RequestData();
+ data.setWssConfig(wssConfig);
+ data.setCallbackHandler(callbackHandler);
+ data.setSamlOneTimeUseReplayCache(new MemoryReplayCache());
+
+ // Successfully verify SAML Token
+ verify(unsignedDoc, wssConfig, data);
+
+ // Now try again - this should work fine as well
+ verify(unsignedDoc, wssConfig, data);
+ }
+
+ /**
+ * Test that creates, sends and processes an unsigned SAML 2
authentication assertion
+ * with a OneTimeUse Element
+ */
+ @org.junit.Test
+ public void testReplayedSAML2OneTimeUse() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setIssuer("www.example.com");
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setTokenPeriodMinutes(5);
+ conditions.setOneTimeUse(true);
+
+ callbackHandler.setConditions(conditions);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper samlAssertion = new AssertionWrapper(samlParms);
+
+ WSSecSAMLToken wsSign = new WSSecSAMLToken();
+
+ Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
+ WSSecHeader secHeader = new WSSecHeader();
+ secHeader.insertSecurityHeader(doc);
+
+ Document unsignedDoc = wsSign.build(doc, samlAssertion, secHeader);
+
+ String outputString =
+ XMLUtils.PrettyDocumentToString(unsignedDoc);
+ assertTrue(outputString.contains("OneTimeUse"));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(outputString);
+ }
+
+ WSSConfig wssConfig = WSSConfig.getNewInstance();
+ RequestData data = new RequestData();
+ data.setWssConfig(wssConfig);
+ data.setCallbackHandler(callbackHandler);
+ data.setSamlOneTimeUseReplayCache(new MemoryReplayCache());
+
+ // Successfully verify SAML Token
+ verify(unsignedDoc, wssConfig, data);
+
+ // Now try again - a replay attack should be detected
+ try {
+ verify(unsignedDoc, wssConfig, data);
+ fail("Expected failure on a replay attack");
+ } catch (WSSecurityException ex) {
+ assertTrue(ex.getErrorCode() ==
WSSecurityException.INVALID_SECURITY);
+ }
+ }
+
+ /**
* Verifies the soap envelope
*
* @param env soap envelope