Spring WS Security on both client and server

Recently, I have been playing with Spring WS with WS-Security. I just want to write down how it works. Do not except anything special, just simple example of basic security operations.

The example

We want to implement both client and server side. The client will sign the message, encrypt some part of it and add a timestamp. To make it more complex and real-life like we will sign the message using private key with alias “client” and encrypt the message using public key called “server”. Server will validate that the request is valid and will just sign the response using his key called “server”. Please note that I have picked Wss4j implementation because the configuration seemed to be easier than Xws.

Client

It’s easy to do configure client interceptor like this.

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
	<property name="interceptors">
		<list>
			<ref local="wsClientSecurityInterceptor"/>
		</list>
	</property>
	...
</bean>

<bean id="wsClientSecurityInterceptor"
	class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
	<property name="securementActions" value="Timestamp Signature Encrypt" />
	<!-- Key alias for signature -->
	<property name="securementUsername" value="client" />
	<property name="securementPassword" value="" />
	<property name="securementSignatureCrypto" ref="clientCrypto"/>
	<property name="securementEncryptionCrypto" ref="clientCrypto"/>
	<property name="securementEncryptionParts" value="{Content}{http://javacrumbs.net/calc}a"/>
	<!-- Key alias for encryption -->
	<property name="securementEncryptionUser" value="server"/>
	
	<!-- Validation config -->
	<property name="validationActions" value="Signature" />
	<property name="validationSignatureCrypto" ref="clientCrypto"/>
</bean>

<bean id="clientCrypto" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
    <property name="keyStorePassword" value="mypasswd"/>
    <property name="keyStoreLocation" value="classpath:security/client-keystore.jks"/>
</bean>

As you can see, there is nothing special. We just define which actions to take and properties. The only confusing part is, that key alias is defined as “securementUsername”.

Whit this configuration we will get following SOAP message.

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
	<SOAP-ENV:Header>
		<wsse:Security
			xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
			SOAP-ENV:mustUnderstand="1">
			<xenc:EncryptedKey Id="EncKeyId-F5114C147B958E706212759086159355"
				xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
				<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
				<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
					<wsse:SecurityTokenReference
						xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
						<ds:X509Data>
							<ds:X509IssuerSerial>
								<ds:X509IssuerName>CN=Test Server,OU=Test</ds:X509IssuerName>
								<ds:X509SerialNumber>1275904530</ds:X509SerialNumber>
							</ds:X509IssuerSerial>
						</ds:X509Data>
					</wsse:SecurityTokenReference>
				</ds:KeyInfo>
				<xenc:CipherData>
					<xenc:CipherValue>fwFM7ShJ1xd7dTGrkh0410sTmW92OPB1q1fpzB21XFIe36siDDJWGgbw5B94yjmGK2YaPOWLb7cpVTYPzc9VUDs7Jc42CtrhT2H6eZ7CDiA60Ugz+qi2UyyfMDK6Vrdj9J68rij5P12AiBeTnd2wlhI29+71XbUpD5weHDHjMtQ=
					</xenc:CipherValue>
				</xenc:CipherData>
				<xenc:ReferenceList>
					<xenc:DataReference URI="#EncDataId-4" />
				</xenc:ReferenceList>
			</xenc:EncryptedKey>
			<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
				Id="Signature-2">
				<ds:SignedInfo>
					<ds:CanonicalizationMethod
						Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
					<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
					<ds:Reference URI="#id-3">
						<ds:Transforms>
							<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
						</ds:Transforms>
						<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
						<ds:DigestValue>AU9utUgz5RylYCRDUAO0JWM48kM=</ds:DigestValue>
					</ds:Reference>
				</ds:SignedInfo>
				<ds:SignatureValue>
					NHjjgpb9/alUOq50CqPKLcdYrp7edYdKJDNvIhh+2OAhYdDvZmD1qGsVKd1H9oKPF17uaF2Sv3aY
					0le6BrvzVx3n2+nYYlHwAWlzBk7wsBt4vLll6q6juLCP+siupTIb1PeZDf3WrAbHUQh5oqjD6cZB
					Sc89pDspWRABQ8wPxYE=
</ds:SignatureValue>
				<ds:KeyInfo Id="KeyId-F5114C147B958E706212759086157652">
					<wsse:SecurityTokenReference
						xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
						wsu:Id="STRId-F5114C147B958E706212759086157673"
						xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
						<ds:X509Data>
							<ds:X509IssuerSerial>
								<ds:X509IssuerName>CN=Lukas Krecan,OU=Test</ds:X509IssuerName>
								<ds:X509SerialNumber>1275900789</ds:X509SerialNumber>
							</ds:X509IssuerSerial>
						</ds:X509Data>
					</wsse:SecurityTokenReference>
				</ds:KeyInfo>
			</ds:Signature>
			<wsu:Timestamp
				xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
				wsu:Id="Timestamp-1">
				<wsu:Created>2010-06-07T11:03:35.749Z</wsu:Created>
				<wsu:Expires>2010-06-07T11:08:35.749Z</wsu:Expires>
			</wsu:Timestamp>
		</wsse:Security>
	</SOAP-ENV:Header>
	<SOAP-ENV:Body
		xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
		wsu:Id="id-3">
		<ns2:plusRequest xmlns:ns2="http://javacrumbs.net/calc">
			<ns2:a>
				<xenc:EncryptedData Id="EncDataId-4"
					Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
					<xenc:EncryptionMethod
						Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
					<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
						<wsse:SecurityTokenReference
							xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
							<wsse:Reference URI="#EncKeyId-F5114C147B958E706212759086159355"
								xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" />
						</wsse:SecurityTokenReference>
					</ds:KeyInfo>
					<xenc:CipherData>
						<xenc:CipherValue>81TEtUhHXo6iZeAmYrtYlm2ObAqOBpjfzf2VOVUg4Hs=
						</xenc:CipherValue>
					</xenc:CipherData>
				</xenc:EncryptedData>
			</ns2:a>
			<ns2:b>2</ns2:b>
		</ns2:plusRequest>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Server config

