Las firmas XAdES se definen en el estándar ETSI TS 101 903. Pueden descargarse una copia del estándar aquí.
Las firmas XAdES son una extensión de XMLDSig, por tanto una firma XAdES es un fichero XML. Para evitar problemas de codificación de caracteres y poder firmar archivos binarios, en el caso de que el documento se incluya en la firma siempre estará codificado en base64.
Arangí implementa 3 tipos de firma XAdES:
En un ciclo de vida típico de un modelo cliente-servidor, la firma realizada por el cliente sería XAdES-BES y, una vez llega al servidor se promocionaría a XAdES-T o XAdES-X-L. Esta acción se puede realizar porque para obtener los sellos de tiempo y la información de validación no es necesario que esté presente la clave privada de firma (que sólo se encuentra en el cliente). Si el cliente realiza la firma XAdES-X-L debe tener acceso a los servidores de sellos de tiempo y los servidores OCSP, lo que no siempre es posible.
En ocasiones se reciben ficheros con firmas y es necesario discernir de qué tipo de XAdES se trata. Para ello existe el método estático getXAdESObject(...) de la clase base XAdESSignature.
import es.accv.arangi.base.XAdESSignature; import es.accv.arangi.base.XAdESTSignature; File file = new File ("c:/temp/xades.xml"); XAdESSignature signature = XAdESSignature.getXAdESObject(file); //-- Si necesitamos que sea un XAdES-T if (signature instanceof XAdESTSignature) { ... }
Por la forma en la que se relacionan la firma y el documento se pueden dar 3 tipos de firmas XAdES:
Los métodos de firma de las clases XAdES permiten estas opciones:
Para cada uno de estos métodos se definen dos clases que servirán para pasarles las opciones con las que se quiere realizar la firma:
KeyStoreManager manager = new KeyStoreManager (new File("c:/temp/uactivo951v_firma.p12"), "1234"); File fileTexto = new File ("c:/temp/documento.txt"); FileDocument documentoTexto = new FileDocument (fileTexto); File fileXML = new File ("c:/temp/documento.xml"); FileDocument documentoXML = new FileDocument (fileXML); //-- Genera una firma attached. XAdESAttachedSignatureOptions optionsAttached = new XAdESAttachedSignatureOptions (); XAdESBESSignature signature1 = XAdESBESSignature.signAttached(manager, documentoTexto, optionsAttached); //-- Genera una firma attached dentro del archivo pasado como documento. Se firmará el tag con ID="titulo". XAdESAttachedNodeToSignObject nodeToSign = new XAdESAttachedNodeToSignObject("titulo"); optionsAttached.setNodeToSign(nodeToSign); XAdESBESSignature signature2 = XAdESBESSignature.signAttached(manager, documentoXML, optionsAttached); //-- Genera una firma enveloped. optionsAttached.setNodeToSign(new XAdESAttachedNodeToSignEnveloped()); XAdESBESSignature signature3 = XAdESBESSignature.signAttached(manager, documentoXML, optionsAttached); //-- Genera una firma detached que referencia al fichero en disco XAdESDetachedSignatureOptions optionsDetached = new XAdESDetachedSignatureOptions (); XAdESBESSignature signature4 = XAdESBESSignature.signDetached(manager, documentoTexto, fileTexto.getAbsolutePath(), optionsDetached); //-- Genera una firma detached que referencia a "2011/04/29/certificados/CER-2584665.pdf" XAdESBESSignature signature5 = XAdESBESSignature.signDetached(manager, documentoTexto, "2011/04/29/certificados/CER-2584665.pdf", optionsDetached); //-- Genera una firma XAdES-T detached realizada con el algoritmo SHA256WithRSA, donde el hash de los //-- sellos de tiempo se realiza mediante el algoritmo SHA-256, se indica que el documento es un XML, //-- se firmo con los roles 'secretario' y 'administrativo', la firma sigue la política de e-factura //-- y la firma se realizó en Vinaròs (Castellón) optionsDetached.setDigitalSignatureAlgorithm(DigitalSignatureAlgorithm.SHA256_RSA); optionsDetached.setTsaHashingAlgorithm(HashingAlgorithm.SHA256); XAdESDataObjectFormat dof = new XAdESDataObjectFormat("un documento de prueba", null, "text/xml", null); optionsDetached.setDof(dof); optionsDetached.setClaimedRoles(new String[] { "secretario", "administrativo" }); ArangiXAdESPolicyIdentifier aspi = new ArangiXAdESPolicyId(new String(Util.loadFile(new File("politica_efactura_xades_namespace.xml")), "UTF-8")); optionsDetached.setPolicyIdentifier(aspi); optionsDetached.setProductionPlace(new ArangiXAdESProductionPlace("Vinaros", "Castellón", "12500", "ES")); XAdESTSignature signature6 = XAdESTSignature.signDetached(manager, documentoTexto, fileTexto.getAbsolutePath(), optionsDetached);
Para comprender mejor el segundo ejemplo puede ver el fichero documento.xml aquí. Del mismo modo puede descargar la firma resultante de aquí.
En el último ejemplo se incluye un fichero que contiene la política de e-factura. Puede descargarse el fichero aquí. El resultado de esta firma lo encontrará aquí.
Las firmas para XAdES-T y XAdES-X-L se implementan de la misma forma que para XAdES-BES. Lógicamente estas clases realizarán más acciones y el resultado será distinto.
La información de validación de las firmas XAdES-X-L se obtiene mediante lladadas a servidores OSCP. Por defecto si no se puede obtener esta información se producirá una excepción. Si se desea que, en caso de no poder obtener una respuesta OCSP, se incluya en la firma información de validación obtenida mediante CRL se deberá realizar las llamadas con el flag 'allowCRLValidation' a true.
XAdESBESSignature signature = XAdESBESSignature.signAttached(manager, documentoTexto, optionsAttached, true);
Hay que tener cuidado con esta opción porque una CRL puede llegar a ocupar varios megabytes, con lo que la firma obtenida puede llegar a ser de un tamaño considerable.
Una vez se ha obtenido la firma es muy sencillo guardarla con el método save() o pasarla a array de bytes con el método toByteArray().
Para promocionar una firma XAdES existen los siguientes métodos:
En el caso de querer promocionar una firma detached será necesario usar el método equivalente que tiene como parámetro el documento firmado:
Para realizar una cofirma en XAdES será necesario proporcionar al método los parámetros habituales para firmar, además de la firma a cofirmar. Normalmente no será necesario pasar al método el documento que originó la firma, ya que éste se puede obtener fácilmente en los XAdES attached y, en el caso de los detached, es posible obtenerlos a través de la referencia incluida en la primera firma. Si dicha referencia no es un fichero o una URL desde la que se pueda obtener el documento si será necesario pasarlo como parámetro al método.
Otra cuestión sobre la cofirma es que no se permite cofirmar un documento distinto al que originó la primera firma.
Los métodos para cofirmar tienen el siguiente aspecto:
Arangí consta de dos métodos para generar contrafirmas:
Las firmas XAdES-X-L presentan una única debilidad: al caducar el certificado de la Autoridad de Sellado de tiempos es imposible validar este certificado. El formato XAdES-A es un XAdES-X-L al que se añade un sello de tiempos (llamado de archivado) para toda la firma. Dicho sellado ha de producirse antes de que caduque el certificado de los sellos de tiempos de la firma.
Arangí no implementa una clase específica para el formato XAdES-A ya que no se realizan firmas en este formato, tal y como se ha comentado se firmará (o completará) en XAdES-X-L y sólo cuando quede poco tiempo para que caduque el certificado de la TSA se resellará la firma obteniéndose un XAdES-A. Así pues, un XAdES-A será tratado por la clase XAdESXLSignature. Ésta dispone de dos métodos para trabajar con el formato XAdES-A: