You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by co...@apache.org on 2013/09/20 12:41:37 UTC
svn commit: r1524969 - in /webservices/wss4j/trunk:
ws-security-common/src/main/java/org/apache/wss4j/common/
ws-security-common/src/main/resources/messages/
ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/
ws-security-dom/src/main/java/org/...
Author: coheigea
Date: Fri Sep 20 10:41:37 2013
New Revision: 1524969
URL: http://svn.apache.org/r1524969
Log:
[WSS-478] - Support the ability to cache SAML2 Assertions with "OneTimeUse" Conditions
Modified:
webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
webservices/wss4j/trunk/ws-security-common/src/main/resources/messages/wss4j_errors.properties
webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java
webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java
webservices/wss4j/trunk/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/ReplayTest.java
webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java
webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java
webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java
webservices/wss4j/trunk/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java
Modified: webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java (original)
+++ webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java Fri Sep 20 10:41:37 2013
@@ -512,6 +512,12 @@ public final class ConfigurationConstant
*/
public static final String ENABLE_TIMESTAMP_CACHE = "ws-security.enable.timestamp.cache";
+ /**
+ * Whether to cache SAML2 Token Identifiers, if the token contains a "OneTimeUse" Condition.
+ * The default value is "true".
+ */
+ public static final String ENABLE_SAML_ONE_TIME_USE_CACHE = "enableSamlOneTimeUseCache";
+
//
// (Non-boolean) Configuration parameters for the actions/processors
//
@@ -825,6 +831,13 @@ public final class ConfigurationConstant
public static final String TIMESTAMP_CACHE_INSTANCE = "timestampCacheInstance";
/**
+ * This holds a reference to a ReplayCache instance used to cache SAML2 Token Identifier
+ * Strings (if the token contains a OneTimeUse Condition). The default instance that is
+ * used is the EHCacheReplayCache.
+ */
+ public static final String SAML_ONE_TIME_USE_CACHE_INSTANCE = "samlOneTimeUseCacheInstance";
+
+ /**
* This holds a reference to a PasswordEncryptor instance, which is used to encrypt or
* decrypt passwords in the Merlin Crypto implementation (or any custom Crypto implementations).
*
Modified: webservices/wss4j/trunk/ws-security-common/src/main/resources/messages/wss4j_errors.properties
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-common/src/main/resources/messages/wss4j_errors.properties?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-common/src/main/resources/messages/wss4j_errors.properties (original)
+++ webservices/wss4j/trunk/ws-security-common/src/main/resources/messages/wss4j_errors.properties Fri Sep 20 10:41:37 2013
@@ -14,6 +14,7 @@ badElement = Bad element, expected \"{0}
badEncAlgo = xenc:EncryptionMethod/@Algorithm is not supported: {0}
badReferenceURI = Reference URI is null
badTokenType01 = Bad UsernameToken Values
+badSamlToken = An error happened processing a SAML Token: \"{0}\"
badUsernameToken = An error happened processing a Username Token: \"{0}\"
certpath = Error during certificate path validation: {0}
dataRef = DataReference - referenced data not found
Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java Fri Sep 20 10:41:37 2013
@@ -91,6 +91,7 @@ public class RequestData {
protected boolean requireSignedEncryptedDataElements;
private ReplayCache timestampReplayCache;
private ReplayCache nonceReplayCache;
+ private ReplayCache samlOneTimeUseReplayCache;
private Collection<Pattern> subjectDNPatterns = new ArrayList<Pattern>();
private final List<BSPRule> ignoredBSPRules = new LinkedList<BSPRule>();
private boolean appendSignatureAfterTimestamp;
@@ -105,6 +106,7 @@ public class RequestData {
private boolean includeSignatureToken;
private boolean enableTimestampReplayCache = true;
private boolean enableNonceReplayCache = true;
+ private boolean enableSamlOneTimeUseReplayCache = true;
private PasswordEncryptor passwordEncryptor;
public void clear() {
@@ -128,6 +130,7 @@ public class RequestData {
enableRevocation = false;
timestampReplayCache = null;
nonceReplayCache = null;
+ samlOneTimeUseReplayCache = null;
subjectDNPatterns.clear();
ignoredBSPRules.clear();
appendSignatureAfterTimestamp = false;
@@ -142,6 +145,7 @@ public class RequestData {
includeSignatureToken = false;
enableTimestampReplayCache = true;
enableNonceReplayCache = true;
+ setEnableSamlOneTimeUseReplayCache(true);
passwordEncryptor = null;
}
@@ -555,6 +559,25 @@ 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 {
+ if (enableSamlOneTimeUseReplayCache && samlOneTimeUseReplayCache == null) {
+ samlOneTimeUseReplayCache = createCache("wss4j-saml-one-time-use-cache-");
+ }
+
+ return samlOneTimeUseReplayCache;
+ }
+
+ /**
* Set the Signature Subject Cert Constraints
*/
public void setSubjectCertConstraints(Collection<Pattern> subjectCertConstraints) {
@@ -673,5 +696,13 @@ public class RequestData {
public void setPasswordEncryptor(PasswordEncryptor passwordEncryptor) {
this.passwordEncryptor = passwordEncryptor;
}
+
+ public boolean isEnableSamlOneTimeUseReplayCache() {
+ return enableSamlOneTimeUseReplayCache;
+ }
+
+ public void setEnableSamlOneTimeUseReplayCache(boolean enableSamlOneTimeUseReplayCache) {
+ this.enableSamlOneTimeUseReplayCache = enableSamlOneTimeUseReplayCache;
+ }
}
Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/processor/SignatureProcessor.java Fri Sep 20 10:41:37 2013
@@ -682,7 +682,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/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SamlAssertionValidator.java Fri Sep 20 10:41:37 2013
@@ -19,13 +19,17 @@
package org.apache.wss4j.dom.validate;
+import java.util.Date;
import java.util.List;
+import org.apache.wss4j.common.cache.ReplayCache;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.OpenSAMLUtil;
import org.apache.wss4j.common.saml.SAMLKeyInfo;
import org.apache.wss4j.common.saml.SamlAssertionWrapper;
import org.apache.wss4j.dom.handler.RequestData;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLVersion;
/**
* This class validates a SAML Assertion, which is wrapped in an "SamlAssertionWrapper" instance.
@@ -94,6 +98,9 @@ public class SamlAssertionValidator exte
// Check conditions
checkConditions(samlAssertion);
+ // Check OneTimeUse Condition
+ checkOneTimeUse(samlAssertion, data);
+
// Validate the assertion against schemas/profiles
validateAssertion(samlAssertion);
@@ -131,6 +138,41 @@ public class SamlAssertionValidator exte
}
/**
+ * 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(
+ SamlAssertionWrapper 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.ErrorCode.INVALID_SECURITY,
+ "badSamlToken",
+ "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 samlAssertion against schemas/profiles
*/
protected void validateAssertion(SamlAssertionWrapper samlAssertion) throws WSSecurityException {
Modified: webservices/wss4j/trunk/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/ReplayTest.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/ReplayTest.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/ReplayTest.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/ReplayTest.java Fri Sep 20 10:41:37 2013
@@ -30,6 +30,7 @@ import org.apache.wss4j.dom.WSSConfig;
import org.apache.wss4j.dom.WSSecurityEngine;
import org.apache.wss4j.dom.WSSecurityEngineResult;
import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
+import org.apache.wss4j.dom.common.SAML2CallbackHandler;
import org.apache.wss4j.dom.common.SOAPUtil;
import org.apache.wss4j.dom.common.SecurityTestUtil;
import org.apache.wss4j.dom.common.UsernamePasswordCallbackHandler;
@@ -37,6 +38,11 @@ import org.apache.wss4j.common.cache.Mem
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoFactory;
import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
import org.apache.wss4j.common.util.XMLUtils;
import org.apache.wss4j.dom.handler.RequestData;
import org.apache.wss4j.dom.util.WSSecurityUtil;
@@ -435,6 +441,106 @@ 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 testEhCacheReplayedSAML2() 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);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
+
+ 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);
+
+ // 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 testEhCacheReplayedSAML2OneTimeUse() 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);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
+
+ 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);
+
+ // 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.ErrorCode.INVALID_SECURITY);
+ }
+ }
+
+ /**
* Verifies the soap envelope
*
* @param env soap envelope
Modified: webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java (original)
+++ webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ConfigurationConverter.java Fri Sep 20 10:41:37 2013
@@ -446,6 +446,10 @@ public final class ConfigurationConverte
decodeBooleanConfigValue(ConfigurationConstants.ENABLE_NONCE_CACHE, true, config);
properties.setEnableNonceReplayCache(enableNonceCache);
+ boolean enableSamlOneTimeUseCache =
+ decodeBooleanConfigValue(ConfigurationConstants.ENABLE_SAML_ONE_TIME_USE_CACHE, true, config);
+ properties.setEnableSamlOneTimeUseReplayCache(enableSamlOneTimeUseCache);
+
boolean encryptSymmetricEncryptionKey =
decodeBooleanConfigValue(ConfigurationConstants.ENC_SYM_ENC_KEY, true, config);
properties.setEncryptSymmetricEncrytionKey(encryptSymmetricEncryptionKey);
@@ -589,6 +593,12 @@ public final class ConfigurationConverte
if (timestampCache != null) {
properties.setTimestampReplayCache(timestampCache);
}
+
+ ReplayCache samlOneTimeUseCache =
+ (ReplayCache)config.get(ConfigurationConstants.SAML_ONE_TIME_USE_CACHE_INSTANCE);
+ if (samlOneTimeUseCache != null) {
+ properties.setSamlOneTimeUseReplayCache(samlOneTimeUseCache);
+ }
}
private static WSSecurityTokenConstants.KeyIdentifier convertKeyIdentifier(String keyIdentifier) {
Modified: webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java (original)
+++ webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/ext/WSSSecurityProperties.java Fri Sep 20 10:41:37 2013
@@ -106,8 +106,10 @@ public class WSSSecurityProperties exten
private boolean enableRevocation = false;
private ReplayCache timestampReplayCache;
private ReplayCache nonceReplayCache;
+ private ReplayCache samlOneTimeUseReplayCache;
private boolean enableTimestampReplayCache = true;
private boolean enableNonceReplayCache = true;
+ private boolean enableSamlOneTimeUseReplayCache = true;
private boolean validateSamlSubjectConfirmation = true;
private Collection<Pattern> subjectDNPatterns = new ArrayList<Pattern>();
@@ -152,8 +154,10 @@ public class WSSSecurityProperties exten
this.enableRevocation = wssSecurityProperties.enableRevocation;
this.timestampReplayCache = wssSecurityProperties.timestampReplayCache;
this.nonceReplayCache = wssSecurityProperties.nonceReplayCache;
+ this.samlOneTimeUseReplayCache = wssSecurityProperties.samlOneTimeUseReplayCache;
this.enableTimestampReplayCache = wssSecurityProperties.enableTimestampReplayCache;
this.enableNonceReplayCache = wssSecurityProperties.enableNonceReplayCache;
+ this.enableSamlOneTimeUseReplayCache = wssSecurityProperties.enableSamlOneTimeUseReplayCache;
this.allowRSA15KeyTransportAlgorithm = wssSecurityProperties.allowRSA15KeyTransportAlgorithm;
this.derivedKeyIterations = wssSecurityProperties.derivedKeyIterations;
this.useDerivedKeyForMAC = wssSecurityProperties.useDerivedKeyForMAC;
@@ -765,6 +769,25 @@ public class WSSSecurityProperties exten
return nonceReplayCache;
}
+
+ /**
+ * 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 {
+ if (enableSamlOneTimeUseReplayCache && samlOneTimeUseReplayCache == null) {
+ samlOneTimeUseReplayCache = createCache("wss4j-saml-one-time-use-cache-");
+ }
+
+ return samlOneTimeUseReplayCache;
+ }
public boolean isDisableBSPEnforcement() {
return disableBSPEnforcement;
@@ -861,6 +884,14 @@ public class WSSSecurityProperties exten
public void setEnableNonceReplayCache(boolean enableNonceReplayCache) {
this.enableNonceReplayCache = enableNonceReplayCache;
}
+
+ public boolean isEnableSamlOneTimeUseReplayCache() {
+ return enableSamlOneTimeUseReplayCache;
+ }
+
+ public void setEnableSamlOneTimeUseReplayCache(boolean enableSamlOneTimeUseReplayCache) {
+ this.enableSamlOneTimeUseReplayCache = enableSamlOneTimeUseReplayCache;
+ }
public boolean isEncryptSymmetricEncrytionKey() {
return encryptSymmetricEncrytionKey;
Modified: webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java (original)
+++ webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/impl/processor/input/WSSSignatureReferenceVerifyInputProcessor.java Fri Sep 20 10:41:37 2013
@@ -188,7 +188,7 @@ public class WSSSignatureReferenceVerify
Date rightNow = new Date();
long currentTime = rightNow.getTime();
long expiresTime = expiresCal.getTimeInMillis();
- replayCache.add(cacheKey, (expiresTime - currentTime) / 1000L);
+ replayCache.add(cacheKey, 1L + (expiresTime - currentTime) / 1000L);
} else {
replayCache.add(cacheKey);
}
Modified: webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java (original)
+++ webservices/wss4j/trunk/ws-security-stax/src/main/java/org/apache/wss4j/stax/validate/SamlTokenValidatorImpl.java Fri Sep 20 10:41:37 2013
@@ -18,6 +18,9 @@
*/
package org.apache.wss4j.stax.validate;
+import java.util.Date;
+
+import org.apache.wss4j.common.cache.ReplayCache;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.SamlAssertionWrapper;
@@ -25,6 +28,8 @@ import org.apache.wss4j.stax.securityTok
import org.apache.wss4j.stax.impl.securityToken.SamlSecurityTokenImpl;
import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
import org.apache.xml.security.stax.securityToken.InboundSecurityToken;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLVersion;
public class SamlTokenValidatorImpl extends SignatureTokenValidatorImpl implements SamlTokenValidator {
@@ -70,6 +75,11 @@ public class SamlTokenValidatorImpl exte
final TokenContext tokenContext) throws WSSecurityException {
// Check conditions
checkConditions(samlAssertionWrapper);
+
+ // Check OneTimeUse Condition
+ checkOneTimeUse(samlAssertionWrapper,
+ tokenContext.getWssSecurityProperties().getSamlOneTimeUseReplayCache());
+
// Validate the assertion against schemas/profiles
validateAssertion(samlAssertionWrapper);
@@ -100,6 +110,38 @@ public class SamlTokenValidatorImpl exte
}
/**
+ * 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(
+ SamlAssertionWrapper samlAssertion, ReplayCache replayCache
+ ) throws WSSecurityException {
+ if (replayCache != null
+ && samlAssertion.getSamlVersion().equals(SAMLVersion.VERSION_20)
+ && samlAssertion.getSaml2().getConditions() != null
+ && samlAssertion.getSaml2().getConditions().getOneTimeUse() != null) {
+ String identifier = samlAssertion.getId();
+
+ if (replayCache.contains(identifier)) {
+ throw new WSSecurityException(
+ WSSecurityException.ErrorCode.INVALID_SECURITY,
+ "badSamlToken",
+ "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);
+ }
+ }
+ }
+
+ /**
* Validate the samlAssertion against schemas/profiles
*/
protected void validateAssertion(SamlAssertionWrapper samlAssertion) throws WSSecurityException {
Modified: webservices/wss4j/trunk/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java?rev=1524969&r1=1524968&r2=1524969&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java (original)
+++ webservices/wss4j/trunk/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/ReplayTest.java Fri Sep 20 10:41:37 2013
@@ -29,12 +29,15 @@ import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult;
import org.apache.wss4j.common.cache.ReplayCache;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.apache.wss4j.stax.WSSec;
import org.apache.wss4j.stax.ext.InboundWSSec;
import org.apache.wss4j.stax.ext.WSSConstants;
import org.apache.wss4j.stax.ext.WSSSecurityProperties;
+import org.apache.wss4j.stax.test.saml.SAML2CallbackHandler;
import org.apache.wss4j.stax.test.utils.StAX2DOM;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.testng.Assert;
@@ -150,4 +153,113 @@ public class ReplayTest extends Abstract
}
}
+ /**
+ * 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 testEhCacheReplayedSAML2() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer("www.example.com");
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setTokenPeriodMinutes(5);
+ callbackHandler.setConditions(conditions);
+
+ InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
+ String action = WSHandlerConstants.SAML_TOKEN_UNSIGNED;
+ Properties properties = new Properties();
+ properties.put(WSHandlerConstants.SAML_CALLBACK_REF, callbackHandler);
+ Document securedDocument = doOutboundSecurityWithWSS4J(sourceDocument, action, properties);
+
+ javax.xml.transform.Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
+ transformer.transform(new DOMSource(securedDocument), new StreamResult(baos));
+ }
+
+ // process SAML Token
+ ReplayCache replayCache = null;
+ {
+ WSSSecurityProperties securityProperties = new WSSSecurityProperties();
+ replayCache = securityProperties.getSamlOneTimeUseReplayCache();
+ InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties);
+ XMLStreamReader xmlStreamReader = wsSecIn.processInMessage(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())));
+
+ Document document = StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader);
+ Assert.assertNotNull(document);
+ }
+
+ // now process SAML Token again
+ {
+ WSSSecurityProperties securityProperties = new WSSSecurityProperties();
+ securityProperties.setSamlOneTimeUseReplayCache(replayCache);
+ InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties);
+ XMLStreamReader xmlStreamReader = wsSecIn.processInMessage(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())));
+
+ Document document = StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader);
+ Assert.assertNotNull(document);
+ }
+ }
+
+ /**
+ * Test that creates, sends and processes an unsigned SAML 2 authentication assertion
+ * with a OneTimeUse Element
+ */
+ @org.junit.Test
+ public void testEhCacheReplayedSAML2OneTimeUse() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer("www.example.com");
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setTokenPeriodMinutes(5);
+ conditions.setOneTimeUse(true);
+ callbackHandler.setConditions(conditions);
+
+ InputStream sourceDocument = this.getClass().getClassLoader().getResourceAsStream("testdata/plain-soap-1.1.xml");
+ String action = WSHandlerConstants.SAML_TOKEN_UNSIGNED;
+ Properties properties = new Properties();
+ properties.put(WSHandlerConstants.SAML_CALLBACK_REF, callbackHandler);
+ Document securedDocument = doOutboundSecurityWithWSS4J(sourceDocument, action, properties);
+
+ javax.xml.transform.Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
+ transformer.transform(new DOMSource(securedDocument), new StreamResult(baos));
+ }
+
+ // process SAML Token
+ ReplayCache replayCache = null;
+ {
+ WSSSecurityProperties securityProperties = new WSSSecurityProperties();
+ replayCache = securityProperties.getSamlOneTimeUseReplayCache();
+ InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties);
+ XMLStreamReader xmlStreamReader = wsSecIn.processInMessage(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())));
+
+ Document document = StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader);
+ Assert.assertNotNull(document);
+ }
+
+ // now process SAML Token again
+ {
+ WSSSecurityProperties securityProperties = new WSSSecurityProperties();
+ securityProperties.setSamlOneTimeUseReplayCache(replayCache);
+ InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties);
+ XMLStreamReader xmlStreamReader = wsSecIn.processInMessage(xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(baos.toByteArray())));
+
+ try {
+ StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), xmlStreamReader);
+ Assert.fail("Exception expected");
+ } catch (XMLStreamException e) {
+ org.junit.Assert.assertTrue(e.getCause() instanceof XMLSecurityException);
+ }
+ }
+ }
+
}