Cómo usar firma digital en Apps WinRT

Avanzado

Encryption

WinRT como muchos de ustedes saben nos permite crear aplicaciones con C# , como suelo mencionar en muchos de mis cursos WinRT:

  • Se programa como en .NET Framework
  • Se ve como .NET Framework
  • Muchos componentes se llaman igual que en el .NET Framework
  • Pero WinRT NO es .NET Framework

Por ello, hay cosas que simplemente no existen aún en WinRT o existen pero con nombres y forma de uso diferentes, ese es el caso de las API de criptografía.

Valga decir que la implementación en WinRT me parece mucho más limpia y mejor estructurada, aunque aún tiene opciones de mejora, sutiles pero que suman para hacer la diferencia.

Sin más preámbulos, en este artículo enseñare a firmar contenidos digitalmente haciendo uso de la API de criptografía, tomare como base el algoritmo HMAC SHA256, como verán el algoritmo que escojamos es irrelevante en el proceso de firmado.

Cómo firmar digitalmente la información?

Creamos un método público llamado ComputeSignature que reciba como parámetro

  1. El nombre del algoritmo a utilizar
  2. El contenido a firmar
  3. Un valor para crear la llave
  4. El tipo de encoding para el contenido

Para efectos de este post asumiremos que el contenido a firmar sea siempre de tipo string. Es importante incluir el tipo de encoding del texto porque un mismo texto puede tener una información binaria diferente de acuerdo al tipo de encoding y dado que las operaciones criptográficas utilizan datos binarios se pueden presentar diferencias al calcular la firma digital en un encoding u otro.

Por defecto el encoding manejado lo estableceremos en UTF8.

El método devuelve una cadena de texto que es la firma del contenido.

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{

}

Agregamos referencia a los siguientes namespaces

 using Windows.Security.Cryptography;  
using Windows.Security.Cryptography.Core;  

Dentro del método inicializamos el proveedor del algoritmo con el valor enviado como parámetro, dado que como parámetro hemos recibido un enum este debemos convertirlo en string.

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{
    var algorithmProvider = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
}

Ahora hay que preparar el contenido que vamos a firmar, la API de criptografía funciona utilizando información expuesta por medio de la interfaz IBuffer, así que hay que convertir la información a dicho formato, esto lo hacemos haciendo uso de la clase CryptographicBuffer la cual expone varios métodos estáticos que permiten convertir datos desde y hacia una interfaz IBuffer.

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{
    var algorithmProvider = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
    var contentBuffer = CryptographicBuffer.ConvertStringToBinary(content, BinaryStringEncoding.Utf8);
}

Para lograr el proceso de la firma se requiere crear una llave criptográfica, más adelante veremos como crear una, por ahora asumamos que la llave llega como parámetro expuesta como una cadena.

Así que ahora hay que convertir la cadena en información que pueda entender la API de criptografía, es decir en forma de un IBuffer.

La llave utilizada para calcular la firma digital es una llave simétrica, por lo cual la información que contiene es información binaria que no necesariamente tiene representación como texto, dado que el método que hemos creado recibe la llave como texto debemos presumir que se trata información binaria expuesta como cadena haciendo uso de Base64, por ello utilizando CryptographicBuffer convertiremos esta cadena directamente a IBuffer.

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{
    var algorithmProvider = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
    var contentBuffer = CryptographicBuffer.ConvertStringToBinary(content, BinaryStringEncoding.Utf8);
    var keyBuffer = CryptographicBuffer.DecodeFromBase64String(key);
}

Estando ya en forma de buffer creamos la llave criptográfica de acuerdo al algoritmo seleccionado:

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{
    var algorithmProvider = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
    var contentBuffer = CryptographicBuffer.ConvertStringToBinary(content, BinaryStringEncoding.Utf8);

    var keyBuffer = CryptographicBuffer.DecodeFromBase64String(key);
    var signatureKey = algorithmProvider.CreateKey(keyBuffer);
}

Finalmente creamos la firma, esto es sencillo, en la clase CryptographicEngine existe el método Sign el cual solo requiere la llave y el contenido para firmar. Este método como pueden adivinar devuelve un IBuffer, nosotros requerimos que devuelva un string así que nuevamente hacemos uso de CryptographicBuffer pero esta vez para convertir el IBuffer a un string con la información binaria expuesta como Base64.

 public string ComputeSignature(string algorithmName, string content, string key,  
                               BinaryStringEncoding encoding = BinaryStringEncoding.Utf8)
{
    var algorithmProvider = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
    var contentBuffer = CryptographicBuffer.ConvertStringToBinary(content, BinaryStringEncoding.Utf8);

    var keyBuffer = CryptographicBuffer.DecodeFromBase64String(key);
    var signatureKey = algorithmProvider.CreateKey(keyBuffer);

    var signedBuffer = CryptographicEngine.Sign(signatureKey, contentBuffer);

    return CryptographicBuffer.EncodeToBase64String(signedBuffer);
}

Cómo crear una llave simétrica para la firma?

Para probar el método ComputeSignature debemos enviar como parámetro una llave de cifrado.

Las llaves no pueden ser de un tamaño escogido por capricho, cada algoritmo tiene algunas restricciones para el tamaño de llave que requiere.

CryptographicBuffer también posee un método GenerateRandom el cual nos permite crear material para la llave. Es decir sino tenemos llaves simétricas podemos generar las propias de la siguiente manera:

 private string GenerateMacSimetricKey(string algorithmName)  
{
    var alg = MacAlgorithmProvider.OpenAlgorithm(algorithmName);
    var keyBuffer = CryptographicBuffer.GenerateRandom(alg.MacLength);
    return CryptographicBuffer.EncodeToBase64String(keyBuffer);
}

Cómo verificar un contenido firmado?

La forma de verificar una firma es en efecto volviendo a calcular la firma, si la cifra calculada es igual a la firma con que ha llegado la información entonces la información es correcta y no ha sido modificada.

WinRT tiene ya incorporado un método para verificar la firma, en la clase CryptographicEngine esta el método VerifySignature, este, como es de esperarse, requiere como parámetros datos de tipo IBuffer y CryptographicKey, si ya los tienes a la mano usar esta alternativa es bastante eficiente, pero si por el contrario tienes la llave en cualquier otro formato y debes convertirla a CryptographicKey no tendrás ninguna ventaja respecto a calcular nuevamente la firma ya que debes hacer prácticamente la misma cantidad de operaciones en código.

Ejemplo de uso

Creamos un método Test para realizar las pruebas.

En el creamos el contenido que deseamos firmar, y generamos una llave para realizar dicha firma.

 public void Test()  
        {
            var contenido = @"Nombre:Juan Carlos Ruiz Pacheco
CC:79919051  
Profesión:Ingeniero de Sistemas  
Nacionalidad:Colombiano";

            var alg = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
            var signatureKey = GenerateMacSimetricKey(MacAlgorithmNames.HmacSha256);
            Debug.WriteLine(string.Format("This is the signature key:{0}", signatureKey));

            var signature = ComputeSignature(MacAlgorithmNames.HmacSha256, contenido, signatureKey);
            Debug.WriteLine(string.Format("This is the computed signature:{0}", signature));
        }

Salida:

 This is the signature key:kbKOyOBMP8MHMDQ2Gtnn1HXgAXDjcuFOAcOzE+fnS2I= 
This is the computed signature:Kcb0oMaIBCLITcBJStWjNj5WN309FEhbbfIb7ocX3bE=

Eso es todo, espero que el artículo les sea de utilidad.