XML Encryption, Criptografía sobre XML

0
20617

XML Encryption, Criptografía sobre XML

En este tutorial vamos a ver ejemplos de cómo realizar la encriptación y desencriptación de una sección de un documento XML, dejando el resto del documento sin encriptar.

Introducción a XML Encryption

La privacidad o confidencialidad de la información es uno de los requisitos que se deben cubrir en la gran mayoría de las aplicaciones.
Debido al auge de XML como formato de representación (por ejemplo, mensajes entre servicios Web) se hace necesario mecanismos que nos permitan ocultar la información sensible.

XML Encryption es un lenguaje definido por W3C que nos permite especificar que partes de la información deseamos que vaya cifrada y que partes no.

El cifrado/descifrado se puede realizar tanto con claves simétricas cómo asimétricas. Recuerde que los procesos de cifrado/descrifrado básado en claves simétricas tienen muchísimo más rendimiento que los basados en claves asimétricas (pública/privada).

Para este tutorial utilizaremos la implementación de Apache (http://xml.apache.org/security/dist/).

Veamos unos ejemplos autocomentados:

  1. Encriptación de parte de una información XML.
  2. Desencriptación de la información anterior.

Documento XML para los ejemplos:

Dada la siguiente información en XML, vamos a encriptar exclusivamente mediante criptografía simétrica la información relaccionada con la tarjeta de crédito.

     
      		Marvis
      		Rondon Marcelo 	
      		marvis@servidor.com
      		
      			83838383
     			01/05
      		
      

Clase de utilidades usada en los ejemplos de este tutorial

package com.autentia.examples.xmlencryption;

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

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.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Clase de utilidad.
 * @author Carlos García Pérez. Autentia.
 * @see     http://www.mobiletest.es
 */
public class DOMUtils {
	
    /**
     * Serializa un objeto Document en un archivo
     */
    public static void outputDocToFile(Document doc, File file) throws Exception {
        FileOutputStream	f			   = new FileOutputStream(file);
        TransformerFactory factory	   	   = TransformerFactory.newInstance();
        Transformer		   transformer	   = factory.newTransformer();
        
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        
        transformer.transform(new DOMSource(doc), new StreamResult(f));

        f.close();
    }
    
    /**
     * Lee un Document desde un archivo
     */
    public static Document loadDocumentFromFile(java.io.File file) throws Exception {
        DocumentBuilderFactory	factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder			builder = null;
        
        factory.setNamespaceAware(true);
        
        builder = factory.newDocumentBuilder();
        
        return builder.parse(file);
    } 
    
	/**
	 * @return Crea un elemento value
	 */
	public static Element createNode(Document document, String tag, String value){
        Element node = document.createElement(tag);
        if (value != null){
        	node.appendChild(document.createTextNode(value));
        }
		return node;
	}    
}

Ejemplo de encriptación de una sección de un documento XML

package com.autentia.examples.xmlencryption;


import java.io.File;
import java.io.FileOutputStream;
import java.security.Key;
import javax.xml.parsers.*;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.encryption.EncryptedData;
import org.apache.xml.security.encryption.EncryptedKey;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Este ejemplo encripta la información relacionada con la tarjeta de crédito de un cliente
 * @author Carlos García Pérez. Autentia.
 * @see     http://www.mobiletest.es 
 */
public class Encrypter {
	private static final String SECRET_KEY_FILENAME = "mykey.dat";
	private static final String	ENCRYPTED_XML_FILENAME    = "infoCifrada.xml";
	
    /**
     * Genera un Document de ejemplo
     */    
    private static Document createSampleDom() throws Exception {
    	DocumentBuilderFactory	factory  = DocumentBuilderFactory.newInstance();
        DocumentBuilder 		builder	 = factory.newDocumentBuilder();
        Document				document = builder.newDocument();
        
        Element person = document.createElement("persona");
        person.setAttribute("id", "468300000");
        
        person.appendChild(DOMUtils.createNode(document, "nombre",	 "Marvis"));
        person.appendChild(DOMUtils.createNode(document, "apellidos", "Rondon Marcelo"));
        person.appendChild(DOMUtils.createNode(document, "email",	    "marvis@servidor.com"));
        
        Element creditCard = document.createElement("tarjetaCredito");
        creditCard.appendChild(DOMUtils.createNode(document, "numero",	 "83838383"));
        creditCard.appendChild(DOMUtils.createNode(document, "fechaExpiracion",	 "01/05"));
        
        person.appendChild(creditCard);
        
        document.appendChild(person);
        

        return document;
    }

    
    /**
     * @return Genera la clave secreta que servirá para encriptar/desencriptar la información 
     * @throws Exception Cualquier incidencia
     */
    private static SecretKey generateAndStoreKeyEncryptionKey() throws Exception {
        // Generamos la clave usando el algoritmo Triple DES
    	KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");	// Algoritmo JCE: Triple DES
        SecretKey	 secret		  = keyGenerator.generateKey();
        byte[]		 bytes		  = secret.getEncoded();
        
        // Guardamos la clave en disco
        File			 keyFile = new File(SECRET_KEY_FILENAME);
        FileOutputStream output	 = new FileOutputStream(keyFile);
        output.write(bytes);
        output.close();
        
        System.out.println("La clave de encriptación está guardada en: " + keyFile.getAbsolutePath());

        return secret;
    }

    /**
     * @return Devuelve la clave de encriptación de datos
     * @throws Exception Cualquier incidencia
     */    
    private static SecretKey generateDataEncryptionKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");	// Algoritmo JCE: Advanced Encryption Standard
        keyGenerator.init(128);
        return keyGenerator.generateKey();
    }


    /**
     * Punto de entrada del ejemplo
     * @throws Exception Cualquier incidencia
     */
    public static void main(String args[]) throws Exception {
    	// Inicializamos el FrameWork de seguridad de Apache a los valores por defecto 
    	org.apache.xml.security.Init.init();
    	 
        Document document = Encrypter.createSampleDom();

        // Obtenemos la clave para encriptar el elemento.
        Key symmetricKey = Encrypter.generateDataEncryptionKey();

        // Obtenemos la clave para encriptar la clave simétrica
        Key kek = Encrypter.generateAndStoreKeyEncryptionKey();
        
        // Inicializa cifrador para cifrar la clave de cifrado de la información del documento  xml
        XMLCipher keyCipher = XMLCipher.getInstance(XMLCipher.TRIPLEDES_KeyWrap);	// URI "http://www.w3.org/2001/04/xmlenc#kw-tripledes";
        keyCipher.init(XMLCipher.WRAP_MODE, kek);
        EncryptedKey encryptedKey = keyCipher.encryptKey(document, symmetricKey);	 // Ciframos la clave simétrica

		// Cifrar el contenido del elemento
        XMLCipher	xmlCipher = XMLCipher.getInstance(XMLCipher.AES_128);	// URI "http://www.w3.org/2001/04/xmlenc#aes128-cbc";
        xmlCipher.init(XMLCipher.ENCRYPT_MODE, symmetricKey);
        
        // Añadimos información sobre la clave de cifrado. KeyInfo
        EncryptedData encryptedData = xmlCipher.getEncryptedData(); 
        KeyInfo		  keyInfo		= new KeyInfo(document);
        keyInfo.add(encryptedKey);
        encryptedData.setKeyInfo(keyInfo);

        /*
         * Criframos!!
         * Reemplazamos en el documento los datos a encriptar por el elemento EncrypteData
         * con el tercer parámetro a true indica que deseamos encriptar el contenido del elemento 
         * y no el elemento en sí.
         */
        Element		node	  = (Element) document.getElementsByTagName("tarjetaCredito").item(0);
        
        xmlCipher.doFinal(document, node, false);

        // Guarda el Document en un archivo
        File file = new File(ENCRYPTED_XML_FILENAME);
        DOMUtils.outputDocToFile(document, file);
        
        System.out.println("Los datos han sido encriptados en: " + file.toURL().toString());        
    }
}

