El objetivo de este ejemplo es mostrar los pasos necesarios para realizar una firma XAdES Mixta. Por simplificar, el formato usado será XAdES-BES en su versión 1.3.2. En este tipo de firmas, el nodo de firma va dentro de un nodo determinado y se firma un hermano del contenedor. De este modo, no es enveloping, puesto que la firma no contiene al nodo que se firma, ni tampoco es enveloped, puesto el nodo firmado no contiene a la firma. Se podría decir que es un híbrido de los dos tipos de firma anteriores. Por supuesto, tampoco es Detached, puesto que los datos firmados no son externos.
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 mixta en la que se firma el nodo titulo y el contenido de la firma se inserta al mismo nivel que dicho nodo 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="#titulo">
<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 Mixta. 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.setEnveloped(true);
Document docToSign = getDocument(RESOURCE_TO_SIGN);
dataToSign.setDocument(docToSign);
dataToSign.addObject(new ObjectToSign(new InternObjectToSign("titulo"), "Documento de ejemplo", null, "text/xml", null));
dataToSign.setParentSignNode("documento");
return dataToSign;
}
Los pasos para crear los datos a firmar son los siguientes:
DataToSign dataToSign = new DataToSign();`
dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES);
dataToSign.setEsquema(XAdESSchemas.XAdES_132);
dataToSign.setXMLEncoding("UTF-8");
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.
dataToSign.addObject(new ObjectToSign(new InternObjectToSign("titulo"), "Documento de ejemplo", null, "text/xml", null));
dataToSign.setParentSignNode("documento");
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:
En cuanto al método createDataToSign() de la clase GenericXMLSignature no es necesario sobreescribirlo puesto que con la implementación original es suficiente.