Encrypting web service demo in Java

The example below shows two simple crypto operations that are exposed via a web service interface. The approach is top-down, as it starts with the interface description from which service stubs are generated.

The service interface is defined in a WSDL file, which describes the signatures of two operations encryptMessage and decryptMessage, the corresponding message types and bindings. The WSDL file uses the document/literal (wrapped) style, which is arguably best suited in many cases.

The WSDL file is the starting point for the skeleton generation of the service implementation, in this case using the Apache Axis framework for Java shipped with Eclipse EE.

The Apache Axis framework generates the necessary Java classes and deployment descriptors to deploy the web service in a servlet container such as Apache Tomcat. The operations from the WSDL file correspond to the Java methods encryptMessage() and decryptMessage() in class EncryptionServiceSoapBindingImpl.

The implementation of the two methods is straightforward. They perform an encryption and decryption of a passed byte array, respectively, using AES with a provided key. Since Cipher Block Chaining is used as mode of operation, the initialization vectors used for the encryption and decryption of a specific byte array have to match. Rather than passing it as an additional argument, a fixed vector is used, which is not smart security-wise, but sufficient for demonstration purposes.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://encryptionservice.example.org" xmlns:impl="http://encryptionservice.example.org" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<wsdl:types>
		<schema elementFormDefault="qualified" targetNamespace="http://encryptionservice.example.org" xmlns="http://www.w3.org/2001/XMLSchema">
			<element name="encryptMessage">
				<complexType>
					<sequence>
						<element name="plaintext" type="xsd:base64Binary"/>
						<element name="key" type="xsd:base64Binary"/>
					</sequence>
				</complexType>
			</element>
			<element name="encryptMessageResponse">
				<complexType>
					<sequence>
						<element name="encryptMessageReturn" type="xsd:base64Binary"/>
					</sequence>
				</complexType>
			</element>
			<element name="decryptMessage">
				<complexType>
					<sequence>
						<element name="ciphertext" type="xsd:base64Binary"/>
						<element name="key" type="xsd:base64Binary"/>
					</sequence>
				</complexType>
			</element>
			<element name="decryptMessageResponse">
				<complexType>
					<sequence>
						<element name="decryptMessageReturn" type="xsd:base64Binary"/>
					</sequence>
				</complexType>
			</element>
		</schema>
	</wsdl:types>
	<wsdl:message name="encryptMessageResponse">
		<wsdl:part element="impl:encryptMessageResponse" name="parameters">
      </wsdl:part>
	</wsdl:message>
	<wsdl:message name="decryptMessageRequest">
		<wsdl:part element="impl:decryptMessage" name="parameters">
      </wsdl:part>
	</wsdl:message>
	<wsdl:message name="decryptMessageResponse">
		<wsdl:part element="impl:decryptMessageResponse" name="parameters">
      </wsdl:part>
	</wsdl:message>
	<wsdl:message name="encryptMessageRequest">
		<wsdl:part element="impl:encryptMessage" name="parameters">
      </wsdl:part>
	</wsdl:message>
	<wsdl:portType name="EncryptionService">
		<wsdl:operation name="encryptMessage">
			<wsdl:input message="impl:encryptMessageRequest" name="encryptMessageRequest">
       </wsdl:input>
			<wsdl:output message="impl:encryptMessageResponse" name="encryptMessageResponse">
       </wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="decryptMessage">
			<wsdl:input message="impl:decryptMessageRequest" name="decryptMessageRequest">
       </wsdl:input>
			<wsdl:output message="impl:decryptMessageResponse" name="decryptMessageResponse">
       </wsdl:output>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="EncryptionServiceSoapBinding" type="impl:EncryptionService">
		<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
		<wsdl:operation name="encryptMessage">
			<wsdlsoap:operation soapAction=""/>
			<wsdl:input name="encryptMessageRequest">
				<wsdlsoap:body use="literal"/>
			</wsdl:input>
			<wsdl:output name="encryptMessageResponse">
				<wsdlsoap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="decryptMessage">
			<wsdlsoap:operation soapAction=""/>
			<wsdl:input name="decryptMessageRequest">
				<wsdlsoap:body use="literal"/>
			</wsdl:input>
			<wsdl:output name="decryptMessageResponse">
				<wsdlsoap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="EncryptionService">
		<wsdl:port binding="impl:EncryptionServiceSoapBinding" name="EncryptionService">
			<wsdlsoap:address location="http://localhost:8080/WS/services/EncryptionService"/>
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>
package org.example.encryptionservice;

import java.rmi.RemoteException;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptionServiceSoapBindingImpl implements org.example.encryptionservice.EncryptionService_PortType {

	private String transformation = "AES/CBC/PKCS5Padding";
	private IvParameterSpec ivSpec;
	private Cipher c;

	public EncryptionServiceSoapBindingImpl() {

		super();

		byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
		ivSpec = new IvParameterSpec(iv);

		try {
			c = Cipher.getInstance(transformation);
		} catch (Exception e) {
			// do nothing
		}

	}

	public byte[] encryptMessage(byte[] plaintext, byte[] key) throws RemoteException {

		SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

		try {
			c.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
			return c.doFinal(plaintext);
		} catch (Exception e) {
			throw new RemoteException("Encryption operation failed.", e);
		}

	}

	public byte[] decryptMessage(byte[] ciphertext, byte[] key) throws java.rmi.RemoteException {

		SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

		try {
			c.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
			return c.doFinal(ciphertext);
		} catch (Exception e) {
			throw new RemoteException("Decryption operation failed.", e);
		}
	}

}