Basic Signing and Encryption Scenario:
Overview
The
scenario described here is a client-server application, where an asymmetric
binding policy is set up to encrypt and sign the SOAP body of messages that
pass back and forth between the client and the server.
Example scenario
Below
shows an overview of the basic signing and encryption scenario, which is
specified by associating an asymmetric binding policy with an endpoint in the
WSDL contract.
When
the client in Figure 1.1 invokes a synchronous operation on
the recipient's endpoint, the request and reply message are processed as
follows:
- As the outgoing request message passes through the WS-SecurityPolicy handler, the handler processes the message in accordance with the policies specified in the client’s asymmetric binding policy. In this example, the handler performs the following processing:
- Encrypt the SOAP body of the message using Bob’s public key.
- Sign the encrypted SOAP body using Alice’s private key.
- As the incoming request message passes through the server's WS-SecurityPolicy handler, the handler processes the message in accordance with the policies specified in the server’s asymmetric binding policy. In this example, the handler performs the following processing:
- Verify the signature using Alice’s public key.
- Decrypt the SOAP body using Bob’s private key.
- As the outgoing reply message passes back through the server's WS-SecurityPolicy handler, the handler performs the following processing:
- Encrypt the SOAP body of the message using Alice’s public key.
- Sign the encrypted SOAP body using Bob’s private key.
- As the incoming reply message passes back through the client's WS-SecurityPolicy handler, the handler performs the following processing:
- Verify the signature using Bob’s public key.
- Decrypt the SOAP body using Alice’s private key.
Using Axis1.4 and WSS4j for message signature and Encryption
Server Side:
Required Third Party Jars: xalan-2.7.1.jar,
xmlsec-1.4.5.jar, wss4j-1.5.12.jar and Axis1.4 jar files
Put all these files in your
web application’s lib directory
Step1:
We need to create a public
private key pair for message signing and encryption, We can use Java keytool
for generating key store and keys using below commands
Below command will create a file called server.keystore
which contains a private and public key pair for encryption purpose. Make sure
your java path is set and java home also set.
Go to command prompt, go to
c:\ and some folder where you want to create your keystore like test
type below command with your own argument.
a)
C:\test> keytool -genkey -dname
"CN=Server,OU=Encryption, O=Mycompany, L=Newyork, S=CT, C=US" -alias
serverkey -keypass Welcome1 -validity 9999 -keyalg RSA -sigalg SHA1withRSA
-keystore server.keystore -storepass Welcome1
explaination :where CN->Common
Name,OU->Organisational
Unit,0->Organisation,L->location,S->State,C->Country Code
keyalg-Key Alogrthim
sigalg-Signature Algorthim
keystore-keystorename
storepass-keystores password
keypass-Key password
validity-key Validity in days
-dname-distinguied name
b) This will create a file
called client.keystore that contains a key pair for message signing. use your own arguments
keytool -genkey -dname
"CN=Client, OU=Signing, O=Mycompany, L=NewYork, S=CT, C=US" -alias
clientkey -keypass Welcome1 -validity 9999 -keyalg RSA -sigalg SHA1withRSA
-keystore client.keystore -storepass Welcome1
c) .In order for the client to trust the server, we need to export the
public key from server.keystore and import it to client.keystore:
keytool -export -alias
serverkey -keystore server.keystore -storepass Welcome1 -file servercert.cer
keytool -import -alias
serverkey -keystore client.keystore -storepass Welcome1 -file servercert.cer
d) In order for the server to trust the client, we need to export the
public key from client.keystore and import it to server.keystore:
keytool -export -alias
clientkey -keystore client.keystore -storepass Welcome1 -file clientcert.cer
keytool -import -alias
clientkey -keystore server.keystore -storepass Welcome1 -file clientcert.cer
Step2: Update server-config.wsdd of existing webservice
located under WEB-INF directory
Add the highlighted code in
the server-config.wsdd for your existing service here my existing service is
UserSoap
see below I added the code in yelow to server-config.wsdd
<ns1:service
name="UserSoap" provider="java:RPC"
style="wrapped" use="literal">
<ns3:operation
name="changePassword" qname="ns1:changePassword"
returnQName="ns1:changePasswordReturn"
returnType="ns2:UserBean" soapAction="" xmlns:ns1="http://user.services.soap.lcc.mycompany.com"
xmlns:ns2="http://beans.user.services.soap.lcc.mycompany.com"
xmlns:ns3="http://xml.apache.org/axis/wsdd/">
<ns3:parameter
qname="ns1:newPassword" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:userId" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:oldPassword" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:retypePassword" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:profile_chgpwd" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:last_pwdchg" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
<ns3:parameter
qname="ns1:submission" type="xsd:string"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
</ns3:operation>
<ns1:requestFlow>
<ns1:handler
type="java:org.apache.ws.axis.security.WSDoAllReceiver">
<ns1:parameter
name="passwordCallbackClass" value="com.mycompany.lcc.soap.services.user.ChangePasswordCallBackHandler"/>
<ns1:parameter
name="action" value="Signature Encrypt"/>
<ns1:parameter
name="signaturePropFile" value="crypto.properties" />
<parameter
name="decryptionPropFile" value="crypto.properties" />
<parameter
name="encryptionPropFile" value="crypto.properties" />
</ns1:handler>
</ns1:requestFlow>
<ns1:parameter
name="allowedMethods" value="changePassword"/>
<ns1:parameter name="typeMappingVersion"
value="1.2"/>
<ns1:parameter
name="wsdlPortType" value="UserSoap"/>
<ns1:parameter name="className"
value="com.mycompany.lcc.soap.services.user.UserSoap"/>
<ns1:parameter
name="wsdlServicePort" value="UserSoap"/>
<ns1:parameter
name="schemaQualified"
value="http://beans.user.services.soap.lcc.mycompany.com,http://user.services.soap.lcc.mycompany.com"/>
<ns1:parameter
name="wsdlTargetNamespace" value="http://user.services.soap.lcc.mycompany.com"/>
<ns1:parameter name="wsdlServiceElement"
value="UserSoapService"/>
<ns1:typeMapping
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
encodingStyle="" qname="ns3:UserBean"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
type="java:com.mycompany.lcc.soap.services.user.beans.UserBean"
xmlns:ns3="http://beans.user.services.soap.lcc.mycompany.com"/>
</ns1:service>
step3: Create a callback handler on server
package com.mycompany.lcc.soap.services.user;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import
javax.security.auth.callback.CallbackHandler;
import
javax.security.auth.callback.UnsupportedCallbackException;
import
org.apache.ws.security.WSPasswordCallback;
public class
ChangePasswordCallBackHandler implements
CallbackHandler{
@Override
public void handle(Callback[] callbacks) throws
IOException,
UnsupportedCallbackException
{
for (int i = 0; i <
callbacks.length; i++) {
WSPasswordCallback pwcb =
(WSPasswordCallback)callbacks[i];
String id = pwcb.getIdentifier();
int usage = pwcb.getUsage();
if (usage ==
WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) {
// used to retrieve password
for private key
if ("serverkey".equals(id)) {
pwcb.setPassword("Welcome1");
}
else if
("clientkey".equals(id)) {
pwcb.setPassword("Welcome1");
}
}
}
}
}
Put above class inside classes or in classpath of
weaplication
Step4: Copy server.keystore
file created in step 1a to WEB-INF/Classes directory
Step5: Create a property file named crypto.properties inside
classes directory
Copy below code to crypto.properties
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=Welcome1
org.apache.ws.security.crypto.merlin.keystore.alias=serverkey
org.apache.ws.security.crypto.merlin.file=server.keystore
Step6 : Restart the tomcat
Server,web service security with message encryption and signing is applied.
Client Side: Stand Alone Client:
Jars required: Required Third Party Jars: xalan-2.7.1.jar,
xmlsec-1.4.5.jar, wss4j-1.5.12.jar and Axis1.4 jar files
Assumption: Webservice client Code is
already generated using wsdl file
Step1: Copy the client.keystore file
from Server Side Step 1b to the root
path of your client project.
Step2:Create a crypto.properties
file and copy below code to it and put it
to the root class path
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=Welcome1
org.apache.ws.security.crypto.merlin.keystore.alias=clientkey
org.apache.ws.security.crypto.merlin.file=client.keystore
Step3: Create a wsdd file called
client_deploy.wsdd and copy it to the root class path
Contents of the wsdd file
given below
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"
/>
<globalConfiguration>
<requestFlow>
<handler type="java:org.apache.ws.axis.security.WSDoAllSender">
<parameter name="user" value="clientkey"
/>
<parameter name="encryptionUser" value="serverkey"/>
<parameter name="action" value="Signature
Encrypt" />
<parameter name="signaturePropFile" value="crypto.properties" />
<parameter name="encryptionPropFile" value="crypto.properties" />
<parameter name="passwordCallbackClass" value="com.ChangePwdClientCallBackHandler"
/>
</handler>
</globalConfiguration>
</deployment>
Step4: Add the below Handler class to your client code
package com;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ChangePwdClientCallBackHandler implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException
{
System.out.println("Inside Handler");
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
String id =
pwcb.getIdentifier();
int usage = pwcb.getUsage();
if (usage == WSPasswordCallback.DECRYPT ||
usage == WSPasswordCallback.SIGNATURE) {
// used to retrieve password for private key
if ("clientkey".equals(id)) {
pwcb.setPassword("Welcome1");
}
}
}
}
}
Step 5:
Add the path to configuration(wsdd file) in your existing web service calling code
package com;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.configuration.FileProvider;
import com.mycompany.lcc.soap.services.user.UserSoap;
import com.mycompany.lcc.soap.services.user.UserSoapServiceLocator;
import com.mycompany.lcc.soap.services.user.beans.UserBean;
public class CpClient {
public static void main(String[] args) throws ServiceException ,RemoteException, MalformedURLException,
FileNotFoundException{
//System.setProperty("axis.ClientConfigFile",
"client_deploy.wsdd");
//Give relative path to wsdd file
EngineConfiguration
config = new FileProvider("./client_deploy.wsdd");
UserSoapServiceLocator
sl= new UserSoapServiceLocator(config);
UserSoap
us = sl.getUserSoap(new java.net.URL("http://localhost:8080/services/UserSoap"));
//calling webservice method
UserBean u = us.changePassword("Test12345", "43243", "Test1234", "Test12345");
System.out.println("isSuccess:"+u.getServiceSuccess());
System.out.println("Error mesg: "+u.getErrorMsg());
System.out.println("Success msg: "+u.getSuccessMsg());
}
}
Web Service Client User Guide for calling webservice
inside Web Application
Running client using
Axis1.4
A jar file containing
all the java classes needed to call Axis1.4 web service will be provided to end user
For Example- suppose
this file name is ChangePasswordClient.jar
Steps to use in
standalone/web project:
1-
Configure build
path of project and set
ChangePasswordClient.jar in class path
as External Jar in eclipse
2 Jar files required for writing simple Axis1.4 Client
Set below jar
files in class path as external jars in eclipse
axis.jar
commons-discovery-0.2.jar
commons-logging.jar
jaxrpc.jar
saaj.jar
wsdl4j.jar
Required Jars for security : xalan-2.7.1.jar, xmlsec-1.4.5.jar, wss4j-1.5.12.jar
3-
Create Java
class and call the webservice
Ex-
package
com;
import
java.net.MalformedURLException;
import
java.rmi.RemoteException;
import
javax.xml.rpc.ServiceException;
import
com.mycompany.lcc.soap.services.user.UserSoap;
import
com.mycompany.lcc.soap.services.user.UserSoapServiceLocator;
import
com.mycompany.lcc.soap.services.user.beans.UserBean;
public class
CPClient {
public static void
main(String[] args)throws ServiceException ,RemoteException, MalformedURLException {
EngineConfiguration config = new FileProvider("./client_deploy.wsdd");
UserSoapServiceLocator
sl= new UserSoapServiceLocator(config);
System.out.println("test1");
UserSoap us =
sl.getUserSoap(new java.net.URL("http://localhost:8080/services/UserSoap"));
System.out.println("test2");
//calling webservice method
UserBean u =
us.changePassword("Test12345", "14936079", "Test1234", "Test12345", null, null,null);
System.out.println("isSuccess:"+u.getServiceSuccess());
System.out.println("Error mesg: "+u.getErrorMsg());
System.out.println("Success msg: "+u.getSuccessMsg());
}
}
above code can be called from inside a servlet or POJO