From dc6b14905c6ebd672bffe123ee3b9acee84d7786 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Sun, 10 Feb 2008 02:48:24 +0000 Subject: [PATCH] Done implementing SWS-207 --- .../callback/CallbackHandlerChain.java | 59 ++++++ .../security/support/KeyStoreFactoryBean.java | 2 +- .../soap/security/support/KeyStoreUtils.java | 123 ++++++++++++ .../wss4j/Wss4jSecurityInterceptor.java | 91 +++++---- .../AbstractWsPasswordCallbackHandler.java | 176 +++++++++++++++++ .../AbstractWss4jCallbackHandler.java | 182 ------------------ .../callback/KeyStoreCallbackHandler.java | 120 ++++++++++++ .../wss4j/callback/SimpleCallbackHandler.java | 48 ----- ...ava => SimplePasswordCallbackHandler.java} | 56 +++--- ...mplePasswordValidationCallbackHandler.java | 77 ++++++++ ...gestPasswordValidationCallbackHandler.java | 90 +++++++++ ...TextPasswordValidationCallbackHandler.java | 86 +++++++++ .../wss4j/callback/acegi/package.html | 6 + .../soap/security/wss4j/callback/package.html | 5 + .../security/xwss/XwsSecurityInterceptor.java | 4 +- .../callback/KeyStoreCallbackHandler.java | 92 +-------- ...ain.java => XwssCallbackHandlerChain.java} | 41 ++-- .../callback/CallbackHandlerChainTest.java | 4 +- ...geInterceptorAcegiCallbackHandlerTest.java | 6 + ...geInterceptorAcegiCallbackHandlerTest.java | 6 + ...terceptorAcegiCallbackHandlerTestCase.java | 102 ++++++++++ ...jMessageInterceptorEncryptionTestCase.java | 7 +- ...Wss4jMessageInterceptorHeaderTestCase.java | 4 +- .../Wss4jMessageInterceptorSignTestCase.java | 4 - ...ssageInterceptorUsernameTokenTestCase.java | 12 +- 25 files changed, 969 insertions(+), 434 deletions(-) create mode 100644 security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreUtils.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWsPasswordCallbackHandler.java delete mode 100755 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/KeyStoreCallbackHandler.java delete mode 100755 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java rename security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/{Wss4jSecurityCallbackHandlerException.java => SimplePasswordCallbackHandler.java} (59%) mode change 100755 => 100644 create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordValidationCallbackHandler.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/package.html create mode 100644 security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/package.html rename security/src/main/java/org/springframework/ws/soap/security/xwss/callback/{CallbackHandlerChain.java => XwssCallbackHandlerChain.java} (79%) rename security/src/test/java/org/springframework/ws/soap/security/{xwss => }/callback/CallbackHandlerChainTest.java (94%) create mode 100755 security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java create mode 100755 security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java create mode 100755 security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java diff --git a/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java b/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java new file mode 100644 index 00000000..affbc0b5 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/callback/CallbackHandlerChain.java @@ -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 CallbackHandlers. For each callback, each of the handlers is called in term. If a + * handler throws a UnsupportedCallbackException, the next handler is tried. + * + * @author Arjen Poutsma + * @since 1.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); + } + } +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java index f452206a..8f430ba5 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java +++ b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java @@ -87,7 +87,7 @@ public class KeyStoreFactoryBean implements FactoryBean, InitializingBean { this.type = type; } - public Object getObject() throws Exception { + public Object getObject() { return keyStore; } diff --git a/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreUtils.java b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreUtils.java new file mode 100644 index 00000000..81e124c3 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreUtils.java @@ -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:javax.net.ssl.keyStore, javax.net.ssl.keyStorePassword, and + * javax.net.ssl.keyStoreType. + *

+ * 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. + *

+ * This behavior corresponds to the standard J2SDK behavior for SSL key stores. + * + * @see The + * standard J2SDK SSL key store mechanism + */ + 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:

  1. If the system property + * javax.net.ssl.trustStore is defined, its value is loaded. If the + * javax.net.ssl.trustStorePassword system property is also defined, its value is used as a password. + * If the javax.net.ssl.trustStoreType system property is defined, its value is used as a key store + * type. + *

    + * If javax.net.ssl.trustStore is defined but the specified file does not exist, then a default, empty + * trust store is created.

  2. If the javax.net.ssl.trustStore system property was not + * specified, but if the file $JAVA_HOME/lib/security/jssecacerts exists, that file is used.
  3. + * Otherwise,
  4. If the file $JAVA_HOME/lib/security/cacerts exists, that file is used.
+ *

+ * This behavior corresponds to the standard J2SDK behavior for SSL trust stores. + * + * @see The + * standard J2SDK SSL trust store mechanism + */ + 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(); + } + +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java index fb0ce53a..4815688f 100755 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java @@ -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}. *

