Skip to content

Ejemplo de firma XAdES X

Vinicio Ochoa edited this page Oct 29, 2020 · 2 revisions

El objetivo de este ejemplo es mostrar los pasos necesarios para realizar una firma XAdES-X tipo 1 (el tipo 2 sería análogo). En cuanto al tipo de firma, se elige firma Enveloped, por lo que es imprescindible entender el ejemplo de firma Enveloped previamente ya que los conceptos son los mismos.

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-X tipo 1 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:UnsignedProperties Id="Signature558204-UnsignedProperties307265">
                    <etsi:UnsignedSignatureProperties>
                        <etsi:SignatureTimeStamp Id="SelloTiempo915050">
                            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                            <etsi:EncapsulatedTimeStamp Id="SelloTiempo-Token495737"><!-- Sello de tiempo en Base64 --></etsi:EncapsulatedTimeStamp>
                        </etsi:SignatureTimeStamp>
                        <etsi:CompleteCertificateRefs Id="CompleteCertificateRefs148344">
                            <etsi:CertRefs>
                                <etsi:Cert URI="cert-a150a32b.cer">
                                    <etsi:CertDigest>
                                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                        <ds:DigestValue><!-- Digest de la respuesta OCSP --></ds:DigestValue>
                                    </etsi:CertDigest>
                                    <etsi:IssuerSerial>
                                        <ds:X509IssuerName><!-- Nombre del emisor del certificado --></ds:X509IssuerName>
                                        <ds:X509SerialNumber><!-- Número de serie del certificado --></ds:X509SerialNumber>
                                    </etsi:IssuerSerial>
                                </etsi:Cert>
                                <etsi:Cert URI="cert-b075a288.cer">
                                    <etsi:CertDigest>
                                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                        <ds:DigestValue><!-- Digest de la respuesta OCSP --></ds:DigestValue>
                                    </etsi:CertDigest>
                                    <etsi:IssuerSerial>
                                        <ds:X509IssuerName><!-- Nombre del emisor del certificado --></ds:X509IssuerName>
                                        <ds:X509SerialNumber><!-- Número de serie del certificado --></ds:X509SerialNumber>
                                    </etsi:IssuerSerial>
                                </etsi:Cert>
                            </etsi:CertRefs>
                        </etsi:CompleteCertificateRefs>
                        <etsi:CompleteRevocationRefs Id="CompleteRevocationRefs104378">
                            <etsi:OCSPRefs>
                                <etsi:OCSPRef>
                                    <etsi:OCSPIdentifier URI="ocsp-a4e3834b.ors">
                                        <etsi:ResponderID>
                                            <etsi:ByName><!-- Nombre del servidor OCSP --></etsi:ByName>
                                        </etsi:ResponderID>
                                        <etsi:ProducedAt><!-- Fecha en la que se realiza la consulta OCSP --></etsi:ProducedAt>
                                    </etsi:OCSPIdentifier>
                                    <etsi:DigestAlgAndValue>
                                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                        <ds:DigestValue><!-- Digest de la respuesta OCSP --></ds:DigestValue>
                                    </etsi:DigestAlgAndValue>
                                </etsi:OCSPRef>
                                <etsi:OCSPRef>
                                    <etsi:OCSPIdentifier URI="ocsp-409487b2.ors">
                                        <etsi:ResponderID>
                                            <etsi:ByName><!-- Nombre del servidor OCSP --></etsi:ByName>
                                        </etsi:ResponderID>
                                        <etsi:ProducedAt><!-- Fecha en la que se realiza la consulta OCSP --></etsi:ProducedAt>
                                    </etsi:OCSPIdentifier>
                                    <etsi:DigestAlgAndValue>
                                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                                        <ds:DigestValue><!-- Digest de la respuesta OCSP --></ds:DigestValue>
                                    </etsi:DigestAlgAndValue>
                                </etsi:OCSPRef>
                            </etsi:OCSPRefs>
                        </etsi:CompleteRevocationRefs>
                        <etsi:SigAndRefsTimeStamp Id="SelloTiempo695452">
                            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                            <etsi:EncapsulatedTimeStamp><!-- Sello de tiempo en Base64 --></etsi:EncapsulatedTimeStamp>
                        </etsi:SigAndRefsTimeStamp>
                    </etsi:UnsignedSignatureProperties>
                </etsi:UnsignedProperties>
            </etsi:QualifyingProperties>
        </ds:Object>
    </ds:Signature>
