Wednesday, December 29, 2010

Sending SOAP messages through HTTPS using SAAJ

Introduction
A month ago a task assigned to me. It was calling the API of a network management device by sending SOAP messages through HTTPS. It wasn't so easy and straight forward to me.

SAAJ
I've used SAAJ for calling web services. It is well known for Java developers.

Steps
The process contains below steps:
1. Export the HTTPS Certificate into a file
2. Import the HTTPS Certificate into Java HTTPS certificates
3. Creating a SOAP connection
4. Creating a SOAP message
5. Populating the message
6. Trust to HTTPS certificates
7. Sending the message
8. Retrieving the reply

Get the Certificate
Assume WSDL address is:
https://192.168.10.1:8991/wsdl/balance.wsdl

Read this to find out how to get the certificate for access to the upper WSDL address.
Here I saved the certificate in "amirssw.cer" file.
Java HTTPS certificates stored in
$JAVA_HOME/jre/lib/security/cacerts

To import your new certificate into "cacerts" run below command:
sudo keytool -import -file amirssw.cer -keystore cacerts

The password is "changeit".

SOAP message
In my sample SOAP message stored in a file: "/home/amir/projects/mine/p8/message/helloWorld.msg".
You can find a sample SOAP message here.

Java Code
This is a simple code that shows clearly how to send a SOAP message through HTTPS. The only different between sending the message on HTTP over HTTPS is the URL and doTrustToCertificates() method call just before sending the message.


package com.neverhood.webssl;

import javax.net.ssl.*;
import javax.xml.soap.*;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.FileInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Vector;

public class Client {

public static void main(String[] args) {
talk("https://192.168.10.1:8991/balance/soap", "/home/amir/projects/mine/p8/message/helloWorld.msg");
}

private static void talk(String urlval, String msgPath) {
try {
// Create message
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

// Object for message parts
SOAPPart sp = msg.getSOAPPart();
StreamSource prepMsg = new StreamSource(new FileInputStream(msgPath));
sp.setContent(prepMsg);

// Save message
msg.saveChanges();

// View input
System.out.println("\n Soap request:\n");
msg.writeTo(System.out);
System.out.println();

// Trust to certificates
doTrustToCertificates();

//SOAPMessage rp = conn.call(msg, urlval);
SOAPMessage rp = sendMessage(msg, urlval);

// View the output
System.out.println("\nXML response\n");

// Create transformer
TransformerFactory tff = TransformerFactory.newInstance();
Transformer tf = tff.newTransformer();

// Get reply content
Source sc = rp.getSOAPPart().getContent();

// Set output transformation
StreamResult result = new StreamResult(System.out);
tf.transform(sc, result);
System.out.println();

// now GET the Response and PARSE IT !
Vector list = new Vector();
SOAPBody soapBody = rp.getSOAPBody();
Iterator iterator1 = soapBody.getChildElements();
while (iterator1.hasNext()) {
SOAPBodyElement ThisBodyElement = (SOAPBodyElement) iterator1.next();
Iterator it2 = ThisBodyElement.getChildElements();
while (it2.hasNext()) {
SOAPElement child2 = (SOAPElement) it2.next();
Iterator it3 = child2.getChildElements();
while (it3.hasNext()) {
SOAPElement child3 = (SOAPElement) it3.next();
String value = child3.getValue();
list.addElement(value);
}
}
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.elementAt(i));
}
}
catch (Exception e) {
System.out.println(e.getMessage());
}

}

static public SOAPMessage sendMessage(SOAPMessage message, String endPoint) throws MalformedURLException, SOAPException {
SOAPMessage result = null;
if (endPoint != null && message != null) {
URL url = new URL(endPoint);
SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection connection = null;
long time = System.currentTimeMillis();
try {
connection = scf.createConnection(); //point-to-point connection
result = connection.call(message, url);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SOAPException soape) {
System.out.print("Can't close SOAPConnection:" + soape);
}
}
}
}
return result;
}

static public void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}

public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}

public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
};

SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
}


Done.