From 915cd9462f902e5d0ed2bc61a9dba84ececc3932 Mon Sep 17 00:00:00 2001
From: Arjen Poutsma true.
+ * Validates a server-side incoming request. Delegates to {@link #validateMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)}
+ * if the {@link #setValidateRequest(boolean) validateRequest} property is true.
*
* @param messageContext the message context, containing the request to be validated
* @param endpoint chosen endpoint to invoke
* @return true if the request was valid; false otherwise.
* @throws Exception in case of errors
- * @see #validateMessage(SoapMessage)
+ * @see #validateMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)
*/
public final boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
if (validateRequest) {
Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
try {
- validateMessage((SoapMessage) messageContext.getRequest());
+ validateMessage((SoapMessage) messageContext.getRequest(), messageContext);
return true;
}
catch (WsSecurityValidationException ex) {
@@ -117,21 +117,21 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
}
/**
- * Secures a server-side outgoing response. Delegates to {@link #secureMessage(SoapMessage)} if the {@link
- * #setSecureResponse(boolean) secureResponse} property is true.
+ * Secures a server-side outgoing response. Delegates to {@link #secureMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)}
+ * if the {@link #setSecureResponse(boolean) secureResponse} property is true.
*
* @param messageContext the message context, containing the response to be secured
* @param endpoint chosen endpoint to invoke
* @return true if the response was secured; false otherwise.
* @throws Exception in case of errors
- * @see #secureMessage(SoapMessage)
+ * @see #secureMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)
*/
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
if (secureResponse) {
Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
try {
- secureMessage((SoapMessage) messageContext.getResponse());
+ secureMessage((SoapMessage) messageContext.getResponse(), messageContext);
return true;
}
catch (WsSecuritySecurementException ex) {
@@ -160,19 +160,19 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
*/
/**
- * Secures a client-side outgoing request. Delegates to {@link #secureMessage(SoapMessage)} if the {@link
- * #setSecureRequest(boolean) secureRequest} property is true.
+ * Secures a client-side outgoing request. Delegates to {@link #secureMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)}
+ * if the {@link #setSecureRequest(boolean) secureRequest} property is true.
*
* @param messageContext the message context, containing the request to be secured
* @return true if the response was secured; false otherwise.
* @throws Exception in case of errors
- * @see #secureMessage(SoapMessage)
+ * @see #secureMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)
*/
public final boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
if (secureRequest) {
Assert.isInstanceOf(SoapMessage.class, messageContext.getRequest());
try {
- secureMessage((SoapMessage) messageContext.getRequest());
+ secureMessage((SoapMessage) messageContext.getRequest(), messageContext);
return true;
}
catch (WsSecuritySecurementException ex) {
@@ -188,20 +188,20 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
}
/**
- * Validates a client-side incoming response. Delegates to {@link #validateMessage(SoapMessage)} if the {@link
- * #setValidateResponse(boolean) validateResponse} property is true.
+ * Validates a client-side incoming response. Delegates to {@link #validateMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)}
+ * if the {@link #setValidateResponse(boolean) validateResponse} property is true.
*
* @param messageContext the message context, containing the response to be validated
* @return true if the request was valid; false otherwise.
* @throws Exception in case of errors
- * @see #validateMessage(SoapMessage)
+ * @see #validateMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)
*/
public final boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
if (validateResponse) {
Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
try {
- validateMessage((SoapMessage) messageContext.getResponse());
+ validateMessage((SoapMessage) messageContext.getResponse(), messageContext);
return true;
}
catch (WsSecurityValidationException ex) {
@@ -284,7 +284,8 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
* @param soapMessage the soap message to validate
* @throws WsSecurityValidationException in case of validation errors
*/
- protected abstract void validateMessage(SoapMessage soapMessage) throws WsSecurityValidationException;
+ protected abstract void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException;
/**
* Abstract template method. Subclasses are required to secure the response contained in the given {@link
@@ -293,5 +294,6 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
* @param soapMessage the soap message to secure
* @throws WsSecuritySecurementException in case of securement errors
*/
- protected abstract void secureMessage(SoapMessage soapMessage) throws WsSecuritySecurementException;
+ protected abstract void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecuritySecurementException;
}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/AbstractCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/callback/AbstractCallbackHandler.java
similarity index 96%
rename from security/src/main/java/org/springframework/ws/soap/security/xwss/callback/AbstractCallbackHandler.java
rename to security/src/main/java/org/springframework/ws/soap/security/callback/AbstractCallbackHandler.java
index 1fbf5153..2ea793bc 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/AbstractCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/callback/AbstractCallbackHandler.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ws.soap.security.xwss.callback;
+package org.springframework.ws.soap.security.callback;
import java.io.IOException;
import javax.security.auth.callback.Callback;
diff --git a/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java b/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java
new file mode 100644
index 00000000..6004d24b
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.callback;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
+import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
+import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
+
+/**
+ * Represents a chain of CallbackHandlers. For each callback, each of the handlers is called in term. If a
+ * handler throws a UnsupportedCallbackException, the next handler is tried.
+ *
+ * @author Arjen Poutsma
+ * @since 1.0.0
+ */
+public class CallbackHandlerChain extends AbstractCallbackHandler {
+
+ private CallbackHandler[] callbackHandlers;
+
+ public CallbackHandlerChain(CallbackHandler[] callbackHandlers) {
+ this.callbackHandlers = callbackHandlers;
+ }
+
+ public void setCallbackHandlers(CallbackHandler[] callbackHandlers) {
+ this.callbackHandlers = callbackHandlers;
+ }
+
+ protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
+ if (callback instanceof CertificateValidationCallback) {
+ handleCertificateValidationCallback((CertificateValidationCallback) callback);
+ }
+ else if (callback instanceof PasswordValidationCallback) {
+ handlePasswordValidationCallback((PasswordValidationCallback) callback);
+ }
+ else if (callback instanceof TimestampValidationCallback) {
+ handleTimestampValidationCallback((TimestampValidationCallback) callback);
+ }
+ else {
+ boolean allUnsupported = true;
+ for (int i = 0; i < callbackHandlers.length; i++) {
+ CallbackHandler callbackHandler = callbackHandlers[i];
+ try {
+ callbackHandler.handle(new Callback[]{callback});
+ allUnsupported = false;
+ }
+ catch (UnsupportedCallbackException ex) {
+ // if an UnsupportedCallbackException occurs, go to the next handler
+ }
+ }
+ if (allUnsupported) {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+ }
+
+ private void handleCertificateValidationCallback(CertificateValidationCallback callback) {
+ callback.setValidator(new CertificateValidatorChain(callback));
+ }
+
+ private void handlePasswordValidationCallback(PasswordValidationCallback callback) {
+ callback.setValidator(new PasswordValidatorChain(callback));
+ }
+
+ private void handleTimestampValidationCallback(TimestampValidationCallback callback) {
+ callback.setValidator(new TimestampValidatorChain(callback));
+ }
+
+ private class TimestampValidatorChain implements TimestampValidationCallback.TimestampValidator {
+
+ private TimestampValidationCallback callback;
+
+ private TimestampValidatorChain(TimestampValidationCallback callback) {
+ this.callback = callback;
+ }
+
+ public void validate(TimestampValidationCallback.Request request)
+ throws TimestampValidationCallback.TimestampValidationException {
+ for (int i = 0; i < callbackHandlers.length; i++) {
+ CallbackHandler callbackHandler = callbackHandlers[i];
+ try {
+ callbackHandler.handle(new Callback[]{callback});
+ callback.getResult();
+ }
+ catch (IOException e) {
+ throw new TimestampValidationCallback.TimestampValidationException(e);
+ }
+ catch (UnsupportedCallbackException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private class PasswordValidatorChain implements PasswordValidationCallback.PasswordValidator {
+
+ private PasswordValidationCallback callback;
+
+ private PasswordValidatorChain(PasswordValidationCallback callback) {
+ this.callback = callback;
+ }
+
+ public boolean validate(PasswordValidationCallback.Request request)
+ throws PasswordValidationCallback.PasswordValidationException {
+ boolean allUnsupported = true;
+ for (int i = 0; i < callbackHandlers.length; i++) {
+ CallbackHandler callbackHandler = callbackHandlers[i];
+ try {
+ callbackHandler.handle(new Callback[]{callback});
+ allUnsupported = false;
+ if (!callback.getResult()) {
+ return false;
+ }
+ }
+ catch (IOException e) {
+ throw new PasswordValidationCallback.PasswordValidationException(e);
+ }
+ catch (UnsupportedCallbackException e) {
+ // ignore
+ }
+ }
+ return !allUnsupported;
+ }
+ }
+
+ private class CertificateValidatorChain implements CertificateValidationCallback.CertificateValidator {
+
+ private CertificateValidationCallback callback;
+
+ private CertificateValidatorChain(CertificateValidationCallback callback) {
+ this.callback = callback;
+ }
+
+ public boolean validate(X509Certificate certificate)
+ throws CertificateValidationCallback.CertificateValidationException {
+ boolean allUnsupported = true;
+ for (int i = 0; i < callbackHandlers.length; i++) {
+ CallbackHandler callbackHandler = callbackHandlers[i];
+ try {
+ callbackHandler.handle(new Callback[]{callback});
+ allUnsupported = false;
+ if (!callback.getResult()) {
+ return false;
+ }
+ }
+ catch (IOException e) {
+ throw new CertificateValidationCallback.CertificateValidationException(e);
+ }
+ catch (UnsupportedCallbackException e) {
+ // ignore
+ }
+ }
+ return !allUnsupported;
+ }
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java
index 982934e6..f452206a 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java
@@ -23,19 +23,21 @@ import java.security.KeyStore;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
/**
- * Spring factory bean for a java.security.KeyStore.
+ * Spring factory bean for a {@link KeyStore}.
*
* To load an existing key store, you must set the location property. If this property is not set, a new,
* empty key store is created, which is most likely not what you want.
*
* @author Arjen Poutsma
* @see #setLocation(org.springframework.core.io.Resource)
+ * @see KeyStore
* @since 1.0.0
*/
public class KeyStoreFactoryBean implements FactoryBean, InitializingBean {
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/AbstractWss4jInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/AbstractWss4jInterceptor.java
new file mode 100644
index 00000000..b52e25ad
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/AbstractWss4jInterceptor.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import java.util.Vector;
+import javax.xml.namespace.QName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.util.WSSecurityUtil;
+import org.w3c.dom.Document;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.server.EndpointInterceptor;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.axiom.AxiomSoapMessage;
+import org.springframework.ws.soap.axiom.support.AxiomUtils;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+
+/**
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public abstract class AbstractWss4jInterceptor implements EndpointInterceptor, InitializingBean {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ protected static final QName WS_SECURITY_NAME =
+ new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security");
+
+ private Vector actions;
+
+ private int actionFlags;
+
+ private String actor;
+
+ protected Vector getActions() {
+ return actions;
+ }
+
+ protected int getActionFlags() {
+ return actionFlags;
+ }
+
+ public final void setActions(String actions) throws WSSecurityException {
+ this.actions = new Vector();
+ this.actionFlags = WSSecurityUtil.decodeAction(actions, this.actions);
+ }
+
+ protected String getActor() {
+ return actor;
+ }
+
+ public void setActor(String actor) {
+ this.actor = actor;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(actions, "'actions' must not be null");
+ }
+
+ /**
+ * Transforms a soap message to a DOM document.
+ *
+ * @param soapMessage the message to transform
+ * @return a DOM document representing the message
+ */
+ protected Document toDocument(SoapMessage soapMessage) {
+ if (soapMessage instanceof SaajSoapMessage) {
+ SaajSoapMessage saajMessage = (SaajSoapMessage) soapMessage;
+ return saajMessage.getSaajMessage().getSOAPPart();
+ }
+ else if (soapMessage instanceof AxiomSoapMessage) {
+ AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage;
+ return AxiomUtils.toDocument(axiomMessage.getAxiomMessage().getSOAPEnvelope());
+ }
+ else {
+ throw new IllegalArgumentException("Unknown SoapMessage implementation [" + soapMessage + "]");
+ }
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jHandler.java
new file mode 100644
index 00000000..6872015e
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jHandler.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Properties;
+import java.util.Vector;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.handler.WSHandler;
+import org.apache.ws.security.handler.WSHandlerConstants;
+import org.apache.ws.security.message.token.Timestamp;
+import org.w3c.dom.Document;
+
+import org.springframework.ws.context.MessageContext;
+
+/**
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+class Wss4jHandler extends WSHandler {
+
+ /** Keys are constants from {@link WSHandlerConstants}; values are strings. */
+ private Properties options = new Properties();
+
+ private CallbackHandler securementCallbackHandler;
+
+ private String securementPassword;
+
+ private Crypto securementEncryptionCrypto;
+
+ private Crypto securementSignatureCrypto;
+
+ public Wss4jHandler() {
+ // set up default handler properties
+ options.setProperty(WSHandlerConstants.MUST_UNDERSTAND, Boolean.toString(true));
+ options.setProperty(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, Boolean.toString(true));
+ }
+
+ protected boolean checkReceiverResults(Vector wsResult, Vector actions) {
+ return super.checkReceiverResults(wsResult, actions);
+ }
+
+ void setOption(String key, String value) {
+ options.setProperty(key, value);
+ }
+
+ void setOption(String key, boolean value) {
+ options.setProperty(key, Boolean.toString(value));
+ }
+
+ public Object getOption(String key) {
+ return options.getProperty(key);
+ }
+
+ void setSecurementCallbackHandler(CallbackHandler securementCallbackHandler) {
+ this.securementCallbackHandler = securementCallbackHandler;
+ }
+
+ void setSecurementPassword(String securementPassword) {
+ this.securementPassword = securementPassword;
+ }
+
+ void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
+ this.securementEncryptionCrypto = securementEncryptionCrypto;
+ }
+
+ void setSecurementSignatureCrypto(Crypto securementSignatureCrypto) {
+ this.securementSignatureCrypto = securementSignatureCrypto;
+ }
+
+ /** Gets the password first from securementCallbackHandler, then from securementPassword if not found. */
+ public WSPasswordCallback getPassword(String username,
+ int doAction,
+ String clsProp,
+ String refProp,
+ RequestData reqData) {
+ WSPasswordCallback callback;
+ if (securementCallbackHandler != null) {
+ int reason = 0;
+
+ switch (doAction) {
+ case WSConstants.UT:
+ case WSConstants.UT_SIGN:
+ reason = WSPasswordCallback.USERNAME_TOKEN;
+ break;
+ case WSConstants.SIGN:
+ reason = WSPasswordCallback.SIGNATURE;
+ break;
+ case WSConstants.ENCR:
+ reason = WSPasswordCallback.KEY_NAME;
+ break;
+ }
+ callback = new WSPasswordCallback(username, reason);
+ Callback[] callbacks = new Callback[]{callback};
+ try {
+ securementCallbackHandler.handle(callbacks);
+ }
+ catch (UnsupportedCallbackException ex) {
+ throw new Wss4jSecuritySecurementException(ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new Wss4jSecuritySecurementException(ex.getMessage(), ex);
+ }
+ }
+ else {
+ callback = new WSPasswordCallback("", WSPasswordCallback.UNKNOWN);
+ callback.setPassword(securementPassword);
+ }
+ return callback;
+ }
+
+ public String getPassword(Object msgContext) {
+ return securementPassword;
+ }
+
+ public Object getProperty(Object msgContext, String key) {
+ return ((MessageContext) msgContext).getProperty(key);
+ }
+
+ protected Crypto loadEncryptionCrypto(RequestData reqData) throws WSSecurityException {
+ return securementEncryptionCrypto;
+ }
+
+ public Crypto loadSignatureCrypto(RequestData reqData) throws WSSecurityException {
+ return securementSignatureCrypto;
+ }
+
+ public void setPassword(Object msgContext, String password) {
+ securementPassword = password;
+ }
+
+ public void setProperty(Object msgContext, String key, Object value) {
+ ((MessageContext) msgContext).setProperty(key, value);
+ }
+
+ protected void doSenderAction(int doAction, Document doc, RequestData reqData, Vector actions, boolean isRequest)
+ throws WSSecurityException {
+ super.doSenderAction(doAction, doc, reqData, actions, isRequest);
+ }
+
+ protected boolean verifyTimestamp(Timestamp timestamp, int timeToLive) throws WSSecurityException {
+ return super.verifyTimestamp(timestamp, timeToLive);
+ }
+
+ protected boolean verifyTrust(X509Certificate cert, RequestData reqData) throws WSSecurityException {
+ return super.verifyTrust(cert, reqData);
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityFaultException.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityFaultException.java
new file mode 100755
index 00000000..59172bea
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityFaultException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import javax.xml.namespace.QName;
+
+import org.springframework.ws.soap.security.WsSecurityFaultException;
+
+/**
+ * WSS4J-specific version of the {@link WsSecurityFaultException}.
+ *
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public class Wss4jSecurityFaultException extends WsSecurityFaultException {
+
+ public Wss4jSecurityFaultException(QName faultCode, String faultString, String faultActor) {
+ super(faultCode, faultString, faultActor);
+ }
+}
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
new file mode 100755
index 00000000..84273068
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import java.security.cert.X509Certificate;
+import java.util.Vector;
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.axiom.soap.SOAPEnvelope;
+import org.apache.axiom.soap.SOAPFactory;
+import org.apache.axiom.soap.SOAPMessage;
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSSecurityEngine;
+import org.apache.ws.security.WSSecurityEngineResult;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.handler.WSHandlerConstants;
+import org.apache.ws.security.handler.WSHandlerResult;
+import org.apache.ws.security.message.token.Timestamp;
+import org.apache.ws.security.util.WSSecurityUtil;
+import org.w3c.dom.Document;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.axiom.AxiomSoapMessage;
+import org.springframework.ws.soap.axiom.support.AxiomUtils;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
+import org.springframework.ws.soap.security.WsSecuritySecurementException;
+import org.springframework.ws.soap.security.WsSecurityValidationException;
+
+/**
+ * A WS-Security endpoint interceptor based on Apache Wss4j. The inteceptor supports both Axiom and Saaj messages. The
+ * interceptor's configuration does not rely on an external configuration files and thus is set using the various
+ * attributes.
+ *
+ * The actions executed by the interceptor are configured via validationActions and
+ * securementActions attributes. Actions are passed as a space separated string.
+ *
+ * Validation actions are:
The order of the actions that the client performed to secure the messages is significant and is
+ * enforced by the interceptor.
+ *
+ * Securement actions are:
+ *
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor implements InitializingBean {
+
+ public static final String SECUREMENT_USER_PROPERTY_NAME = "Wss4jSecurityInterceptor.securementUser";
+
+ private CallbackHandler validationCallbackHandler;
+
+ private int securementAction;
+
+ private String securementActions;
+
+ private Vector securementActionsVector;
+
+ private String securementUsername;
+
+ private boolean timestampStrict = true;
+
+ private int timeToLive = 300;
+
+ private int validationAction;
+
+ private String validationActions;
+
+ private Vector validationActionsVector;
+
+ private String validationActor;
+
+ private Crypto validationDecryptionCrypto;
+
+ private Crypto validationSignatureCrypto;
+
+ private Wss4jHandler handler = new Wss4jHandler();
+
+ private boolean enableSignatureConfirmation;
+
+ public void setSecurementActions(String securementActions) {
+ this.securementActions = securementActions;
+ securementActionsVector = new Vector();
+ try {
+ securementAction = WSSecurityUtil.decodeAction(securementActions, securementActionsVector);
+ }
+ catch (WSSecurityException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ /**
+ * The actor name of the wsse:Security header.
+ *
+ * If this parameter is omitted, the actor name is not set.
+ *
+ * The value of the actor or role has to match the receiver's setting or may contain standard values.
+ */
+ public void setSecurementActor(String securementActor) {
+ handler.setOption(WSHandlerConstants.ACTOR, securementActor);
+ }
+
+ public void setSecurementCallbackHandler(CallbackHandler securementCallbackHandler) {
+ handler.setSecurementCallbackHandler(securementCallbackHandler);
+ }
+
+ public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
+ handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
+ }
+
+ /**
+ * Defines which key identifier type to use. The WS-Security specifications recommends to use the identifier type
+ * IssuerSerial. For possible encryption key identifier types refer to {@link
+ * org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For encryption IssuerSerial,
+ * X509KeyIdentifier, DirectReference, Thumbprint,
+ * SKIKeyIdentifier, and EmbeddedKeyName are valid only.
+ */
+ public void setSecurementEncryptionKeyIdentifier(String securementEncryptionKeyIdentifier) {
+ handler.setOption(WSHandlerConstants.ENC_KEY_ID, securementEncryptionKeyIdentifier);
+ }
+
+ /**
+ * Defines which algorithm to use to encrypt the generated symmetric key. Currently WSS4J supports {@link
+ * WSConstants#KEYTRANSPORT_RSA15} only.
+ */
+ public void setSecurementEncryptionKeyTransportAlgorithm(String securementEncryptionKeyTransportAlgorithm) {
+ handler.setOption(WSHandlerConstants.ENC_KEY_TRANSPORT, securementEncryptionKeyTransportAlgorithm);
+ }
+
+ /**
+ * Parameter to define which parts of the request shall be encrypted.
+ *
+ * The value of this parameter is a list of semi-colon separated element names that identify the elements to
+ * encrypt. An encryption mode specifier and a namespace identification, each inside a pair of curly brackets, may
+ * preceed each element name.
+ *
+ * The encryption mode specifier is either {Content} or {Element}. Please refer to the W3C
+ * XML Encryption specification about the differences between Element and Content encryption. The encryption mode
+ * defaults to Content if it is omitted. Example of a list:
+ *
+ * <parameter name="encryptionParts"
+ * value="{Content}{http://example.org/paymentv2}CreditCard;
+ * {Element}{}UserName" />
+ *
+ * The the first entry of the list identifies the element CreditCard in the namespace
+ * http://example.org/paymentv2, and will encrypt its content. Be aware that the element name, the
+ * namespace identifier, and the encryption modifier are case sensitive.
+ *
+ * The encryption modifier and the namespace identifier can be ommited. In this case the encryption mode defaults to
+ * Content and the namespace is set to the SOAP namespace.
+ *
+ * An empty encryption mode defaults to Content, an empty namespace identifier defaults to the SOAP
+ * namespace. The second line of the example defines Element as encryption mode for an
+ * UserName element in the SOAP namespace.
+ *
+ * To specify an element without a namespace use the string Null as the namespace name (this is a case
+ * sensitive string)
+ *
+ * If no list is specified, the handler encrypts the SOAP Body in Content mode by default.
+ */
+ public void setSecurementEncryptionParts(String securementEncryptionParts) {
+ handler.setOption(WSHandlerConstants.ENCRYPTION_PARTS, securementEncryptionParts);
+ }
+
+ /**
+ * Defines which symmetric encryption algorithm to use. WSS4J supports the following alorithms: {@link
+ * WSConstants#TRIPLE_DES}, {@link WSConstants#AES_128}, {@link WSConstants#AES_256}, and {@link
+ * WSConstants#AES_192}. Except for AES 192 all of these algorithms are required by the XML Encryption
+ * specification.
+ */
+ public void setSecurementEncryptionSymAlgorithm(String securementEncryptionSymAlgorithm) {
+ this.handler.setOption(WSHandlerConstants.ENC_SYM_ALGO, securementEncryptionSymAlgorithm);
+ }
+
+ /**
+ * The user's name for encryption.
+ *
+ * The encryption functions uses the public key of this user's certificate to encrypt the generated symmetric key.
+ *
+ * If this parameter is not set, then the encryption function falls back to the {@link
+ * org.apache.ws.security.handler.WSHandlerConstants#USER} parameter to get the certificate.
+ *
+ * If only encryption of the SOAP body data is requested, it is recommended to use this parameter to define
+ * the username. The application can then use the standard user and password functions (see example at {@link
+ * org.apache.ws.security.handler.WSHandlerConstants#USER} to enable HTTP authentication functions.
+ *
+ * Encryption only does not authenticate a user / sender, therefore it does not need a password.
+ *
+ * Placing the username of the encryption certficate in the WSDD is not a security risk, because the public key of
+ * that certificate is used only.
+ *
+ * The application may set this parameter using the following method:
+ *
+ * call.setProperty(WSHandlerConstants.ENCYRPTION_USER, "encryptionuser");
+ *
+ * However, the parameter in the WSDD deployment file overwrites the property setting (deployment setting overwrites
+ * application setting).
+ */
+ public void setSecurementEncryptionUser(String securementEncryptionUser) {
+ handler.setOption(WSHandlerConstants.ENCRYPTION_USER, securementEncryptionUser);
+ }
+
+ public void setSecurementPassword(String securementPassword) {
+ this.handler.setSecurementPassword(securementPassword);
+ }
+
+ /**
+ * Specific parameter for UsernameToken action to define the encoding of the passowrd.
+ *
+ * The parameter can be set to either {@link WSConstants#PW_DIGEST} or to {@link WSConstants#PW_TEXT}.
+ *
+ * The default setting is PW_DIGEST.
+ */
+ public void setSecurementPasswordType(String securementUsernameTokenPasswordType) {
+ handler.setOption(WSHandlerConstants.PASSWORD_TYPE, securementUsernameTokenPasswordType);
+ }
+
+ /**
+ * Defines which signature algorithm to use. Currently this parameter is ignored - SHA1RSA is the only supported
+ * algorithm, will be enhanced soon.
+ */
+ public void setSecurementSignatureAlgorithm(String securementSignatureAlgorithm) {
+ handler.setOption(WSHandlerConstants.SIG_ALGO, securementSignatureAlgorithm);
+ }
+
+ public void setSecurementSignatureCrypto(Crypto securementSignatureCrypto) {
+ handler.setSecurementSignatureCrypto(securementSignatureCrypto);
+ }
+
+ /**
+ * Defines which key identifier type to use. The WS-Security specifications recommends to use the identifier type
+ * IssuerSerial. For possible signature key identifier types refer to {@link
+ * org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For signature IssuerSerial and
+ * DirectReference are valid only.
+ */
+ public void setSecurementSignatureKeyIdentifier(String securementSignatureKeyIdentifier) {
+ handler.setOption(WSHandlerConstants.SIG_KEY_ID, securementSignatureKeyIdentifier);
+ }
+
+ /**
+ * Parameter to define which parts of the request shall be signed.
+ *
+ * Refer to {@link #setSecurementEncryptionParts(String)} for a detailed description of the format of the value
+ * string.
+ *
+ * If this parameter is not specified the handler signs the SOAP Body by default.
+ *
+ * The WS Security specifications define several formats to transfer the signature tokens (certificates) or
+ * references to these tokens. Thus, the plain element name Token signs the token and takes care of the
+ * different format.
+ *
+ * To sign the SOAP body and the signature token the value of this parameter must contain:
+ *
+ * <parameter name="signatureParts"
+ * value="{}{http://schemas.xmlsoap.org/soap/envelope/}Body; Token" />
+ *
+ * To specify an element without a namespace use the string Null as the namespace name (this is a case
+ * sensitive string)
+ *
+ * If there is no other element in the request with a local name of Body then the SOAP namespace
+ * identifier can be empty ({}).
+ */
+ public void setSecurementSignatureParts(String securementSignatureParts) {
+ handler.setOption(WSHandlerConstants.SIGNATURE_PARTS, securementSignatureParts);
+ }
+
+ public void setSecurementUsername(String securementUsername) {
+ this.securementUsername = securementUsername;
+ }
+
+ /** Sets the server-side time to live */
+ public void setTimeToLive(int timeToLive) {
+ if (timeToLive <= 0) {
+ throw new IllegalArgumentException("timeToLive must be positive");
+ }
+ this.timeToLive = timeToLive;
+ }
+
+ /** Sets the validation actions to be executed by the interceptor. */
+ public void setValidationActions(String actions) {
+ this.validationActions = actions;
+ try {
+ validationActionsVector = new Vector();
+ validationAction = WSSecurityUtil.decodeAction(actions, validationActionsVector);
+ }
+ catch (WSSecurityException ex) {
+ throw new IllegalArgumentException(ex);
+ }
+ }
+
+ public void setValidationActor(String validationActor) {
+ this.validationActor = validationActor;
+ }
+
+ public void setValidationCallbackHandler(CallbackHandler callbackHandler) {
+ this.validationCallbackHandler = callbackHandler;
+ }
+
+ /** Sets the Crypto to use to decrypt incoming messages */
+ public void setValidationDecryptionCrypto(Crypto decryptionCrypto) {
+ this.validationDecryptionCrypto = decryptionCrypto;
+ }
+
+ /** Sets the Crypto to use to verify the signature of incoming messages */
+ public void setValidationSignatureCrypto(Crypto signatureCrypto) {
+ this.validationSignatureCrypto = signatureCrypto;
+ }
+
+ /** Whether to enable signatureConfirmation or not. By default signatureConfirmation is enabled */
+ public void setEnableSignatureConfirmation(boolean enableSignatureConfirmation) {
+ handler.setOption(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, enableSignatureConfirmation);
+ this.enableSignatureConfirmation = enableSignatureConfirmation;
+ }
+
+ /** Sets if the generated timestamp header's precision is in milliseconds. */
+ public void setTimestampPrecisionInMilliseconds(boolean timestampPrecisionInMilliseconds) {
+ handler.setOption(WSHandlerConstants.TIMESTAMP_PRECISION, timestampPrecisionInMilliseconds);
+ }
+
+ /** Sets whether or not timestamp verification is done with the server-side time to live */
+ public void setTimestampStrict(boolean timestampStrict) {
+ this.timestampStrict = timestampStrict;
+ }
+
+ /**
+ * Enables the mustUnderstand attribute on WS-Security headers on outgoing messages. Default is
+ * true.
+ */
+ public void setSecurementMustUnderstand(boolean securementMustUnderstand) {
+ handler.setOption(WSHandlerConstants.MUST_UNDERSTAND, securementMustUnderstand);
+ }
+
+ /**
+ * Sets the additional elements in UsernameTokens.
+ *
+ * The value of this parameter is a list of element names that are added to the UsernameToken. The names of the list
+ * a separated by spaces.
+ *
+ * The list may containe the names nonce and created only. Use this option if the password
+ * type is passwordText and the handler shall add the Nonce and/or Created
+ * elements.
+ */
+ public void setSecurementUsernameTokenElements(String securementUsernameTokenElements) {
+ handler.setOption(WSHandlerConstants.ADD_UT_ELEMENTS, securementUsernameTokenElements);
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.isTrue(validationActions != null || securementActions != null,
+ "validationActions or securementActions are required");
+ if (validationActions != null) {
+ if ((validationAction & WSConstants.UT) != 0) {
+ Assert.notNull(validationCallbackHandler, "validationCallbackHandler is required");
+ }
+
+ if ((validationAction & WSConstants.SIGN) != 0) {
+ Assert.notNull(validationSignatureCrypto, "validationSignatureCrypto is required");
+ }
+
+ if ((validationAction & WSConstants.ENCR) != 0) {
+ Assert.notNull(validationDecryptionCrypto, "validationDecryptionCrypto is required");
+ }
+ }
+ // securement actions are not to be validated at start up as they could
+ // be configured dynamically via the message context
+ }
+
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecuritySecurementException {
+ if (securementAction == WSConstants.NO_SECURITY && !enableSignatureConfirmation) {
+ return;
+ }
+ RequestData requestData = initializeRequestData(messageContext);
+
+ Document envelopeAsDocument = getEnvelopeAsDocument(soapMessage);
+ try {
+ // In case on signature confirmation with no other securement
+ // action, we need to pass an empty securementActionsVector to avoid
+ // NPE
+ if (securementAction == WSConstants.NO_SECURITY) {
+ securementActionsVector = new Vector(0);
+ }
+
+ handler.doSenderAction(securementAction, envelopeAsDocument, requestData, securementActionsVector, false);
+ }
+ catch (WSSecurityException ex) {
+ throw new Wss4jSecuritySecurementException(ex.getMessage(), ex);
+ }
+
+ replaceMessageIfNecessary(soapMessage, envelopeAsDocument);
+ }
+
+ /** Creates and initializes a request data */
+ private RequestData initializeRequestData(MessageContext messageContext) {
+ RequestData requestData = new RequestData();
+ requestData.setMsgContext(messageContext);
+
+ // reads securementUsername first from the context then from the
+ // property
+ String su = (String) messageContext
+ .getProperty(SECUREMENT_USER_PROPERTY_NAME);
+ if (su != null && !su.equals("")) {
+ requestData.setUsername(su);
+ }
+ else {
+ requestData.setUsername(securementUsername);
+
+ }
+
+ requestData.setUsername(securementUsername);
+ return requestData;
+ }
+
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("validating message: " + soapMessage + " with actions: " + validationActions);
+ }
+
+ if (validationAction == WSConstants.NO_SECURITY) {
+ return;
+ }
+
+ Document envelopeAsDocument = getEnvelopeAsDocument(soapMessage);
+
+ // Header processing
+ WSSecurityEngine securityEngine = WSSecurityEngine.getInstance();
+
+ try {
+ Vector results = securityEngine.processSecurityHeader(envelopeAsDocument, validationActor,
+ validationCallbackHandler, validationSignatureCrypto, validationDecryptionCrypto);
+
+ // Results verification
+ if (results == null) {
+ throw new Wss4jSecurityValidationException("No WS-Security header found");
+ }
+
+ if (!handler.checkReceiverResults(results, validationActionsVector)) {
+ throw new Wss4jSecurityValidationException("Security processing failed (actions mismatch)");
+ }
+
+ // puts the results in the context
+ // useful for Signature Confirmation
+ updateContextWithResults(messageContext, results);
+
+ verifyCertificateTrust(results);
+
+ verifyTimestamp(results);
+ }
+ catch (WSSecurityException ex) {
+ throw new Wss4jSecurityValidationException(ex.getMessage(), ex);
+ }
+
+ replaceMessageIfNecessary(soapMessage, envelopeAsDocument);
+
+ soapMessage.getEnvelope().getHeader().removeHeaderElement(WS_SECURITY_NAME);
+ }
+
+ /**
+ * Transforms a soap message to a DOM document.
+ *
+ * @param soapMessage the message to transform
+ * @return a DOM document representing the message
+ */
+ private Document getEnvelopeAsDocument(SoapMessage soapMessage) {
+ if (soapMessage instanceof SaajSoapMessage) {
+ SaajSoapMessage saajMessage = (SaajSoapMessage) soapMessage;
+ return saajMessage.getSaajMessage().getSOAPPart();
+ }
+
+ if (soapMessage instanceof AxiomSoapMessage) {
+ AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage;
+ return AxiomUtils.toDocument(axiomMessage.getAxiomMessage().getSOAPEnvelope());
+ }
+
+ throw new UnsupportedOperationException("Message type not supported: " + soapMessage);
+ }
+
+ /**
+ * Puts the results of WS-Security headers processing in the message context. Some actions like Signature
+ * Confirmation
+ */
+ private void updateContextWithResults(MessageContext messageContext, Vector results) {
+ Vector handlerResults;
+ if ((handlerResults = (Vector) messageContext
+ .getProperty(WSHandlerConstants.RECV_RESULTS)) == null) {
+ handlerResults = new Vector();
+ messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
+ }
+ WSHandlerResult rResult = new WSHandlerResult(validationActor, results);
+ handlerResults.add(0, rResult);
+ messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
+ }
+
+ /**
+ *
+ * @param results
+ * @throws WSSecurityException
+ */
+ protected void verifyCertificateTrust(Vector results) throws WSSecurityException {
+ RequestData requestData = new RequestData();
+ requestData.setSigCrypto(validationSignatureCrypto);
+ WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.SIGN);
+
+ if (actionResult != null) {
+ X509Certificate returnCert = actionResult.getCertificate();
+ if (!handler.verifyTrust(returnCert, requestData)) {
+ throw new Wss4jSecurityValidationException("The certificate used for the signature is not trusted");
+ }
+ }
+ }
+
+ /**
+ *
+ * @param results
+ * @throws WSSecurityException
+ */
+ protected void verifyTimestamp(Vector results) throws WSSecurityException {
+ WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.TS);
+
+ if (actionResult != null) {
+ Timestamp timestamp = actionResult.getTimestamp();
+
+ if (timestamp != null && timestampStrict) {
+ if (!handler.verifyTimestamp(timestamp, timeToLive)) {
+ throw new Wss4jSecurityValidationException("Invalid timestamp : " + timestamp.getID());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Replaces an axiom message.
+ *
+ * @param soapMessage the soap message to replace
+ * @param envelope the new envelope
+ */
+ private void replaceMessageIfNecessary(SoapMessage soapMessage, Document envelope) {
+ if (soapMessage instanceof AxiomSoapMessage) {
+ // construct a new Axiom message with the processed envelope
+ AxiomSoapMessage axiomMessage = (AxiomSoapMessage) soapMessage;
+ SOAPEnvelope envelopeFromDOMDocument = AxiomUtils.toEnvelope(envelope);
+ SOAPFactory factory = (SOAPFactory) axiomMessage.getAxiomMessage().getOMFactory();
+ SOAPMessage newMessage = factory.createSOAPMessage();
+ newMessage.setSOAPEnvelope(envelopeFromDOMDocument);
+
+ // replace the Axiom message
+ axiomMessage.setAxiomMessage(newMessage);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecuritySecurementException.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecuritySecurementException.java
new file mode 100755
index 00000000..7e8fef6d
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecuritySecurementException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import org.springframework.ws.soap.security.WsSecuritySecurementException;
+
+/**
+ * WSS4J-specific version of the {@link WsSecuritySecurementException}.
+ *
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public class Wss4jSecuritySecurementException extends WsSecuritySecurementException {
+
+ public Wss4jSecuritySecurementException(String msg) {
+ super(msg);
+ }
+
+ public Wss4jSecuritySecurementException(String msg, Throwable ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityValidationException.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityValidationException.java
new file mode 100755
index 00000000..841fa5d1
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityValidationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import org.springframework.ws.soap.security.WsSecurityValidationException;
+
+/**
+ * WSS4J-specific version of the {@link WsSecurityValidationException}.
+ *
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @since 1.5.0
+ */
+public class Wss4jSecurityValidationException extends WsSecurityValidationException {
+
+ public Wss4jSecurityValidationException(String msg) {
+ super(msg);
+ }
+
+ public Wss4jSecurityValidationException(String msg, Throwable ex) {
+ super(msg, ex);
+ }
+
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java
new file mode 100755
index 00000000..6f3869f3
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.callback;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStore.Entry;
+import javax.crypto.SecretKey;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
+
+/**
+ * A base class for callback handlers.
+ *
+ * @author Tareq Abed Rabbo
+ */
+public abstract class AbstractWss4jCallbackHandler extends AbstractCallbackHandler implements InitializingBean {
+
+ private boolean passwordDigestRequired;
+
+ private boolean passwordPlainTextRequired;
+
+ private String keyPassword;
+
+ private KeyStore keyStore;
+
+ /** Sets the key store to use if a symmetric key name is embedded. */
+ public void setKeyStore(KeyStore keyStore) {
+ this.keyStore = keyStore;
+ }
+
+ /** Sets if a digest password is required. */
+ public void setPasswordDigestRequired(boolean passwordDigestRequired) {
+ this.passwordDigestRequired = passwordDigestRequired;
+ }
+
+ /** Sets the password of the key used for decryption. */
+ public void setKeyPassword(String keyPassword) {
+ this.keyPassword = keyPassword;
+ }
+
+ /** Sets if a plain text password is required. */
+ public void setPasswordPlainTextRequired(boolean passwordPlainTextRequired) {
+ this.passwordPlainTextRequired = passwordPlainTextRequired;
+ }
+
+ /** Returns the password of the key used for decryption. */
+ public String getKeyPassword() {
+ return keyPassword;
+ }
+
+ /** Returns if a digest password is required. */
+ public boolean isPasswordDigestRequired() {
+ return passwordDigestRequired;
+ }
+
+ /** Returns if a plain text password is required. */
+ public boolean isPasswordPlainTextRequired() {
+ return passwordPlainTextRequired;
+ }
+
+ /** Gets the key store to use if a symmetric key name is embedded. */
+ public KeyStore getKeyStore() {
+ return keyStore;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert
+ .isTrue(!(passwordDigestRequired && passwordPlainTextRequired),
+ "passwordDigestRequired and passwordPlainTextRequired can not be true in the same time");
+ }
+
+ protected final void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
+
+ if (callback instanceof WSPasswordCallback) {
+ WSPasswordCallback passwordCallback = (WSPasswordCallback) callback;
+
+ int usage = passwordCallback.getUsage();
+
+ if (passwordDigestRequired && !(usage == WSPasswordCallback.USERNAME_TOKEN)) {
+ throw new WSSecurityException("digest password required");
+ }
+
+ if (passwordPlainTextRequired && !(usage == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN)) {
+ throw new WSSecurityException("plain text password required");
+ }
+
+ String id = passwordCallback.getIdentifer();
+ switch (usage) {
+
+ // plain text password
+ case WSPasswordCallback.USERNAME_TOKEN_UNKNOWN:
+ validateUsernameTokenPlainText(passwordCallback);
+ return;
+
+ // digest password
+ case WSPasswordCallback.USERNAME_TOKEN:
+ validateUsernameTokenDigest(passwordCallback);
+ return;
+
+ // decryption
+ case WSPasswordCallback.DECRYPT:
+ passwordCallback.setPassword(getDecryptionKeyPassword(id));
+ return;
+
+ // decryption with an embedded symmetric key name
+ case WSPasswordCallback.KEY_NAME:
+ try {
+ KeyStore.PasswordProtection protection =
+ new KeyStore.PasswordProtection(getSymmetricKeyPassword(id).toCharArray());
+ Entry entry = keyStore.getEntry(id, protection);
+ if (entry instanceof KeyStore.SecretKeyEntry) {
+ KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) entry;
+ SecretKey secretKey = secretKeyEntry.getSecretKey();
+ passwordCallback.setKey(secretKey.getEncoded());
+ }
+ else {
+ throw new RuntimeException("key must be instance of javax.crypto.SecretKey:" + id);
+ }
+ }
+ catch (GeneralSecurityException ex) {
+ throw new Wss4jSecurityCallbackHandlerException(ex
+ .getMessage(), ex);
+ }
+ return;
+ default:
+ throw new UnsupportedOperationException("usage type not suporrted:" + usage);
+ }
+ }
+ else {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+
+ protected String getDecryptionKeyPassword(String id) {
+ return keyPassword;
+ }
+
+ protected String getSymmetricKeyPassword(String id) {
+ return keyPassword;
+ }
+
+ /**
+ * validates a Username token with a plain text password. The implementation must validate the username and the
+ * password and must throw an exception if the token is not valid
+ *
+ * @param callback the callback created by Wss4j
+ * @throws WSSecurityException if the token is not valid
+ */
+ abstract protected void validateUsernameTokenPlainText(WSPasswordCallback callback) throws WSSecurityException;
+
+ /**
+ * validates a Username token with a digest password. The implementation must fetch the clear password of the and
+ * set the password attribute of the callback. Wss4j performs the validation logic.
+ *
+ * @param callback the callback created by Wss4j
+ * @throws WSSecurityException if the token is not valid
+ */
+ abstract protected void validateUsernameTokenDigest(WSPasswordCallback callback) throws WSSecurityException;
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java
new file mode 100755
index 00000000..f9c5e63b
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.callback;
+
+import java.util.Properties;
+
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+
+/** @author Tareq Abed Rabbo */
+public class SimpleCallbackHandler extends AbstractWss4jCallbackHandler {
+
+ private Properties users = new Properties();
+
+ public void setUsers(Properties users) {
+ this.users = users;
+ }
+
+ public Properties getUsers() {
+ return users;
+ }
+
+ protected void validateUsernameTokenPlainText(WSPasswordCallback callback) throws WSSecurityException {
+ String storedPassword = users.getProperty(callback.getIdentifer());
+ if (!(storedPassword != null && storedPassword.equals(callback
+ .getPassword()))) {
+ throw new WSSecurityException(WSSecurityException.FAILURE);
+ }
+ }
+
+ protected void validateUsernameTokenDigest(WSPasswordCallback callback) throws WSSecurityException {
+ callback.setPassword(users.getProperty((callback.getIdentifer())));
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java
new file mode 100755
index 00000000..a7b2cc72
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.callback;
+
+import org.springframework.ws.soap.security.WsSecurityException;
+
+public class Wss4jSecurityCallbackHandlerException extends WsSecurityException {
+
+ public Wss4jSecurityCallbackHandlerException(String msg, Throwable ex) {
+ super(msg, ex);
+ }
+
+ public Wss4jSecurityCallbackHandlerException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiCallbackHandler.java
new file mode 100755
index 00000000..d378bc8e
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiCallbackHandler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.callback.acegi;
+
+import org.acegisecurity.Authentication;
+import org.acegisecurity.AuthenticationException;
+import org.acegisecurity.AuthenticationManager;
+import org.acegisecurity.context.SecurityContextHolder;
+import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import org.acegisecurity.providers.dao.UserCache;
+import org.acegisecurity.providers.dao.cache.NullUserCache;
+import org.acegisecurity.userdetails.UserDetails;
+import org.acegisecurity.userdetails.UserDetailsService;
+import org.acegisecurity.userdetails.UsernameNotFoundException;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.util.Assert;
+import org.springframework.ws.soap.security.wss4j.callback.AbstractWss4jCallbackHandler;
+
+/** @author Tareq Abed Rabbo */
+public class AcegiCallbackHandler extends AbstractWss4jCallbackHandler {
+
+ private AuthenticationManager authenticationManager;
+
+ private UserCache userCache = new NullUserCache();
+
+ private UserDetailsService userDetailsService;
+
+ public UserCache getUserCache() {
+ return userCache;
+ }
+
+ public void setUserCache(UserCache userCache) {
+ this.userCache = userCache;
+ }
+
+ public UserDetailsService getUserDetailsService() {
+ return userDetailsService;
+ }
+
+ public void setUserDetailsService(UserDetailsService userDetailsService) {
+ this.userDetailsService = userDetailsService;
+ }
+
+ public AuthenticationManager getAuthenticationManager() {
+ return authenticationManager;
+ }
+
+ public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+ this.authenticationManager = authenticationManager;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ super.afterPropertiesSet();
+
+ if (isPasswordPlainTextRequired()) {
+ Assert.notNull(authenticationManager, "authenticationManager is required");
+ }
+
+ if (isPasswordDigestRequired()) {
+ Assert
+ .notNull(userDetailsService, "userDetailsService is required");
+ }
+
+ }
+
+ protected void validateUsernameTokenPlainText(WSPasswordCallback callback) throws WSSecurityException {
+ if (isPasswordPlainTextRequired()) {
+ Assert
+ .notNull(authenticationManager,
+ "authenticationManager is required to validate a usernameToken with a plain text password");
+ }
+ try {
+ Authentication authResult = authenticationManager
+ .authenticate(
+ new UsernamePasswordAuthenticationToken(callback.getIdentifer(), callback.getPassword()));
+ if (logger.isDebugEnabled()) {
+ logger
+ .debug("Authentication success: " + authResult.toString());
+ }
+ SecurityContextHolder.getContext().setAuthentication(authResult);
+ }
+ catch (AuthenticationException failed) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Authentication request for user '" + callback.getIdentifer() + "' failed: " +
+ failed.toString());
+ }
+ SecurityContextHolder.getContext().setAuthentication(null);
+ throw new WSSecurityException(WSSecurityException.FAILURE);
+ }
+ }
+
+ protected void validateUsernameTokenDigest(WSPasswordCallback callback) throws WSSecurityException {
+ if (isPasswordDigestRequired()) {
+ Assert
+ .notNull(userDetailsService,
+ "userDetailsService is required to validate a usernameToken with a digest password");
+ }
+ UserDetails user = loadUserDetails(callback.getIdentifer());
+ if (user != null) {
+ callback.setPassword(user.getPassword());
+ }
+ }
+
+ private UserDetails loadUserDetails(String username) throws DataAccessException {
+ UserDetails user = userCache.getUserFromCache(username);
+
+ if (user == null) {
+ try {
+ user = userDetailsService.loadUserByUsername(username);
+ }
+ catch (UsernameNotFoundException notFound) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Username '" + username + "' not found");
+ }
+ return null;
+ }
+ userCache.putUserInCache(user);
+ }
+ return user;
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiSecurityContextUpdateAdvice.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiSecurityContextUpdateAdvice.java
new file mode 100755
index 00000000..0e06f1b5
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiSecurityContextUpdateAdvice.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.callback.acegi;
+
+import java.lang.reflect.Method;
+import java.util.Vector;
+
+import org.acegisecurity.context.SecurityContextHolder;
+import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.WSSecurityEngineResult;
+import org.apache.ws.security.WSUsernameTokenPrincipal;
+import org.apache.ws.security.handler.WSHandlerConstants;
+import org.apache.ws.security.handler.WSHandlerResult;
+import org.apache.ws.security.util.WSSecurityUtil;
+
+import org.springframework.aop.AfterReturningAdvice;
+import org.springframework.aop.ThrowsAdvice;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor;
+
+/**
+ * This class is responsible for setting Acegi's security context after the request is validated. It must be used in
+ * conjunction with AcegiCallbackHandler when validating a username token with a digest password.
+ *
+ * @author tareq.abedrabbo
+ */
+public class AcegiSecurityContextUpdateAdvice implements AfterReturningAdvice, ThrowsAdvice {
+
+ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
+
+ if (!(Wss4jSecurityInterceptor.class
+ .isAssignableFrom(target.getClass()))) {
+ throw new IllegalArgumentException(
+ "AcegiSecurityContextUpdateAdvice can only be applied to a Wss4jSecurityInterceptor");
+ }
+
+ MessageContext context = (MessageContext) args[0];
+
+ Vector wsHandlerResults = (Vector) context
+ .getProperty(WSHandlerConstants.RECV_RESULTS);
+
+ if (wsHandlerResults != null) {
+ WSHandlerResult handlerResult = (WSHandlerResult) wsHandlerResults
+ .get(0);
+ Vector results = handlerResult.getResults();
+ WSSecurityEngineResult actionResult = WSSecurityUtil
+ .fetchActionResult(results, WSConstants.UT);
+ if (actionResult != null) {
+ WSUsernameTokenPrincipal principal = (WSUsernameTokenPrincipal) actionResult
+ .getPrincipal();
+ if (principal.getPasswordType().equals(WSConstants.PASSWORD_DIGEST)) {
+ String user = principal.getName();
+ String password = principal.getPassword();
+ UsernamePasswordAuthenticationToken authRequest =
+ new UsernamePasswordAuthenticationToken(user, password);
+ SecurityContextHolder.getContext().setAuthentication(authRequest);
+ }
+ }
+ }
+ }
+
+ public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
+ SecurityContextHolder.getContext().setAuthentication(null);
+ }
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/package.html b/security/src/main/java/org/springframework/ws/soap/security/wss4j/package.html
new file mode 100644
index 00000000..a6e7beca
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/package.html
@@ -0,0 +1,6 @@
+
+
+Contains classes for using the Apache WSS4J WS-Security implementation within
+Spring-WS.
+
+
\ No newline at end of file
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBean.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBean.java
new file mode 100755
index 00000000..3991f031
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBean.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.support;
+
+import java.util.Properties;
+
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * Spring factory bean for a WSS4J {@link Crypto}.
+ *
+ * Requires the {@link #setConfiguration(java.util.Properties) configuration} property to be set. This configuration
+ * should have the org.apache.ws.security.crypto.provider property defined.
+ *
+ * @author Tareq Abed Rabbo
+ * @author Arjen Poutsma
+ * @see org.apache.ws.security.components.crypto.Crypto
+ * @since 1.5.0
+ */
+public class CryptoFactoryBean implements FactoryBean, BeanClassLoaderAware, InitializingBean {
+
+ private Properties configuration;
+
+ private ClassLoader classLoader;
+
+ private Crypto crypto;
+
+ /**
+ * Sets the configuration of the Crypto.
+ *
+ * @see org.apache.ws.security.components.crypto.CryptoFactory#getInstance(java.util.Properties)
+ */
+ public void setConfiguration(Properties properties) {
+ this.configuration = properties;
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(configuration, "'configuration' is required");
+
+ this.crypto = CryptoFactory.getInstance(configuration, classLoader);
+ }
+
+ public Class getObjectType() {
+ return Crypto.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public Object getObject() throws Exception {
+ return crypto;
+ }
+
+}
diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/package.html b/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/package.html
new file mode 100644
index 00000000..e06c6873
--- /dev/null
+++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/support/package.html
@@ -0,0 +1,5 @@
+
+
+Contains support classes for working with WSS4J.
+
+
\ No newline at end of file
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java
index dcfd27d4..24a505cf 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java
@@ -25,14 +25,16 @@ import com.sun.xml.wss.XWSSProcessor;
import com.sun.xml.wss.XWSSProcessorFactory;
import com.sun.xml.wss.XWSSecurityException;
import com.sun.xml.wss.impl.WssSoapFaultException;
+
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
+import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
import org.springframework.ws.soap.security.WsSecurityValidationException;
-import org.springframework.ws.soap.security.xwss.callback.CallbackHandlerChain;
+import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
/**
* WS-Security endpoint interceptor that is based on Sun's XML and Web Services Security package (XWSS). This
@@ -87,9 +89,7 @@ public class XwsSecurityInterceptor extends AbstractWsSecurityInterceptor implem
this.callbackHandler = new CallbackHandlerChain(callbackHandler);
}
- /**
- * Sets the policy configuration to use for XWSS. Required.
- */
+ /** Sets the policy configuration to use for XWSS. Required. */
public void setPolicyConfiguration(Resource policyConfiguration) {
this.policyConfiguration = policyConfiguration;
}
@@ -121,7 +121,8 @@ public class XwsSecurityInterceptor extends AbstractWsSecurityInterceptor implem
* @throws XwsSecuritySecurementException in case of errors
* @throws IllegalArgumentException when soapMessage is not a SaajSoapMessage
*/
- protected void secureMessage(SoapMessage soapMessage) throws XwsSecuritySecurementException {
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws XwsSecuritySecurementException {
Assert.isTrue(soapMessage instanceof SaajSoapMessage, "XwsSecurityInterceptor requires a SaajSoapMessage. " +
"Use a SaajSoapMessageFactory to create the SOAP messages.");
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
@@ -145,7 +146,8 @@ public class XwsSecurityInterceptor extends AbstractWsSecurityInterceptor implem
* @throws XwsSecurityValidationException in case of errors
* @throws IllegalArgumentException when soapMessage is not a SaajSoapMessage
*/
- protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidationException {
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
Assert.isTrue(soapMessage instanceof SaajSoapMessage, "XwsSecurityInterceptor requires a SaajSoapMessage. " +
"Use a SaajSoapMessageFactory to create the SOAP messages.");
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CryptographyCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CryptographyCallbackHandler.java
index fa627616..8bdd487d 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CryptographyCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CryptographyCallbackHandler.java
@@ -26,6 +26,8 @@ import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
import com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
+
/**
* Default callback handler that handles cryptographic callback. This handler determines the exact callback passed, and
* calls a template method for it. By default, all template methods throw an UnsupportedCallbackException,
@@ -80,9 +82,9 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* implementation delegates to specific handling methods.
*
* @see #handlePrivateKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.PrivateKeyRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.PrivateKeyRequest)
* @see #handleSymmetricKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.SymmetricKeyRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.SymmetricKeyRequest)
*/
protected final void handleDecryptionKeyCallback(DecryptionKeyCallback callback)
throws IOException, UnsupportedCallbackException {
@@ -102,13 +104,13 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handleDecryptionKeyCallback(). Default implementation delegates to specific handling methods.
*
* @see #handlePublicKeyBasedPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
+ * com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
* @see #handleX509CertificateBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509CertificateBasedRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509CertificateBasedRequest)
* @see #handleX509IssuerSerialBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509IssuerSerialBasedRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509IssuerSerialBasedRequest)
* @see #handleX509SubjectKeyIdentifierBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest)
*/
protected final void handlePrivateKeyRequest(DecryptionKeyCallback callback,
DecryptionKeyCallback.PrivateKeyRequest request)
@@ -180,7 +182,7 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handleDecryptionKeyCallback(). Default implementation delegates to specific handling methods.
*
* @see #handleAliasSymmetricKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.DecryptionKeyCallback.AliasSymmetricKeyRequest)
+ * com.sun.xml.wss.impl.callback.DecryptionKeyCallback.AliasSymmetricKeyRequest)
*/
protected final void handleSymmetricKeyRequest(DecryptionKeyCallback callback,
DecryptionKeyCallback.SymmetricKeyRequest request)
@@ -215,9 +217,9 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* implementation delegates to specific handling methods.
*
* @see #handleSymmetricKeyRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.SymmetricKeyRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.SymmetricKeyRequest)
* @see #handleX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.X509CertificateRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.X509CertificateRequest)
*/
protected final void handleEncryptionKeyCallback(EncryptionKeyCallback callback)
throws IOException, UnsupportedCallbackException {
@@ -239,7 +241,7 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handleEncryptionKeyCallback(). Default implementation delegates to specific handling methods.
*
* @see #handleAliasSymmetricKeyRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasSymmetricKeyRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasSymmetricKeyRequest)
*/
protected final void handleSymmetricKeyRequest(EncryptionKeyCallback callback,
EncryptionKeyCallback.SymmetricKeyRequest request)
@@ -265,11 +267,11 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handleEncryptionKeyCallback(). Default implementation delegates to specific handling methods.
*
* @see #handleAliasX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasX509CertificateRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasX509CertificateRequest)
* @see #handleDefaultX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.DefaultX509CertificateRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.DefaultX509CertificateRequest)
* @see #handlePublicKeyBasedRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
- *com.sun.xml.wss.impl.callback.EncryptionKeyCallback.PublicKeyBasedRequest)
+ * com.sun.xml.wss.impl.callback.EncryptionKeyCallback.PublicKeyBasedRequest)
*/
protected final void handleX509CertificateRequest(EncryptionKeyCallback callback,
EncryptionKeyCallback.X509CertificateRequest request)
@@ -331,7 +333,7 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* implementation delegates to specific handling methods.
*
* @see #handlePrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureKeyCallback.PrivKeyCertRequest)
+ * com.sun.xml.wss.impl.callback.SignatureKeyCallback.PrivKeyCertRequest)
*/
protected final void handleSignatureKeyCallback(SignatureKeyCallback callback)
throws IOException, UnsupportedCallbackException {
@@ -348,11 +350,11 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handleSignatureKeyCallback(). Default implementation delegates to specific handling methods.
*
* @see #handleDefaultPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureKeyCallback.DefaultPrivKeyCertRequest)
+ * com.sun.xml.wss.impl.callback.SignatureKeyCallback.DefaultPrivKeyCertRequest)
* @see #handleAliasPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureKeyCallback.AliasPrivKeyCertRequest)
+ * com.sun.xml.wss.impl.callback.SignatureKeyCallback.AliasPrivKeyCertRequest)
* @see #handlePublicKeyBasedPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
+ * com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
*/
protected final void handlePrivKeyCertRequest(SignatureKeyCallback cb,
SignatureKeyCallback.PrivKeyCertRequest request)
@@ -413,7 +415,7 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* Default implementation delegates to specific handling methods.
*
* @see #handleX509CertificateRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509CertificateRequest)
+ * com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509CertificateRequest)
*/
protected final void handleSignatureVerificationKeyCallback(SignatureVerificationKeyCallback callback)
throws UnsupportedCallbackException, IOException {
@@ -432,11 +434,11 @@ public class CryptographyCallbackHandler extends AbstractCallbackHandler {
* handling methods.
*
* @see #handlePublicKeyBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.PublicKeyBasedRequest)
+ * com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.PublicKeyBasedRequest)
* @see #handleX509IssuerSerialBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest)
+ * com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest)
* @see #handleX509SubjectKeyIdentifierBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
- *com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest)
+ * com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest)
*/
protected final void handleX509CertificateRequest(SignatureVerificationKeyCallback callback,
SignatureVerificationKeyCallback.X509CertificateRequest request)
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/MockValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/MockValidationCallbackHandler.java
index a5b93dad..aff1df36 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/MockValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/MockValidationCallbackHandler.java
@@ -24,6 +24,8 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
+
/**
* Mock implementation of of callback handler that accepts all password and certificate validation callbacks.
*
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimplePasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimplePasswordValidationCallbackHandler.java
index 1dc70b37..ca2331f3 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimplePasswordValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimplePasswordValidationCallbackHandler.java
@@ -25,8 +25,10 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
+
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Simple callback handler that validates passwords agains a in-memory Properties object. Password
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimpleUsernamePasswordCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimpleUsernamePasswordCallbackHandler.java
index 82294c5f..8aa033ea 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimpleUsernamePasswordCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/SimpleUsernamePasswordCallbackHandler.java
@@ -22,8 +22,10 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.PasswordCallback;
import com.sun.xml.wss.impl.callback.UsernameCallback;
+
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Simple callback handler that supplies a username and password to a username token at runtime.
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java
index d8a75845..5f35cda6 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java
@@ -27,17 +27,15 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.x509.X509AuthenticationToken;
+
import org.springframework.util.Assert;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Callback handler that validates a certificate using an Acegi AuthenticationManager. Logic based on
- * Acegi's X509ProcessingFilter.
- *
- * An Acegi X509AuthenticationToken is created with the certificate as the credentials.
- *
- * The configured authentication manager is expected to supply a provider which can handle this token (usually an
- * instance of X509AuthenticationProvider).
X509ProcessingFilter. An Acegi X509AuthenticationToken is created with the
+ * certificate as the credentials. The configured authentication manager is expected to supply a provider which can
+ * handle this token (usually an instance of X509AuthenticationProvider).
*
* This class only handles CertificateValidationCallbacks, and throws an
* UnsupportedCallbackException for others.
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java
index 7e77d2fa..3d023503 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java
@@ -29,9 +29,10 @@ import org.acegisecurity.providers.dao.cache.NullUserCache;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
+
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator;
/**
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java
index b35b7170..3bfd5519 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java
@@ -26,8 +26,9 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
import org.springframework.util.Assert;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Callback handler that validates a certificate uses an Acegi AuthenticationManager. Logic based on
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiUsernamePasswordCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiUsernamePasswordCallbackHandler.java
index c903fcb0..54d7ee94 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiUsernamePasswordCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiUsernamePasswordCallbackHandler.java
@@ -26,7 +26,7 @@ import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Callback handler that adds username/password information to a mesage using an Acegi {@link SecurityContext}.
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/AbstractJaasValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/AbstractJaasValidationCallbackHandler.java
index 1cbd2747..e02ab9b6 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/AbstractJaasValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/AbstractJaasValidationCallbackHandler.java
@@ -18,7 +18,7 @@ package org.springframework.ws.soap.security.xwss.callback.jaas;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Abstract base class for integrating with JAAS. Provides a login context name property.
diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/JaasPlainTextPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/JaasPlainTextPasswordValidationCallbackHandler.java
index 51d89f83..af90426c 100644
--- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/JaasPlainTextPasswordValidationCallbackHandler.java
+++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/JaasPlainTextPasswordValidationCallbackHandler.java
@@ -25,7 +25,8 @@ import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Provides basic support for integrating with JAAS and plain text passwords.
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jInterceptorTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jInterceptorTest.java
new file mode 100755
index 00000000..5c4f94d2
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jInterceptorTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jInterceptorTest extends Wss4jInterceptorTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java
new file mode 100755
index 00000000..c5430080
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java
@@ -0,0 +1,6 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest
+ extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorEncryptionTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorEncryptionTest.java
new file mode 100755
index 00000000..8863fe25
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorEncryptionTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorEncryptionTest extends Wss4jMessageInterceptorEncryptionTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorHeaderTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorHeaderTest.java
new file mode 100755
index 00000000..030aa393
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorHeaderTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorHeaderTest extends Wss4jMessageInterceptorHeaderTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSignTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSignTest.java
new file mode 100755
index 00000000..d6cc1c48
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSignTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorTimestampTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorTimestampTest.java
new file mode 100755
index 00000000..bf235956
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorTimestampTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorTimestampTest extends Wss4jMessageInterceptorTimestampTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenSignatureTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenSignatureTest.java
new file mode 100755
index 00000000..1446ae2a
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenSignatureTest.java
@@ -0,0 +1,6 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorUsernameTokenSignatureTest
+ extends Wss4jMessageInterceptorUsernameTokenSignatureTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenTest.java
new file mode 100755
index 00000000..04709ff4
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorUsernameTokenTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class AxiomWss4jMessageInterceptorUsernameTokenTest extends Wss4jMessageInterceptorUsernameTokenTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jInterceptorTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jInterceptorTest.java
new file mode 100755
index 00000000..d8fa08df
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jInterceptorTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jInterceptorTest extends Wss4jInterceptorTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java
new file mode 100755
index 00000000..bbbe7f9c
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java
@@ -0,0 +1,6 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorAcegiCallbackHandlerTest
+ extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorEncryptionTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorEncryptionTest.java
new file mode 100755
index 00000000..2096df70
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorEncryptionTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorEncryptionTest extends Wss4jMessageInterceptorEncryptionTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorHeaderTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorHeaderTest.java
new file mode 100755
index 00000000..10fc8fe0
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorHeaderTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorHeaderTest extends Wss4jMessageInterceptorHeaderTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java
new file mode 100755
index 00000000..25ee62ff
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSignTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorTimestampTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorTimestampTest.java
new file mode 100755
index 00000000..a0e892fc
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorTimestampTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorTimestampTest extends Wss4jMessageInterceptorTimestampTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenSignatureTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenSignatureTest.java
new file mode 100755
index 00000000..4ce023d3
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenSignatureTest.java
@@ -0,0 +1,6 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorUsernameTokenSignatureTest
+ extends Wss4jMessageInterceptorUsernameTokenSignatureTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenTest.java
new file mode 100755
index 00000000..2372e257
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorUsernameTokenTest.java
@@ -0,0 +1,5 @@
+package org.springframework.ws.soap.security.wss4j;
+
+public class SaajWss4jMessageInterceptorUsernameTokenTest extends Wss4jMessageInterceptorUsernameTokenTestCase {
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jInterceptorTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jInterceptorTestCase.java
new file mode 100755
index 00000000..59e05247
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jInterceptorTestCase.java
@@ -0,0 +1,57 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.security.WsSecuritySecurementException;
+import org.springframework.ws.soap.security.WsSecurityValidationException;
+
+public abstract class Wss4jInterceptorTestCase extends Wss4jTestCase {
+
+ public void testhandleRequest() throws Exception {
+ SoapMessage request = loadMessage("empty-soap.xml");
+ final Object requestMessage = getMessage(request);
+ SoapMessage validatedRequest = loadMessage("empty-soap.xml");
+ final Object validatedRequestMessage = getMessage(validatedRequest);
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor() {
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecuritySecurementException {
+ fail("secure not expected");
+ }
+
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
+ assertEquals("Invalid message", requestMessage, getMessage(soapMessage));
+ setMessage(soapMessage, validatedRequestMessage);
+ }
+ };
+ MessageContext context = new DefaultMessageContext(request, getMessageFactory());
+ interceptor.handleRequest(context, null);
+ assertEquals("Invalid request", validatedRequestMessage, getMessage((SoapMessage) context.getRequest()));
+ }
+
+ public void testhandleResponse() throws Exception {
+ SoapMessage securedResponse = loadMessage("empty-soap.xml");
+ final Object securedResponseMessage = getMessage(securedResponse);
+
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor() {
+
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecuritySecurementException {
+ setMessage(soapMessage, securedResponseMessage);
+ }
+
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
+ fail("validate not expected");
+ }
+
+ };
+ SoapMessage request = loadMessage("empty-soap.xml");
+ MessageContext context = new DefaultMessageContext(request, getMessageFactory());
+ context.getResponse();
+ interceptor.handleResponse(context, null);
+ assertEquals("Invalid response", securedResponseMessage, getMessage((SoapMessage) context.getResponse()));
+ }
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java
new file mode 100755
index 00000000..bb040934
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java
@@ -0,0 +1,79 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.util.Properties;
+
+import org.acegisecurity.Authentication;
+import org.acegisecurity.context.SecurityContextHolder;
+import org.acegisecurity.userdetails.memory.InMemoryDaoImpl;
+import org.apache.ws.security.WSConstants;
+
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.EndpointInterceptor;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.security.wss4j.callback.acegi.AcegiCallbackHandler;
+import org.springframework.ws.soap.security.wss4j.callback.acegi.AcegiSecurityContextUpdateAdvice;
+
+public abstract class Wss4jMessageInterceptorAcegiCallbackHandlerTestCase extends Wss4jTestCase {
+
+ private Properties users = new Properties();
+
+ protected void onSetup() throws Exception {
+ users.setProperty("Bert", "Ernie,ROLE_TEST");
+ }
+
+ public void testValidateUsernameTokenDigest() throws Exception {
+ EndpointInterceptor interceptor = prepareInterceptor("UsernameToken", true, true);
+ SoapMessage message = loadMessage("usernameTokenDigest-soap.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.handleRequest(messageContext, null);
+ assertValidateUsernameToken(message);
+ }
+
+ protected void assertValidateUsernameToken(SoapMessage message) throws Exception {
+ Object result = getMessage(message);
+ assertNotNull("No result returned", result);
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ getDocument(message));
+ Authentication authentication = SecurityContextHolder.getContext()
+ .getAuthentication();
+ assertNotNull("authentication must not be null", authentication);
+ }
+
+ protected EndpointInterceptor prepareInterceptor(String actions, boolean validating, boolean digest)
+ throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ if (validating) {
+ interceptor.setValidationActions(actions);
+ }
+ else {
+ interceptor.setSecurementActions(actions);
+ }
+ AcegiCallbackHandler callbackHandler = new AcegiCallbackHandler();
+ InMemoryDaoImpl userDetailsService = new InMemoryDaoImpl();
+ userDetailsService.setUserProperties(users);
+ userDetailsService.afterPropertiesSet();
+ callbackHandler.setUserDetailsService(userDetailsService);
+ if (digest) {
+ callbackHandler.setPasswordDigestRequired(true);
+ callbackHandler.setPasswordPlainTextRequired(false);
+ interceptor.setSecurementPasswordType(WSConstants.PW_DIGEST);
+ }
+ else {
+ callbackHandler.setPasswordDigestRequired(false);
+ callbackHandler.setPasswordPlainTextRequired(true);
+ interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
+ }
+ interceptor.setValidationCallbackHandler(callbackHandler);
+ interceptor.afterPropertiesSet();
+
+ ProxyFactory factory = new ProxyFactory(interceptor);
+ AcegiSecurityContextUpdateAdvice advice = new AcegiSecurityContextUpdateAdvice();
+ NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(advice);
+ advisor.setMappedName("handleRequest");
+ factory.addAdvisor(advisor);
+ return (EndpointInterceptor) factory.getProxy();
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java
new file mode 100755
index 00000000..5e107ff3
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java
@@ -0,0 +1,68 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.util.Properties;
+
+import org.apache.ws.security.components.crypto.Crypto;
+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.wss4j.callback.SimpleCallbackHandler;
+import org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean;
+
+public abstract class Wss4jMessageInterceptorEncryptionTestCase extends Wss4jTestCase {
+
+ protected Wss4jSecurityInterceptor interceptor;
+
+ protected void onSetup() throws Exception {
+ interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setValidationActions("Encrypt");
+ interceptor.setSecurementActions("Encrypt");
+
+ SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
+ callbackHandler.setKeyPassword("123456");
+ interceptor.setValidationCallbackHandler(callbackHandler);
+
+ CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
+
+ Properties cryptoFactoryBeanConfig = new Properties();
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.provider",
+ "org.apache.ws.security.components.crypto.Merlin");
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", "jceks");
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", "123456");
+
+ // from the class path
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.file", "private.jks");
+ cryptoFactoryBean.setConfiguration(cryptoFactoryBeanConfig);
+ cryptoFactoryBean.afterPropertiesSet();
+ interceptor.setValidationDecryptionCrypto((Crypto) cryptoFactoryBean
+ .getObject());
+ interceptor.setSecurementEncryptionCrypto((Crypto) cryptoFactoryBean
+ .getObject());
+
+ interceptor.afterPropertiesSet();
+ }
+
+ public void testDecryptRequest() throws Exception {
+ SoapMessage message = loadMessage("encrypted-soap.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, messageContext);
+ Document document = getDocument((SoapMessage) messageContext.getRequest());
+ assertXpathEvaluatesTo("Decryption error", "Hello", "/SOAP-ENV:Envelope/SOAP-ENV:Body/echo:echoRequest/text()",
+ document);
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ getDocument(message));
+ }
+
+ public void testEncryptResponse() throws Exception {
+ SoapMessage message = loadMessage("empty-soap.xml");
+ MessageContext messageContext = getMessageContext(message);
+ interceptor.setSecurementEncryptionUser("rsakey");
+ interceptor.secureMessage(message, messageContext);
+ Document document = getDocument(message);
+ assertXpathExists("Encryption error", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/xenc:EncryptedKey",
+ document);
+ //TODO see why the clear message appears in the unit test
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java
new file mode 100755
index 00000000..5c845f99
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java
@@ -0,0 +1,55 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.util.Iterator;
+import java.util.Properties;
+import javax.xml.namespace.QName;
+
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapHeaderElement;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.security.wss4j.callback.SimpleCallbackHandler;
+
+public abstract class Wss4jMessageInterceptorHeaderTestCase extends Wss4jTestCase {
+
+ private Wss4jSecurityInterceptor interceptor;
+
+ protected void onSetup() throws Exception {
+ Properties users = new Properties();
+ users.setProperty("Bert", "Ernie");
+ interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setValidateRequest(true);
+ interceptor.setSecureResponse(true);
+ interceptor.setValidationActions("UsernameToken");
+ SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
+ callbackHandler.setUsers(users);
+ interceptor.setValidationCallbackHandler(callbackHandler);
+ interceptor.afterPropertiesSet();
+ }
+
+ public void testValidateUsernameTokenPlainText() throws Exception {
+ SoapMessage message = loadMessage("usernameTokenPlainTextWithHeaders-soap.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, messageContext);
+ Object result = getMessage(message);
+ assertNotNull("No result returned", result);
+
+ for (Iterator i = message.getEnvelope().getHeader()
+ .examineAllHeaderElements(); i.hasNext();) {
+ SoapHeaderElement element = (SoapHeaderElement) i.next();
+ QName name = element.getName();
+ if (name
+ .getNamespaceURI()
+ .equals("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")) {
+ fail("Security Header not removed");
+ }
+
+ }
+
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ getDocument(message));
+ assertXpathExists("header1 not found", "/SOAP-ENV:Envelope/SOAP-ENV:Header/header1", getDocument(message));
+ assertXpathExists("header2 not found", "/SOAP-ENV:Envelope/SOAP-ENV:Header/header2", getDocument(message));
+
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java
new file mode 100755
index 00000000..038707fc
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java
@@ -0,0 +1,88 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.util.Properties;
+
+import org.apache.ws.security.components.crypto.Crypto;
+import org.w3c.dom.Document;
+
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.security.wss4j.callback.SimpleCallbackHandler;
+import org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean;
+
+public abstract class Wss4jMessageInterceptorSignTestCase extends Wss4jTestCase {
+
+ protected Wss4jSecurityInterceptor interceptor;
+
+ protected void onSetup() throws Exception {
+ interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setValidationActions("Signature");
+ SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
+ interceptor.setValidationCallbackHandler(callbackHandler);
+
+ CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
+ Properties cryptoFactoryBeanConfig = new Properties();
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.provider",
+ "org.apache.ws.security.components.crypto.Merlin");
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", "jceks");
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", "123456");
+
+ // from the class path
+ cryptoFactoryBeanConfig.setProperty("org.apache.ws.security.crypto.merlin.file", "private.jks");
+ cryptoFactoryBean.setConfiguration(cryptoFactoryBeanConfig);
+ cryptoFactoryBean.afterPropertiesSet();
+ interceptor.setValidationSignatureCrypto((Crypto) cryptoFactoryBean
+ .getObject());
+ interceptor.setSecurementSignatureCrypto((Crypto) cryptoFactoryBean
+ .getObject());
+ interceptor.afterPropertiesSet();
+
+ }
+
+ public void testValidateCertificate() throws Exception {
+ SoapMessage message = loadMessage("signed-soap.xml");
+
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, messageContext);
+ Object result = getMessage(message);
+ assertNotNull("No result returned", result);
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ getDocument(message));
+ }
+
+ public void testValidateCertificateWithSignatureConfirmation() throws Exception {
+ SoapMessage message = loadMessage("signed-soap.xml");
+ MessageContext messageContext = getMessageContext(message);
+ interceptor.setEnableSignatureConfirmation(true);
+ interceptor.validateMessage(message, messageContext);
+ WebServiceMessage response = messageContext.getResponse();
+ interceptor.secureMessage(message, messageContext);
+ assertNotNull("No result returned", response);
+ Document document = getDocument((SoapMessage) response);
+ message.writeTo(System.out);
+ assertXpathExists("Absent SignatureConfirmation element",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse11:SignatureConfirmation", document);
+ }
+
+ public void testSignResponse() throws Exception {
+ interceptor.setSecurementActions("Signature");
+ interceptor.setEnableSignatureConfirmation(false);
+ interceptor.setSecurementPassword("123456");
+ interceptor.setSecurementUsername("rsaKey");
+ SoapMessage message = loadMessage("empty-soap.xml");
+ MessageContext messageContext = getMessageContext(message);
+
+ // interceptor.setSecurementSignatureKeyIdentifier("IssuerSerial");
+
+ interceptor.secureMessage(message, messageContext);
+
+ Document document = getDocument(message);
+ assertXpathExists("Absent SignatureConfirmation element",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature", document);
+
+
+ }
+
+}
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
new file mode 100755
index 00000000..a2b2d3a0
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorTimestampTestCase.java
@@ -0,0 +1,78 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.lang.reflect.Field;
+
+import org.w3c.dom.Document;
+
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+
+public abstract class Wss4jMessageInterceptorTimestampTestCase extends Wss4jTestCase {
+
+ public void testAddTimestamp() throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setSecurementActions("Timestamp");
+ interceptor.afterPropertiesSet();
+ SoapMessage message = loadMessage("empty-soap.xml");
+ MessageContext context = getMessageContext(message);
+ interceptor.secureMessage(message, context);
+ Document document = getDocument(message);
+ assertXpathExists("timestamp header not found",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsu:Timestamp", document);
+ }
+
+ public void testValidateTimestamp() throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setValidationActions("Timestamp");
+ interceptor.afterPropertiesSet();
+ SoapMessage message = getMessageWithTimestamp();
+
+ MessageContext context = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, context);
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ 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);
+ }
+ }
+ };
+ interceptor.setValidationActions("Timestamp");
+ interceptor.setTimeToLive(-10);
+ interceptor.setTimestampStrict(true);
+ interceptor.afterPropertiesSet();
+ SoapMessage message = getMessageWithTimestamp();
+ MessageContext context = new DefaultMessageContext(message, getMessageFactory());
+
+ try {
+ interceptor.validateMessage(message, context);
+ }
+ catch (Wss4jSecurityValidationException ex) {
+ // expected
+ return;
+ }
+ fail("Time to live validation failed");
+ }
+
+ private SoapMessage getMessageWithTimestamp() throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setSecurementActions("Timestamp");
+ interceptor.afterPropertiesSet();
+ SoapMessage message = loadMessage("empty-soap.xml");
+ MessageContext context = getMessageContext(message);
+ interceptor.secureMessage(message, context);
+ return message;
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenSignatureTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenSignatureTestCase.java
new file mode 100755
index 00000000..c2535475
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenSignatureTestCase.java
@@ -0,0 +1,27 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import org.w3c.dom.Document;
+
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+
+public abstract class Wss4jMessageInterceptorUsernameTokenSignatureTestCase extends Wss4jTestCase {
+
+ public void testAddUsernameTokenSignature() throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ interceptor.setSecurementActions("UsernameTokenSignature");
+ interceptor.setSecurementUsername("Bert");
+ interceptor.setSecurementPassword("Ernie");
+ interceptor.afterPropertiesSet();
+ SoapMessage message = loadMessage("empty-soap.xml");
+ MessageContext context = getMessageContext(message);
+ interceptor.secureMessage(message, context);
+
+ Document doc = getDocument(message);
+ assertXpathEvaluatesTo("Invalid Username", "Bert",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Username/text()", doc);
+ assertXpathExists("Invalid Password",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Password[@Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest']/text()",
+ doc);
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java
new file mode 100755
index 00000000..5c82bb16
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java
@@ -0,0 +1,115 @@
+package org.springframework.ws.soap.security.wss4j;
+
+import java.util.Properties;
+
+import org.apache.ws.security.WSConstants;
+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.wss4j.callback.SimpleCallbackHandler;
+
+public abstract class Wss4jMessageInterceptorUsernameTokenTestCase extends Wss4jTestCase {
+
+ private Properties users = new Properties();
+
+ protected void onSetup() throws Exception {
+ users.setProperty("Bert", "Ernie");
+ }
+
+ public void testValidateUsernameTokenPlainText() throws Exception {
+ Wss4jSecurityInterceptor interceptor = prepareInterceptor("UsernameToken", true, false);
+ SoapMessage message = loadMessage("usernameTokenPlainText-soap.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, messageContext);
+ assertValidateUsernameToken(message);
+ }
+
+ public void testValidateUsernameTokenDigest() throws Exception {
+ Wss4jSecurityInterceptor interceptor = prepareInterceptor("UsernameToken", true, true);
+ SoapMessage message = loadMessage("usernameTokenDigest-soap.xml");
+ MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
+ interceptor.validateMessage(message, messageContext);
+ assertValidateUsernameToken(message);
+ }
+
+ public void testAddUsernameTokenPlainText() throws Exception {
+ Wss4jSecurityInterceptor interceptor = prepareInterceptor("UsernameToken", false, false);
+ interceptor.setSecurementUsername("Bert");
+ interceptor.setSecurementPassword("Ernie");
+ SoapMessage message = loadMessage("empty-soap.xml");
+
+ MessageContext messageContext = getMessageContext(message);
+
+ interceptor.secureMessage(message, messageContext);
+ assertAddUsernameTokenPlainText(message);
+ }
+
+ public void testAddUsernameTokenDigest() throws Exception {
+ Wss4jSecurityInterceptor interceptor = prepareInterceptor("UsernameToken", false, true);
+ interceptor.setSecurementUsername("Bert");
+ interceptor.setSecurementPassword("Ernie");
+ SoapMessage message = loadMessage("empty-soap.xml");
+
+ MessageContext messageContext = getMessageContext(message);
+ interceptor.secureMessage(message, messageContext);
+ assertAddUsernameTokenDigest(message);
+ }
+
+ protected void assertValidateUsernameToken(SoapMessage message) throws Exception {
+ Object result = getMessage(message);
+ assertNotNull("No result returned", result);
+ assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
+ getDocument(message));
+ }
+
+ protected void assertAddUsernameTokenPlainText(SoapMessage message) throws Exception {
+ Object result = getMessage(message);
+ assertNotNull("No result returned", result);
+ Document doc = getDocument(message);
+ assertXpathEvaluatesTo("Invalid Username", "Bert",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Username/text()", doc);
+ assertXpathEvaluatesTo("Invalid Password", "Ernie",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Password[@Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText']/text()",
+ doc);
+ }
+
+ protected void assertAddUsernameTokenDigest(SoapMessage message) throws Exception {
+ Object result = getMessage(message);
+ Document doc = getDocument(message);
+ assertNotNull("No result returned", result);
+ assertXpathEvaluatesTo("Invalid Username", "Bert",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Username/text()", doc);
+ assertXpathExists("Password does not exist",
+ "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Password[@Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest']",
+ doc);
+
+ }
+
+ protected Wss4jSecurityInterceptor prepareInterceptor(String actions, boolean validating, boolean digest)
+ throws Exception {
+ Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
+ if (validating) {
+ interceptor.setValidationActions(actions);
+ }
+ else {
+ interceptor.setSecurementActions(actions);
+ }
+ SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
+ callbackHandler.setUsers(users);
+ if (digest) {
+ callbackHandler.setPasswordDigestRequired(true);
+ callbackHandler.setPasswordPlainTextRequired(false);
+ interceptor.setSecurementPasswordType(WSConstants.PW_DIGEST);
+ }
+ else {
+ callbackHandler.setPasswordDigestRequired(false);
+ callbackHandler.setPasswordPlainTextRequired(true);
+ interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
+ }
+ interceptor.setValidationCallbackHandler(callbackHandler);
+ interceptor.afterPropertiesSet();
+ return interceptor;
+ }
+}
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
new file mode 100755
index 00000000..9a620525
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jTestCase.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.MimeHeaders;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
+import junit.framework.TestCase;
+import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.ws.WebServiceMessage;
+import org.springframework.ws.WebServiceMessageFactory;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.axiom.AxiomSoapMessage;
+import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
+import org.springframework.ws.soap.axiom.support.AxiomUtils;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
+import org.springframework.xml.xpath.XPathExpression;
+import org.springframework.xml.xpath.XPathExpressionFactory;
+
+public abstract class Wss4jTestCase extends TestCase {
+
+ protected MessageFactory messageFactory;
+
+ protected final boolean axiomTest = this.getClass().getSimpleName()
+ .startsWith("Axiom");
+
+ protected final boolean saajTest = this.getClass().getSimpleName()
+ .startsWith("Saaj");
+
+ protected Map namespaces;
+
+ protected final void setUp() throws Exception {
+ if (!axiomTest && !saajTest) {
+ throw new IllegalArgumentException("test class name must statrt with either Axiom or Saaj");
+ }
+ messageFactory = MessageFactory.newInstance();
+ namespaces = new HashMap();
+ namespaces.put("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
+ namespaces.put("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
+ namespaces.put("ds", "http://www.w3.org/2000/09/xmldsig#");
+ namespaces.put("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.put("wsse11", "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd");
+ namespaces.put("echo", "http://www.springframework.org/spring-ws/samples/echo");
+ namespaces.put("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
+ onSetup();
+ }
+
+ protected void assertXpathEvaluatesTo(String message,
+ String expectedValue,
+ String xpathExpression,
+ Document document) {
+ XPathExpression expression = XPathExpressionFactory
+ .createXPathExpression(xpathExpression, namespaces);
+ String actualValue = expression.evaluateAsString(document);
+ assertEquals(message, expectedValue, actualValue);
+ }
+
+ protected void assertXpathExists(String message, String xpathExpression, Document document) {
+ XPathExpression expression = XPathExpressionFactory
+ .createXPathExpression(xpathExpression, namespaces);
+ Node node = expression.evaluateAsNode(document);
+ assertNotNull(message, node);
+ }
+
+ protected void assertXpathNotExists(String message, String xpathExpression, Document document) {
+ XPathExpression expression = XPathExpressionFactory
+ .createXPathExpression(xpathExpression, namespaces);
+ Node node = expression.evaluateAsNode(document);
+ assertNull(message, node);
+ }
+
+ protected SaajSoapMessage loadSaajMessage(String fileName) throws Exception {
+ MimeHeaders mimeHeaders = new MimeHeaders();
+ mimeHeaders.addHeader("Content-Type", "text/xml");
+ Resource resource = new ClassPathResource(fileName, getClass());
+ InputStream is = resource.getInputStream();
+ try {
+ assertTrue("Could not load SAAJ message [" + resource + "]", resource.exists());
+ is = resource.getInputStream();
+ return new SaajSoapMessage(messageFactory.createMessage(mimeHeaders, is));
+ }
+ finally {
+ is.close();
+ }
+ }
+
+ protected AxiomSoapMessage loadAxiomMessage(String fileName) throws Exception {
+ Resource resource = new ClassPathResource(fileName, getClass());
+ InputStream is = resource.getInputStream();
+ try {
+ assertTrue("Could not load Axiom message [" + resource + "]", resource.exists());
+ is = resource.getInputStream();
+
+ XMLStreamReader parser = XMLInputFactory.newInstance()
+ .createXMLStreamReader(is);
+ StAXSOAPModelBuilder builder = new StAXSOAPModelBuilder(parser, null);
+ org.apache.axiom.soap.SOAPMessage soapMessage = builder
+ .getSoapMessage();
+ return new AxiomSoapMessage(soapMessage, "", true);
+ }
+ finally {
+ is.close();
+ }
+ }
+
+ protected Object getMessage(SoapMessage soapMessage) {
+ if (soapMessage instanceof SaajSoapMessage) {
+ return ((SaajSoapMessage) soapMessage).getSaajMessage();
+ }
+ if (soapMessage instanceof AxiomSoapMessage) {
+ return ((AxiomSoapMessage) soapMessage).getAxiomMessage();
+
+ }
+ throw new IllegalArgumentException("Illegal message: " + soapMessage);
+ }
+
+ protected void setMessage(SoapMessage soapMessage, Object message) {
+ if (soapMessage instanceof SaajSoapMessage) {
+ ((SaajSoapMessage) soapMessage)
+ .setSaajMessage((SOAPMessage) message);
+ return;
+ }
+ if (soapMessage instanceof AxiomSoapMessage) {
+ ((AxiomSoapMessage) soapMessage)
+ .setAxiomMessage((org.apache.axiom.soap.SOAPMessage) message);
+ return;
+ }
+ throw new IllegalArgumentException("Illegal message: " + message);
+ }
+
+ protected void onSetup() throws Exception {
+ }
+
+ protected SoapMessage loadMessage(String fileName) throws Exception {
+ if (axiomTest) {
+ return loadAxiomMessage(fileName);
+ }
+ if (saajTest) {
+ return loadSaajMessage(fileName);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ protected WebServiceMessageFactory getMessageFactory() throws Exception {
+ if (axiomTest) {
+ return new AxiomSoapMessageFactory();
+ }
+ if (saajTest) {
+ return new SaajSoapMessageFactory(messageFactory);
+ }
+ throw new IllegalArgumentException();
+ }
+
+ protected Document getDocument(SoapMessage message) throws Exception {
+ if (axiomTest) {
+ return AxiomUtils.toDocument(((AxiomSoapMessage) message).getAxiomMessage().getSOAPEnvelope());
+ }
+ if (saajTest) {
+ return ((SaajSoapMessage) message).getSaajMessage().getSOAPPart();
+ }
+ throw new IllegalArgumentException();
+ }
+
+ protected MessageContext getMessageContext(final SoapMessage response) throws Exception {
+ return new DefaultMessageContext(response, getMessageFactory()) {
+ public WebServiceMessage getResponse() {
+ return response;
+ }
+ };
+ }
+
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBeanTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBeanTest.java
new file mode 100644
index 00000000..bc66a6da
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/support/CryptoFactoryBeanTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright ${YEAR} the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.wss4j.support;
+
+import java.util.Properties;
+
+import junit.framework.TestCase;
+import org.apache.ws.security.components.crypto.Crypto;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.util.ClassUtils;
+
+public class CryptoFactoryBeanTest extends TestCase {
+
+ private CryptoFactoryBean factoryBean;
+
+ protected void setUp() throws Exception {
+ factoryBean = new CryptoFactoryBean();
+ }
+
+ public void testMerlin() throws Exception {
+ Properties configuration =
+ PropertiesLoaderUtils.loadProperties(new ClassPathResource("merlin.properties", getClass()));
+ factoryBean.setConfiguration(configuration);
+ factoryBean.setBeanClassLoader(ClassUtils.getDefaultClassLoader());
+ factoryBean.afterPropertiesSet();
+
+ Object result = factoryBean.getObject();
+ assertNotNull("No result", result);
+ assertTrue("Not a crypto instance", result instanceof Crypto);
+ }
+}
\ No newline at end of file
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorKeyStoreTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorKeyStoreTestCase.java
similarity index 92%
rename from security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorKeyStoreTestCase.java
rename to security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorKeyStoreTestCase.java
index bf1660c6..3d757002 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorKeyStoreTestCase.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorKeyStoreTestCase.java
@@ -21,7 +21,7 @@ import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
-public abstract class XwssMessageInterceptorKeyStoreTestCase extends XwssMessageInterceptorTestCase {
+public abstract class AbstractXwssMessageInterceptorKeyStoreTestCase extends AbstractXwssMessageInterceptorTestCase {
protected X509Certificate certificate;
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorTestCase.java
new file mode 100644
index 00000000..ca408f4a
--- /dev/null
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/AbstractXwssMessageInterceptorTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ws.soap.security.xwss;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.MimeHeaders;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPMessage;
+
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.xml.xpath.XPathExpression;
+import org.springframework.xml.xpath.XPathExpressionFactory;
+
+public abstract class AbstractXwssMessageInterceptorTestCase extends XMLTestCase {
+
+ protected XwsSecurityInterceptor interceptor;
+
+ private MessageFactory messageFactory;
+
+ private Map namespaces;
+
+ protected final void setUp() throws Exception {
+ interceptor = new XwsSecurityInterceptor();
+ messageFactory = MessageFactory.newInstance();
+ namespaces = new HashMap();
+ namespaces.put("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
+ namespaces.put("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
+ namespaces.put("ds", "http://www.w3.org/2000/09/xmldsig#");
+ namespaces.put("xenc", "http://www.w3.org/2001/04/xmlenc#");
+ onSetup();
+ }
+
+ protected void assertXpathEvaluatesTo(String message,
+ String expectedValue,
+ String xpathExpression,
+ SOAPMessage soapMessage) {
+ XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
+ Document document = soapMessage.getSOAPPart();
+ String actualValue = expression.evaluateAsString(document);
+ assertEquals(message, expectedValue, actualValue);
+ }
+
+ protected void assertXpathExists(String message, String xpathExpression, SOAPMessage soapMessage) {
+ XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
+ Document document = soapMessage.getSOAPPart();
+ Node node = expression.evaluateAsNode(document);
+ assertNotNull(message, node);
+ }
+
+ protected void assertXpathNotExists(String message, String xpathExpression, SOAPMessage soapMessage) {
+ XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
+ Document document = soapMessage.getSOAPPart();
+ Node node = expression.evaluateAsNode(document);
+ assertNull(message, node);
+ }
+
+ protected SaajSoapMessage loadSaajMessage(String fileName) throws SOAPException, IOException {
+ MimeHeaders mimeHeaders = new MimeHeaders();
+ mimeHeaders.addHeader("Content-Type", "text/xml");
+ Resource resource = new ClassPathResource(fileName, getClass());
+ InputStream is = resource.getInputStream();
+ try {
+ assertTrue("Could not load SAAJ message [" + resource + "]", resource.exists());
+ is = resource.getInputStream();
+ return new SaajSoapMessage(messageFactory.createMessage(mimeHeaders, is));
+ }
+ finally {
+ is.close();
+ }
+ }
+
+ protected void onSetup() throws Exception {
+ }
+}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
index c9245b2f..ee204b7c 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptorTest.java
@@ -41,11 +41,13 @@ public class XwsSecurityInterceptorTest extends TestCase {
final SOAPMessage validatedRequest = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
- protected void secureMessage(SoapMessage soapMessage) throws XwsSecuritySecurementException {
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws XwsSecuritySecurementException {
fail("secure not expected");
}
- protected void validateMessage(SoapMessage message) throws WsSecurityValidationException {
+ protected void validateMessage(SoapMessage message, MessageContext messageContext)
+ throws WsSecurityValidationException {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
assertEquals("Invalid message", request, saajSoapMessage.getSaajMessage());
saajSoapMessage.setSaajMessage(validatedRequest);
@@ -62,12 +64,14 @@ public class XwsSecurityInterceptorTest extends TestCase {
final SOAPMessage securedResponse = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
- protected void secureMessage(SoapMessage message) throws XwsSecuritySecurementException {
+ protected void secureMessage(SoapMessage message, MessageContext messageContext)
+ throws XwsSecuritySecurementException {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
saajSoapMessage.setSaajMessage(securedResponse);
}
- protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidationException {
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
fail("validate not expected");
}
@@ -85,13 +89,15 @@ public class XwsSecurityInterceptorTest extends TestCase {
final SOAPMessage securedRequest = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
- protected void secureMessage(SoapMessage soapMessage) throws XwsSecuritySecurementException {
+ protected void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws XwsSecuritySecurementException {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
assertEquals("Invalid message", request, saajSoapMessage.getSaajMessage());
saajSoapMessage.setSaajMessage(securedRequest);
}
- protected void validateMessage(SoapMessage message) throws WsSecurityValidationException {
+ protected void validateMessage(SoapMessage message, MessageContext messageContext)
+ throws WsSecurityValidationException {
fail("validate not expected");
}
@@ -106,11 +112,13 @@ public class XwsSecurityInterceptorTest extends TestCase {
final SOAPMessage validatedResponse = messageFactory.createMessage();
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
- protected void secureMessage(SoapMessage message) throws XwsSecuritySecurementException {
+ protected void secureMessage(SoapMessage message, MessageContext messageContext)
+ throws XwsSecuritySecurementException {
fail("secure not expected");
}
- protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidationException {
+ protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
+ throws WsSecurityValidationException {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) soapMessage;
saajSoapMessage.setSaajMessage(validatedResponse);
}
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorEncryptTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorEncryptTest.java
index 1789977f..0f878b70 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorEncryptTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorEncryptTest.java
@@ -24,9 +24,9 @@ import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
-public class XwssMessageInterceptorEncryptTest extends XwssMessageInterceptorKeyStoreTestCase {
+public class XwssMessageInterceptorEncryptTest extends AbstractXwssMessageInterceptorKeyStoreTestCase {
public void testEncryptDefaultCertificate() throws Exception {
interceptor.setPolicyConfiguration(new ClassPathResource("encrypt-config.xml", getClass()));
@@ -53,7 +53,7 @@ public class XwssMessageInterceptorEncryptTest extends XwssMessageInterceptorKey
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathExists("BinarySecurityToken does not exist",
@@ -87,7 +87,7 @@ public class XwssMessageInterceptorEncryptTest extends XwssMessageInterceptorKey
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathExists("BinarySecurityToken does not exist",
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorSignTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorSignTest.java
index 90a00a95..bd5fc9a6 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorSignTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorSignTest.java
@@ -23,11 +23,12 @@ import javax.xml.soap.SOAPMessage;
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
+
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
-public class XwssMessageInterceptorSignTest extends XwssMessageInterceptorKeyStoreTestCase {
+public class XwssMessageInterceptorSignTest extends AbstractXwssMessageInterceptorKeyStoreTestCase {
public void testSignDefaultCertificate() throws Exception {
interceptor.setPolicyConfiguration(new ClassPathResource("sign-config.xml", getClass()));
@@ -54,7 +55,7 @@ public class XwssMessageInterceptorSignTest extends XwssMessageInterceptorKeySto
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathExists("BinarySecurityToken does not exist",
@@ -89,7 +90,7 @@ public class XwssMessageInterceptorSignTest extends XwssMessageInterceptorKeySto
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathExists("BinarySecurityToken does not exist",
@@ -120,7 +121,7 @@ public class XwssMessageInterceptorSignTest extends XwssMessageInterceptorKeySto
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("signed-soap.xml");
- interceptor.validateMessage(message);
+ interceptor.validateMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorUsernameTokenTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorUsernameTokenTest.java
index 66efda87..c8f35ea1 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorUsernameTokenTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/XwssMessageInterceptorUsernameTokenTest.java
@@ -24,11 +24,12 @@ import com.sun.xml.wss.impl.callback.PasswordCallback;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
import com.sun.xml.wss.impl.callback.UsernameCallback;
+
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
-import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
+import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
-public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageInterceptorTestCase {
+public class XwssMessageInterceptorUsernameTokenTest extends AbstractXwssMessageInterceptorTestCase {
public void testAddUsernameTokenDigest() throws Exception {
interceptor.setPolicyConfiguration(new ClassPathResource("usernameToken-digest-config.xml", getClass()));
@@ -50,7 +51,7 @@ public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageIntercep
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathEvaluatesTo("Invalid Username", "Bert",
@@ -80,7 +81,7 @@ public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageIntercep
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("empty-soap.xml");
- interceptor.secureMessage(message);
+ interceptor.secureMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathEvaluatesTo("Invalid Username", "Bert",
@@ -122,7 +123,7 @@ public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageIntercep
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("usernameTokenPlainText-soap.xml");
- interceptor.validateMessage(message);
+ interceptor.validateMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
@@ -161,7 +162,7 @@ public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageIntercep
interceptor.setCallbackHandler(handler);
interceptor.afterPropertiesSet();
SaajSoapMessage message = loadSaajMessage("usernameTokenDigest-soap.xml");
- interceptor.validateMessage(message);
+ interceptor.validateMessage(message, null);
SOAPMessage result = message.getSaajMessage();
assertNotNull("No result returned", result);
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java
index 13d40537..02a9c5a3 100644
--- a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java
+++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java
@@ -22,6 +22,8 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import junit.framework.TestCase;
+import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
+
public class CallbackHandlerChainTest extends TestCase {
private CallbackHandler supported = new CallbackHandler() {
diff --git a/security/src/test/resources/org/springframework/ws/soap/security/wss4j/echo-response.xml b/security/src/test/resources/org/springframework/ws/soap/security/wss4j/echo-response.xml
new file mode 100755
index 00000000..dcef295a
--- /dev/null
+++ b/security/src/test/resources/org/springframework/ws/soap/security/wss4j/echo-response.xml
@@ -0,0 +1,6 @@
+