Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spring Web Service to Securely Transfer SOAP Request and Response Call

DZone's Guide to

Spring Web Service to Securely Transfer SOAP Request and Response Call

In this post, we explore the way in which you can apply encryption to a SOAP request and response call using a 256-bit key.

Free Resource

Address your unique security needs at every stage of the software development life cycle. Brought to you in partnership with Synopsys.

In this article, I will explain the steps to transfer a SOAP Request and Response call securely with a 256-bit key.

I have used the Spring framework with JAXws and AES algorithms for encryption and decryption.

I hope this article will help you to understand this secure way of transferring SOAP calls using a public key as shown in below figure. 

Architecture

Sample Soap UI output

Soap UI Output

Web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">

<display-name>Spring Web Service with AES Encryption </display-name>

<servlet>
  <servlet-name>dispatcher</servlet-name> 
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>wsservice</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class> 
<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>wsservice</servlet-name> 
  <url-pattern>/AESWS</url-pattern> 
</servlet-mapping>

<listener>
  <listener-class>
  org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

<session-config>
<session-timeout>5</session-timeout>
</session-config>

</web-app>

Dispatcher-servlet.xml

The Config.properties files are configured in the property-placeholder. The AES Key is configured in these properties files.

Below is the AES encrypted Java file to marshal and unmarshal the data.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

    <context:annotation-config />

<context:component-scan base-package="com.shri" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix">
  <value>/WEB-INF/views/</value>
  </property>
  <property name="suffix">
    <value>.jsp</value>
  </property>
</bean>

<mvc:resources mapping="/resources/**" location="/resources/" />

<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message" />
</bean>

<context:property-placeholder location="classpath:config.properties, classpath:message.properties"
ignore-unresolvable="true"/>

<mvc:annotation-driven />

<!-- AWS Encryption starts -->
<bean id="encrypt" class="com.shri.ws.shared.AESEncrypted" >
<constructor-arg value="${aes_key}" type="String"></constructor-arg>  
</bean> 
<!-- AWS Encryption end-->

</beans>

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ws="http://jax-ws.dev.java.net/spring/core"
xmlns:wss="http://jax-ws.dev.java.net/spring/servlet" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://jax-ws.dev.java.net/spring/core
   http://jax-ws.dev.java.net/spring/core.xsd
   http://jax-ws.dev.java.net/spring/servlet
   http://jax-ws.dev.java.net/spring/servlet.xsd">

<import resource="dispatcher-servlet.xml" />

<!-- Web service binding status -->
<wss:binding url="/AESWS">
  <wss:service>
  <ws:service bean="#shriAESWS" />
  </wss:service>
</wss:binding>
<!-- Web service binding status ends -->

<!-- Web service methods bean Declaration -->

<!-- Service -->
<bean id="shriAESWS" class="com.shri.ws.service.WSAppService">
<property name="enqObjectImpl" ref="enqObjectImpl"></property>
</bean>
<!-- Service Ends -->

<!-- Implementation Object -->
<bean id="enqObjectImpl" class="com.shri.ws.impl.EnqObjectImpl" />
<!-- Implementation Object Ends -->
<!-- Web service methods bean Declaration Ends -->

</beans>

config.properties file

This is the public AES 256-bit key used for Encryption and Decryption.

aes_key= x!A%D*G-KaPdSgVkYp3s6v8y/B?E(H+M

The WSAppService extends SpringBeanAutowiringSupport.

Include your DAO Process once the request contains a valid file.

Create additional request using the autowired @WebMethod (operationName="User_DEFINE") in the method.

@WebService(serviceName = "WSAppService", portName = "WSAppServicePort", 
targetNamespace = "http://example.org/AESWS")
public class WSAppService extends SpringBeanAutowiringSupport {

@Resource
private WebServiceContext wsctx;

static Logger log = Logger.getLogger(WSAppService.class.getName());

private EnqObjectImpl enqObjectImpl;

@WebMethod(exclude = true)
public void setEnqObjectImpl(EnqObjectImpl enqObjectImpl) {
this.enqObjectImpl = enqObjectImpl;
}

//WebService 
@WebMethod(operationName = "WS")
@WebResult(name = "OUTBOUND")
@XmlJavaTypeAdapter(AESEncrypted.class) //Used to encrypt and decrypt
public String startProcess(
@WebParam(name = "INBOUND") @XmlJavaTypeAdapter(AESEncrypted.class) String request ){

    log.info(" Request : " + request);
    String response; // response
    Outbound outbound = new Outbound(); 

    try{
    log.info(request);
    //Validate the request
    if(request.trim().length()>0 && !request.trim().equalsIgnoreCase("?")) 

        //After unmarsel the header request tag, validate the request by XSD schema 
        //Step1
        XmlToObject xmlToObject = new XmlToObject();

        //Step2
        //Convert request string to object 
        Inbound inbound = (Inbound)XmlToObject.convert(Inbound.class, request, outbound);

        if (outbound.getStatus() == "failure") {
          response = outbound.toString(); // Outbound XML tag
          return response;
        }else{
          //Do your DAO process here 
          log.info(inbound.getName());
          log.info(inbound.getAddress());
          //Move to service and start DAO Process
          outbound = enqObjectImpl.startProcess(inbound, outbound);
          outbound.setStatus("success");
        }

    }else{
    outbound.setStatus("failure");
    }

  }catch(Exception err){
    log.info(err.toString());
    outbound.setStatus("failure");
  }
  response = outbound.toString();
  return response;
}

}

XmlToObject 

Convert XML code to an Object by using JAXB; validation is also enabled in this process. If we want, we can push through the response with a valid failure message.

public class XmlToObject {

static Logger log = Logger.getLogger(XmlToObject.class.getName());

public static Object convert (Class entityClass,String xml, Outbound outbound) throws SAXException{

  log.info("Entity class "+ entityClass.getName());
  //XML Validation 
  ValidationEventCollector vec = new ValidationEventCollector();

  Object  inbound = null;
  try {
  //It generate the URL entityClass.getResource() for the folder  
  URL xsdURL = entityClass.getResource("schema.xsd");   

  SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
  Schema schema = sf.newSchema(xsdURL); 
  JAXBContext jaxbContext = JAXBContext.newInstance(entityClass);

  Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
  unmarshaller.setSchema(schema);
  unmarshaller.setEventHandler(vec);
  StringReader reader = new StringReader(xml);
  inbound = (Object)unmarshaller.unmarshal(reader);

  } catch (JAXBException err) {
  if (vec != null && vec.hasEvents()) {
          for (ValidationEvent ve : vec.getEvents()) {

              String msg = ve.getMessage();
              ValidationEventLocator vel = ve.getLocator(); 
              int numLigne = vel.getLineNumber();
              int numColonne = vel.getColumnNumber();

              log.info("Error validation xml" + "Error : " + numLigne + "." + numColonne + ": " + msg +" : "+ ve.getSeverity());
              }
          }
    outbound.setStatus("failure");
    if (err.getLinkedException () == null){
           log.debug(err.toString());
          //Add This message to your response by adding additional datatype in Outdata file
    }else{
           log.debug(err.getLinkedException().getMessage());
    }
  }  

  return inbound;
  }
}

AESEncrypted.java

This file helps to encrypt and decrypt the data and it overwrites it with @Service org.springframework.stereotype.Service.

I used base64 code with an Apache common codec jar.

@Service
public class AESEncrypted extends XmlAdapter<String, String>{

