WSO2Con 2013 CFP Banner

Secure Message Exchanges with Multiple Users

Discuss this article on Stack Overflow
By Ruchith Fernando
  • 5 Sep, 2006
  • Level: 
  • Reads: 16315

In many practical scenarios a service is expected to encrypt responses to its clients. In such scenarios the service will have to use the client's public key certificate to encrypt the response. It is possible with Apache Axis2 to implement such a scenario in four simple steps using Apache Rampart (the WS-Security module based on Apache WSS4J).

Overview

The overview is as follows, the client sends a message encrypted with the service's public key. The client usually also adds on information about its client certificate or even the whole certificate inside the message. This enables the server to find/use the client's public key to encrypt the response back to the client. The main problem in this scenario is about certificate maintenance. Basically the need to keep adding client certificates to the service's key store when more clients need to access the service. If the client sends its public key in the request, then the server can just peek into the request and extract the public key certificate needed for encrypting the response. The following steps show how we can achieve this using Axis2's Rampart module.

Step 1: Client's Outflow Configuration

For the service to encrypt the response to a certain client, the service should be able to access out the client's public key certificate. The client will send its public key certificate information across to the service by signing the request using its private key, where there will be information available to identify the public key certificate to use in verifying the signature.

An example Rampart outflow configuration to sign a request is as follows:

