You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2012/03/09 16:50:53 UTC

svn commit: r1298889 - in /cxf/branches/2.5.x-fixes: parent/ rt/ws/security/ rt/ws/security/src/main/java/org/apache/cxf/ws/security/ rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ rt/ws/security/src/main/java/org/apache/cxf/ws/security...

Author: coheigea
Date: Fri Mar  9 15:50:52 2012
New Revision: 1298889

URL: http://svn.apache.org/viewvc?rev=1298889&view=rev
Log:
[CXF-1636] - Adding support for caching UsernameToken nonces and Timestamps

Added:
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCache.java
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCacheFactory.java
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/MemoryReplayCacheFactory.java
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ReplayCacheFactory.java
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/resources/cxf-ehcache.xml
    cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/SecurityHeaderCacheInterceptor.java
Modified:
    cxf/branches/2.5.x-fixes/parent/pom.xml
    cxf/branches/2.5.x-fixes/rt/ws/security/pom.xml
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/SecurityConstants.java
    cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java
    cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/UsernameTokenTest.java
    cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/x509/X509TokenTest.java

Modified: cxf/branches/2.5.x-fixes/parent/pom.xml
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/parent/pom.xml?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/parent/pom.xml (original)
+++ cxf/branches/2.5.x-fixes/parent/pom.xml Fri Mar  9 15:50:52 2012
@@ -125,7 +125,7 @@
         <cxf.cglib.bundle.version>2.2_1</cxf.cglib.bundle.version>
         <cxf.fastinfoset.bundle.version>1.2.7_3</cxf.fastinfoset.bundle.version>
         <cxf.hazelcast.version>1.9.4</cxf.hazelcast.version>
-        <cxf.ehcache.version>2.4.4</cxf.ehcache.version>
+        <cxf.ehcache.version>2.5.1</cxf.ehcache.version>
         
         <!-- various OSGi related versions -->
         <cxf.aries.version>0.3.1</cxf.aries.version>

Modified: cxf/branches/2.5.x-fixes/rt/ws/security/pom.xml
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/pom.xml?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/pom.xml (original)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/pom.xml Fri Mar  9 15:50:52 2012
@@ -72,7 +72,12 @@
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
-
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache-core</artifactId>
+            <version>${cxf.ehcache.version}</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.ws.security</groupId>
             <artifactId>wss4j</artifactId>

Modified: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/SecurityConstants.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/SecurityConstants.java?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/SecurityConstants.java (original)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/SecurityConstants.java Fri Mar  9 15:50:52 2012
@@ -138,6 +138,43 @@ public final class SecurityConstants {
     public static final String CACHE_ISSUED_TOKEN_IN_ENDPOINT = 
         "ws-security.cache.issued.token.in.endpoint";
     
+    /**
+     * Set this to "false" to not cache UsernameToken nonces. The default value is "true" for
+     * message recipients, and "false" for message initiators. Set it to true to cache for
+     * both cases.
+     */
+    public static final String ENABLE_NONCE_CACHE = 
+        "ws-security.enable.nonce.cache";
+    
+    /**
+     * This holds a reference to a ReplayCache instance used to cache UsernameToken nonces. The
+     * default instance that is used is the EHCacheReplayCache.
+     */
+    public static final String NONCE_CACHE_INSTANCE = 
+        "ws-security.nonce.cache.instance";
+    
+    /**
+     * Set this to "false" to not cache Timestamp Created Strings (these are only cached in 
+     * conjunction with a message Signature). The default value is "true" for message recipients, 
+     * and "false" for message initiators. Set it to true to cache for both cases.
+     */
+    public static final String ENABLE_TIMESTAMP_CACHE = 
+        "ws-security.enable.timestamp.cache";
+    
+    /**
+     * This holds a reference to a ReplayCache instance used to cache Timestamp Created Strings. The
+     * default instance that is used is the EHCacheReplayCache.
+     */
+    public static final String TIMESTAMP_CACHE_INSTANCE = 
+        "ws-security.timestamp.cache.instance";
+    
+    /**
+     * Set this property to point to a configuration file for the underlying caching implementation.
+     * The default configuration file that is used is cxf-ehcache.xml in this module.
+     */
+    public static final String CACHE_CONFIG_FILE = 
+        "ws-security.cache.config.file";
+    
     public static final Set<String> ALL_PROPERTIES;
     
     static {
@@ -152,7 +189,9 @@ public final class SecurityConstants {
             SIGNATURE_TOKEN_VALIDATOR, IS_BSP_COMPLIANT, TIMESTAMP_FUTURE_TTL,
             BST_TOKEN_VALIDATOR, SAML_CALLBACK_HANDLER, STS_TOKEN_ON_BEHALF_OF,
             KERBEROS_CLIENT, SCT_TOKEN_VALIDATOR, CACHE_ISSUED_TOKEN_IN_ENDPOINT,
-            KERBEROS_JAAS_CONTEXT_NAME, KERBEROS_SPN, SPNEGO_CLIENT_ACTION
+            KERBEROS_JAAS_CONTEXT_NAME, KERBEROS_SPN, SPNEGO_CLIENT_ACTION,
+            ENABLE_NONCE_CACHE, NONCE_CACHE_INSTANCE, ENABLE_TIMESTAMP_CACHE,
+            TIMESTAMP_CACHE_INSTANCE, CACHE_CONFIG_FILE
         }));
         ALL_PROPERTIES = Collections.unmodifiableSet(s);
     }

