Realizar firmas XAdES desde .Net (1)

Creado el día 25/03/2011 17:35 por zorry

Es raro que hasta hace poco, trabajando tanto tiempo con BizTalk y con XML, no haya tenido que trabajar con protocolos de firma de XML. En esta serie de artículos voy a hablar acerca de cómo realizar una firma mediante XAdES (XML Advanced Electronic Signatures). XAdES consiste en una capa superior al estándar de firma XML Xml-DSig (o XML Signature). Pero más raro aún, buscando en internet, es no haber logrado encontrar ningún artículo que hable de conseguir realizar firmas mediante este estándar con .Net, e incluso algunos afirmando que este tipo de firma sólo puede realizarse en Java. Pero nada más lejos de la realidad.

Al finalizar este artículo seremos capaces de realizar firmas compatibles con el estándar XAdES-BES (es el nivel básico de seguridad ofrecido por el estándar, como se puede leer en el artículo de la wikipedia que he enlazado más arriba). Fundamentalmente, un XML con firma XAdES-BES consiste en un documento XML con una firma Enveloped. Este tipo de firma consiste en un XML con nuestros datos, en el cual al final del mismo existe un nodo Signature (establecido en el estándar Xml-DSig) dentro del cual se encuentra la firma del mismo.

Vamos a trabajar con los siguientes objetos en .Net, con lo cual, estaría bien tener ciertas nociones en el uso de los mismos para poder seguir el artículo:

  • X509Certificate2: Realizaremos la firma mediante un certificado instalado en el almacén de certificados Windows de la máquina. Este objeto representa toda la información del mismo: Clave pública y privada (si existe), emisor del certificado, caducidad, etc etc…
  • SignedXml: Este objeto es la implementación de Microsoft en .Net del estándar de firma XML Signature. Es clave para poder realizar las firmas correctamente.
  • XmlDocument (y en general todo el DOM XML de Microsoft): Estos objetos hacen accesible el XML a firmar (y el firmado) mediante el modelo de objetos DOM.
  • Reference, DataObject: Objetos auxiliares, que nos permitirán generar la estructura de la firma correctamente.

Adicionalmente, también estaría bien conocer las bases de la criptografía, al menos las bases de la firma mediante claves asíncronas. Y conocer el uso de certificados de usuario X509.

Comenzaremos esta introducción, analizando un XML firmado mediante el estándar XAdES-BES. Por ejemplo, si tenemos este XML que necesitamos firmar:

<?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>

Este sería el XML firmado (el ejemplo está acortado para aportar claridad al artículo):

<?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>
  <Signature Id="SignatureUsuario" xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
            <XPath xmlns:ds="http://www.w3.org/2000/09/xmldsig#">not(ancestor-or-self::ds:Signature)</XPath>
          </Transform>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue><!-- Digest del XML completo en Base64 --></DigestValue>
      </Reference>
      <Reference Id="SignatureUsuario-XADES-Properties-Ref" URI="#XADES-Properties" Type="http://uri.etsi.org/01903/v1.2.2#SignedProperties">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue><!-- Digest del objeto XAdES en Base64 --></DigestValue>
      </Reference>
      <Reference Id="SignatureUsuario-KeyInfo-Ref" URI="#KeyInfo">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue><!-- Digest del objeto KeyInfo en Base64 --></DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue><!-- Valor de la firma en Base64 --></SignatureValue>
    <KeyInfo Id="KeyInfo">
      <X509Data>
        <X509SubjectName><!-- Subject del certificado que realiza la firma --></X509SubjectName>
        <X509Certificate><!-- Clave pública del certificado en Base64 --></X509Certificate>
      </X509Data>
    </KeyInfo>
    <Object Id="XADES">
      <etsi:QualifyingProperties Target="SignatureUsuario" xmlns:etsi="http://uri.etsi.org/01903/v1.3.2#">
        <etsi:SignedProperties Id="XADES-Properties">
          <etsi:SignedSignatureProperties>
            <etsi:SigningTime>2011-03-28T09:56:09Z</etsi:SigningTime>
            <etsi:SigningCertificate>
              <etsi:Cert>
                <etsi:CertDigest>
                  <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                  <ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><!-- Digest del certificado en Base64 --></ds:DigestValue>
                </etsi:CertDigest>
                <etsi:IssuerSerial>
                  <ds:X509IssuerName xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><!-- Asunto del emisor del certificado --></ds:X509IssuerName>
                  <ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><!-- Número de serie del certificado emisor --></ds:X509SerialNumber>
                </etsi:IssuerSerial>
              </etsi:Cert>
            </etsi:SigningCertificate>
          </etsi:SignedSignatureProperties>
        </etsi:SignedProperties>
      </etsi:QualifyingProperties>
    </Object>
  </Signature>
</documento>

En este XML podemos observar cómo se introduce un nodo Signature como último elemento que cuelga del nodo raíz. Esta firma es por tanto, una firma Enveloped. Dentro de este nodo firma podemos diferenciar dos partes:

  • Parte de la firma XmlDSig: Consiste en la parte del nodo Signature comprendida entre las líneas 6 y 40 del ejemplo. Estos nodos son generados de manera nativa por el objeto SignedXml de .Net al realizar el cálculo de la misma.
  • Extensión XAdES: Consiste en el resto del nodo Signature, esto es, los nodos comprendidos entre las líneas 41 y 61. En estas líneas se describen ciertas propiedades del certificado de usuario que está realizando la firma.

Recordemos que el estándar XML-DSig admite tres tipos de firmas diferentes:

  • Enveloped: El XML que contiene la firma se incrusta en el interior del XML a firmar, como en el ejemplo anterior.
  • Enveloping: El documento firmado se incrusta dentro del XML de la firma.
  • Detached: El XML que contiene la firma es independiente del documento firmado. Por tanto, para validar la firma, es necesario transmitir separadamente el XML de la firma y el XML firmado.

No se aceptan más comentarios