Documento encriptado (infoCifrada.xml)

En el siguiente documento se muestra la información que sería enviada al receptor. En ella se puede observar entre otras cosas:

  1. La información relacionada con la tarjeta de crédito ha sido sustituida por el elemento xenc:EncryptedData
  2. El elemento xenc:EncryptionMethod contiene información sobre el método de encriptación usado.
  3. El elemento KeyInfo contiene información sobre la clave de encriptación.
  4. El elemento CipherData contiene la información encriptada, es decir, la información sobre la tarjeta de crédito.
  5. El elemento EncryptedKey contiene la clave de encriptación/desencriptación de la información almacenada en CipherCipherValue.

	Marvis
	Rondon Marcelo
	marvis@servidor.com
	
		
		
			
				
				
					
						gMp/3ZuYVyHn74JDKr3WCLDrf7H+S6wLqGEdRdgqQGw=
					
				
			
		
		
			
				Wr1njyJlYYOM9lAYqcwGCWkw2L4pUjQD2GGVoU9lVZ0wKqHY8y3l3GY8FY4i5K3G8grIe2xN4u7x
				7RtkFiXZgGMeYnQp6oB6ckKp3KFKHVqtucc9AVzOgC7XAw/oe61HRFqe6RRVzXjNMLU5TaV7lJF1
				I8NVPQmUSDX7NRtnR68=
			
		
	
 

