This post discusses how to secure your web service with Rampart - through message encryption.
1. Setting up the environment
- Make sure you have installed Java and set your PATH env variable to C:\Program Files\Java\jdk1.5.0_06\bin [i.e : JAVA_HOME\bin]
- Download Axis2 1.4 - unzip and set the AXIS2_HOME env variable
- Download Rampart 1.4
- Copy [RAMPART_HOME]\lib\*.jar files to [AXIS2_HOME]\lib
- Copy [RAMPART_HOME]\modules\*.mar files to [AXIS2_HOME]\repository\modules
- Download Bouncy Castle jar and copy it to [AXIS2_HOME]\lib
- Download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 5.0 from here and copy the two jar files from the extracted jce directory (local_policy.jar and US_export_policy.jar) to $JAVA_HOME/jre/lib/security.
- Add all the jars inside [AXIS2_HOME]\lib to your CLASSPATH [I have created a batch file for this - so you can skip this step if you want]
2. Create keystores for the service and the client
- Assume you have a folder c:\keystores and two subfolders c:\keystores\service and c:\keystores\client
- For the case of encrption client will use the public key of the service and the service will use the public key of the client.
- For the case of decryption client will use it's private key and the service will use it's private key.
- So we need to have two key pairs for both the service and the client.
- Lets first create a key pair for the service and store them in a keystore.
\> cd c:\keystores\service
\> keytool -genkey -alias service -keyalg RSA -keysize 1024 -keypass servicekey -keystore service.jks -storepass servicestorekey
- This will create a keystore "service.jks" inside c:\keystores\service
- Password of the service.jks is "servicestorekey" [-storepass servicestorekey]
- Password of the private key of the service is "servicekey" [-keypass servicekey]
- Now lets create a key pair for the client and store them in a different keystore.
\> cd c:\keystores\client
\> keytool -genkey -alias client -keyalg RSA -keysize 1024 -keypass clientkey -keystore client.jks -storepass clientstorekey
- This will create a keystore "client.jks" inside c:\keystores\client
- Password of the client.jks is "clientstorekey" [-storepass clientstorekey]
- Password of the private key of the client is "clientkey" [-keypass clientkey]
- Now we are done creating two keystores with public/private key pairs for our service and the client
- But.. as I mentioned earlier client needs to know the public key of the service and the service needs to know the public key of the client.
- So we need to export the public key of the service from the service.jks and import it to the client.jks
\> cd c:\keystores\service
\> keytool -alias service -export -keystore service.jks -storepass servicestorekey -file servicepublickey.cer
\> cd c:\keystores\client
\> keytool -import -alias service -file ../service/servicepublickey.cer -keystore client.jks -storepass clientstorekey
- Now we need to export the public key of the client from the client.jks and import it to the service.jks
\> cd c:\keystores\client
\> keytool -alias client -export -keystore client.jks -storepass clientstorekey -file clientpublickey.cer
\> cd c:\keystores\service
\> keytool -import -alias client -file ../client/clientpublickey.cer -keystore service.jks -storepass servicestorekey
- Now we are done and we have two keystores.
1. c:\keystores\client\client.jks
2. c:\keystores\service\service.jks
3. Write and deploy the service
- Download and extract rampart-sample.zip from here.
- Copy c:\keystores\service\service.jks and [rampart-sample]\service\service.properties to [AXIS2_HOME]
- Build the service
[rampart-sample]\> classpath.bat
[rampart-sample]\> cd service
\> javac org/apache/rampart/samples/sample05/*.java
\> jar cvf SimpleService.aar *
- Copy the [rampart-sample]\service\SimpleService.aar to [AXIS2_HOME]\repository\services
- Run Axis2 simple server [[AXIS2_HOME]\bin\axis2server.bat]
- Type http://localhost:8080/axis2/services/ in the browser - you will simple SimpleService being deployed.
Now, lets highlight some of the Rampart related stuff we used while creating the service.
Service should ideally use the public key of the Client to encrypt the messages it sends and use it's own private key to decrypt messages it recieves.
By now we know both these keys are in service.jks keystore and we have copied it to the service classpath - where the service can pick the required keys.
All the configuration properties related to the service.jks are written to the property file [AXIS2_HOME]\service.properties
If you open the file service.properties, you'll see the following two properties there.
org.apache.ws.security.crypto.merlin.keystore.password=servicestorekey
org.apache.ws.security.crypto.merlin.file=service.jks
Here we specify the name of the keystore to use and the password to access the keystore. [this is the password we gave while we were creating the keystore].
Now the question is, how does the service pick this service.properties file.
That - we mention in the services.xml file [[rampart-sample]\service\META-INF\services.xml]
<parameter name="OutflowSecurity">
<action>
<items>Encrypt</items>
<encryptionuser>client</encryptionuser>
<encryptionpropfile>service.properties</encryptionpropfile>
</action>
</parameter>
If you can recall - we use the alias 'client' at the time we import the client public key to the service.jks. So the same name is used here for the encryptionuser.
The above discussion is related to encryption. How come Rampart knows the password of it's private key - to decrypt the receiving messages.
<parameter name="InflowSecurity">
<action>
<items>Encrypt</items>
<passwordCallbackClass>org.apache.rampart.samples.sample05.PWCBHandler</passwordCallbackClass>
<decryptionPropFile>service.properties</decryptionPropFile>
</action>
</parameter>
Here the Rampart uses a callback mechanism to retrieve the password - where the service author needs to implement the password callback class [[[rampart-sample]\service\org\apache\rampart\samples\sample05\PWCBHandler.java].
4. Write and run the client
- Build the Client
[rampart-sample]\> classpath.bat
[rampart-sample]\> cd client
\> javac org/apache/rampart/samples/sample05/*.java
\> java org/apache/rampart/samples/sample05/Client http://localhost:8080/axis2/services/SimpleService.SimpleServiceHttpEndpoint C:\axis2-1.4\repository
- C:\axis2-1.4\repository is for [AXIS2_HOME]\repository
- If everything is fine client should run with no issues.
- Configurations related to Rampart is similar to the way it was discussed previously for the service.
- You'll find client.jks and client.properties at [rampart-sample]\client - which is in the path of execution.
- You'll also find password callback class at the client end at [rampart-sample]\client\org\apache\rampart\samples\sample05\PWCBHandler.java
F a c i l e L o g i nby Prabath Siriwardena [prabath at wso2.com] |
|
Thursday, July 3, 2008
Web services security: Encryption with Rampart
Posted by
Prabath
at
Thursday, July 03, 2008
Labels: Rampart, Security, Web Services, WS-Security
Subscribe to:
Post Comments (Atom)
2 comments:
Hello, Prabath!
First of all, thank you for your tutorial article!
I tried to follow all the steps exactly as described in your tutorial (the only difference being the value for the %AXIS2_HOME%).
I've encountered the following minor problems, which I'll enumerate here to ease the following up of the tutorial for somebody else. (I also mention that I have never been working before with the BouncyCastle JCE provider.)
Places where more details should be useful:
1) from the provided link to the BouncyCastle site it's not very clear what exactly should be downloaded from there. There are 2 JCE provider jars per jdk version, but, as it's written there, the one with -ext- in the name should be ignored here (as it contains the proprietary algorithm IDEA). For Java 5, from what I've figured out that, it should be that with the name of the form bcprov-jdk15-%version%.jar (currently, the last version is 1.40, so the file should be bcprov-jdk15-140.jar). This jar should be copied in both:
i) the %AXIS2_HOME%\lib ($AXIS2_HOME/lib) directory, and:
ii) somewhere in the runtime classpath, for example (maybe not the best example) in %JAVA_HOME%\jre\lib\ext\
Remark: it's not generally recommendable to put our jars in system wide locations such $JAVA_HOME/jre/lib/ext/ or $AXIS2_HOME/lib/, but given the nature of this libray (a JCE provider), it deserves to be there (of course, if we have writing rights to the $JAVA_HOME directory).
2) The next thing to mention is that we have to add an entry in the java.security file (%JAVA_HOME%\jre\lib\security),
in the # List of providers and their preference orders (see above):, more precisely:
security.provider.N+1=org.bouncycastle.jce.provider.BouncyCastleProvider,
where N is the preference order of the last JCE provider mentioned there (by default, there are 6 in Java 5), so (in that case) we'll have to add the line:
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
Not doing so will result in error messages like this:
org.apache.ws.security.WSSecurityException: WSHandler: Encryption: error during message processingorg.apache.
ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used (unsupported key transport en
cryption algorithm: No such algorithm: http://www.w3.org/2001/04/xmlenc#rsa-1_5)
or, more detailed:
Exception in thread "main" org.apache.axis2.AxisFault: WSHandler: Encryption: error during message processing org.apache.ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used (unsupported key transport encryption algorithm: No such algorithm: http://www.w3.org/2001/04/xmlenc#rsa-1_5)
at org.apache.rampart.handler.WSDoAllSender.processMessage(WSDoAllSender.java:67)
at org.apache.rampart.handler.WSDoAllHandler.invoke(WSDoAllHandler.java:72)
at org.apache.axis2.engine.Phase.invoke(Phase.java:317)
at org.apache.axis2.engine.AxisEngine.invoke(AxisEngine.java:264)
at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:429)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:401)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:228)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)
at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:548)
at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:528)
at org.apache.rampart.samples.sample05.Client.main(Client.java:45)
Caused by: org.apache.ws.security.WSSecurityException: WSHandler: Encryption: error during message processing org.apache.ws.security.WSSecurityException: An unsupported signature or encryption algorithm was used (unsupported key transport encryption algorithm: No such algorithm: http://www.w3.org/2001/04/xmlenc#rsa-1_5)
at org.apache.ws.security.action.EncryptionAction.execute(EncryptionAction.java:65)
at org.apache.ws.security.handler.WSHandler.doSenderAction(WSHandler.java:197)
at org.apache.rampart.handler.WSDoAllSender.processBasic(WSDoAllSender.java:201)
at org.apache.rampart.handler.WSDoAllSender.processMessage(WSDoAllSender.java:64)
... 10 more
3) we should overwrite the client java keystore file [rampart-sample]\client\client.jks, which is already present in the rampart-sample.zip with the one built by us during the keystores setup (the tutorial path would be C:\keystores\client\client.jks).
Not doing so will result in error messages like this:
Exception in thread "main" org.apache.axis2.AxisFault: WSDoAllReceiver: security processing failed
at org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(Utils.java:512)
at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(OutInAxisOperation.java:370)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:416)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:228)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)
at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:548)
at org.apache.axis2.client.ServiceClient.sendReceive(ServiceClient.java:528)
at org.apache.rampart.samples.sample05.Client.main(Client.java:45)
After those supplemental steps, everything should go smooth, and we should see the result as:
<ns:echoResponse xmlns:ns="http://sample05.samples.rampart.apache.org"><ns:return>Hello world</ns:return></ns:echoResponse>
In conclusion, thanks again, Prabath, for your tutorial, it might help many of us which have to do with WSS etc.
PS: Another suggestion might be to continue this tutorial with a .NET (or native win32?) client that would consume such a securized service, it's a situation not very rarely encountered in practice -- Java EE on the server side, and .NET clients on the client one (and who knows?, maybe I can contribute myself on some parts of such a post).
Thanks a lot, Emil for your valuable thoughts - highly apppreciated.
Post a Comment