Added: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCache.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCache.java?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCache.java (added)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCache.java Fri Mar  9 15:50:52 2012
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.security.cache;
+
+import java.net.URL;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+
+import org.apache.ws.security.cache.ReplayCache;
+
+/**
+ * An in-memory EHCache implementation of the ReplayCache interface.
+ */
+public class EHCacheReplayCache implements ReplayCache {
+    
+    public static final long DEFAULT_TTL = 3600L;
+    private Cache cache;
+    private CacheManager cacheManager;
+    
+    public EHCacheReplayCache(String key, URL configFileURL) {
+        if (cacheManager == null) {
+            if (configFileURL == null) {
+                cacheManager = CacheManager.create();
+            } else {
+                cacheManager = CacheManager.create(configFileURL);
+            }
+        }
+        if (!cacheManager.cacheExists(key)) {
+            cache = new Cache(key, 50000, true, false, DEFAULT_TTL, DEFAULT_TTL);
+            cacheManager.addCache(cache);
+        } else {
+            cache = cacheManager.getCache(key);
+        }
+    }
+    
+    /**
+     * Add the given identifier to the cache. It will be cached for a default amount of time.
+     * @param identifier The identifier to be added
+     */
+    public void add(String identifier) {
+        add(identifier, DEFAULT_TTL);
+    }
+    
+    /**
+     * Add the given identifier to the cache to be cached for the given time
+     * @param identifier The identifier to be added
+     * @param timeToLive The length of time to cache the Identifier in seconds
+     */
+    public void add(String identifier, long timeToLive) {
+        if (identifier == null || "".equals(identifier)) {
+            return;
+        }
+        
+        int ttl = (int)timeToLive;
+        if (timeToLive != (long)ttl) {
+            // Default to 60 minutes
+            ttl = 3600;
+        }
+        
+        cache.put(new Element(identifier, identifier, false, ttl, ttl));
+    }
+    
+    /**
+     * Return true if the given identifier is contained in the cache
+     * @param identifier The identifier to check
+     */
+    public boolean contains(String identifier) {
+        Element element = cache.get(identifier);
+        if (element != null) {
+            if (cache.isExpired(element)) {
+                cache.remove(identifier);
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+    
+}

Added: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCacheFactory.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCacheFactory.java?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCacheFactory.java (added)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/EHCacheReplayCacheFactory.java Fri Mar  9 15:50:52 2012
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.security.cache;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.ws.security.SecurityConstants;
+import org.apache.ws.security.cache.ReplayCache;
+
+/**
+ * A factory to return an EHCacheReplayCache instance.
+ */
+public class EHCacheReplayCacheFactory extends ReplayCacheFactory {
+    
+    public ReplayCache newReplayCache(String key, Message message) {
+        URL configFileURL = getConfigFileURL(message);
+        if (configFileURL == null) {
+            String defaultConfigFile = "cxf-ehcache.xml";
+            ResourceManager rm = message.getExchange().get(Bus.class).getExtension(ResourceManager.class);
+            configFileURL = rm.resolveResource(defaultConfigFile, URL.class);
+            try {
+                if (configFileURL == null) {
+                    configFileURL = 
+                        ClassLoaderUtils.getResource(defaultConfigFile, EHCacheReplayCacheFactory.class);
+                }
+                if (configFileURL == null) {
+                    configFileURL = new URL(defaultConfigFile);
+                }
+            } catch (IOException e) {
+                // Do nothing
+            }
+        }
+        if (configFileURL != null) {
+            message.setContextualProperty(SecurityConstants.CACHE_CONFIG_FILE, configFileURL);
+        }
+        return new EHCacheReplayCache(key, configFileURL);
+    }
+    
+}

Added: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/MemoryReplayCacheFactory.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/MemoryReplayCacheFactory.java?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/MemoryReplayCacheFactory.java (added)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/MemoryReplayCacheFactory.java Fri Mar  9 15:50:52 2012
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.security.cache;
+
+import org.apache.cxf.message.Message;
+import org.apache.ws.security.cache.MemoryReplayCache;
+import org.apache.ws.security.cache.ReplayCache;
+
+/**
+ * A factory to return a MemoryReplayCache instance.
+ */
+public class MemoryReplayCacheFactory extends ReplayCacheFactory {
+    
+    public ReplayCache newReplayCache(String key, Message message) {
+        return new MemoryReplayCache();
+    }
+    
+}

Added: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ReplayCacheFactory.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ReplayCacheFactory.java?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ReplayCacheFactory.java (added)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/ReplayCacheFactory.java Fri Mar  9 15:50:52 2012
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.ws.security.cache;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.ws.security.SecurityConstants;
+import org.apache.ws.security.cache.ReplayCache;
+
+/**
+ * An abstract factory to return a ReplayCache instance. It returns an EHCacheReplayCacheFactory
+ * if EH-Cache is available. Otherwise it returns a MemoryReplayCacheFactory.
+ */
+public abstract class ReplayCacheFactory {
+    
+    private static boolean ehCacheInstalled;
+    
+    static {
+        try {
+            Class<?> cacheManagerClass = 
+                ClassLoaderUtils.loadClass("net.sf.ehcache.CacheManager", ReplayCacheFactory.class);
+            if (cacheManagerClass != null) {
+                ehCacheInstalled = true;
+            }
+        } catch (Exception e) {
+            //ignore
+        }
+    }
+    
+    protected static synchronized boolean isEhCacheInstalled() {
+        return ehCacheInstalled;
+    }
+    
+    public static ReplayCacheFactory newInstance() {
+        if (isEhCacheInstalled()) {
+            return new EHCacheReplayCacheFactory();
+        }
+        
+        return new MemoryReplayCacheFactory();
+    }
+    
+    public abstract ReplayCache newReplayCache(String key, Message message);
+    
+    protected URL getConfigFileURL(Message message) {
+        Object o = message.getContextualProperty(SecurityConstants.CACHE_CONFIG_FILE);
+        if (o instanceof String) {
+            URL url = null;
+            ResourceManager rm = message.getExchange().get(Bus.class).getExtension(ResourceManager.class);
+            url = rm.resolveResource((String)o, URL.class);
+            try {
+                if (url == null) {
+                    url = ClassLoaderUtils.getResource((String)o, ReplayCacheFactory.class);
+                }
+                if (url == null) {
+                    url = new URL((String)o);
+                }
+                return url;
+            } catch (IOException e) {
+                // Do nothing
+            }
+        } else if (o instanceof URL) {
+            return (URL)o;        
+        }
+        return null;
+    }
+    
+}

Modified: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java (original)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java Fri Mar  9 15:50:52 2012
@@ -56,8 +56,10 @@ import org.apache.cxf.message.MessageUti
 import org.apache.cxf.phase.Phase;
 import org.apache.cxf.phase.PhaseInterceptor;
 import org.apache.cxf.security.SecurityContext;
+import org.apache.cxf.service.model.EndpointInfo;
 import org.apache.cxf.staxutils.StaxUtils;
 import org.apache.cxf.ws.security.SecurityConstants;
+import org.apache.cxf.ws.security.cache.ReplayCacheFactory;
 import org.apache.cxf.ws.security.tokenstore.SecurityToken;
 import org.apache.cxf.ws.security.tokenstore.TokenStore;
 import org.apache.ws.security.CustomTokenPrincipal;
@@ -68,6 +70,7 @@ import org.apache.ws.security.WSSConfig;
 import org.apache.ws.security.WSSecurityEngine;
 import org.apache.ws.security.WSSecurityEngineResult;
 import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.cache.ReplayCache;
 import org.apache.ws.security.components.crypto.Crypto;
 import org.apache.ws.security.handler.RequestData;
 import org.apache.ws.security.handler.WSHandlerConstants;
@@ -236,6 +239,18 @@ public class WSS4JInInterceptor extends 
             if (passwordTypeStrict == null) {
                 setProperty(WSHandlerConstants.PASSWORD_TYPE_STRICT, "true");
             }
+            
+            // Configure replay caching
+            ReplayCache nonceCache = 
+                getReplayCache(
+                    msg, SecurityConstants.ENABLE_NONCE_CACHE, SecurityConstants.NONCE_CACHE_INSTANCE
+                );
+            reqData.setNonceReplayCache(nonceCache);
+            ReplayCache timestampCache = 
+                getReplayCache(
+                    msg, SecurityConstants.ENABLE_TIMESTAMP_CACHE, SecurityConstants.TIMESTAMP_CACHE_INSTANCE
+                );
+            reqData.setTimestampReplayCache(timestampCache);
 
             /*
              * Get and check the Signature specific parameters first because
@@ -644,7 +659,54 @@ public class WSS4JInInterceptor extends 
         return ret;
     }
     
-    
+    /**
+     * Get a ReplayCache instance. It first checks to see whether caching has been explicitly 
+     * enabled or disabled via the booleanKey argument. If it has been set to false then no
+     * replay caching is done (for this booleanKey). If it has not been specified, then caching
+     * is enabled only if we are not the initiator of the exchange. If it has been specified, then
+     * caching is enabled.
+     * 
+     * It tries to get an instance of ReplayCache via the instanceKey argument from a 
+     * contextual property, and failing that the message exchange. If it can't find any, then it
+     * defaults to using an EH-Cache instance and stores that on the message exchange.
+     */
+    protected ReplayCache getReplayCache(
+        SoapMessage message, String booleanKey, String instanceKey
+    ) {
+        boolean specified = false;
+        Object o = message.getContextualProperty(booleanKey);
+        if (o != null) {
+            if (!MessageUtils.isTrue(o)) {
+                return null;
+            }
+            specified = true;
+        }
+
+        if (!specified && MessageUtils.isRequestor(message)) {
+            return null;
+        }
+        Endpoint ep = message.getExchange().get(Endpoint.class);
+        if (ep != null && ep.getEndpointInfo() != null) {
+            EndpointInfo info = ep.getEndpointInfo();
+            synchronized (info) {
+                ReplayCache replayCache = 
+                        (ReplayCache)message.getContextualProperty(instanceKey);
+                if (replayCache == null) {
+                    replayCache = (ReplayCache)info.getProperty(instanceKey);
+                }
+                if (replayCache == null) {
+                    ReplayCacheFactory replayCacheFactory = ReplayCacheFactory.newInstance();
+                    replayCache = replayCacheFactory.newReplayCache(instanceKey, message);
+                    info.setProperty(instanceKey, replayCache);
+                }
+                return replayCache;
+            }
+        }
+        return null;
+    }
+
+
+
     /**
      * Create a SoapFault from a WSSecurityException, following the SOAP Message Security
      * 1.1 specification, chapter 12 "Error Handling".

Added: cxf/branches/2.5.x-fixes/rt/ws/security/src/main/resources/cxf-ehcache.xml
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/ws/security/src/main/resources/cxf-ehcache.xml?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/rt/ws/security/src/main/resources/cxf-ehcache.xml (added)
+++ cxf/branches/2.5.x-fixes/rt/ws/security/src/main/resources/cxf-ehcache.xml Fri Mar  9 15:50:52 2012
@@ -0,0 +1,16 @@
+<ehcache xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true">
+
+    <diskStore path="java.io.tmpdir"/>
+
+    <defaultCache
+            maxElementsInMemory="50000"
+            eternal="false"
+            timeToIdleSeconds="3600"
+            timeToLiveSeconds="3600"
+            overflowToDisk="true"
+            maxElementsOnDisk="10000000"
+            diskPersistent="false"
+            diskExpiryThreadIntervalSeconds="120"
+            memoryStoreEvictionPolicy="LRU"
+            />
+</ehcache>

Added: cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/SecurityHeaderCacheInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/SecurityHeaderCacheInterceptor.java?rev=1298889&view=auto
==============================================================================
--- cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/SecurityHeaderCacheInterceptor.java (added)
+++ cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/SecurityHeaderCacheInterceptor.java Fri Mar  9 15:50:52 2012
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.ws.ut;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+import javax.xml.soap.SOAPElement;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPHeaderElement;
+import javax.xml.soap.SOAPMessage;
+
+import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptor;
+import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor;
+import org.apache.ws.security.WSConstants;
+
+/**
+ * Cache the first security header and then use it instead of all subsequent security headers, until
+ * clear() is called.
+ */
+public class SecurityHeaderCacheInterceptor implements PhaseInterceptor<SoapMessage> {
+    
+    private static final QName SEC_HEADER = 
+        new QName(WSConstants.WSSE_NS, WSConstants.WSSE_LN, WSConstants.WSSE_PREFIX);
+    private Set<String> afterInterceptors = new HashSet<String>();
+    private SOAPHeaderElement cachedSecurityHeader;
+    
+    public SecurityHeaderCacheInterceptor() {
+        getAfter().add(PolicyBasedWSS4JOutInterceptor.class.getName());
+    }
+    
+    public void handleMessage(SoapMessage mc) throws Fault {
+        SOAPMessage saaj = mc.getContent(SOAPMessage.class);
+        if (cachedSecurityHeader == null) {
+            try {
+                Iterator<?> cachedHeadersIterator = 
+                    saaj.getSOAPHeader().getChildElements(SEC_HEADER);
+                if (cachedHeadersIterator.hasNext()) {
+                    cachedSecurityHeader = (SOAPHeaderElement)cachedHeadersIterator.next();
+                }
+            } catch (SOAPException e) {
+                // Ignore
+            }
+        } else {
+            try {
+                saaj.getSOAPHeader().removeContents();
+                
+                SOAPHeaderElement secHeaderElement = 
+                    saaj.getSOAPHeader().addHeaderElement(SEC_HEADER);
+                
+                Iterator<?> cachedHeadersIterator = 
+                    cachedSecurityHeader.getChildElements();
+                while (cachedHeadersIterator.hasNext()) {
+                    secHeaderElement.addChildElement((SOAPElement)cachedHeadersIterator.next());
+                }
+                
+            } catch (SOAPException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void clear() {
+        cachedSecurityHeader = null;
+    }
+    
+    public void handleFault(SoapMessage arg0) {
+        // Complete
+    }
+
+    public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors() {
+        return null;
+    }
+
+    public Set<String> getAfter() {
+        return afterInterceptors;
+    }
+
+    public Set<String> getBefore() {
+        return Collections.emptySet();
+    }
+
+    public String getId() {
+        return SecurityHeaderCacheInterceptor.class.getName();
+    }
+
+    public String getPhase() {
+        return Phase.PRE_PROTOCOL_ENDING;
+    }
+    
+}

Modified: cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/UsernameTokenTest.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/UsernameTokenTest.java?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/UsernameTokenTest.java (original)
+++ cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/ut/UsernameTokenTest.java Fri Mar  9 15:50:52 2012
@@ -26,11 +26,11 @@ import javax.xml.ws.Service;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.frontend.ClientProxy;
 import org.apache.cxf.systest.ws.ut.server.Server;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
-
 import org.example.contract.doubleit.DoubleItPortType;
-
 import org.junit.BeforeClass;
 
 /**
@@ -214,4 +214,40 @@ public class UsernameTokenTest extends A
             assertTrue(ex.getMessage().contains(error));
         }
     }
+    
+    @org.junit.Test
+    public void testPasswordHashedReplay() throws Exception {
+
+        SpringBusFactory bf = new SpringBusFactory();
+        URL busFile = UsernameTokenTest.class.getResource("client/client.xml");
+
+        Bus bus = bf.createBus(busFile.toString());
+        SpringBusFactory.setDefaultBus(bus);
+        SpringBusFactory.setThreadDefaultBus(bus);
+
+        URL wsdl = UsernameTokenTest.class.getResource("DoubleItUt.wsdl");
+        Service service = Service.create(wsdl, SERVICE_QNAME);
+        
+        QName portQName = new QName(NAMESPACE, "DoubleItHashedPort");
+        DoubleItPortType utPort = 
+                service.getPort(portQName, DoubleItPortType.class);
+        updateAddressPort(utPort, PORT);
+        
+        Client cxfClient = ClientProxy.getClient(utPort);
+        SecurityHeaderCacheInterceptor cacheInterceptor =
+            new SecurityHeaderCacheInterceptor();
+        cxfClient.getOutInterceptors().add(cacheInterceptor);
+        
+        // Make two invocations with the same UsernameToken
+        utPort.doubleIt(25);
+        try {
+            utPort.doubleIt(25);
+            fail("Failure expected on a replayed UsernameToken");
+        } catch (javax.xml.ws.soap.SOAPFaultException ex) {
+            String error = "A replay attack has been detected";
+            assertTrue(ex.getMessage().contains(error));
+        }
+        
+    }
+    
 }

Modified: cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/x509/X509TokenTest.java
URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/x509/X509TokenTest.java?rev=1298889&r1=1298888&r2=1298889&view=diff
==============================================================================
--- cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/x509/X509TokenTest.java (original)
+++ cxf/branches/2.5.x-fixes/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/x509/X509TokenTest.java Fri Mar  9 15:50:52 2012
@@ -29,6 +29,9 @@ import javax.xml.ws.Service;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.frontend.ClientProxy;
+import org.apache.cxf.systest.ws.ut.SecurityHeaderCacheInterceptor;
 import org.apache.cxf.systest.ws.x509.server.Server;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
 
@@ -344,6 +347,42 @@ public class X509TokenTest extends Abstr
         x509Port.doubleIt(25);
     }
     
+    @org.junit.Test
+    public void testAsymmetricSignatureReplay() throws Exception {
+        if (!unrestrictedPoliciesInstalled) {
+            return;
+        }
+
+        SpringBusFactory bf = new SpringBusFactory();
+        URL busFile = X509TokenTest.class.getResource("client/client.xml");
+
+        Bus bus = bf.createBus(busFile.toString());
+        SpringBusFactory.setDefaultBus(bus);
+        SpringBusFactory.setThreadDefaultBus(bus);
+
+        URL wsdl = X509TokenTest.class.getResource("DoubleItX509Signature.wsdl");
+        Service service = Service.create(wsdl, SERVICE_QNAME);
+        QName portQName = new QName(NAMESPACE, "DoubleItAsymmetricSignaturePort");
+        DoubleItPortType x509Port = 
+                service.getPort(portQName, DoubleItPortType.class);
+        updateAddressPort(x509Port, PORT);
+        
+        Client cxfClient = ClientProxy.getClient(x509Port);
+        SecurityHeaderCacheInterceptor cacheInterceptor =
+            new SecurityHeaderCacheInterceptor();
+        cxfClient.getOutInterceptors().add(cacheInterceptor);
+        
+        // Make two invocations with the same security header
+        x509Port.doubleIt(25);
+        try {
+            x509Port.doubleIt(25);
+            fail("Failure expected on a replayed Timestamp");
+        } catch (javax.xml.ws.soap.SOAPFaultException ex) {
+            String error = "A replay attack has been detected";
+            assertTrue(ex.getMessage().contains(error));
+        }
+    }
+    
     private boolean checkUnrestrictedPoliciesInstalled() {
         try {
             byte[] data = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};