Announcement Announcement Module
Collapse
No announcement yet.
usernameToken + signature Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • usernameToken + signature

    Hello,

    I am using spring-ws stack for ws communication and it's working fine with encryption and signature. Now I have added usernameToken and I have a problem.

    The scenarios is as follows:

    All communication must be encrypted and signed. For this, I have server and client certificates in jks. On top of these I want to add usernameToken for authentication of the different users that make requests to the server. The passwords will be created by the 'server' side and will have a limited valid period. The passwords to the clients will be constantly renewed. I want to use encryption and signing only for transport security and on the server, after the stripping of the security elements, I want to end up with the user and password digest so I can authenticate and authorize it.

    I want to send requests from user Abe with password AbePassword as Digest and sign the message with client certificate. Later on I will want to send requests from user Abe with a different password AbeChangedPassword as Digest and sign the message with the same client certificate.

    The problem resides in the way UsernameToken and Signature actions work together. In this case the password for usernameToken is used to access the jks and retrieve the client certificate and this will fail as the password is the password for user Abe, not for the jks. There is no way to distinguish between the 2 of them. Also when UsernameToken and Signature are used together, there is no usernameToken tag generated in the soap message.

    Below is my config

    Code:
    <bean id="securityInterceptor"
    		class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
    	<!-- encrypt, sign & usernameToken request -->
    	<!-- usernameToken -->
    	<property name="securementActions" value="Encrypt Signature UsernameToken" />
    	<property name="securementUsername" value="Abe" />
    	<property name="securementPassword" value="AbePassword" />
    	<!-- encrypt --> 
    	<property name="securementEncryptionUser" value="server" />
    	<property name="securementEncryptionCrypto" ref="cryptoFactory" />
    	<!-- sign -->
    	<property name="securementSignatureKeyIdentifier" value="DirectReference" />
    	<property name="securementSignatureUser" value="client" />
    	<property name="securementSignatureCrypto" ref="cryptoFactory" />
    </bean>
    
    <bean id="cryptoFactory"
    	class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
    	<property name="keyStorePassword" value="password" />
    	<property name="keyStoreLocation" value="classpath:store.jks" />
    </bean>
    The following is the exception that I get
    Code:
    2010-11-02 17:45:57,485 DEBUG [org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor] - Securing message [SaajSoapMessage {http://example.org/SampleTypes}getSampleRequest] with actions [Encrypt Signature UsernameToken]
    2010-11-02 17:45:57,579 DEBUG [org.apache.ws.security.handler.WSHandler] - Performing Action: 4
    2010-11-02 17:45:57,583 DEBUG [org.apache.ws.security.message.WSSecEncrypt] - Beginning Encryption...
    2010-11-02 17:45:57,821 DEBUG [org.apache.ws.security.message.WSSecEncryptedKey] - cipher blksize: 0, symm key length: 16
    2010-11-02 17:45:57,889 DEBUG [org.apache.ws.security.message.WSSecEncrypt] - Encryption complete.
    2010-11-02 17:45:57,889 DEBUG [org.apache.ws.security.handler.WSHandler] - Performing Action: 2
    2010-11-02 17:45:57,892 DEBUG [org.apache.ws.security.message.WSSecSignature] - Beginning signing...
    2010-11-02 17:45:57,893 DEBUG [org.apache.ws.security.message.WSSecSignature] - automatic sig algo detection: RSA
    2010-11-02 17:45:57,916 ERROR [org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor] - Could not secure response: Error during Signature: ; nested exception is: 
    	org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key; nested exception is org.apache.ws.security.WSSecurityException: Error during Signature: ; nested exception is: 
    	org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key
    org.springframework.ws.soap.security.wss4j.Wss4jSecuritySecurementException: Error during Signature: ; nested exception is: 
    	org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key; nested exception is org.apache.ws.security.WSSecurityException: Error during Signature: ; nested exception is: 
    	org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key
    	at org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor.secureMessage(Wss4jSecurityInterceptor.java:497)
    	at org.springframework.ws.soap.security.AbstractWsSecurityInterceptor.handleRequest(AbstractWsSecurityInterceptor.java:188)
    	at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:542)
    	at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:502)
    	at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:351)
    	at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:345)
    	at eu.webmedia.bes.sapi.client.ComRCCCService.makeGetLastSendReplyRequest(ComRCCCService.java:102)
    	at eu.webmedia.bes.sapi.test.client.InvokerTest.testMakeGetLastSendReplyRequest(InvokerTest.java:58)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    	at java.lang.reflect.Method.invoke(Method.java:585)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    Caused by: org.apache.ws.security.WSSecurityException: Error during Signature: ; nested exception is: 
    	org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key
    	at org.apache.ws.security.action.SignatureAction.execute(SignatureAction.java:60)
    	at org.apache.ws.security.handler.WSHandler.doSenderAction(WSHandler.java:202)
    	at org.springframework.ws.soap.security.wss4j.Wss4jHandler.doSenderAction(Wss4jHandler.java:166)
    	at org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor.secureMessage(Wss4jSecurityInterceptor.java:494)
    	... 29 more
    Caused by: org.apache.ws.security.WSSecurityException: Signature creation failed; nested exception is: 
    	java.security.UnrecoverableKeyException: Cannot recover key
    	at org.apache.ws.security.message.WSSecSignature.computeSignature(WSSecSignature.java:721)
    	at org.apache.ws.security.message.WSSecSignature.build(WSSecSignature.java:780)
    	at org.apache.ws.security.action.SignatureAction.execute(SignatureAction.java:57)
    	... 32 more
    Caused by: java.security.UnrecoverableKeyException: Cannot recover key
    	at sun.security.provider.KeyProtector.recover(KeyProtector.java:301)
    	at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:120)
    	at java.security.KeyStore.getKey(KeyStore.java:731)
    	at org.apache.ws.security.components.crypto.CryptoBase.getPrivateKey(CryptoBase.java:216)
    	at org.apache.ws.security.message.WSSecSignature.computeSignature(WSSecSignature.java:713)
    	... 34 more
    I use spring-ws 1.5.9 and spring 2.5.6
    Any ideas how to use them both ?

  • #2
    I managed to make it work by setting separate password callback handlers for the 2 cases: usernameToken and signature. Code is below

    Passwords for the callback handlers can be obtained in a dynamic manner if needed; here they are set in spring config for easy usage
    Code:
    <bean id="securityInterceptor"
    	class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
    	<!-- encrypt, sign & usernameToken request -->
    	<!-- usernameToken -->
    	<property name="securementActions" value="Encrypt Signature UsernameToken" />
    	<property name="securementUsername" value="Abe" />
    	<!-- encrypt --> 
    	<property name="securementEncryptionUser" value="server" />
    	<property name="securementEncryptionCrypto" ref="cryptoFactory" />
    	<!-- sign -->
    	<property name="securementSignatureKeyIdentifier" value="DirectReference" />
    	<property name="securementSignatureUser" value="client" />
    	<property name="securementSignatureCrypto" ref="cryptoFactory" />
    	<property name="securementCallbackHandlers">
    		<set>
    			<ref bean="signCallbackHandler" />
    			<ref bean="usernameCallbackHandler" />
    		</set>
    	</property>
    </bean>
    
    <bean id="usernameCallbackHandler" class="org.example.security.SimplePasswordSetterCallbackHandler">
    	<property name="password" value="AbePassword"/>
    	<property name="usage">
    		<util:constant static-field="org.apache.ws.security.WSPasswordCallback.USERNAME_TOKEN"/>
    	</property>
    </bean>
    <bean id="signCallbackHandler" class="org.example.security.SimplePasswordSetterCallbackHandler">
    	<property name="password" value="keystorePassword"/>
    	<property name="usage">
    		<util:constant static-field="org.apache.ws.security.WSPasswordCallback.SIGNATURE"/>
    	</property>
    </bean>
    Code:
    public class SimplePasswordSetterCallbackHandler extends AbstractCallbackHandler {
    	private String password;
    	private int usage;
    
    	@Override
    	protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
    		if (callback instanceof WSPasswordCallback) {
    			if (usage == ((WSPasswordCallback) callback).getUsage()) {
    				((WSPasswordCallback) callback).setPassword(password);
    			}
    			else {
    				throw new UnsupportedCallbackException(callback);
    			}
    		}
    
    	}
    
    	public void setPassword(String password) {
    		this.password = password;
    	}
    
    	public void setUsage(int usage) {
    		this.usage = usage;
    	}
    
    }
    This generates correctly UsernameToken tag in the xml message.
    Hope this helps someone out there.
    Last edited by bogdang; Nov 3rd, 2010, 06:25 AM.

    Comment


    • #3
      The proposed solution above does no longer work in newer Spring WS versions (I'm using the 2.1.2.RELEASE). Is there any way to get this to work when using a Spring WS version containing changeset 1984: "SWS-711 - Upgrade to wss4j 1.6"?

      The workaround of only allowing a single, remotely controlled, password for the UsernameToken action (for any securementUsername) and using that same password for the local keystore for Signature/Encrypt is not acceptable.

      WSS4J does use password callback handlers still (see org.apache.ws.security.handler.WSHandler.getPasswo rdCB()), Spring WS just doesn't seem to allow for configuring anything but a single securementPassword in the Wss4jSecurityInterceptor. As a consequence, the "else" branch is always hit during securement and the single securementPassword is used for all securementActions.

      The reference doc at http://static.springsource.org/sprin.../security.html seems to suggest that Wss4jSecurityInterceptor has a securementCallbackHandler, but that is no longer the case.

      Did I overlook something or is it no longer possible to use both UsernameToken and Signature/Encrypt actions for securement as a WS client?

      Comment

      Working...
      X