Done implementing SWS-207

This commit is contained in:
Arjen Poutsma
2008-02-10 02:48:24 +00:00
parent fca4cd3699
commit dc6b14905c
25 changed files with 969 additions and 434 deletions

View File

@@ -0,0 +1,59 @@
/*
* 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.callback;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
/**
* 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.5.0
*/
public class CallbackHandlerChain extends AbstractCallbackHandler {
private final CallbackHandler[] callbackHandlers;
public CallbackHandlerChain(CallbackHandler[] callbackHandlers) {
this.callbackHandlers = callbackHandlers;
}
public CallbackHandler[] getCallbackHandlers() {
return callbackHandlers;
}
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
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);
}
}
}

View File

@@ -87,7 +87,7 @@ public class KeyStoreFactoryBean implements FactoryBean, InitializingBean {
this.type = type;
}
public Object getObject() throws Exception {
public Object getObject() {
return keyStore;
}

View File

@@ -0,0 +1,123 @@
/*
* 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.support;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
/**
* Generic utility methods for dealing with {@link KeyStore} objects.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
public abstract class KeyStoreUtils {
/**
* Loads the key store indicated by system properties. This method tries to load a key store by consulting the
* following system properties:<code>javax.net.ssl.keyStore</code>, <code>javax.net.ssl.keyStorePassword</code>, and
* <code>javax.net.ssl.keyStoreType</code>.
* <p/>
* If these properties specify a file with an appropriate password, the factory uses this file for the key store. If
* that file does not exist, then a default, empty keystore is created.
* <p/>
* This behavior corresponds to the standard J2SDK behavior for SSL key stores.
*
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509KeyManager">The
* standard J2SDK SSL key store mechanism</a>
*/
public static KeyStore loadDefaultKeyStore() throws GeneralSecurityException, IOException {
Resource location = null;
String type = null;
String password = null;
String locationProperty = System.getProperty("javax.net.ssl.keyStore");
if (StringUtils.hasLength(locationProperty)) {
File f = new File(locationProperty);
if (f.exists() && f.isFile() && f.canRead()) {
location = new FileSystemResource(f);
}
String passwordProperty = System.getProperty("javax.net.ssl.keyStorePassword");
if (StringUtils.hasLength(passwordProperty)) {
password = passwordProperty;
}
type = System.getProperty("javax.net.ssl.trustStore");
}
// use the factory bean here, easier to setup
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
factoryBean.setLocation(location);
factoryBean.setPassword(password);
factoryBean.setType(type);
factoryBean.afterPropertiesSet();
return (KeyStore) factoryBean.getObject();
}
/**
* Loads a default trust store. This method uses the following algorithm: <ol> <li> If the system property
* <code>javax.net.ssl.trustStore</code> is defined, its value is loaded. If the
* <code>javax.net.ssl.trustStorePassword</code> system property is also defined, its value is used as a password.
* If the <code>javax.net.ssl.trustStoreType</code> system property is defined, its value is used as a key store
* type.
* <p/>
* If <code>javax.net.ssl.trustStore</code> is defined but the specified file does not exist, then a default, empty
* trust store is created. </li> <li> If the <code>javax.net.ssl.trustStore</code> system property was not
* specified, but if the file <code>$JAVA_HOME/lib/security/jssecacerts</code> exists, that file is used. </li>
* Otherwise, <li>If the file <code>$JAVA_HOME/lib/security/cacerts</code> exists, that file is used. </ol>
* <p/>
* This behavior corresponds to the standard J2SDK behavior for SSL trust stores.
*
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
* standard J2SDK SSL trust store mechanism</a>
*/
public static KeyStore loadDefaultTrustStore() throws GeneralSecurityException, IOException {
Resource location = null;
String type = null;
String password = null;
String locationProperty = System.getProperty("javax.net.ssl.trustStore");
if (StringUtils.hasLength(locationProperty)) {
File f = new File(locationProperty);
if (f.exists() && f.isFile() && f.canRead()) {
location = new FileSystemResource(f);
}
String passwordProperty = System.getProperty("javax.net.ssl.trustStorePassword");
if (StringUtils.hasLength(passwordProperty)) {
password = passwordProperty;
}
type = System.getProperty("javax.net.ssl.trustStoreType");
}
else {
String javaHome = System.getProperty("java.home");
location = new FileSystemResource(javaHome + "/lib/security/jssecacerts");
if (!location.exists()) {
location = new FileSystemResource(javaHome + "/lib/security/cacerts");
}
}
// use the factory bean here, easier to setup
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
factoryBean.setLocation(location);
factoryBean.setPassword(password);
factoryBean.setType(type);
factoryBean.afterPropertiesSet();
return (KeyStore) factoryBean.getObject();
}
}

View File

@@ -46,39 +46,31 @@ 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;
import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
/**
* A WS-Security endpoint interceptor based on Apache's WSS4J. This inteceptor supports messages created by the {@link
* org.springframework.ws.soap.axiom.AxiomSoapMessageFactory} and the {@link org.springframework.ws.soap.saaj.SaajSoapMessageFactory}.
* <p/>
* The validation and securement actions executed by this interceptor are configured via <code>validationActions</code> and
* <code>securementActions</code> properties, respectively. Actions should be passed as a space-separated strings.
* The validation and securement actions executed by this interceptor are configured via <code>validationActions</code>
* and <code>securementActions</code> properties, respectively. Actions should be passed as a space-separated strings.
* <p/>
* Valid <strong>validation</strong> actions are:
*
* <blockquote><table>
* <tr><th>Validation action</th><th>Description</th></tr>
* <tr><td><code>UsernameToken</code></td><td>Validates username token</td></tr>
* <tr><td><code>Timestamp</code></td><td>Validates the timestamp</td></tr>
* <tr><td><code>Encrypt</code></td><td>Decrypts the message</td></tr>
* <tr><td><code>Signature</code></td><td>Validates the signature</td></tr>
* <tr><td><code>NoSecurity</code></td><td>No action performed</td></tr>
* </table></blockquote>
* <p/>
* <strong>Securement</strong> actions are:
* <blockquote><table>
* <tr><th>Securement action</th><th>Description</th></tr>
* <tr><td><code>UsernameToken</td></code><td>Adds a username token</td></tr>
* <tr><td><code>UsernameTokenSignature</td></code><td>Adds a username token and a signature username token secrect key</td></tr>
* <tr><td><code>Timestamp</td></code><td>Adds a timestamp</td></tr>
* <tr><td><code>Encrypt</td></code><td>Encrypts the response</td></tr>
* <tr><td><code>Signature</td></code><td>Signs the response</td></tr>
* <tr><td><code>NoSecurity</td></code><td>No action performed</td></tr>
* </table></blockquote>
* <blockquote><table> <tr><th>Validation action</th><th>Description</th></tr> <tr><td><code>UsernameToken</code></td><td>Validates
* username token</td></tr> <tr><td><code>Timestamp</code></td><td>Validates the timestamp</td></tr>
* <tr><td><code>Encrypt</code></td><td>Decrypts the message</td></tr> <tr><td><code>Signature</code></td><td>Validates
* the signature</td></tr> <tr><td><code>NoSecurity</code></td><td>No action performed</td></tr> </table></blockquote>
* <p/>
* The order of the actions that the client performed to secure the messages is significant and is
* enforced by the interceptor.
*
* <strong>Securement</strong> actions are: <blockquote><table> <tr><th>Securement action</th><th>Description</th></tr>
* <tr><td><code>UsernameToken</td></code><td>Adds a username token</td></tr> <tr><td><code>UsernameTokenSignature</td></code><td>Adds
* a username token and a signature username token secrect key</td></tr> <tr><td><code>Timestamp</td></code><td>Adds a
* timestamp</td></tr> <tr><td><code>Encrypt</td></code><td>Encrypts the response</td></tr>
* <tr><td><code>Signature</td></code><td>Signs the response</td></tr> <tr><td><code>NoSecurity</td></code><td>No action
* performed</td></tr> </table></blockquote>
* <p/>
* The order of the actions that the client performed to secure the messages is significant and is enforced by the
* interceptor.
*
* @author Tareq Abed Rabbo
* @author Arjen Poutsma
@@ -89,8 +81,6 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
public static final String SECUREMENT_USER_PROPERTY_NAME = "Wss4jSecurityInterceptor.securementUser";
private CallbackHandler validationCallbackHandler;
private int securementAction;
private String securementActions;
@@ -99,9 +89,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
private String securementUsername;
private boolean timestampStrict = true;
private int timeToLive = 300;
private CallbackHandler validationCallbackHandler;
private int validationAction;
@@ -115,10 +103,14 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
private Crypto validationSignatureCrypto;
private Wss4jHandler handler = new Wss4jHandler();
private boolean timestampStrict = true;
private boolean enableSignatureConfirmation;
private int timeToLive = 300;
private Wss4jHandler handler = new Wss4jHandler();
public void setSecurementActions(String securementActions) {
this.securementActions = securementActions;
securementActionsVector = new Vector();
@@ -141,10 +133,24 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
handler.setOption(WSHandlerConstants.ACTOR, securementActor);
}
/**
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handler to use when securing messages.
*
* @see #setSecurementCallbackHandlers(CallbackHandler[])
*/
public void setSecurementCallbackHandler(CallbackHandler securementCallbackHandler) {
handler.setSecurementCallbackHandler(securementCallbackHandler);
}
/**
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handlers to use when securing messages.
*
* @see #setSecurementCallbackHandler(CallbackHandler)
*/
public void setSecurementCallbackHandlers(CallbackHandler[] securementCallbackHandler) {
handler.setSecurementCallbackHandler(new CallbackHandlerChain(securementCallbackHandler));
}
public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
}
@@ -333,10 +339,24 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
this.validationActor = validationActor;
}
/**
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handler to use when validating messages.
*
* @see #setValidationCallbackHandlers(CallbackHandler[])
*/
public void setValidationCallbackHandler(CallbackHandler callbackHandler) {
this.validationCallbackHandler = callbackHandler;
}
/**
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handlers to use when validating messages.
*
* @see #setValidationCallbackHandler(CallbackHandler)
*/
public void setValidationCallbackHandlers(CallbackHandler[] callbackHandler) {
this.validationCallbackHandler = new CallbackHandlerChain(callbackHandler);
}
/** Sets the Crypto to use to decrypt incoming messages */
public void setValidationDecryptionCrypto(Crypto decryptionCrypto) {
this.validationDecryptionCrypto = decryptionCrypto;
@@ -410,6 +430,9 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
if (securementAction == WSConstants.NO_SECURITY && !enableSignatureConfirmation) {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Securing message [" + soapMessage + "] with actions [" + securementActions + "]");
}
RequestData requestData = initializeRequestData(messageContext);
Document envelopeAsDocument = toDocument(soapMessage);
@@ -450,7 +473,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
throws WsSecurityValidationException {
if (logger.isDebugEnabled()) {
logger.debug("Validating message [" + soapMessage + "] with actions " + validationActions);
logger.debug("Validating message [" + soapMessage + "] with actions [" + validationActions + "]");
}
if (validationAction == WSConstants.NO_SECURITY) {
@@ -508,11 +531,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
}
/**
* Verifies the trust of a certificate.
* @param results
* @throws WSSecurityException
*/
/** Verifies the trust of a certificate. */
protected void verifyCertificateTrust(Vector results) throws WSSecurityException {
RequestData requestData = new RequestData();
requestData.setSigCrypto(validationSignatureCrypto);

View File

@@ -0,0 +1,176 @@
/*
* 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.callback;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
/**
* Abstract base class for {@link javax.security.auth.callback.CallbackHandler} implementations that handle {@link
* WSPasswordCallback} callbacks.
*
* @author Arjen Poutsma
* @since 1.5.0
*/
public abstract class AbstractWsPasswordCallbackHandler extends AbstractCallbackHandler {
/**
* Handles {@link WSPasswordCallback} callbacks. Inspects the callback {@link WSPasswordCallback#getUsage() usage}
* code, and calls the various <code>handle*</code> template methods.
*
* @param callback the callback
* @throws IOException in case of I/O errors
* @throws UnsupportedCallbackException when the callback is not supported
*/
protected final void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
if (callback instanceof WSPasswordCallback) {
WSPasswordCallback passwordCallback = (WSPasswordCallback) callback;
switch (passwordCallback.getUsage()) {
case WSPasswordCallback.DECRYPT:
handleDecrypt(passwordCallback);
break;
case WSPasswordCallback.USERNAME_TOKEN:
handleUsernameToken(passwordCallback);
break;
case WSPasswordCallback.SIGNATURE:
handleSignature(passwordCallback);
break;
case WSPasswordCallback.KEY_NAME:
handleKeyName(passwordCallback);
break;
case WSPasswordCallback.USERNAME_TOKEN_UNKNOWN:
handleUsernameTokenUnknown(passwordCallback);
break;
case WSPasswordCallback.SECURITY_CONTEXT_TOKEN:
handleSecurityContextToken(passwordCallback);
break;
case WSPasswordCallback.CUSTOM_TOKEN:
handleCustomToken(passwordCallback);
break;
case WSPasswordCallback.ENCRYPTED_KEY_TOKEN:
handleEncryptedKeyToken(callback);
break;
default:
throw new UnsupportedCallbackException(callback,
"Unknown usage [" + passwordCallback.getUsage() + "]");
}
}
else {
throw new UnsupportedCallbackException(callback);
}
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#DECRYPT} usage.
* <p/>
* This method is invoked when WSS4J needs a password to get the private key of the {@link
* WSPasswordCallback#getIdentifer() identifier} (username) from the keystore. WSS4J uses this private key to
* decrypt the session (symmetric) key. Because the encryption method uses the public key to encrypt the session key
* it needs no password (a public key is usually not protected by a password).
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleDecrypt(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#USERNAME_TOKEN} usage.
* <p/>
* This method is invoked when WSS4J needs the password to fill in or to verify a UsernameToken.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#SIGNATURE} usage.
* <p/>
* This method is invoked when WSS4J needs the password to get the private key of the {@link
* WSPasswordCallback#getIdentifer() identifier} (username) from the keystore. WSS4J uses this private key to
* produce a signature. The signature verfication uses the public key to verfiy the signature.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleSignature(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#KEY_NAME} usage.
* <p/>
* This method is invoked when WSS4J needs the key associated with the {@link WSPasswordCallback#getIdentifer()
* identifier}. WSS4J uses this key to encrypt or decrypt parts of the SOAP request. Note, the key must match the
* symmetric encryption/decryption algorithm specified (refer to {@link org.apache.ws.security.handler.WSHandlerConstants#ENC_SYM_ALGO}).
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleKeyName(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#USERNAME_TOKEN_UNKNOWN} usage.
* <p/>
* This method is invoked for a not specified password type or a plain text password type. Only the {@link
* WSPasswordCallback#getPassword() password} is set. The callback class now may check if the username and password
* match. If they don't match, the subclass should throw an exception.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#SECURITY_CONTEXT_TOKEN} usage.
* <p/>
* This method is invoked when WSS4J needs the key to to be associated with a SecurityContextToken.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleSecurityContextToken(WSPasswordCallback callback)
throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#CUSTOM_TOKEN} usage.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleCustomToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
/**
* Invoked when the callback has a {@link WSPasswordCallback#ENCRYPTED_KEY_TOKEN} usage.
* <p/>
* Default implementation throws an {@link UnsupportedCallbackException}.
*/
protected void handleEncryptedKeyToken(Callback callback) throws IOException, UnsupportedCallbackException {
throw new UnsupportedCallbackException(callback);
}
}

View File

@@ -1,182 +0,0 @@
/*
* 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;
}

View File

@@ -0,0 +1,120 @@
/*
* 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.callback;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.crypto.SecretKey;
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.ws.soap.security.support.KeyStoreUtils;
/**
* Callback handler that uses Java Security <code>KeyStore</code>s to handle cryptographic callbacks. Allows for
* specific key stores to be set for various cryptographic operations.
*
* @author Tareq Abed Rabbo
* @author Arjen Poutsma
* @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
* @since 1.5.0
*/
public class KeyStoreCallbackHandler extends AbstractWsPasswordCallbackHandler implements InitializingBean {
private String privateKeyPassword;
private char[] symmetricKeyPassword;
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 the password used to retrieve private keys from the keystore. This property is required for decription based
* on private keys, and signing.
*/
public void setPrivateKeyPassword(String privateKeyPassword) {
if (privateKeyPassword != null) {
this.privateKeyPassword = privateKeyPassword;
}
}
/**
* Sets the password used to retrieve keys from the symmetric keystore. If this property is not set, it default to
* the private key password.
*
* @see #setPrivateKeyPassword(String)
*/
public void setSymmetricKeyPassword(String symmetricKeyPassword) {
if (symmetricKeyPassword != null) {
this.symmetricKeyPassword = symmetricKeyPassword.toCharArray();
}
}
public void afterPropertiesSet() throws Exception {
if (keyStore == null) {
loadDefaultKeyStore();
}
if (symmetricKeyPassword == null) {
symmetricKeyPassword = privateKeyPassword.toCharArray();
}
}
protected void handleDecrypt(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
callback.setPassword(privateKeyPassword);
}
protected void handleKeyName(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
try {
String identifier = callback.getIdentifer();
KeyStore.PasswordProtection protection = new KeyStore.PasswordProtection(symmetricKeyPassword);
KeyStore.Entry entry = keyStore.getEntry(identifier, protection);
if (entry instanceof KeyStore.SecretKeyEntry) {
KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) entry;
SecretKey secretKey = secretKeyEntry.getSecretKey();
callback.setKey(secretKey.getEncoded());
}
else {
throw new WSSecurityException("Key entry [" + entry + "] is not a javax.crypto.SecretKey");
}
}
catch (GeneralSecurityException ex) {
throw new WSSecurityException("Could not obtain symmetric key", ex);
}
}
/** Loads the key store indicated by system properties. Delegates to {@link KeyStoreUtils#loadDefaultKeyStore()}. */
protected void loadDefaultKeyStore() {
try {
keyStore = KeyStoreUtils.loadDefaultKeyStore();
if (logger.isDebugEnabled()) {
logger.debug("Loaded default key store");
}
}
catch (Exception ex) {
logger.warn("Could not open default key store", ex);
}
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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())));
}
}

View File

@@ -1,31 +1,25 @@
/*
* 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);
}
}
/*
* 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.callback;
/**
* @author Arjen Poutsma
* @since 1.5.0
*/
public class SimplePasswordCallbackHandler {
}

View File

@@ -0,0 +1,77 @@
/*
* 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.callback;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
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;
/**
* Simple callback handler that validates passwords agains a in-memory <code>Properties</code> object. Password
* validation is done on a case-sensitive basis.
*
* @author Tareq Abed Rabbo
* @author Arjen Poutsma
* @see #setUsers(java.util.Properties)
* @since 1.5.0
*/
public class SimplePasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler
implements InitializingBean {
private Properties users = new Properties();
/** Sets the users to validate against. Property names are usernames, property values are passwords. */
public void setUsers(Properties users) {
this.users = users;
}
public void setUsersMap(Map users) {
for (Iterator iterator = users.keySet().iterator(); iterator.hasNext();) {
String username = (String) iterator.next();
String password = (String) users.get(username);
this.users.setProperty(username, password);
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(users, "users is required");
}
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
String identifier = callback.getIdentifer();
callback.setPassword(users.getProperty(identifier));
}
protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
throws IOException, UnsupportedCallbackException {
String identifier = callback.getIdentifer();
String storedPassword = users.getProperty(identifier);
String givenPassword = callback.getPassword();
if (storedPassword == null || !storedPassword.equals(givenPassword)) {
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.callback.acegi;
import java.io.IOException;
import javax.security.auth.callback.UnsupportedCallbackException;
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.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
/**
* Callback handler that validates a password digest using an Acegi <code>UserDetailsService</code>. Logic based on
* Acegi's <code>DigestProcessingFilter</code>.
* <p/>
* An Acegi <code>UserDetailService</code> is used to load <code>UserDetails</code> from. The digest of the password
* contained in this details object is then compared with the digest in the message.
*
* @author Arjen Poutsma
* @see org.acegisecurity.userdetails.UserDetailsService
* @see org.acegisecurity.ui.digestauth.DigestProcessingFilter
* @since 1.0.0
*/
public class AcegiDigestPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler {
private UserCache userCache = new NullUserCache();
private UserDetailsService userDetailsService;
/** Sets the users cache. Not required, but can benefit performance. */
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
/** Sets the Acegi user details service. Required. */
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(userDetailsService, "userDetailsService is required");
}
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
String identifier = callback.getIdentifer();
UserDetails user = loadUserDetails(identifier);
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;
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.callback.acegi;
import java.io.IOException;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSecurityException;
import org.springframework.util.Assert;
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
/**
* Callback handler that validates a certificate uses an Acegi <code>AuthenticationManager</code>. Logic based on
* Acegi's <code>BasicProcessingFilter</code>.
* <p/>
* This handler requires an Acegi <code>AuthenticationManager</code> to operate. It can be set using the
* <code>authenticationManager</code> property. An Acegi <code>UsernamePasswordAuthenticationToken</code> is created
* with the username as principal and password as credentials.
*
* @author Arjen Poutsma
* @see org.acegisecurity.providers.UsernamePasswordAuthenticationToken
* @see org.acegisecurity.ui.basicauth.BasicProcessingFilter
* @since 1.5.0
*/
public class AcegiPlainTextPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler {
private AuthenticationManager authenticationManager;
private boolean ignoreFailure = false;
/** Sets the Acegi authentication manager. Required. */
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setIgnoreFailure(boolean ignoreFailure) {
this.ignoreFailure = ignoreFailure;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(authenticationManager, "authenticationManager is required");
}
protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
throws IOException, UnsupportedCallbackException {
String identifier = callback.getIdentifer();
try {
Authentication authResult = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(identifier, 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 '" + identifier + "' failed: " + failed.toString());
}
SecurityContextHolder.getContext().setAuthentication(null);
if (!ignoreFailure) {
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
}
}
}
}

View File

@@ -0,0 +1,6 @@
<html>
<body>
Contains <code>CallbackHandler</code> implementations for WSS4J that use the <a href="http://www.acegisecurity.org/">Acegi
Security System for Spring</a>.
</body>
</html>

View File

@@ -0,0 +1,5 @@
<html>
<body>
Contains <code>CallbackHandler</code> implementations for WSS4J.
</body>
</html>

View File

@@ -34,7 +34,7 @@ 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.xwss.callback.XwssCallbackHandlerChain;
/**
* WS-Security endpoint interceptor that is based on Sun's XML and Web Services Security package (XWSS). This
@@ -86,7 +86,7 @@ public class XwsSecurityInterceptor extends AbstractWsSecurityInterceptor implem
* @see #setCallbackHandler(javax.security.auth.callback.CallbackHandler)
*/
public void setCallbackHandlers(CallbackHandler[] callbackHandler) {
this.callbackHandler = new CallbackHandlerChain(callbackHandler);
this.callbackHandler = new XwssCallbackHandlerChain(callbackHandler);
}
/** Sets the policy configuration to use for XWSS. Required. */

View File

@@ -16,7 +16,6 @@
package org.springframework.ws.soap.security.xwss.callback;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -42,11 +41,9 @@ 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.apache.xml.security.utils.RFC2253Parser;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.springframework.ws.soap.security.support.KeyStoreFactoryBean;
import org.springframework.ws.soap.security.support.KeyStoreUtils;
/**
* Callback handler that uses Java Security <code>KeyStore</code>s to handle cryptographic callbacks. Allows for
@@ -562,43 +559,10 @@ public class KeyStoreCallbackHandler extends CryptographyCallbackHandler impleme
}
}
/**
* Loads the key store indicated by system properties. This method tries to load a key store by consulting the
* following system properties:<code>javax.net.ssl.keyStore</code>, <code>javax.net.ssl.keyStorePassword</code>, and
* <code>javax.net.ssl.keyStoreType</code>.
* <p/>
* If these properties specify a file with an appropriate password, the factory uses this file for the key store. If
* that file does not exist, then a default, empty keystore is created.
* <p/>
* This behavior corresponds to the standard J2SDK behavior for SSL key stores.
*
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509KeyManager">The
* standard J2SDK SSL key store mechanism</a>
*/
/** Loads the key store indicated by system properties. Delegates to {@link KeyStoreUtils#loadDefaultKeyStore()}. */
protected void loadDefaultKeyStore() {
Resource location = null;
String type = null;
String password = null;
String locationProperty = System.getProperty("javax.net.ssl.keyStore");
if (StringUtils.hasLength(locationProperty)) {
File f = new File(locationProperty);
if (f.exists() && f.isFile() && f.canRead()) {
location = new FileSystemResource(f);
}
String passwordProperty = System.getProperty("javax.net.ssl.keyStorePassword");
if (StringUtils.hasLength(passwordProperty)) {
password = passwordProperty;
}
type = System.getProperty("javax.net.ssl.trustStore");
}
// use the factory bean here, easier to setup
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
factoryBean.setLocation(location);
factoryBean.setPassword(password);
factoryBean.setType(type);
try {
factoryBean.afterPropertiesSet();
keyStore = (KeyStore) factoryBean.getObject();
keyStore = KeyStoreUtils.loadDefaultKeyStore();
if (logger.isDebugEnabled()) {
logger.debug("Loaded default key store");
}
@@ -608,54 +572,10 @@ public class KeyStoreCallbackHandler extends CryptographyCallbackHandler impleme
}
}
/**
* Loads a default trust store. This method uses the following algorithm: <ol> <li> If the system property
* <code>javax.net.ssl.trustStore</code> is defined, its value is loaded. If the
* <code>javax.net.ssl.trustStorePassword</code> system property is also defined, its value is used as a password.
* If the <code>javax.net.ssl.trustStoreType</code> system property is defined, its value is used as a key store
* type.
* <p/>
* If <code>javax.net.ssl.trustStore</code> is defined but the specified file does not exist, then a default, empty
* trust store is created. </li> <li> If the <code>javax.net.ssl.trustStore</code> system property was not
* specified, but if the file <code>$JAVA_HOME/lib/security/jssecacerts</code> exists, that file is used. </li>
* Otherwise, <li>If the file <code>$JAVA_HOME/lib/security/cacerts</code> exists, that file is used. </ol>
* <p/>
* This behavior corresponds to the standard J2SDK behavior for SSL trust stores.
*
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
* standard J2SDK SSL trust store mechanism</a>
*/
/** Loads a default trust store. Delegates to {@link KeyStoreUtils#loadDefaultTrustStore()}. */
protected void loadDefaultTrustStore() {
Resource location = null;
String type = null;
String password = null;
String locationProperty = System.getProperty("javax.net.ssl.trustStore");
if (StringUtils.hasLength(locationProperty)) {
File f = new File(locationProperty);
if (f.exists() && f.isFile() && f.canRead()) {
location = new FileSystemResource(f);
}
String passwordProperty = System.getProperty("javax.net.ssl.trustStorePassword");
if (StringUtils.hasLength(passwordProperty)) {
password = passwordProperty;
}
type = System.getProperty("javax.net.ssl.trustStoreType");
}
else {
String javaHome = System.getProperty("java.home");
location = new FileSystemResource(javaHome + "/lib/security/jssecacerts");
if (!location.exists()) {
location = new FileSystemResource(javaHome + "/lib/security/cacerts");
}
}
// use the factory bean here, easier to setup
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
factoryBean.setLocation(location);
factoryBean.setPassword(password);
factoryBean.setType(type);
try {
factoryBean.afterPropertiesSet();
trustStore = (KeyStore) factoryBean.getObject();
trustStore = KeyStoreUtils.loadDefaultTrustStore();
if (logger.isDebugEnabled()) {
logger.debug("Loaded default trust store");
}

View File

@@ -26,7 +26,7 @@ import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
/**
* Represents a chain of <code>CallbackHandler</code>s. For each callback, each of the handlers is called in term. If a
@@ -35,16 +35,10 @@ import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
* @author Arjen Poutsma
* @since 1.0.0
*/
public class CallbackHandlerChain extends AbstractCallbackHandler {
public class XwssCallbackHandlerChain extends CallbackHandlerChain {
private CallbackHandler[] callbackHandlers;
public CallbackHandlerChain(CallbackHandler[] callbackHandlers) {
this.callbackHandlers = callbackHandlers;
}
public void setCallbackHandlers(CallbackHandler[] callbackHandlers) {
this.callbackHandlers = callbackHandlers;
public XwssCallbackHandlerChain(CallbackHandler[] callbackHandlers) {
super(callbackHandlers);
}
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
@@ -58,20 +52,7 @@ public class CallbackHandlerChain extends AbstractCallbackHandler {
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);
}
super.handleInternal(callback);
}
}
@@ -97,8 +78,8 @@ public class CallbackHandlerChain extends AbstractCallbackHandler {
public void validate(TimestampValidationCallback.Request request)
throws TimestampValidationCallback.TimestampValidationException {
for (int i = 0; i < callbackHandlers.length; i++) {
CallbackHandler callbackHandler = callbackHandlers[i];
for (int i = 0; i < getCallbackHandlers().length; i++) {
CallbackHandler callbackHandler = getCallbackHandlers()[i];
try {
callbackHandler.handle(new Callback[]{callback});
callback.getResult();
@@ -124,8 +105,8 @@ public class CallbackHandlerChain extends AbstractCallbackHandler {
public boolean validate(PasswordValidationCallback.Request request)
throws PasswordValidationCallback.PasswordValidationException {
boolean allUnsupported = true;
for (int i = 0; i < callbackHandlers.length; i++) {
CallbackHandler callbackHandler = callbackHandlers[i];
for (int i = 0; i < getCallbackHandlers().length; i++) {
CallbackHandler callbackHandler = getCallbackHandlers()[i];
try {
callbackHandler.handle(new Callback[]{callback});
allUnsupported = false;
@@ -155,8 +136,8 @@ public class CallbackHandlerChain extends AbstractCallbackHandler {
public boolean validate(X509Certificate certificate)
throws CertificateValidationCallback.CertificateValidationException {
boolean allUnsupported = true;
for (int i = 0; i < callbackHandlers.length; i++) {
CallbackHandler callbackHandler = callbackHandlers[i];
for (int i = 0; i < getCallbackHandlers().length; i++) {
CallbackHandler callbackHandler = getCallbackHandlers()[i];
try {
callbackHandler.handle(new Callback[]{callback});
allUnsupported = false;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ws.soap.security.xwss.callback;
package org.springframework.ws.soap.security.callback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
@@ -46,7 +46,7 @@ public class CallbackHandlerChainTest extends TestCase {
chain.handle(new Callback[]{callback});
}
public void testUnsupportedNormal() throws Exception {
public void testUnsupportedSupported() throws Exception {
CallbackHandlerChain chain = new CallbackHandlerChain(new CallbackHandler[]{unsupported, supported});
chain.handle(new Callback[]{callback});
}

View File

@@ -0,0 +1,6 @@
package org.springframework.ws.soap.security.wss4j;
public class AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest
extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
}

View File

@@ -0,0 +1,6 @@
package org.springframework.ws.soap.security.wss4j;
public class SaajWss4jMessageInterceptorAcegiCallbackHandlerTest
extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase {
}

View File

@@ -0,0 +1,102 @@
package org.springframework.ws.soap.security.wss4j;
import java.util.Properties;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.TestingAuthenticationToken;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.memory.InMemoryDaoImpl;
import org.apache.ws.security.WSConstants;
import org.easymock.MockControl;
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.AcegiDigestPasswordValidationCallbackHandler;
import org.springframework.ws.soap.security.wss4j.callback.acegi.AcegiPlainTextPasswordValidationCallbackHandler;
public abstract class Wss4jMessageInterceptorAcegiCallbackHandlerTestCase extends Wss4jTestCase {
private Properties users = new Properties();
private MockControl control;
private AuthenticationManager mock;
protected void onSetup() throws Exception {
control = MockControl.createControl(AuthenticationManager.class);
mock = (AuthenticationManager) control.getMock();
users.setProperty("Bert", "Ernie,ROLE_TEST");
}
protected void tearDown() throws Exception {
control.verify();
}
public void testValidateUsernameTokenPlainText() throws Exception {
EndpointInterceptor interceptor = prepareInterceptor("UsernameToken", true, false);
SoapMessage message = loadMessage("usernameTokenPlainText-soap.xml");
MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
interceptor.handleRequest(messageContext, null);
assertValidateUsernameToken(message);
}
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);
}
if (digest) {
AcegiDigestPasswordValidationCallbackHandler callbackHandler =
new AcegiDigestPasswordValidationCallbackHandler();
InMemoryDaoImpl userDetailsService = new InMemoryDaoImpl();
userDetailsService.setUserProperties(users);
userDetailsService.afterPropertiesSet();
callbackHandler.setUserDetailsService(userDetailsService);
interceptor.setSecurementPasswordType(WSConstants.PW_DIGEST);
interceptor.setValidationCallbackHandler(callbackHandler);
interceptor.afterPropertiesSet();
}
else {
AcegiPlainTextPasswordValidationCallbackHandler callbackHandler =
new AcegiPlainTextPasswordValidationCallbackHandler();
Authentication authResult = new TestingAuthenticationToken("Bert", "Ernie", new GrantedAuthority[0]);
control.expectAndReturn(mock.authenticate(new UsernamePasswordAuthenticationToken("Bert", "Ernie")),
authResult);
callbackHandler.setAuthenticationManager(mock);
callbackHandler.afterPropertiesSet();
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
interceptor.setValidationCallbackHandler(callbackHandler);
interceptor.afterPropertiesSet();
}
control.replay();
return interceptor;
}
}

View File

@@ -8,7 +8,7 @@ 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.callback.KeyStoreCallbackHandler;
import org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean;
public abstract class Wss4jMessageInterceptorEncryptionTestCase extends Wss4jTestCase {
@@ -20,8 +20,8 @@ public abstract class Wss4jMessageInterceptorEncryptionTestCase extends Wss4jTes
interceptor.setValidationActions("Encrypt");
interceptor.setSecurementActions("Encrypt");
SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
callbackHandler.setKeyPassword("123456");
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("123456");
interceptor.setValidationCallbackHandler(callbackHandler);
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
@@ -63,6 +63,5 @@ public abstract class Wss4jMessageInterceptorEncryptionTestCase extends Wss4jTes
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
}
}

View File

@@ -8,7 +8,7 @@ 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;
import org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler;
public abstract class Wss4jMessageInterceptorHeaderTestCase extends Wss4jTestCase {
@@ -21,7 +21,7 @@ public abstract class Wss4jMessageInterceptorHeaderTestCase extends Wss4jTestCas
interceptor.setValidateRequest(true);
interceptor.setSecureResponse(true);
interceptor.setValidationActions("UsernameToken");
SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
callbackHandler.setUsers(users);
interceptor.setValidationCallbackHandler(callbackHandler);
interceptor.afterPropertiesSet();

View File

@@ -9,7 +9,6 @@ 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 {
@@ -19,8 +18,6 @@ public abstract class Wss4jMessageInterceptorSignTestCase extends Wss4jTestCase
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();
@@ -61,7 +58,6 @@ public abstract class Wss4jMessageInterceptorSignTestCase extends Wss4jTestCase
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);
}

View File

@@ -8,7 +8,7 @@ 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.callback.SimplePasswordValidationCallbackHandler;
public abstract class Wss4jMessageInterceptorUsernameTokenTestCase extends Wss4jTestCase {
@@ -96,16 +96,16 @@ public abstract class Wss4jMessageInterceptorUsernameTokenTestCase extends Wss4j
else {
interceptor.setSecurementActions(actions);
}
SimpleCallbackHandler callbackHandler = new SimpleCallbackHandler();
SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
callbackHandler.setUsers(users);
if (digest) {
callbackHandler.setPasswordDigestRequired(true);
callbackHandler.setPasswordPlainTextRequired(false);
// callbackHandler.setPasswordDigestRequired(true);
// callbackHandler.setPasswordPlainTextRequired(false);
interceptor.setSecurementPasswordType(WSConstants.PW_DIGEST);
}
else {
callbackHandler.setPasswordDigestRequired(false);
callbackHandler.setPasswordPlainTextRequired(true);
// callbackHandler.setPasswordDigestRequired(false);
// callbackHandler.setPasswordPlainTextRequired(true);
interceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
}
interceptor.setValidationCallbackHandler(callbackHandler);