To configure server, you have to define Spring WS server interceptor like this (full example).

<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
	<property name="interceptors">
		<list>
			<ref local="wsServerSecurityInterceptor" />
		</list>
	</property>
</bean>

<bean id="wsServerSecurityInterceptor"	class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
	<!-- Validation part -->
	<property name="validationActions" value="Timestamp Signature Encrypt"/>
	<property name="validationSignatureCrypto" ref="serverCrypto"/>
	<property name="validationDecryptionCrypto" ref="serverCrypto"/>
	<property name="validationCallbackHandler">
		<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
			<property name="keyStore">
				<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
				    <property name="password" value="mypasswd"/>
				</bean>
			</property>
			<property name="privateKeyPassword" value=""/>
		</bean> 
	</property>
	<!-- Sign the response -->
	<property name="securementActions" value="Signature" />
	<property name="securementUsername" value="server" />
	<property name="securementPassword" value="" />
	<property name="securementSignatureCrypto" ref="serverCrypto"/>
</bean>

<bean id="serverCrypto" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
    <property name="keyStorePassword" value="mypasswd"/>
    <property name="keyStoreLocation" value="classpath:security/server-keystore.jks"/>
</bean>

No surprise here neither. The response will look like this.

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
	<SOAP-ENV:Header>
		<wsse:Security
			xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
			SOAP-ENV:mustUnderstand="1">
			<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
				Id="Signature-6">
				<ds:SignedInfo>
					<ds:CanonicalizationMethod
						Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
					<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
					<ds:Reference URI="#id-7">
						<ds:Transforms>
							<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
						</ds:Transforms>
						<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
						<ds:DigestValue>hEdDfxM6Nfs62Jxe8EOsELCDtUk=</ds:DigestValue>
					</ds:Reference>
					<ds:Reference URI="#SigConf-5">
						<ds:Transforms>
							<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
						</ds:Transforms>
						<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
						<ds:DigestValue>TTSRri5KJqXeMJfjzXyVmUewPxc=</ds:DigestValue>
					</ds:Reference>
				</ds:SignedInfo>
				<ds:SignatureValue>
					V5by3bOoGQNajfs7i9xQ+cbAqIkI0NS9N9FQlLb/dAuQfguE7jKRP9iypOeRLHCPr7g3BNg+NCrX
					6YcgDQ0TfXNhdL00AmoEfDmWSNvIVNE49kZEn3Ji/RW4VtdEiV79VD7Vuay0YAYGo9DSQvzq3FP6
					YEhfzfMqvfbWMdEKcO8=
</ds:SignatureValue>
				<ds:KeyInfo Id="KeyId-F5114C147B958E706212759086160837">
					<wsse:SecurityTokenReference
						xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
						wsu:Id="STRId-F5114C147B958E706212759086160838"
						xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
						<ds:X509Data>
							<ds:X509IssuerSerial>
								<ds:X509IssuerName>CN=Test Server,OU=Test</ds:X509IssuerName>
								<ds:X509SerialNumber>1275904530</ds:X509SerialNumber>
							</ds:X509IssuerSerial>
						</ds:X509Data>
					</wsse:SecurityTokenReference>
				</ds:KeyInfo>
			</ds:Signature>
			<wsse11:SignatureConfirmation
				xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
				xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
				Value="NHjjgpb9/alUOq50CqPKLcdYrp7edYdKJDNvIhh+2OAhYdDvZmD1qGsVKd1H9oKPF17uaF2Sv3aY0le6BrvzVx3n2+nYYlHwAWlzBk7wsBt4vLll6q6juLCP+siupTIb1PeZDf3WrAbHUQh5oqjD6cZBSc89pDspWRABQ8wPxYE="
				wsu:Id="SigConf-5" />
		</wsse:Security>
	</SOAP-ENV:Header>
	<SOAP-ENV:Body
		xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
		wsu:Id="id-7">
		<ns2:plusResponse xmlns:ns2="http://javacrumbs.net/calc">
			<ns2:result>3</ns2:result>
		</ns2:plusResponse>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

As we have seen it’s possible to configure WS-Security without much hassle. To learn more, visit the official Spring WS reference. You can download full example here.

7 thoughts on “Spring WS Security on both client and server

  1. Christos Kapasakalidis

    Hi!

    Excellent example. It works just fine!
    It would be useful if you could display how you create the keystores. Because when I replace them with my one it is not working.

    Thanks,
    Chris

  2. Alex

    Hi,
    May I know how do you generate the server-keystore.jks and client-keystore.jks ?

    Regards,
    Alex

  3. Lukáš Křečan

    Sorry, I do not remember. There is a great tool that I generally use for KeyStore manipulation http://portecle.sourceforge.net/ You can inspect the sample files from https://java-crumbs.svn.sourceforge.net/svnroot/java-crumbs/simple-server-test/branches/simple-server-test-security/simple-server-test/src/main/resources/security/ and try to figure it out.

    If I recall it correctly, you need to have Client certificate and server private key on the server side, and server certificate and client private key on the client side.

  4. Dhana

    You can download full example here “link” is broken, Could please give me the latest download link..

  5. Anonymous

    I have updated the links. Unfortunately, I was not able to find client sources any more.

Comments are closed.