Wednesday, 7 October 2015

Encrypt and decrypt XML file in Java


web services are based on exchanging SOAP message, which is in plain XML format. For real world applications, transferring plain xml data is not secure. To provide authenticity, data integrity and confidentiality to the messages, we need to encrypt and provide digital signature to the xml data. We can do this by using Apache Santuario project. By using Apache Santuario, we can encrypt and decrypt xml file. Following procedure explains step by step.

Step 1: First you need an xml document to encrypt.
employee.xml
<?xml version="1.0"?>
<employees>

 <employee id="1">
  <firstname>Hari Krishna</firstname>
  <lastname>Gurram</lastname>
  <address>
   <area>Marthali</area>
   <city>Bangalore</city>
   <state>Karnataka</state>
   <country>India</country>
  </address>
 </employee>

 <employee id="2">
  <firstname>Joel Babu</firstname>
  <lastname>Chelli</lastname>
  <address>
   <area>Jubli Hills</area>
   <city>Hyderababd</city>
   <state>Andhra Pradesh</state>
   <country>India</country>
  </address>
 </employee>

 <employee id="3">
  <firstname>Susantha</firstname>
  <lastname>Sarm</lastname>
  <address>
   <area>Gandhi Nagar</area>
   <city>Bhuvaneswar</city>
   <state>Orissa</state>
   <country>India</country>
  </address>
 </employee>

</employees>


Step 2: Generate secret key to encrypt xml file. Following snippet takes algorithm name as input and generate secret key.

public static SecretKey getSecretKey(String algorithm) {
 KeyGenerator keyGenerator = null;
 try {
  keyGenerator = KeyGenerator.getInstance(algorithm);
 } catch (NoSuchAlgorithmException e) {
  e.printStackTrace();
 }
 return keyGenerator.generateKey();
}


Step 3: Get Document reference for employee.xml file.

public static Document getDocument(String xmlFile) throws Exception {
 /* Get the instance of BuilderFactory class. */
 DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();

 /* Instantiate DocumentBuilder object. */
 DocumentBuilder docBuilder = builder.newDocumentBuilder();

 /* Get the Document object */
 Document document = docBuilder.parse(xmlFile);
 return document;
}


Step 4: Encrypt document in step 3 using secret key generated in Step 2.

public static Document encryptDocument(Document document, SecretKey secretKey, String algorithm) throws Exception {
 /* Get Document root element */
 Element rootElement = document.getDocumentElement();
 String algorithmURI = algorithm;
 XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);

 /* Initialize cipher with given secret key and operational mode */
 xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey);

 /* Process the contents of document */
 xmlCipher.doFinal(document, rootElement, true);
 return document;
}

Step 5: Save encrypted document to a file.

Step 6: Decrypt the encrypted document using secret key and algorithm used for encryption.

public static Document decryptDocument(Document document, SecretKey secretKey, String algorithm) throws Exception {
 Element encryptedDataElement = (Element) document.getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);

 XMLCipher xmlCipher = XMLCipher.getInstance();

 xmlCipher.init(XMLCipher.DECRYPT_MODE, secretKey);
 xmlCipher.doFinal(document, encryptedDataElement);
 return document;
}


