Working on SWS-207
This commit is contained in:
@@ -569,9 +569,9 @@
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>wss4j</groupId>
|
||||
<groupId>org.apache.ws.security</groupId>
|
||||
<artifactId>wss4j</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<version>1.5.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
|
||||
5
pom.xml
5
pom.xml
@@ -15,7 +15,7 @@
|
||||
<url>http://static.springframework.org/spring-ws/site/index.html</url>
|
||||
<issueManagement>
|
||||
<system>JIRA</system>
|
||||
<url>http://opensource2.atlassian.com/projects/spring/browse/SWS/</url>
|
||||
<url>http://jira.springframework.org/browse/SWS</url>
|
||||
</issueManagement>
|
||||
<ciManagement>
|
||||
<system>bamboo</system>
|
||||
@@ -49,6 +49,9 @@
|
||||
<contributor>
|
||||
<name>Rick Evans</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Tareq Abed Rabbo</name>
|
||||
</contributor>
|
||||
</contributors>
|
||||
<organization>
|
||||
<name>The Spring Web Services Framework</name>
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
<name>Spring External Dependencies Repository</name>
|
||||
<url>https://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>wso2</id>
|
||||
<name>WSO2 Repository</name>
|
||||
<url>http://dist.wso2.org/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<profiles>
|
||||
<profile>
|
||||
@@ -103,7 +108,17 @@
|
||||
<artifactId>saaj-impl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- WS-Security dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.ws.commons.axiom</groupId>
|
||||
<artifactId>axiom-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ws.commons.axiom</groupId>
|
||||
<artifactId>axiom-impl</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- XWSS dependencies -->
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.wss</groupId>
|
||||
<artifactId>xws-security</artifactId>
|
||||
@@ -112,6 +127,12 @@
|
||||
<groupId>xml-security</groupId>
|
||||
<artifactId>xmlsec</artifactId>
|
||||
</dependency>
|
||||
<!-- WSS4J dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.ws.security</groupId>
|
||||
<artifactId>wss4j</artifactId>
|
||||
</dependency>
|
||||
<!-- Acegi depdencies -->
|
||||
<dependency>
|
||||
<groupId>org.acegisecurity</groupId>
|
||||
<artifactId>acegi-security</artifactId>
|
||||
|
||||
@@ -52,7 +52,7 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
|
||||
/** Logger available to subclasses. */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static final QName WS_SECURITY_NAME =
|
||||
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 boolean secureResponse = true;
|
||||
@@ -88,20 +88,20 @@ public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validates a server-side incoming request. Delegates to {@link #validateMessage(SoapMessage)} if the {@link
|
||||
* #setValidateRequest(boolean) validateRequest} property is <code>true</code>.
|
||||
* 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 <code>true</code>.
|
||||
*
|
||||
* @param messageContext the message context, containing the request to be validated
|
||||
* @param endpoint chosen endpoint to invoke
|
||||
* @return <code>true</code> if the request was valid; <code>false</code> 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 <code>true</code>.
|
||||
* 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 <code>true</code>.
|
||||
*
|
||||
* @param messageContext the message context, containing the response to be secured
|
||||
* @param endpoint chosen endpoint to invoke
|
||||
* @return <code>true</code> if the response was secured; <code>false</code> 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 <code>true</code>.
|
||||
* 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 <code>true</code>.
|
||||
*
|
||||
* @param messageContext the message context, containing the request to be secured
|
||||
* @return <code>true</code> if the response was secured; <code>false</code> 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 <code>true</code>.
|
||||
* 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 <code>true</code>.
|
||||
*
|
||||
* @param messageContext the message context, containing the response to be validated
|
||||
* @return <code>true</code> if the request was valid; <code>false</code> 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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 <code>CallbackHandler</code>s. For each callback, each of the handlers is called in term. If a
|
||||
* handler throws a <code>UnsupportedCallbackException</code>, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <code>java.security.KeyStore</code>.
|
||||
* Spring factory bean for a {@link KeyStore}.
|
||||
* <p/>
|
||||
* To load an existing key store, you must set the <code>location</code> 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 {
|
||||
|
||||
@@ -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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* The actions executed by the interceptor are configured via <code>validationActions</code> and
|
||||
* <code>securementActions</code> attributes. Actions are passed as a space separated string.
|
||||
* <p/>
|
||||
* Validation actions are: <ul> <li><strong>UsernameToken</strong>: validates username token</li>
|
||||
* <li><strong>Timestamp</strong>: validates the timestamp</li> <li><strong>Encrypt</strong>: decrypts the message</li>
|
||||
* <li><strong>Signature</strong>: validates the signature</li> <li><strong>NoSecurity</strong>: no action
|
||||
* performed</li> </ul> The order of the actions that the client performed to secure the messages is significant and is
|
||||
* enforced by the interceptor.
|
||||
* <p/>
|
||||
* Securement actions are: <ul> <li><strong>UsernameToken</strong>: adds a username token</li>
|
||||
* <li><strong>UsernameTokenSignature</strong>: adds a username token and a sinagture username token secrect key</li>
|
||||
* <li><strong>Timestamp</strong>: adds a timestamp</li> <li><strong>Encrypt</strong>: encrypts the response</li>
|
||||
* <li><strong>Signature</strong>: signs the response</li> <li><strong>NoSecurity</strong>: no action performed</li>
|
||||
* </ul>
|
||||
*
|
||||
* @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 <code>wsse:Security</code> header.
|
||||
* <p/>
|
||||
* If this parameter is omitted, the actor name is not set.
|
||||
* <p/>
|
||||
* 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
|
||||
* <code>IssuerSerial</code>. For possible encryption key identifier types refer to {@link
|
||||
* org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For encryption <code>IssuerSerial</code>,
|
||||
* <code>X509KeyIdentifier</code>, <code>DirectReference</code>, <code>Thumbprint</code>,
|
||||
* <code>SKIKeyIdentifier</code>, and <code>EmbeddedKeyName</code> 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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* The encryption mode specifier is either <code>{Content}</code> or <code>{Element}</code>. Please refer to the W3C
|
||||
* XML Encryption specification about the differences between Element and Content encryption. The encryption mode
|
||||
* defaults to <code>Content</code> if it is omitted. Example of a list:
|
||||
* <pre>
|
||||
* <parameter name="encryptionParts"
|
||||
* value="{Content}{http://example.org/paymentv2}CreditCard;
|
||||
* {Element}{}UserName" />
|
||||
* </pre>
|
||||
* The the first entry of the list identifies the element <code>CreditCard</code> in the namespace
|
||||
* <code>http://example.org/paymentv2</code>, and will encrypt its content. Be aware that the element name, the
|
||||
* namespace identifier, and the encryption modifier are case sensitive.
|
||||
* <p/>
|
||||
* The encryption modifier and the namespace identifier can be ommited. In this case the encryption mode defaults to
|
||||
* <code>Content</code> and the namespace is set to the SOAP namespace.
|
||||
* <p/>
|
||||
* An empty encryption mode defaults to <code>Content</code>, an empty namespace identifier defaults to the SOAP
|
||||
* namespace. The second line of the example defines <code>Element</code> as encryption mode for an
|
||||
* <code>UserName</code> element in the SOAP namespace.
|
||||
* <p/>
|
||||
* To specify an element without a namespace use the string <code>Null</code> as the namespace name (this is a case
|
||||
* sensitive string)
|
||||
* <p/>
|
||||
* If no list is specified, the handler encrypts the SOAP Body in <code>Content</code> 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.
|
||||
* <p/>
|
||||
* The encryption functions uses the public key of this user's certificate to encrypt the generated symmetric key.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* If <b>only</b> 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.
|
||||
* <p/>
|
||||
* Encryption only does not authenticate a user / sender, therefore it does not need a password.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* The application may set this parameter using the following method:
|
||||
* <pre>
|
||||
* call.setProperty(WSHandlerConstants.ENCYRPTION_USER, "encryptionuser");
|
||||
* </pre>
|
||||
* 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.
|
||||
* <p/>
|
||||
* The parameter can be set to either {@link WSConstants#PW_DIGEST} or to {@link WSConstants#PW_TEXT}.
|
||||
* <p/>
|
||||
* 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
|
||||
* <code>IssuerSerial</code>. For possible signature key identifier types refer to {@link
|
||||
* org.apache.ws.security.handler.WSHandlerConstants#keyIdentifier}. For signature <code>IssuerSerial</code> and
|
||||
* <code>DirectReference</code> 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.
|
||||
* <p/>
|
||||
* Refer to {@link #setSecurementEncryptionParts(String)} for a detailed description of the format of the value
|
||||
* string.
|
||||
* <p/>
|
||||
* If this parameter is not specified the handler signs the SOAP Body by default.
|
||||
* <p/>
|
||||
* The WS Security specifications define several formats to transfer the signature tokens (certificates) or
|
||||
* references to these tokens. Thus, the plain element name <code>Token</code> signs the token and takes care of the
|
||||
* different format.
|
||||
* <p/>
|
||||
* To sign the SOAP body <b>and</b> the signature token the value of this parameter must contain:
|
||||
* <pre>
|
||||
* <parameter name="signatureParts"
|
||||
* value="{}{http://schemas.xmlsoap.org/soap/envelope/}Body; Token" />
|
||||
* </pre>
|
||||
* To specify an element without a namespace use the string <code>Null</code> as the namespace name (this is a case
|
||||
* sensitive string)
|
||||
* <p/>
|
||||
* If there is no other element in the request with a local name of <code>Body</code> then the SOAP namespace
|
||||
* identifier can be empty (<code>{}</code>).
|
||||
*/
|
||||
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 <code>mustUnderstand</code> attribute on WS-Security headers on outgoing messages. Default is
|
||||
* <code>true</code>.
|
||||
*/
|
||||
public void setSecurementMustUnderstand(boolean securementMustUnderstand) {
|
||||
handler.setOption(WSHandlerConstants.MUST_UNDERSTAND, securementMustUnderstand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the additional elements in <code>UsernameToken</code>s.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* The list may containe the names <code>nonce</code> and <code>created</code> only. Use this option if the password
|
||||
* type is <code>passwordText</code> and the handler shall add the <code>Nonce</code> and/or <code>Created</code>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains classes for using the <a href="http://ws.apache.org/wss4j/">Apache WSS4J</a> WS-Security implementation within
|
||||
Spring-WS.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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}.
|
||||
* <p/>
|
||||
* Requires the {@link #setConfiguration(java.util.Properties) configuration} property to be set. This configuration
|
||||
* should have the <code>org.apache.ws.security.crypto.provider</code> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains support classes for working with WSS4J.
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 <code>SaajSoapMessage</code>
|
||||
*/
|
||||
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 <code>SaajSoapMessage</code>
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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 <code>UnsupportedCallbackException</code>,
|
||||
@@ -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 {
|
||||
* <code>handleDecryptionKeyCallback()</code>. 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 {
|
||||
* <code>handleDecryptionKeyCallback()</code>. 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 {
|
||||
* <code>handleEncryptionKeyCallback()</code>. 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 {
|
||||
* <code>handleEncryptionKeyCallback()</code>. 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 {
|
||||
* <code>handleSignatureKeyCallback()</code>. 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)
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
|
||||
@@ -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 <code>Properties</code> object. Password
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 <code>AuthenticationManager</code>. Logic based on
|
||||
* Acegi's <code>X509ProcessingFilter</code>.
|
||||
* <p/>
|
||||
* An Acegi <code>X509AuthenticationToken</code> is created with the certificate as the credentials.
|
||||
* <p/>
|
||||
* The configured authentication manager is expected to supply a provider which can handle this token (usually an
|
||||
* instance of <code>X509AuthenticationProvider</code>).</p>
|
||||
* Acegi's <code>X509ProcessingFilter</code>. <p/> An Acegi <code>X509AuthenticationToken</code> is created with the
|
||||
* certificate as the credentials. <p/> The configured authentication manager is expected to supply a provider which can
|
||||
* handle this token (usually an instance of <code>X509AuthenticationProvider</code>).</p>
|
||||
* <p/>
|
||||
* This class only handles <code>CertificateValidationCallback</code>s, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others.
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 <code>AuthenticationManager</code>. Logic based on
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jInterceptorTest extends Wss4jInterceptorTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest
|
||||
extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorEncryptionTest extends Wss4jMessageInterceptorEncryptionTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorHeaderTest extends Wss4jMessageInterceptorHeaderTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorTimestampTest extends Wss4jMessageInterceptorTimestampTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorUsernameTokenSignatureTest
|
||||
extends Wss4jMessageInterceptorUsernameTokenSignatureTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class AxiomWss4jMessageInterceptorUsernameTokenTest extends Wss4jMessageInterceptorUsernameTokenTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jInterceptorTest extends Wss4jInterceptorTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorAcegiCallbackHandlerTest
|
||||
extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorEncryptionTest extends Wss4jMessageInterceptorEncryptionTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorHeaderTest extends Wss4jMessageInterceptorHeaderTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorTimestampTest extends Wss4jMessageInterceptorTimestampTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorUsernameTokenSignatureTest
|
||||
extends Wss4jMessageInterceptorUsernameTokenSignatureTestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ws.soap.security.wss4j;
|
||||
|
||||
public class SaajWss4jMessageInterceptorUsernameTokenTest extends Wss4jMessageInterceptorUsernameTokenTestCase {
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header/>
|
||||
<SOAP-ENV:Body>
|
||||
<echoResponse xmlns="http://www.springframework.org/spring-ws/samples/echo">Hello</echoResponse>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,5 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<xenc:EncryptedKey Id="EncKeyId-25488452" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
|
||||
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<wsse:SecurityTokenReference
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509IssuerSerial xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509IssuerName
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">CN=client,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown
|
||||
</ds:X509IssuerName>
|
||||
<ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">1196367787
|
||||
</ds:X509SerialNumber>
|
||||
</ds:X509IssuerSerial>
|
||||
</ds:X509Data>
|
||||
</wsse:SecurityTokenReference>
|
||||
</ds:KeyInfo>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>CtuWCgHBFl1zHdRU70e4aPoRwpMH8GMzWKmywSxdrMz4dS+hOexUu8sY2SUl0jdO+IfL/oNYj4eic7G6utkddKeOCHM8Rj7gtr2Elol2q1ZHXc5N1DUk99t7dYs+XmMU2ULqyGh0T0rmhbABmbxZDB4NzwCBFvXxEUUFFBbN460=</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
<xenc:ReferenceList>
|
||||
<xenc:DataReference URI="#EncDataId-22297736"/>
|
||||
</xenc:ReferenceList>
|
||||
</xenc:EncryptedKey>
|
||||
</wsse:Security>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body>
|
||||
<xenc:EncryptedData Id="EncDataId-22297736" Type="http://www.w3.org/2001/04/xmlenc#Content"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/>
|
||||
<xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
<xenc:CipherValue
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">pqzkZJz0/RdUsf8y1z04PvWv48lYtrfmskeW8304PtXD2lXS2iGva5SlL7vc96aA6915zzyld4O0
|
||||
c+0ISwgvHnharmwatlih/kGfHNY0/LBDidzsbbjrsMWFiq3MLy3x9Sj79pHnChd2BSw9D49kgtUN
|
||||
W8uIAUxLlsPqdSst9hEq/RYUk0M6mx8HBy2DnE3t36ifrB/1QnY4ohQoMrBARu4HIWidCxhajFSX
|
||||
i1ZNX8ff4RrD3JUoAlDX6J5zj3+YfxtaaZhoMXRshxAhJwoLidKGlFKN6gsZXqorDJ8ETZ6SdCJ3
|
||||
O2f4c+WW1xQLUdvt
|
||||
</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</xenc:EncryptedData>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-30541453">
|
||||
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
|
||||
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
|
||||
<ds:Reference URI="#id-7237831" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:Transforms xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
|
||||
</ds:Transforms>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
|
||||
<ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">I6X9SMGsJ5sOBL5NUM4H3KMTpc8=
|
||||
</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
</ds:SignedInfo>
|
||||
<ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
DX4XwuzPSkwTXIPKTWKXXf05PWssJjkFUVxg8Joj4OmB6yqDzzTJ1GHRI/3PFZLjoUQq7dL4JqAL
|
||||
BjcD2lb9ka0c/EILe59QtD1USz1jM8i/zAtNDv3J38yIIHeAp+v/zdT8cNAvG+unwpJvAYMaS34j
|
||||
mziXbS3/NLMFkroYR3Q=
|
||||
</ds:SignatureValue>
|
||||
<ds:KeyInfo Id="KeyId-23710309" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<wsse:SecurityTokenReference
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="STRId-970341"
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509IssuerSerial xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:X509IssuerName
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">CN=client,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown
|
||||
</ds:X509IssuerName>
|
||||
<ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">1196367787
|
||||
</ds:X509SerialNumber>
|
||||
</ds:X509IssuerSerial>
|
||||
</ds:X509Data>
|
||||
</wsse:SecurityTokenReference>
|
||||
</ds:KeyInfo>
|
||||
</ds:Signature>
|
||||
</wsse:Security>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="id-7237831">
|
||||
<tns:echoRequest xmlns:tns="http://www.springframework.org/spring-ws/samples/echo">Hello</tns:echoRequest>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,6 @@
|
||||
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
|
||||
org.apache.ws.security.crypto.merlin.keystore.type=pkcs12
|
||||
org.apache.ws.security.crypto.merlin.keystore.password=security
|
||||
org.apache.ws.security.crypto.merlin.keystore.alias=16c73ab6-b892-458f-abf5-2f875f74882e
|
||||
org.apache.ws.security.crypto.merlin.alias.password=security
|
||||
org.apache.ws.security.crypto.merlin.file=org/springframework/ws/soap/security/wss4j/support/x509.PFX.MSFT
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<wsse:UsernameToken
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="XWSSGID-1149205720423-1352053129"
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<wsse:Username>Bert</wsse:Username>
|
||||
<wsse:Password
|
||||
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">kwNstEaiFOrI7B31j7GuETYvdgk=
|
||||
</wsse:Password>
|
||||
<wsse:Nonce
|
||||
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">9mdsYDCrjjYRur0rxzYt2oD7
|
||||
</wsse:Nonce>
|
||||
<wsu:Created
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2006-06-01T23:48:42Z
|
||||
</wsu:Created>
|
||||
</wsse:UsernameToken>
|
||||
</wsse:Security>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,20 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<wsse:UsernameToken
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="XWSSGID-1149200055993710197275"
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<wsse:Username>Bert</wsse:Username>
|
||||
<wsse:Password
|
||||
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
||||
>Ernie
|
||||
</wsse:Password>
|
||||
</wsse:UsernameToken>
|
||||
</wsse:Security>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,22 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<header1>1</header1>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<wsse:UsernameToken
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="XWSSGID-1149200055993710197275"
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<wsse:Username>Bert</wsse:Username>
|
||||
<wsse:Password
|
||||
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
||||
>Ernie
|
||||
</wsse:Password>
|
||||
</wsse:UsernameToken>
|
||||
</wsse:Security>
|
||||
<header2>2</header2>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
BIN
security/src/test/resources/private.jks
Executable file
BIN
security/src/test/resources/private.jks
Executable file
Binary file not shown.
Reference in New Issue
Block a user