
31/03/2011 16:01 por
zorry
En el artículo anterior hemos codificado un método que permite realizar la firma XAdES. Sin embargo, al tratar de ejecutar su prueba unitaria, observamos que el método de firma nos devuelve una CryptographicException: Malformed reference element. Esta excepción es levantada por el objeto SignedXml al no conseguir encontrar el nodo con atributo Id y el valor especificado en las diferentes referencias. Vamos a ver cómo solucionar este problema y por fin, conseguir realizar la firma completa.
En concreto, las referencias que nos están dando problemas son las referencias al nodo con Id “KeyInfo” (la clave pública de firma) y la referencia al nodo “XADES-Properties” (el objeto XAdES). Para poder adaptar el objeto SignedXml y lograr realizar estas referencias, deberemos crearnos un objeto que herede de SignedXml. En nuestro ejemplo lo llamaremos XAdESSignedXml. En este objeto, sobrescribiremos el método GetIdElement, que se encarga de buscar los nodos a los que se realizan las referencias, encargándonos en este objeto de buscar manualmente en el Xml los nodos que nos hacen falta:
sealed class XAdESSignedXml: SignedXml
{
private readonly List<DataObject> _dataObjects = new List<DataObject>();
public const string XadesSignaturePropertiesNamespace = "http://uri.etsi.org/01903/v1.2.2#SignedProperties";
public XAdESSignedXml(XmlDocument document) : base(document) { }
public override XmlElement GetIdElement(XmlDocument doc, string id)
{
if (String.IsNullOrEmpty(id)) return null;
XmlElement xmlElement = base.GetIdElement(doc, id);
if (xmlElement != null) return xmlElement;
//Search XAdES node
foreach (DataObject dataObject in _dataObjects)
{
XmlElement nodeWithSameId = findNodeWithAttributeValueIn(dataObject.Data, "Id", id);
if (nodeWithSameId != null)
return nodeWithSameId;
}
//Search the KeyInfo node
if (KeyInfo != null)
{
XmlElement nodeWithSameId = findNodeWithAttributeValueIn(KeyInfo.GetXml().SelectNodes("."), "Id", id);
if (nodeWithSameId != null)
return nodeWithSameId;
}
return null;
}
[... métodos auxiliares...}
}
Sobrescribiendo este método, logramos poder buscar las referencias no solamente en el Xml que se va a firmar, sino también en los objetos que van a formar parte del Xml de firma (dentro del nodo Signature). De esta manera, primero se busca el nodo referenciado mediante la clase SignedXml, y si no se encuentra se pasa a nuestro código propio. Primero se busca, en la colección de objetos _dataObjects el objeto correspondiente al objeto XAdES. Si se encuentra, se devuelve, y si no, se busca el objeto KeyInfo.
De esta manera, SignedXml es capaz de calcular del DigestValue para cada referencia, para posteriormente calcular la firma.
Pero para poder usarlo, tendremos que modificar el método de firma, para emplear nuestro objeto en lugar del SignedXml. Para ello, en el método de firma, sustituiremos:
SignedXml signer = new SignedXml(toSign);
Por lo siguiente:
XAdESSignedXml signer = new XAdESSignedXml(toSign);
Ejecutaremos nuestra prueba unitaria. Si hemos hecho todo bien, obtendremos una prueba correcta, obteniendo el siguiente resultado:
<?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>EhaeWtAs6PEFAMHhHlJDT509kNE=</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>lBINUyb1H5+S/ufalq/oBHd3Z0E=</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>VFx0acqOZinC2c+Dv07GiRx9+LE=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>EukSphW1wBeWo4X0TwVNj8sPFUafQDQT6aGlKeuRYFSRH83NJU+v4Rk6EKa8VlOmx8UwIBEY/I2B+6T/IF6UYCWvNhuX+W6r7GdlNldBE50Vjo8XLX36L1HiyTXuh1hNc2qOpvlFuR6h4xlfHD8XqQ6XXpu6ScCLdMqJjAVb/Co=</SignatureValue>
<KeyInfo Id="KeyInfo">
<X509Data>
<X509SubjectName>CN=TestCertificate, OU=Testing, OU=local</X509SubjectName>
<X509Certificate>MIIB2TCCAYegAwIBAgIQZfvBf6QChY9MxURe7gvy3jAJBgUrDgMCHQUAMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTExMDMyOTA5MTQyNFoXDTM5MTIzMTIzNTk1OVowPDEOMAwGA1UECxMFbG9jYWwxEDAOBgNVBAsTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3RDZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyCpLpQjTmY1rymBnrBMj48gVWwseQdqLj47baOX5/dSRNMi8DdTBWV9eHryi4l1ivoG65AD8GHWcMoqiEQ2ZyjhHD/yR2CX5sd6Dm26e+caZIZ7mqm+wD4brR6NpNcJtIPBR2HYiHLPctCHffFmr16hOI6DlYJ5UoBm5TElw7HkCAwEAAaNLMEkwRwYDVR0BBEAwPoAQEuQJLQYdHU8AjWEh3BZkY6EYMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5ghAGN2wAqgBkihHPuNSqXDX0MAkGBSsOAwIdBQADQQBTpAn4LfxKfCosDnDQDu6A4GhG8QWKjo9faHkS2xqajiYAiWf76oCIqj1hudcS94xfCp3gaRtajaUaB/ceVa5s</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-31T10:56:13Z</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#">kRV4rHiKIE3iM5ZKx5DBNsDbrd8=</ds:DigestValue>
</etsi:CertDigest>
<etsi:IssuerSerial>
<ds:X509IssuerName xmlns:ds="http://www.w3.org/2000/09/xmldsig#">CN=Root Agency</ds:X509IssuerName>
<ds:X509SerialNumber xmlns:ds="http://www.w3.org/2000/09/xmldsig#">65FBC17FA402858F4CC5445EEE0BF2DE</ds:X509SerialNumber>
</etsi:IssuerSerial>
</etsi:Cert>
</etsi:SigningCertificate>
</etsi:SignedSignatureProperties>
</etsi:SignedProperties>
</etsi:QualifyingProperties>
</Object>
</Signature>
</documento>
Descarga el proyecto de ejemplo de firma
En esta serie de cuatro artículos hemos podido ver, de una manera eminentemente práctica, de que manera realizar una firma mediante el estándar XAdES-BES. Hemos podido ver que la solución al problema es relativamente sencilla, con algún que otro problema que tiene fácil solución. Espero que sea de ayuda para la comunidad.
ee6a6ec3-1364-4a11-beda-abb0794ca442|1|4.0