<parameter name="OutflowSecurity">
<action>
<items>Timestamp Signature Encrypt</items>
<user>client</user>
<encryptionUser>service</encryptionUser>
<signaturePropFile>client.properties</signaturePropFile>
<passwordCallbackClass>org.apache.rampart.samples.reqsigcert.
PWCBHandler</passwordCallbackClass>
<signatureKeyIdentifier>DirectReference</signatureKeyIdentifier>
<signatureParts>{Element}{http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp</signatureParts>
</action>
</parameter>

Note: We add a Timestamp into the message and we sign that Timestamp. This is to make sure this request cannot be replayed again.

Also it's very important that we encrypt the request body sent to the service to prevent a man-in-the-middle attack. If the body is sent as plain text it will allow an attacker to intercept the message, create an appropriate response and return it to the client.

<encryptionUser>service</encryptionUser>

The above "encryptionUser" element specifies the alias of the encryption certificate. In this case the service's public key certificate is available in the keystore under the alias 'service'.

There are few different ways that signature can let the service know about the public key certificate used. This is by the way the key is referenced in by the signature:

  • Reference to a Binary Security Token:
  • Here the public key certificate is available in the request message itself.
    • Reference to a Subject Key Identifier
    This is the value of certificate extension with the object id 2.5.29.14
    • Reference to an Issuer of the Certificate and the Serial Number
    This is a combination of the distinguished name (DN) of the issuer of the public key certificate and the serial number of the certificate

Out of the above three mechanisms "Reference to Subject Key Identifier" and "Reference to an Issuer and the Serial Number" requires the client's certificate to be stored at the service, in addition to the certificate of the issuer of the client certificate. But in the case of the "Reference to a Binary Security Token" there's no need to store the certificate, and we only have to store the certificate of the issuer that issued the certificate. In this case the service will trust all the clients bearing valid signatures created using private keys of which the certificates are signed by the trusted issuer.

Note the "signatureKeyIdentifier" child element of the "action" element in the above configuration.

<signatureKeyIdentifier>DirectReference</signatureKeyIdentifier>

This tells Rampart to add the public key certificate of the client into the message (Security header) as a BinarySecurityToken and to add a reference to it from the signature. This does not require the service to have the client's certificate stored.

In the case the service stores each and every certificate of the clients that it trusts, then the client can use a different reference mechanism as shown below:

  • Reference to an issuer of a certificate and its serial number

Do not use the "signatureKeyIdentifier" child element. This is the default signature key reference reference mechanism.

  • Reference to a Subject Key Identifier
<signatureKeyIdentifier>SKIKeyIdentifier</signatureKeyIdentifier>

In most practical use cases it is not feasible to store and manage all certificates of all trusted clients for a given services. Also it's not feasible to host different services to different clients as well. Therefore, the service can expect the clients to send their certificates in the message itself using the direct reference mechanism that was mentioned earlier. In this case the service will only have to store and manage a list of trusted issuers of certificates (CAs). Any client holding a valid certificate issued by a trusted issuer will be able to access the service.

Step 2: Service Inflow Configuration

Now the service should be configured to accept the signed request. Following is an example Rampart inflow configuration for the service to configure Rampart to accept a Timestamp and to verify the signature.

<parameter name="InflowSecurity">
<action>
<items>Timestamp Signature Encrypt</items>
<passwordCallbackClass>org.apache.rampart.samples.reqsigcert.
PWCBHandler</passwordCallbackClass>
<signaturePropFile>service.properties</signaturePropFile>
</action>
</parameter>

Note: There's no need to specify the reference mechanism of the certificate. Apache Rampart/WSS4J can figure out by processing the Signature element in the Security header.

The client certificate will be extracted and stored in the inflow message context by WSS4J and this will be used by Rampart in the outflow to encrypt the response to the client.

Step 3: Service Outflow Configuration

At this point we have to encrypt the request with the client's certificate. Following is an example outflow configuration for the service:

<parameter name="OutflowSecurity">
<action>
<items>Timestamp Encrypt Signature</items>
<encryptionPropFile>service.properties</encryptionPropFile>
<encryptionKeyIdentifier>SKIKeyIdentifier</encryptionKeyIdentifier>
<encryptionUser>useReqSigCert</encryptionUser>
<user>service</user>
<signaturePropFile>service.properties</signaturePropFile>
<passwordCallbackClass>org.apache.rampart.samples.reqsigcert.
PWCBHandler</passwordCallbackClass>
<signatureParts>{Element}{http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}
{http://schemas.xmlsoap.org/soap/envelope/}Body</signatureParts>
</action>
</parameter>

With the above configuration Rampart will encrypt the Body contents of the SOAP Envelope. The most important item in this is the "encryptionUser" element.

<encryptionUser>useReqSigCert</encryptionUser>

This instructs Rampart/WSS4J to use the certificate that was used to sign the request. One can specify the encrypted parts to encrypt different parts of the message to be encrypted.

It is very important that the service signs the Timestamp and the encrypted body of the response to prevent a third party from sending a bogus encrypted response to the requester. Note that the "user" child element specifies that alias of the private key of the service that is used to sign the response.

Step 4: Client Inflow Configuration

Now we simply have to configure Rampart at the client's inflow to accept an encrypted and signed message, and decrypt it as shown below:

<parametername=InflowSecurity>
<action>
<items>Timestamp Encrypt Signature</items>
<passwordCallbackClass>org.apache.rampart.samples.reqsigcert.PWCBHandler
</passwordCallbackClass>
<decryptionPropFile>client.properties</decryptionPropFile>
<signaturePropFile>client.properties</signaturePropFile>
</action>
</parameter>

Try it out !

You can try this out with the example source code provided here . You will require JDK 1.4 and Apache Ant 1.6.2 or higher as prerequisites.

Simply unzip the "client-cert-encr.zip" and open up two shells within the "client-cert-encr" directory where it contains the Apache Ant build.xml file.

Use one shell to execute the following to start the standalone server hosting the secured service:

$ ant service

Now use the other shell to execute the client using the following:

$ ant client

If you wish to see the message exchange between the client and the service, start the TCPMon tool to listen on port 9090 (example) and redirect the requests to port 8080 of localhost. Then you can change the following property in the build.xml file

<property name="client.port" value="8080"/>

to

<property name="client.port" value="9090"/>

Now when you run the client you will be able to see the messages exchanged.

The keystore used by the service contains the client's certificate and the certificate of the issuer. Therefore, even though the default configuration provided with the sample uses a reference to a binary security token, you can try out the other signature key reference mechanisms by changing the client's configuration parameters available in "client-cert-encr/reqsigcert/client.axis2.xml" file. The service configuration is available in "client-cert-encr/reqsigcert/services.xml".

Author

Ruchith Fernando, Senior Software Engineer, WSO2 Inc. ruchithf at apache dot org

WSO2Con 2014 USA