    //Secret Key values set from properties files
    private static SecretKeySpec secretKey; 

private static Cipher cipher;

static Logger log = Logger.getLogger(AESEncrypted.class.getName());

public AESEncrypted(){}

//Value set from dispatcher Servlet constructor INJECTION
public AESEncrypted(String awsKey){
  try {
  secretKey = new SecretKeySpec(awsKey.getBytes(), "AES");
    cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
  } catch (Exception e) {
  log.info("Secrate Key Loading issue !! " );
  }
}

/**
 * Encrypts the value to be placed back in XML
 */
@Override
public String marshal(String plaintext)   {
  try{
         cipher.init(Cipher.ENCRYPT_MODE, secretKey);
         byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF8"));
         String encryptedString = new String(Base64.encodeBase64(cipherText),"UTF-8");
         return encryptedString;
  }catch(Exception err){
  log.info(err.getMessage());
      return "";
   }
}
/**
 * Decrypts the string value
 */
@Override
public String unmarshal(String encryptedText)  {
    try{
      cipher.init(Cipher.DECRYPT_MODE, secretKey);
      byte[] cipherText = Base64.decodeBase64(encryptedText.getBytes("UTF8"));
      String decryptedString = new String(cipher.doFinal(cipherText), "UTF-8");
      return decryptedString;
    }catch(Exception err){
     log.info(err.getMessage());   
      return "";
     }
 }
}

Inbound.java 

Here, we request an XML data file.

I converted the below Java file to an XSD file with the simple steps below. 

1. location> schemagen Program.java

Image title

package com.shri.ws.vo;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

/**
 * @author shrisowdhaman
 * Nov 15, 2017
 */
@XmlType(name = "REQ",propOrder={"name","address"})
@XmlRootElement(name ="REQ")
public class Inbound {

  private String name;

  private String address;

  @XmlJavaTypeAdapter(CollapsedStringAdapter.class) 
  @XmlElement(name="NAME",required=true, nillable=false)
  public String getName() {
  return name;
  }

  public void setName(String name) {
  this.name = name;
  }

  @XmlJavaTypeAdapter(CollapsedStringAdapter.class) 
  @XmlElement(name="ADDRESS",required=true, nillable=false)
  public String getAddress() {
  return address;
  } 
  public void setAddress(String address) {
  this.address = address;
  }
}

Schema.xsd

Used to validate the request file with a schema. Validation code available in XmlToObject.java 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="REQ" nillable="true" type="xs:anyType"/>

  <xs:complexType name="REQ">
    <xs:sequence>
      <xs:element name="NAME" type="xs:string"/>
      <xs:element name="ADDRESS" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>


</xs:schema>

EnqObject.java 

public interface EnqObject {

public Outbound startProcess(Inbound inbound,Outbound outbound);
}

EnqObjectImpl.java

@Service
public class EnqObjectImpl implements EnqObject{

static Logger log = Logger.getLogger(EnqObjectImpl.class.getName());

public Outbound startProcess(Inbound inbound, Outbound outbound) {

if (inbound != null) {
    try {
    //Call DAO Area and save the values in database
    log.info("Inside Inbond");
    } catch (Exception err) {
    outbound.setStatus("failure");
    }
  }  else {
  outbound.setStatus("failure");
  }

  return outbound;
  }

}

You can check my Source Code here.

Java, by default, supports 128-bit keys. If you want to use a 256-bit key, follow the below steps.

Update the Java Cryptography Extension (JCE) to Unlimited Strength Jurisdiction Policy Files to support a 256-bit key.

1. Download the java policy files for Java 7 or Java 8.
2. Locate the jre\lib\security directory for the Java instance that the Atom is using.
For example, this location might be:
C:\Program Files\Java\jre1.8.0_144\lib\security. 
C:\Program Files\Java\jdk1.8.0_144\jre\lib\security
3.Replace the following .jar files directory: local_policy.jar and US_export_policy.jar.
4.Stop and restart the eclipse/server.

Check AES Encryption code here.

Find out how Synopsys can help you build security and quality into your SDLC and supply chain. We offer application testing and remediation expertise, guidance for structuring a software security initiative, training, and professional services for a proactive approach to application security.

Topics:
webservice ,spring ,security ,spring security ,encryption

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}