</documento>

Si se compara esta firma con la firma ejemplo XAdES-C se puede observar que lo que aporta sobre ella es un Timestamp para evitar que se vea comprometida la cadena de certificados en el futuro (tag etsi:SigAndRefsTimeStamp dentro de las UnsignedProperties).

El programa de ejemplo extiende el ejemplo de firma genérica implementando aquellos métodos abstractos que lo hacen específico para el formato de firma XAdES-X. 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_X);
    dataToSign.setXAdESXType(XADES_X_TYPES.TYPE_1);
    TrustAbstract truster = TrustFactory.getInstance().getTruster(TRUSTER_NAME);
    if (truster == null) {
        (... Error al crear el validador de confianza ...)
     }
    dataToSign.setCertStatusManager(new OCSPLiveConsultant(URL_OCSP, truster));
    dataToSign.setTimeStampGenerator(new HTTPTimeStampGenerator(URL_TSA, TSPAlgoritmos.SHA1));
    dataToSign.setEsquema(XAdESSchemas.XAdES_132);
    dataToSign.setXMLEncoding("UTF-8");
    dataToSign.addClaimedRol(new SimpleClaimedRole("Rol de firma"));
    dataToSign.setEnveloped(true);
    dataToSign.addObject(new ObjectToSign(new AllXMLToSign(), "Documento de ejemplo", null, "text/xml", null));
    Document docToSign = getDocument(RESOURCE_TO_SIGN);
    dataToSign.setDocument(docToSign);
    return dataToSign;
}

Como se puede ver, el código es similar al mostrado en el ejemplo de firma Enveloped. La clave para elegir el formato XAdES-X y el tipo 1 ó 2 es establecerlo en el objeto DataToSign:

dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_X);
dataToSign.setXAdESXType(XADES_X_TYPES.TYPE_1);

Además, habrá que establecer un validador de estados de certificados utilizando para ello cualquier implementación de la interfaz ICertStatusRecoverer (interfaz que forma parte de la librería MITyCLibAPI del proyecto de Componentes). En el ejemplo se utiliza un validador de estados basado en OCSP. Para crear este validador se necesita crear previamente un validador de confianza (instancia de la clase TrustAbstract de la librería MITyCLibAPI) que será capaz de obtener rutas de certificación a partir de un determinado certificado. En este caso, el validador de confianza que se utiliza confía en una PKI de pruebas utilizada durante el desarrollo y en la PKI del DNIe.

TrustAbstract truster = TrustFactory.getInstance().getTruster(TRUSTER_NAME);
if (truster == null) {
   (... Error al crear el validador de confianza ...)
}

Como se puede ver, en código anterior se ha usado la constante TRUSTER_NAME cuyo valor se corresponde con el nombre del validador de confianza que debe aparecer en el fichero de propiedades trust/trust.properties, accesible como recurso del ejemplo. Para más información sobre la creación y configuración de un validador de confianza se recomienda leer la documentación de la librería MITyCLibTrust.

Una vez creado el validador se podrá crear y establecer para su uso durante el proceso de firma el recuperador de estados basado en el protocolo OCSP.

dataToSign.setCertStatusManager(new OCSPLiveConsultant(URL_OCSP, truster));

Como se puede ver en el código anterior, la validación se hará contra el OCSP indicado por la constante URL_OCSP que aparece en la propia clase.

En el caso en que se hubiera elegido utilizar otro tipo de validación de estados, por ejemplo mediante CRLs, habría que haber establecido como gestor de estados de certificados otra implementación de la interfaz ICertStatusRecoverer, adaptada según las necesidades específicas.

Por último, puesto que se trata de una firma que requiere de la generación de sellos de tiempo, es necesario establecer el generador de sellos de tiempo a utilizar en el objeto DataToSign. El generador de sellos de tiempo debe ser una instancia de la interfaz ITimeStampGenerator de la librería MITyCLibAPI. En este caso, puesto que las peticiones de sellos de tiempo se realizarán mediante protocolo HTTP(s), se utiliza la clase HTTPTimeStampGenerator de la librería MITyCLibTSA:

dataToSign.setTimeStampGenerator(new HTTPTimeStampGenerator(URL_TSA, TSPAlgoritmos.SHA1));  

Como se puede ver, la TSA a utilizar se define como una constante en la propia clase.