Ejemplo de desencriptación del archivo (infoCifrada.xml)

package com.autentia.examples.xmlencryption;

import java.io.File;

import java.security.Key;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.utils.JavaUtils;
import org.apache.xml.security.utils.EncryptionConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Este ejemplo desencripta la información encriptada de un documento XML
 * @author Carlos García Pérez. Autentia.
 * @see    http://www.mobiletest.es
 */
public class Decrypter {
	private static final String SECRET_KEY_FILENAME 	  = "mykey.dat";
	private static final String	ENCRYPTED_XML_FILENAME    = "infoCifrada.xml";
	private static final String	DESENCRYPTED_XML_FILENAME = "infoDescifrada.xml";
	

    /**
     * @return La clave de encriptación/encriptación desde un archivo
     * @throws Exception Cualquier incidencia
     */
    private static SecretKey loadDesencryptionKey() throws Exception {
        DESedeKeySpec	 keySpec = new DESedeKeySpec(JavaUtils.getBytesFromFile(SECRET_KEY_FILENAME));
        SecretKeyFactory skf	 = SecretKeyFactory.getInstance("DESede");
        SecretKey		 key	 = skf.generateSecret(keySpec);
        
        return key;
    }

    /**
     * Punto de entrada del ejemplo
     * @throws Exception Cualquier incidencia
     */
    public static void main(String args[]) throws Exception {
    	 // Inicializamos el FrameWork de seguridad de Apache a los valores por defecto 
    	org.apache.xml.security.Init.init();
    	
    	// Obtenemos el documento xml encriptado
        Document document = DOMUtils.loadDocumentFromFile(new File(ENCRYPTED_XML_FILENAME));
        
        // Accedemos al nodo con la información encriptada.		 namespace: "http://www.w3.org/2001/04/xmlenc#", localName: "EncryptedData"
        Element	node = (Element) document.getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);
        Key		kek	 = Decrypter.loadDesencryptionKey();	// Carga la clave para desencriptar la información
        
        // La clave que será usada para desencriptar los datos del xml se obtendrá desde el KeyInfo del EncrypteData usando la EncryptedKey  
        XMLCipher cipher = XMLCipher.getInstance();
        cipher.init(XMLCipher.DECRYPT_MODE, null);	// Key=null para que use como clave el EncryptedKey 
        cipher.setKEK(kek);		
        
        // Desencriptamos reemplazando los datos encriptados con su contenido desencriptado
        cipher.doFinal(document, node);

        // Guarda el Document en un archivo
        File file = new File(DESENCRYPTED_XML_FILENAME);
        DOMUtils.outputDocToFile(document, file);
        
        System.out.println("Los datos han sido desencriptados en: " + file.toURL().toString());
    }
}

Saludos, Carlos García.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad