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.
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
Hi,
May I know how do you generate the server-keystore.jks and client-keystore.jks ?
Regards,
Alex
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.
Can you please provide end to end configuration ?
http://ruchirawageesha.blogspot.in/2010/07/how-to-create-clientserver-keystores.html
You can download full example here “link” is broken, Could please give me the latest download link..
I have updated the links. Unfortunately, I was not able to find client sources any more.