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


Reply via email to