Wednesday, July 2, 2008

Web services security: Encryption with Rampart

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

17 comments:

Emil Prager said...

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

Prabath said...

Thanks a lot, Emil for your valuable thoughts - highly apppreciated.

Elena said...

Good words.

Meng said...

Thank you Prabath for sharing your expertise. I've been trying to figure out this Axis, Rampart encryption stuff and your tutorial really helped!

Shivani said...

Thank you Prabath for the tutorial! I tried using it with Eclise IDE - 3.3.1.1, Axis2 1.3,
Rampart 1.4, Jboss 4.2 and getting Error. Please advise on how to resolve it.

ERROR:

Exception in thread "main" org.apache.axis2.deployment.DeploymentException: org.apache.axis2.transport.java.JavaTransportSender
at org.apache.axis2.deployment.AxisConfigBuilder.processTransportSenders(AxisConfigBuilder.java:560)
at org.apache.axis2.deployment.AxisConfigBuilder.populateConfig(AxisConfigBuilder.java:112)
at org.apache.axis2.deployment.DeploymentEngine.populateAxisConfiguration(DeploymentEngine.java:615)
at org.apache.axis2.deployment.FileSystemConfigurator.getAxisConfiguration(FileSystemConfigurator.java:115)
at org.apache.axis2.context.ConfigurationContextFactory.createConfigurationContext(ConfigurationContextFactory.java:64)
at org.apache.axis2.context.ConfigurationContextFactory.createConfigurationContextFromFileSystem(ConfigurationContextFactory.java:180)
at org.apache.rampart.samples.sample05.Client.main(Client.java:37)
Caused by: java.lang.ClassNotFoundException: org.apache.axis2.transport.java.JavaTransportSender
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:164)
at org.apache.axis2.util.Loader.loadClass(Loader.java:261)
at org.apache.axis2.deployment.AxisConfigBuilder.processTransportSenders(AxisConfigBuilder.java:535)

Shivani said...

Hi Prabath,

Disregard the transport sender issue. It was resolved by making sure that the client and service property files were accessed OK.

Want to thank you for posting this tutorial and Emil for the Bouncy Castle JCE details. Great job! It helped me understand and implement the WS security!!!!!

crystal said...

Thank you so much!!polo shirt men'ssweate,Burberry Polo Shirts lacoste sweater, ralph lauren Columbia Jackets,ski clothing. Free Shipping, PayPal Payment. Enjoy your shopping experience on mensclothingus.com.You can find the father who desire fashionable, intellectual mens clothing simultaneously.
http://blog.livedoor.jp/lljj332
http://shoes-puma.jugem.jp
http://poloshirts--myfashion.blogspot.com
http://blades.blogsome.com
http://gillettefusion.edublogs.org

crystal said...

Awesome!!!Best wishes for you !!cheap polo shirts is the father of the summer should be prepared to most commonly used item, it has both style and shape of Ralph Lauren Polo, and vest with a random function polo ralph lauren, so that in the short-sleeved apply to both on many occasions, the pink and black color men's polo shirts brought into effect, lightweight cotton, linen texture to demonstrate masculine temperament and sense of fashion exhaustively.

venus said...

God bless you!I really agree with your opinions.Also,there are some new fashion things here,gillette razor blades.gillette mach3 razor bladesfor men.As for ladies,gillette venus razor blades must the best gift for you in summer,gillette fusion blades are all the best choice for you.

crystal said...

Perfect!!You are a outstanding person!Have you ever wore chaussures puma,Here are the most popular puma CAT,Puma shoes store gives some preview of puma speed cat,and casual but no sweat puma basket.
http://community.fox2now.com/venusjj
http://cheappolos.blog.drecom.jp
http://d.hatena.ne.jp/crystal666
http://www2.atword.jp/pumaspeed
http://www.seriousblogging.com/basketspuma

crystal said...

Do not mean bad.Thank you so much!I just want to show some fashion things to all of you.I like puma speed, puma femmes and other puma shoes. These puma sport items are at store recently and available for anyone.

venus said...

Fantastic!God bless you!Meanwhile,you can visit my China Wholesale,we have the highest quality but the lowest price fashion products wholesale from China.Here are the most popular China Wholesale productsfor all of you.You can visit http://chinaclothes.net.Also the polo clothing is a great choice for you.
real life
chaussures puma zone
chaussures puma online
polo shirts lj's blog
mensclothingus
ugg boots'camp

KK said...

