Ejemplo de firma Enveloped

El objetivo de este ejemplo es mostrar los pasos necesarios para realizar una firma XAdES Enveloped. Por simplificar, el formato usado será XAdES-BES en su versión 1.3.2. En este tipo de firmas, el elemento que se firma contiene al propio nodo de firma.

Siendo el documento original a firmar el siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<documento id="documento">
    <titulo id="titulo">Documento de pruebas</titulo>
    <descripcion id="descripcion">Documento destinado a realizar pruebas de firma</descripcion>
</documento>

Una firma XAdES-BES Enveloped en la que se firma todo el documento completo tendría el siguiente aspecto:

<?xml version="1.0" encoding="UTF-8"?>
<documento id="documento">
    <titulo id="titulo">Documento de pruebas</titulo>
    <descripcion id="descripcion">Documento destinado a realizar pruebas de firma</descripcion>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:etsi="http://uri.etsi.org/01903/v1.3.2#" Id="Signature504735">
        <ds:SignedInfo Id="Signature-SignedInfo1024952">
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <ds:Reference Id="SignedPropertiesID429729" Type="http://uri.etsi.org/01903#SignedProperties" URI="#Signature504735-SignedProperties48056">
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue><!-- Digest del elemento referenciado en Base64 --></ds:DigestValue>
            </ds:Reference>
            <ds:Reference URI="#Certificate1237555">
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue><!-- Digest del elemento referenciado en Base64 --></ds:DigestValue>
            </ds:Reference>
            <ds:Reference Id="Reference-ID-200615" URI="">
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue><!-- Digest del elemento referenciado en Base64 --></ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue Id="SignatureValue552465">
            <!-- Valor de la firma en Base64 -->
        </ds:SignatureValue>
        <ds:KeyInfo Id="Certificate1237555">
            <ds:X509Data>
                <ds:X509Certificate>
                    <!-- Certificado firmante en Base64 -->
                </ds:X509Certificate>
            </ds:X509Data>
            <ds:KeyValue>
                <ds:RSAKeyValue>
                    <ds:Modulus><!-- Módulo de la clave RSA en Base64 --></ds:Modulus>
                    <ds:Exponent><!-- Exponente de la clave RSA en Base64 --></ds:Exponent>
                </ds:RSAKeyValue>
            </ds:KeyValue>
        </ds:KeyInfo>
        <ds:Object Id="Signature504735-Object873466">
            <etsi:QualifyingProperties Target="#Signature504735">
                <etsi:SignedProperties Id="Signature504735-SignedProperties48056">
                    <etsi:SignedSignatureProperties>
                        <etsi:SigningTime><!-- Fecha y hora de la firma --></etsi:SigningTime>
                        <etsi:SigningCertificate>
                            <etsi:Cert>
                                <etsi:CertDigest>
                                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                    <ds:DigestValue><!-- Digest del certificado en Base64 --></ds:DigestValue>
                                </etsi:CertDigest>
                                <etsi:IssuerSerial>
                                    <ds:X509IssuerName><!-- Nombre de emisión del certificado firmante --></ds:X509IssuerName>
                                    <ds:X509SerialNumber><!-- Número de serie del certificado firmante --></ds:X509SerialNumber>
                                </etsi:IssuerSerial>
                            </etsi:Cert>
                        </etsi:SigningCertificate>
                    </etsi:SignedSignatureProperties>
                    <etsi:SignedDataObjectProperties>
                        <etsi:DataObjectFormat ObjectReference="#Reference-ID-200615">
                            <etsi:Description><!-- Descripcion del objeto firmado ---></etsi:Description>
                            <etsi:MimeType><!-- Tipo MIME del objeto firmado --></etsi:MimeType>
                        </etsi:DataObjectFormat>
                    </etsi:SignedDataObjectProperties>
                </etsi:SignedProperties>
            </etsi:QualifyingProperties>
        </ds:Object>
    </ds:Signature>
</documento>

El programa de ejemplo extiende el ejemplo de firma genérica implementando aquellos métodos abstractos que lo hacen específico para el tipo de firma Enveloped. El código completo del ejemplo se puede ver aquí. También se puede ver el Javadoc asociado al ejemplo aquí.

La implementación del método abstracto createDataToSign() de la clase GenericXMLSignature es la siguiente:

@Override
protected DataToSign createDataToSign() {
    DataToSign dataToSign = new DataToSign();
    dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES);
    dataToSign.setEsquema(XAdESSchemas.XAdES_132);
    dataToSign.setXMLEncoding("UTF-8");
    dataToSign.addClaimedRol(new SimpleClaimedRole("Rol de firma"));
    dataToSign.setEnveloped(true);
    Document docToSign = getDocument(RESOURCE_TO_SIGN);
    dataToSign.setDocument(docToSign);
    dataToSign.addObject(new ObjectToSign(new AllXMLToSign(), "Documento de ejemplo", null, "text/xml", null));
    return dataToSign;
}

Los pasos para crear los datos a firmar son los siguientes:

  • Crear un objeto DataToSign.
    DataToSign dataToSign = new DataToSign();`
    
  • Asignar el formato, en este caso XAdES-BES, mediante el valor XAdES_BES del enumerado EnumFormatoFirma.
    dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES);
    
  • Asignar el esquema, en este caso el 1.3.2 (actualmente la última versión de XAdES), mediante el valor XAdES_132 del enumerado XAdESSchemas.
    dataToSign.setEsquema(XAdESSchemas.XAdES_132);
    
  • Establecer la codificacion del XML a firmar. El estándar recomienda que sea UTF-8:
    dataToSign.setXMLEncoding("UTF-8");
    
  • Establecer el rol que juega el ente firmante. Se indica un rol de ejemplo:
     dataToSign.addClaimedRol(new SimpleClaimedRole("Rol de firma"));
    
  • Puesto que queremos que la firma sea un nodo de un XML determinado, es decir, queremos crear un contenedor de firma, se lo tenemos que indicar al objeto DataToSign de la siguiente forma:
    dataToSign.setEnveloped(true);
    Document docToSign = getDocument(RESOURCE_TO_SIGN);
    dataToSign.setDocument(docToSign);
    

    El método getDocument(String resource), que está definido en la clase GenericXMLSignature, permite parsear el documento asociado al recurso pasado como parámetro (en este caso es una constante definida en la propia clase) para obtener un objeto org.w3c.dom.Document.

  • Por último, habría que indicar que es lo que se quiere firmar:
    dataToSign.addObject(new ObjectToSign(new AllXMLToSign(), "Documento de ejemplo", null, "text/xml", null));
    

    Puesto que no se dice nada del padre del nodo de firma, se asume que el nodo de firma se insertará como último hijo del nodo raíz del documento que se está firmando. Si quisieramos colocar el nodo de firma en un nodo determinado habría que indicarlo mediante el método setParentSignNode(String parentNode), que recibe como argumento el Id del nodo en el que se desea insertar la firma.

    Mediante el método addObject se podrían añadir tantos objetos a firmar como se deseen. El constructor ObjectToSign recibe los siguientes parámetros:

    • El objeto a firmar. En el ejemplo, puesto que se quiere firmar todo el documento XML, se usa el objeto AllXMLToSign para tal efecto.
    • Descripción del objeto a firmar. Se trata de un valor opcional para aportar información extra.
    • Objecto identificador del objeto descrito. Actualmente no se está usando. Se permite para total compatibilidad con el formato XAdES.
    • Tipo MIME del objeto descrito: En este caso, puesto que estamos firmando un documento XML se usa "text/xml".
    • Codificación del objeto firmado. En el caso de que se estuviera firmando un documento binario que está codificado en Base64 se podría usar la constante URI_BASE_64 de la clase ConstantesXADES. En el ejemplo, puesto que se esta firmando el propio XML este parámetro recibe el valor null

    Si quisiéramos firmar únicamente un nodo determinado e incluir en ese nodo la firma, se haría del siguiente modo:

    dataToSign.addObject(new ObjectToSign(new InternObjectToSign("titulo"), "Documento de ejemplo", null, "text/xml", null));
    dataToSign.setParentSignNode("titulo");
    

    En este caso se usa el objeto InternObjectToSign para indicar que es el nodo con Id titulo el que se desea firmar. Además se indica que el padre de la firma es el propio nodo con Id titulo, para que sea una firma Enveloped.

En cuanto al método createDataToSign() de la clase GenericXMLSignature no es necesario sobreescribirlo puesto que con la implementación original es suficiente.