diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java index 6d5f2a99..cfc29d30 100755 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java @@ -121,7 +121,9 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl private boolean enableSignatureConfirmation; - private int timeToLive = 300; + private int validationTimeToLive = 300; + + private int securementTimeToLive = 300; private final Wss4jHandler handler = new Wss4jHandler(); @@ -346,12 +348,27 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl this.securementUsername = securementUsername; } - /** Sets the server-side time to live */ - public void setTimeToLive(int timeToLive) { - if (timeToLive <= 0) { + /** Sets the time to live on the outgoing message */ + public void setSecurementTimeToLive(int securementTimeToLive) { + if (securementTimeToLive <= 0) { throw new IllegalArgumentException("timeToLive must be positive"); } - this.timeToLive = timeToLive; + this.securementTimeToLive = securementTimeToLive; + } + + /** Sets the server-side time to live */ + public void setValidationTimeToLive(int validationTimeToLive) { + if (validationTimeToLive <= 0) { + throw new IllegalArgumentException("timeToLive must be positive"); + } + this.validationTimeToLive = validationTimeToLive; + } + + /** Sets the server-side time to live + * @deprecated Use {@link #setValidationTimeToLive(int)} instead. + * */ + public void setTimeToLive(int timeToLive) { + setValidationTimeToLive(timeToLive); } /** Sets the validation actions to be executed by the interceptor. */ @@ -497,6 +514,9 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl else { requestData.setUsername(securementUsername); } + + requestData.setTimeToLive(securementTimeToLive); + return requestData; } @@ -596,12 +616,11 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl if (actionResult != null) { Timestamp timestamp = (Timestamp) actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP); if (timestamp != null && timestampStrict) { - if (!handler.verifyTimestamp(timestamp, timeToLive)) { + if (!handler.verifyTimestamp(timestamp, validationTimeToLive)) { throw new Wss4jSecurityValidationException("Invalid timestamp : " + timestamp.getID()); } } } - } private void processPrincipal(Vector results) { diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorTimestampTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorTimestampTestCase.java index 5a2a496b..a777a93f 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorTimestampTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorTimestampTestCase.java @@ -17,12 +17,21 @@ package org.springframework.ws.soap.security.wss4j; import java.lang.reflect.Field; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.Result; +import javax.xml.transform.Source; import org.w3c.dom.Document; import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.security.WsSecurityValidationException; +import org.springframework.xml.transform.StringResult; public abstract class Wss4jMessageInterceptorTimestampTestCase extends Wss4jTestCase { @@ -50,36 +59,41 @@ public abstract class Wss4jMessageInterceptorTimestampTestCase extends Wss4jTest getDocument(message)); } - public void testValidateTimestampWithTtl() throws Exception { - Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor() { - public void setTimeToLive(int t) { - try { - Field ttl = Wss4jSecurityInterceptor.class - .getDeclaredField("timeToLive"); - ttl.setAccessible(true); - ttl.set(this, new Integer(t)); - - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - }; + public void testValidateTimestampWithExpiredTtl() throws Exception { + Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor(); interceptor.setValidationActions("Timestamp"); - interceptor.setTimeToLive(-10); - interceptor.setTimestampStrict(true); interceptor.afterPropertiesSet(); - SoapMessage message = getMessageWithTimestamp(); + SoapMessage message = loadMessage("expiredTimestamp-soap.xml"); MessageContext context = new DefaultMessageContext(message, getMessageFactory()); - try { interceptor.validateMessage(message, context); + fail(); } - catch (Wss4jSecurityValidationException ex) { + catch (WsSecurityValidationException e) { // expected - return; } - fail("Time to live validation failed"); + } + + public void testSecureTimestampWithCustomTtl() throws Exception { + int ttlInSeconds = 1; + Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor(); + interceptor.setSecurementActions("Timestamp"); + interceptor.setTimestampStrict(true); + interceptor.setSecurementTimeToLive(ttlInSeconds); + interceptor.afterPropertiesSet(); + SoapMessage message = loadMessage("empty-soap.xml"); + MessageContext context = new DefaultMessageContext(message, getMessageFactory()); + interceptor.secureMessage(message, context); + + String created = xpathTemplate.evaluateAsString("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsu:Timestamp/wsu:Created/text()", + message.getEnvelope().getSource()); + String expires = xpathTemplate.evaluateAsString("/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsu:Timestamp/wsu:Expires/text()", + message.getEnvelope().getSource()); + + DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'"); + + long actualTtl = format.parse(expires).getTime() - format.parse(created).getTime(); + assertEquals("invalid ttl", 1000 * ttlInSeconds, actualTtl); } private SoapMessage getMessageWithTimestamp() throws Exception { diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jTestCase.java index 830bd5e8..d3d9bc62 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jTestCase.java @@ -65,7 +65,6 @@ public abstract class Wss4jTestCase extends TestCase { "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); namespaces.setProperty("ds", "http://www.w3.org/2000/09/xmldsig#"); namespaces.setProperty("xenc", "http://www.w3.org/2001/04/xmlenc#"); -// namespaces.put("wsse11", "http://docs.oasis-open.org/wss/2005/xx/oasis-2005xx-wss-wssecurity-secext-1.1.xsd"); namespaces.setProperty("wsse11", "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"); namespaces.setProperty("echo", "http://www.springframework.org/spring-ws/samples/echo"); namespaces.setProperty("wsu", diff --git a/security/src/test/resources/org/springframework/ws/soap/security/wss4j/expiredTimestamp-soap.xml b/security/src/test/resources/org/springframework/ws/soap/security/wss4j/expiredTimestamp-soap.xml new file mode 100644 index 00000000..4c12d50f --- /dev/null +++ b/security/src/test/resources/org/springframework/ws/soap/security/wss4j/expiredTimestamp-soap.xml @@ -0,0 +1,14 @@ + + + + + + 2009-12-25T15:43:22.687Z + 2009-12-25T15:48:22.687Z + + + + + QQQ + + \ No newline at end of file