Hi,
I am using Eclipse, axis2 1.3, rampart, 1.3, jdk 1.6, bcprov-jdk16-144.jar. I have done exactly the same stuff as mentioned but
I am getting the following error in the client side.

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/xml/utils/URI$MalformedURIException
at org.apache.ws.security.message.WSSecEncrypt.doEncryption(WSSecEncrypt.java:414)
at org.apache.ws.security.message.WSSecEncrypt.doEncryption(WSSecEncrypt.java:406)
at org.apache.ws.security.message.WSSecEncrypt.encryptForInternalRef(WSSecEncrypt.java:321)
at org.apache.ws.security.message.WSSecEncrypt.build(WSSecEncrypt.java:279)
at org.apache.ws.security.action.EncryptionAction.execute(EncryptionAction.java:62)
at org.apache.ws.security.handler.WSHandler.doSenderAction(WSHandler.java:192)
at org.apache.rampart.handler.WSDoAllSender.processBasic(WSDoAllSender.java:201)
at org.apache.rampart.handler.WSDoAllSender.processMessage(WSDoAllSender.java:64)
at org.apache.rampart.handler.WSDoAllHandler.invoke(WSDoAllHandler.java:72)
at org.apache.axis2.engine.Phase.invoke(Phase.java:292)
at org.apache.axis2.engine.AxisEngine.invoke(AxisEngine.java:212)
at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:377)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:374)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:211)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)
at wtp.ConverterStub.celsiusToFarenheit(ConverterStub.java:469)
at wtp.ConverterClient.main(ConverterClient.java:23)
Caused by: java.lang.ClassNotFoundException: org.apache.xml.utils.URI$MalformedURIException
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 17 more



Any help will be very great.

Toba Joseph said...

Thanks to Prabath for this great post and also for further clarifications provided by Emil.

It's unfortunate that some greedy spammers are taking over this thread. I wish you could delete their posts or have a better way of blocking them.

Toba Joseph said...

Hello Emil. Thank you for this awesome post.
I need your help on the error below. I got this while running the sample. What am I doing wrong?

Exception in thread "main" org.apache.axis2.AxisFault: The system is attempting to engage a module that is not available: rampart
at org.apache.axis2.engine.AxisConfiguration.engageModule(AxisConfiguration.java:464)
at org.apache.axis2.engine.AxisConfiguration.engageGlobalModules(AxisConfiguration.java:591)

at org.apache.axis2.deployment.DeploymentEngine.engageModules(DeploymentEngine.java:615)
at org.apache.axis2.deployment.FileSystemConfigurator.engageGlobalModules(FileSystemConfigur
ator.java:142)
at org.apache.axis2.context.ConfigurationContextFactory.createConfigurationContext(Configura
tionContextFactory.java:81)
at org.apache.axis2.context.ConfigurationContextFactory.createConfigurationContextFromFileSy
stem(ConfigurationContextFactory.java:184)
at org.apache.rampart.samples.sample05.Client.main(Client.java:37)

Crishantha Nanayakkara said...

Dear Prabath,

The link for rampart-sample.zip is no longer available. Appriciate if you can update that.

Tx
Crishantha

ramalingareddy2 said...

Hi Prabhat,

Firt i want to thank you for such a nice explanation. Its very nice.

BY following this post i have tried the example, I am able to Configure the security to a producer, While accessing from the consumer i am facing the issue. Below is the stack trace. Please help me in this regard.


Exception in thread "main" java.lang.RuntimeException: Undefined 'Security policy namespace cannot be null.' resource property
at org.apache.rampart.RampartException.getMessage(RampartException.java:81)
at org.apache.rampart.RampartException.(RampartException.java:41)
at org.apache.rampart.RampartException.(RampartException.java:57)
at org.apache.rampart.RampartMessageData.setWSSecurityVersions(RampartMessageData.java:387)
at org.apache.rampart.RampartMessageData.(RampartMessageData.java:261)
at org.apache.rampart.MessageBuilder.build(MessageBuilder.java:61)
at org.apache.rampart.handler.RampartSender.invoke(RampartSender.java:65)
at org.apache.axis2.engine.Phase.invokeHandler(Phase.java:340)
at org.apache.axis2.engine.Phase.invoke(Phase.java:313)
at org.apache.axis2.engine.AxisEngine.invoke(AxisEngine.java:262)
at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:427)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:406)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:165)
at com.pyro.service.MMTServiceStub.sendVoucherStatus(MMTServiceStub.java:1073)
at com.pyro.test.TestClient.main(TestClient.java:39)

Thanks for nice tutorial,
Regards,
Ramaling Reddy.T