Nota del autor

Si la entrada que estás leyendo carece de imágenes, no se ve el vídeo que teóricamente lleva incrustado o el código fuente mostrado aparece sin formato, podéis conocer los motivos aquí. Poco a poco iré restableciendo la normalidad en el blog.
Este blog es un archivo de los artículos situados previamente en Lobosoft.es y ha dejado de ser actualizado. Las nuevas entradas pueden encontrarse en www.lobosoft.es. Un saludo,
Lobosoft.

miércoles, 25 de junio de 2008

Firmando digitalmente archivos XML con .NET

En una entrada anterior presentaba, a grandes rasgos, la recomendación de la W3C respecto a las firmas XML. Su versión 2.0 es bastante reciente (de 10 de junio), y se trata de un documento bastante completo donde poder profundizar en este tema.


Hoy veremos cómo implementar en .NET un sistema de firma y validación de archivos XML. El proceso, como veremos, es un simple calco de lo que presentaba en la teoría.


Partiremos de un archivo XML (prueba.xml) muy sencillo. Simplemente tiene un par de etiquetas: y . Como veréis, modifico los carecteres de cierre de la etiqueta ( ">" por "]"). Su finalidad es únicamente la de evitar el procesado de Wordpress previo a guardar la entrada, que provoca la modificación de las mismas, y que no se muestren adecuadamente. Dicho esto, el documento del que partimos en nuestro ejemplo será similar a este:



<saludo]
<hola]Mundo</hola]
</saludo]

Básicamente, tendremos una método encargado de cargar el documento XML y generar, a partir de una cadena de caracteres que le pasemos, la clave a usar en la firma. Para eso hacemos uso del RSACryptoServiceProvider, una clase de System.Cryptography que nos permite realizar la encriptación asimétrica mediante el algoritmo RSA. También cabría destacar el uso de la propiedad PreserveWhitespace del objeto XmlDocument, por lo que hablaba en el anterior post  respecto al CanonicalizationMethod.



[csharp]
private XmlDocument LoadDocument(string filename, string keystr, out RSACryptoServiceProvider rsaKey)
{
try
{
// Create a new CspParameters object to specify
// a key container.
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = keystr;

// Create a new RSA signing key and save it in the container.
rsaKey = new RSACryptoServiceProvider(cspParams);

// Create a new XML document.
XmlDocument xmlDoc = new XmlDocument();

// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(filename);

return xmlDoc;
}
}
[/csharp]

Hecho esto, sólo nos queda firmar o verificar la firma. Para ello, pasamos el objeto XmlDocument y la clave RSA al correspondiente método.


SignXML lleva a cabo la firma del documento XML, recuperando el árbol del mismo, generando la firma e insertando al final del mismo un nuevo elemento Signature.



[csharp]public string Sign(string key, string filename)
{
try
{
RSACryptoServiceProvider rsaKey;
XmlDocument xmlDoc = LoadDocument(filename, key, out rsaKey);

// Firmamos el documento XML
DigitalSign.SignXml(xmlDoc, rsaKey);

// Guardamos los cambios
xmlDoc.Save(filename);

// Mensaje de salida
return "Firmado el documento XML.";
}
}[/csharp]

Por su parte, VerifyXML genera la firma del XML y la contrasta con la almacenada en el documento XML.



 [csharp]
public string Verify(string key, string filename)
{
try
{
RSACryptoServiceProvider rsaKey;
XmlDocument xmlDoc = LoadDocument(filename, key, out rsaKey);

// Verificamos la firma del XML
bool result = DigitalSign.VerifyXml(xmlDoc, rsaKey);

// Y mostramos el resultado de la verificación
if (result)
{
return "La firma del XML es válida.";
}
else
{
return "La firma del XML no es válida.";
}
}
}
[/csharp]

La codificación interna de DigitalSign y VerifyXML la presentamos a continuación:



[csharp]
internal class DigitalSign
{
///


/// Firma un documento XML
///

/// El documento a firmar
/// Clave usada para firmarlo
internal static void SignXml(XmlDocument Doc, RSA Key)
{
try
{
// Creamos el objeto de firma XML
SignedXml signedXml = new SignedXml(Doc);

// Añadimos la clave a usar
signedXml.SigningKey = Key;

// Creamos una nueva referencia para ser firmada
Reference reference = new Reference();
reference.Uri = "";

// Añadimos una transformación de encapsulación o "ensobramiento" a la referencia
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);

// Añadimos la referencia al objeto de firma
signedXml.AddReference(reference);

// La calculamos
signedXml.ComputeSignature();

// Obtenemos la representación XML de la firma y la guardamos en un objeto XmlElement
XmlElement xmlDigitalSignature = signedXml.GetXml();

// Y por último añadimos el elemento al documento XML
Doc.DocumentElement.AppendChild(Doc.ImportNode(xmlDigitalSignature, true));
}
}
[/csharp]

[csharp]
internal static Boolean VerifyXml(XmlDocument Doc, RSA Key)
{
try
{
// Crea un nuevo objeto SignedXML y le pasa el documento
SignedXml signedXml = new SignedXml(Doc);

// Encuentra la firma y crea un nuevo objeto XmlNodeList
XmlNodeList nodeList = Doc.GetElementsByTagName("Signature");

// Si no la encuentra, lanza una excepción
if (nodeList.Count <= 0)
{
throw new CryptographicException("Error de verificación: No se encontró ninguna firma en el documento.");
}

// En este caso sólo se permite una firma por el documento XML completo. Si encuentra más de una
// lanza una excepción
if (nodeList.Count >= 2)
{
throw new CryptographicException("Error de verificación: se ha encontrado más de una firma en el documento.");
}

// Carga el primer nodo con la firma
signedXml.LoadXml((XmlElement)nodeList[0]);

// Y valida la firma, devolviendo si es válida o no
return signedXml.CheckSignature(Key);
}
}
[/csharp]

Y, tras aplicar la firma digital al documento de partida, prueba.xml, el resultado que obtendremos será el siguiente:


<saludo]
<hola]Mundo</hola]
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" ]
<SignedInfo ]
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /]
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /]
<Reference URI=""]
<Transforms]
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /]
</Transforms]
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /]
<DigestValue]MDAWGdR299ZLHW13klBnxnSi094=</DigestValue]
</Reference]
</SignedInfo]
<SignatureValue]mmyQoNoNqIFjADmEhkDPshvG5KTEWxooLKdcp1oSz2DlALYd0HthnZ+q5eKcU9bU8g5TKIvNnPl3kT7/r5w19+TKdrYD43GFJZswWtrUXRTroroIcFdRpZFWNiFDI9JdJwMUUMKKkJ7JzWTrLoM5/W2+hzk+bwIQ4xU5AUDmUP0=</SignatureValue]
</Signature]
</saludo]

Por último, incluyo un código de ejemplo, con el proyecto de firma digital, y una aplicación de consola que firma y verifica los datos de la firma en un documento XML.


 [download#9]

No hay comentarios:

Publicar un comentario