domingo, 8 de abril de 2012

transform .pfx into jks keystore and XML sign / transformar un certificado pfx en un almacen jks y firmar xml

Hola a todos,

En esta ocasión quiero compartir con vosotros algo en lo que todos nos vemos envueltos algunas vez, firmas digitales... toda una aventura :).

En este articulo os mostraré como, a partir de un archivo .pfx, poder realizar peticiones XML firmadas desde java, para ello dividiremos el articulo en dos partes:

Transformar archivo pfx en jks

Existen formas de hacer esta transformacion en varios pasos, con la ayuda de openssl, en este articulo usaremos una característica del comando del jdk keytool que se introdujo en la version 6 de java (no estoy muy seguro de si en la versión 5 de la jdk ya venía).

Usaremos el siguiente comando:

"C:\Archivos de programa\Java\jdk1.6.0_17\bin\keytool" -importkeystore -srckeystore tu_archivo_pfx
-destkeystore FirmaKeystore -srcstoretype pkcs12 -deststoretype jks -srcstorepass clave_de_tu_pfx
-deststorepass clave_para_tu_nuevo_almacen -srcalias c4a3f5f494165a13ccbb1473e3b69c6f_1f97c812-2351-470b-aae7-10698e811751 -destalias nuevo_alias -v

Aunque el ejemplo está indicado para windows, en unix funciona igual. Como se ve, hay algunos valores en azul, los comentamos:
  • srckeystore: Es la ruta a tu archivo pfx de origen.
  • srcstorepass: Clave del archivo pfx, que nos debe dar el proveedor del certificado.
  • deststorepass: Clave que asignaremos a nuestro nuevo almacen (en ejemplo llamado FirmaKeystore).
  • destalias: Alias que asignaremos en nuestro almacen al certificado que se genera.

Aún queda un punto pendiente, en 'srcalias' tenemoms un 'chorizo' que no es aleatorio, es el alias dentro del pfx del certificado, para obtener este dato ejecutamos el siguiente comando:

keytool -list -storetype pkcs12 -keystore your_pfx_file -v | grep Alias

Se observa el comando '| grep Alias' al final, esto solo es valido para unix, en windows lo
copiaremos de la salida de comando en pantalla. Usamos ese valor en la opcion 'srcalias'.

Con esto ya tenemos nuestro almacen generado.

Firmar XMLs

Una vez que tenemos nuestro almacen generado, con el siguiente código firmamos nuestro XML. Se usa el API de Apache (abajo teneis los imports)

   /**
     * Punto de entrada al ejemplo.
     */
    public Document firmarXml(Document doc) throws Exception {

        System.out.println("/ INICIO.");

        // Obtenemos las propiedades para firmar el documento.
        String sTipoAlmacen = "jks";
        String sAlmacen = "ruta_a_nuestro_almacen_generado_en_el_punto1";
        String sClaveAlmacen = "clave_que_dimos_en_deststorepass";
        String sClavePrivada = "valor_de_srcstorepass";
        String sAlias = "valor_de_destalias";

        org.apache.xml.security.Init.init();

        Constants.setSignatureSpecNSprefix("ds"); // Sino, pone por defecto como prefijo: "ns"

        // Cargamos el almacen de claves
        KeyStore ks  = KeyStore.getInstance(sTipoAlmacen);
        ks.load(new FileInputStream(sAlmacen), sClaveAlmacen.toCharArray());

        // Obtenemos la clave privada, pues la necesitaremos para encriptar.
        PrivateKey privateKey = (PrivateKey) ks.getKey(sAlias, sClavePrivada.toCharArray());

        File    signatureFile = new File("signature.xml");
        String  baseURI = signatureFile.toURI().toString();   // BaseURI para las URL Relativas.

        // Instanciamos un objeto XMLSignature desde el Document. El algoritmo de firma será RSA
        XMLSignature xmlSignature = new XMLSignature(doc, baseURI, XMLSignature.ALGO_ID_SIGNATURE_RSA);

        // Añadimos el nodo de la firma a la raiz antes de firmar.
        // Observe que ambos elementos pueden ser mezclados en una forma con referencias separadas
        doc.getDocumentElement().appendChild(xmlSignature.getElement());

        // Creamos el objeto que mapea: Document/Reference
        Transforms transforms = new Transforms(doc);
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        transforms.addTransform(Transforms.TRANSFORM_C14N_OMIT_COMMENTS);

        // Añadimos lo anterior Documento / Referencia
        // ALGO_ID_DIGEST_SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1";
        xmlSignature.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

        // Añadimos el KeyInfo del certificado cuya clave privada usamos
        X509Certificate cert = (X509Certificate) ks.getCertificate(sAlias);

        xmlSignature.addKeyInfo(cert.getPublicKey());
        xmlSignature.addKeyInfo(cert);

        // Realizamos la firma
        xmlSignature.sign(privateKey);

        System.out.println("\\ FIN.");
        return doc;
    }

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

La entrada del método es el XML a firmar en formato org.w3c.dom.Document, el cual es devuelto al final del proceso.

Recordar que debemos importar a nuestro almacen cualquier certificado intermedio o de root que
complete la ruta de certificación del certificado que hemos transformado, ya que si no
no se podrá completar la ruta de certificación.

Espero que os sirva, y cualquier cosa me comentais

Hasta pronto!!

UPDATE: El comando correcto es -importkeystore, no -import keystore como estaba incialmente indicado. Ya está corregido!! :)

6 comentarios:

  1. Hola muy bue aporte, una consulta la primera parte se puede realizar por codigo java?

    ResponderEliminar
  2. Hola! Gracias por tus comentarios. Creo que es posible hacerlo, yo personalmente nunca lo he necesitado. Mira este link por si te sirve de ayuda: http://stackoverflow.com/questions/5312559/how-do-i-programmatically-create-a-new-keystore

    ResponderEliminar
  3. Buenas francisco el dato de -srcalias ? cual es dentro de todo lo que aparece cunado ejecutamos keytool -list -storetype pkcs12 -keystore your_pfx_file -v gracias

    ResponderEliminar
  4. Hola Francisco el dato que incluyes en -srcalias ? de todo loq ue aparece cundo se ase esto keytool -list -storetype pkcs12 -keystore your_pfx_file cual es.... de ante mano gracias por aporte

    ResponderEliminar
  5. Buenas amigo Francisco el código del -srcalias que resulta del keytool -list -storetype pkcs12 -keystore your_pfx_file -v como lo ubico

    ResponderEliminar
  6. Hola Juan! Gracias por tus comentarios, para obtener ese alias tienes que ejecutar el comando keytool -list -storetype pkcs12 -keystore your_pfx_file -v | grep Alias sobre tu fichero pfx, está descrito en la entrada

    ResponderEliminar