- * The validation and securement actions executed by this interceptor are configured via validationActions and - * securementActions properties, respectively. Actions should be passed as a space-separated strings. + * The validation and securement actions executed by this interceptor are configured via validationActions + * and securementActions properties, respectively. Actions should be passed as a space-separated strings. *

* Valid validation actions are: - * - *

- * - * - * - * - * - * - *
Validation actionDescription
UsernameTokenValidates username token
TimestampValidates the timestamp
EncryptDecrypts the message
SignatureValidates the signature
NoSecurityNo action performed
*

- * Securement actions are: - *

- * - * - * - * - * - * - * - *
Securement actionDescription
UsernameTokenAdds a username token
UsernameTokenSignatureAdds a username token and a signature username token secrect key
TimestampAdds a timestamp
EncryptEncrypts the response
SignatureSigns the response
NoSecurityNo action performed
+ *
+ *
Validation actionDescription
UsernameTokenValidates + * username token
TimestampValidates the timestamp
EncryptDecrypts the message
SignatureValidates + * the signature
NoSecurityNo action performed
*

- * The order of the actions that the client performed to secure the messages is significant and is - * enforced by the interceptor. - * + * Securement actions are:

+ * + *
Securement actionDescription
UsernameTokenAdds a username token
UsernameTokenSignatureAdds + * a username token and a signature username token secrect key
TimestampAdds a + * timestamp
EncryptEncrypts the response
SignatureSigns the response
NoSecurityNo action + * performed
+ *

+ * 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); diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWsPasswordCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWsPasswordCallbackHandler.java new file mode 100644 index 00000000..9e1f7423 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWsPasswordCallbackHandler.java @@ -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 handle* 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. + *

+ * 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). + *

+ * 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. + *

+ * This method is invoked when WSS4J needs the password to fill in or to verify a UsernameToken. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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}). + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * This method is invoked when WSS4J needs the key to to be associated with a SecurityContextToken. + *

+ * 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. + *

+ * 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. + *

+ * Default implementation throws an {@link UnsupportedCallbackException}. + */ + protected void handleEncryptedKeyToken(Callback callback) throws IOException, UnsupportedCallbackException { + throw new UnsupportedCallbackException(callback); + } +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java deleted file mode 100755 index 6f3869f3..00000000 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWss4jCallbackHandler.java +++ /dev/null @@ -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; -} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/KeyStoreCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/KeyStoreCallbackHandler.java new file mode 100644 index 00000000..f57850fa --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/KeyStoreCallbackHandler.java @@ -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 KeyStores 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); + } + } + +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java deleted file mode 100755 index f9c5e63b..00000000 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimpleCallbackHandler.java +++ /dev/null @@ -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()))); - } -} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordCallbackHandler.java old mode 100755 new mode 100644 similarity index 59% rename from security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java rename to security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordCallbackHandler.java index a7b2cc72..730c27f3 --- a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/Wss4jSecurityCallbackHandlerException.java +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordCallbackHandler.java @@ -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 { + +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordValidationCallbackHandler.java new file mode 100644 index 00000000..d2cc5d89 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/SimplePasswordValidationCallbackHandler.java @@ -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 Properties 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); + } + } + +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..5cb34b55 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java @@ -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 UserDetailsService. Logic based on + * Acegi's DigestProcessingFilter. + *

+ * An Acegi UserDetailService is used to load UserDetails 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; + } +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..cbce2e1e --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java @@ -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 AuthenticationManager. Logic based on + * Acegi's BasicProcessingFilter. + *

+ * This handler requires an Acegi AuthenticationManager to operate. It can be set using the + * authenticationManager property. An Acegi UsernamePasswordAuthenticationToken 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); + } + } + } +} diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/package.html b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/package.html new file mode 100644 index 00000000..f553860c --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/package.html @@ -0,0 +1,6 @@ + + +Contains CallbackHandler implementations for WSS4J that use the Acegi + Security System for Spring. + + \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/package.html b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/package.html new file mode 100644 index 00000000..7bf2f27b --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/package.html @@ -0,0 +1,5 @@ + + +Contains CallbackHandler implementations for WSS4J. + + \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java index 5450a6db..f9c85b63 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java @@ -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. */ diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/KeyStoreCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/KeyStoreCallbackHandler.java index 669f1a8e..225c8226 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/KeyStoreCallbackHandler.java +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/KeyStoreCallbackHandler.java @@ -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 KeyStores 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:javax.net.ssl.keyStore, javax.net.ssl.keyStorePassword, and - * javax.net.ssl.keyStoreType. - *

- * 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. - *

- * This behavior corresponds to the standard J2SDK behavior for SSL key stores. - * - * @see The - * standard J2SDK SSL key store mechanism - */ + /** 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:

  1. If the system property - * javax.net.ssl.trustStore is defined, its value is loaded. If the - * javax.net.ssl.trustStorePassword system property is also defined, its value is used as a password. - * If the javax.net.ssl.trustStoreType system property is defined, its value is used as a key store - * type. - *

    - * If javax.net.ssl.trustStore is defined but the specified file does not exist, then a default, empty - * trust store is created.

  2. If the javax.net.ssl.trustStore system property was not - * specified, but if the file $JAVA_HOME/lib/security/jssecacerts exists, that file is used.
  3. - * Otherwise,
  4. If the file $JAVA_HOME/lib/security/cacerts exists, that file is used.
- *

- * This behavior corresponds to the standard J2SDK behavior for SSL trust stores. - * - * @see The - * standard J2SDK SSL trust store mechanism - */ + /** 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"); } diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChain.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/XwssCallbackHandlerChain.java similarity index 79% rename from security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChain.java rename to security/src/main/java/org/springframework/ws/soap/security/xwss/callback/XwssCallbackHandlerChain.java index 35fc0050..63109f53 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChain.java +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/XwssCallbackHandlerChain.java @@ -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 CallbackHandlers. 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; diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java b/security/src/test/java/org/springframework/ws/soap/security/callback/CallbackHandlerChainTest.java similarity index 94% rename from security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java rename to security/src/test/java/org/springframework/ws/soap/security/callback/CallbackHandlerChainTest.java index 13d40537..33748b4e 100644 --- a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/CallbackHandlerChainTest.java +++ b/security/src/test/java/org/springframework/ws/soap/security/callback/CallbackHandlerChainTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ws.soap.security.xwss.callback; +package org.springframework.ws.soap.security.callback; import 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}); } diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java new file mode 100755 index 00000000..c5430080 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest.java @@ -0,0 +1,6 @@ +package org.springframework.ws.soap.security.wss4j; + +public class AxiomWss4jMessageInterceptorAcegiCallbackHandlerTest + extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase { + +} diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java new file mode 100755 index 00000000..bbbe7f9c --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorAcegiCallbackHandlerTest.java @@ -0,0 +1,6 @@ +package org.springframework.ws.soap.security.wss4j; + +public class SaajWss4jMessageInterceptorAcegiCallbackHandlerTest + extends Wss4jMessageInterceptorAcegiCallbackHandlerTestCase { + +} diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java new file mode 100755 index 00000000..33db8016 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java @@ -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; + } +} + diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java index 5e107ff3..bc0eaee3 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorEncryptionTestCase.java @@ -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 } } diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java index 5c845f99..18520314 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorHeaderTestCase.java @@ -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(); diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java index 038707fc..6b347015 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSignTestCase.java @@ -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); } diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java index 5c82bb16..e437cc60 100755 --- a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorUsernameTokenTestCase.java @@ -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);