Complete application is given below. I provided comments to understand application.

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class SecretKeyUtil {

 /**
  * Generate secret key for given algorithm
  * 
  * @param algorithm
  *            : Generate secret key specific to this algorithm
  * @return SecretKey
  */
 public static SecretKey getSecretKey(String algorithm) {
  KeyGenerator keyGenerator = null;
  try {
   keyGenerator = KeyGenerator.getInstance(algorithm);
  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  }
  return keyGenerator.generateKey();
 }

 /**
  * Convert secret key to string.
  * 
  * @param secretKey
  * 
  * @return String representation of secret key
  */
 public static String keyToString(SecretKey secretKey) {
  /* Get key in encoding format */
  byte encoded[] = secretKey.getEncoded();

  /*
   * Encodes the specified byte array into a String using Base64 encoding
   * scheme
   */
  String encodedKey = Base64.getEncoder().encodeToString(encoded);

  return encodedKey;
 }

 /**
  * Save secret key to a file
  * 
  * @param secretKey
  *            : Secret key to save into file
  * @param fileName
  *            : File name to store
  */
 public static void saveSecretKey(SecretKey secretKey, String fileName) {
  byte[] keyBytes = secretKey.getEncoded();
  File keyFile = new File(fileName);
  FileOutputStream fOutStream = null;
  try {
   fOutStream = new FileOutputStream(keyFile);
   fOutStream.write(keyBytes);
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (fOutStream != null) {
    try {
     fOutStream.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
 }
}


import java.io.File;
import java.io.FileOutputStream;

import javax.crypto.SecretKey;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.utils.EncryptionConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class XMLUtil {

 static {
  org.apache.xml.security.Init.init();
 }

 /**
  * Return DOM Document object for given xml file
  * 
  * @param xmlFile
  * @return
  * @throws Exception
  */
 public static Document getDocument(String xmlFile) throws Exception {
  /* Get the instance of BuilderFactory class. */
  DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();

  /* Instantiate DocumentBuilder object. */
  DocumentBuilder docBuilder = builder.newDocumentBuilder();

  /* Get the Document object */
  Document document = docBuilder.parse(xmlFile);
  return document;
 }

 /**
  * Save document to a file
  * 
  * @param document
  *            : Document to be saved
  * @param fileName
  *            : Represent file name to save
  * @throws Exception
  */
 public static void saveDocumentTo(Document document, String fileName)
   throws Exception {
  File encryptionFile = new File(fileName);
  FileOutputStream fOutStream = new FileOutputStream(encryptionFile);

  TransformerFactory factory = TransformerFactory.newInstance();
  Transformer transformer = factory.newTransformer();
  transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
  DOMSource source = new DOMSource(document);
  StreamResult result = new StreamResult(fOutStream);
  transformer.transform(source, result);

  fOutStream.close();
 }

 /**
  * Encrypt document with given algorithm and secret key.
  * 
  * @param document
  * @param secretKey
  * @param algorithm
  * @return
  * @throws Exception
  */
 public static Document encryptDocument(Document document,
   SecretKey secretKey, String algorithm) throws Exception {
  /* Get Document root element */
  Element rootElement = document.getDocumentElement();
  String algorithmURI = algorithm;
  XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);

  /* Initialize cipher with given secret key and operational mode */
  xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey);

  /* Process the contents of document */
  xmlCipher.doFinal(document, rootElement, true);
  return document;
 }

 /**
  * Decrypt document using given key and algorithm
  * 
  * @param document
  * @param secretKey
  * @param algorithm
  * @return
  * @throws Exception
  */
 public static Document decryptDocument(Document document,
   SecretKey secretKey, String algorithm) throws Exception {
  Element encryptedDataElement = (Element) document
    .getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS,
      EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);

  XMLCipher xmlCipher = XMLCipher.getInstance();

  xmlCipher.init(XMLCipher.DECRYPT_MODE, secretKey);
  xmlCipher.doFinal(document, encryptedDataElement);
  return document;
 }
}


import javax.crypto.SecretKey;

import org.apache.xml.security.encryption.XMLCipher;
import org.w3c.dom.Document;

public class Test {
 public static void main(String args[]) throws Exception {
  String xmlFile = "/Users/harikrishna_gurram/employee.xml";
  String encryptedFile = "/Users/harikrishna_gurram/encrypted.xml";
  String decryptedFile = "/Users/harikrishna_gurram/decrypted.xml";

  SecretKey secretKey = SecretKeyUtil.getSecretKey("AES");
  Document document = XMLUtil.getDocument(xmlFile);

  Document encryptedDoc = XMLUtil.encryptDocument(document, secretKey,
    XMLCipher.AES_128);
  XMLUtil.saveDocumentTo(encryptedDoc, encryptedFile);

  Document decryptedDoc = XMLUtil.decryptDocument(encryptedDoc,
    secretKey, XMLCipher.AES_128);
  XMLUtil.saveDocumentTo(decryptedDoc, decryptedFile);

  System.out.println("Done");
 }
}




2 comments:

  1. Hi, thanks for your post it was very useful for me. Could you explain me how did you install the apache santuario library? I am trying to implement your code but i am getting a "NoClassDeffFound" excepion when using the XMLCipher. Thanks!

    ReplyDelete
    Replies
    1. You can take the dependency from 'https://mvnrepository.com/artifact/org.apache.santuario/xmlsec'

      Delete