MAC, hash y firmas
En este artículo se describe cómo se pueden usar códigos de autenticación de mensajes (MAC), hashes y firmas en aplicaciones de Plataforma universal de Windows (UWP) para detectar alteraciones de mensajes.
Códigos de autenticación de mensajes (MAC)
El cifrado ayuda a evitar que un individuo no autorizado lea un mensaje, pero no impide que ese individuo manipule el mensaje. Un mensaje modificado, incluso si la modificación no da como resultado nada más que un sentido, puede tener costos reales. Un código de autenticación de mensajes (MAC) ayuda a evitar la manipulación de mensajes. Por ejemplo, considere el siguiente escenario:
- Bob y Alice comparten una clave secreta y están de acuerdo con una función MAC que se va a usar.
- Bob crea un mensaje y escribe el mensaje y la clave secreta en una función MAC para recuperar un valor MAC.
- Bob envía el mensaje [sin cifrar] y el valor MAC a Alice a través de una red.
- Alice usa la clave secreta y el mensaje como entrada para la función MAC. Compara el valor MAC generado con el valor MAC enviado por Bob. Si son iguales, el mensaje no se cambió en tránsito.
Tenga en cuenta que Eva, un tercero interceptando la conversación entre Bob y Alice, no puede manipular eficazmente el mensaje. Eve no tiene acceso a la clave privada y, por lo tanto, no puede crear un valor MAC que haría que el mensaje manipulado parezca legítimo para Alice.
La creación de un código de autenticación de mensajes garantiza que el mensaje original no se modificó y, mediante una clave secreta compartida, que alguien con acceso a esa clave privada firmó el hash del mensaje.
Puede usar MacAlgorithmProvider para enumerar los algoritmos MAC disponibles y generar una clave simétrica. Puede usar métodos estáticos en la clase CryptographicEngine para realizar el cifrado necesario que crea el valor MAC.
Las firmas digitales son el equivalente de clave pública de códigos de autenticación de mensajes de clave privada (MAC). Aunque los MAC usan claves privadas para permitir que un destinatario del mensaje compruebe que un mensaje no se ha modificado durante la transmisión, las firmas usan un par de claves pública o privada.
En este código de ejemplo se muestra cómo usar la clase MacAlgorithmProvider para crear un código de autenticación de mensajes hash (HMAC).
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
namespace SampleMacAlgorithmProvider
{
sealed partial class MacAlgProviderApp : Application
{
public MacAlgProviderApp()
{
// Initialize the application.
this.InitializeComponent();
// Initialize the hashing process.
String strMsg = "This is a message to be authenticated";
String strAlgName = MacAlgorithmNames.HmacSha384;
IBuffer buffMsg;
CryptographicKey hmacKey;
IBuffer buffHMAC;
// Create a hashed message authentication code (HMAC)
this.CreateHMAC(
strMsg,
strAlgName,
out buffMsg,
out hmacKey,
out buffHMAC);
// Verify the HMAC.
this.VerifyHMAC(
buffMsg,
hmacKey,
buffHMAC);
}
void CreateHMAC(
String strMsg,
String strAlgName,
out IBuffer buffMsg,
out CryptographicKey hmacKey,
out IBuffer buffHMAC)
{
// Create a MacAlgorithmProvider object for the specified algorithm.
MacAlgorithmProvider objMacProv = MacAlgorithmProvider.OpenAlgorithm(strAlgName);
// Demonstrate how to retrieve the name of the algorithm used.
String strNameUsed = objMacProv.AlgorithmName;
// Create a buffer that contains the message to be signed.
BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
buffMsg = CryptographicBuffer.ConvertStringToBinary(strMsg, encoding);
// Create a key to be signed with the message.
IBuffer buffKeyMaterial = CryptographicBuffer.GenerateRandom(objMacProv.MacLength);
hmacKey = objMacProv.CreateKey(buffKeyMaterial);
// Sign the key and message together.
buffHMAC = CryptographicEngine.Sign(hmacKey, buffMsg);
// Verify that the HMAC length is correct for the selected algorithm
if (buffHMAC.Length != objMacProv.MacLength)
{
throw new Exception("Error computing digest");
}
}
public void VerifyHMAC(
IBuffer buffMsg,
CryptographicKey hmacKey,
IBuffer buffHMAC)
{
// The input key must be securely shared between the sender of the HMAC and
// the recipient. The recipient uses the CryptographicEngine.VerifySignature()
// method as follows to verify that the message has not been altered in transit.
Boolean IsAuthenticated = CryptographicEngine.VerifySignature(hmacKey, buffMsg, buffHMAC);
if (!IsAuthenticated)
{
throw new Exception("The message cannot be verified.");
}
}
}
}
Códigos hash
Una función hash criptográfica toma un bloque arbitrario largo de datos y devuelve una cadena de bits de tamaño fijo. Las funciones hash se usan normalmente al firmar datos. Dado que la mayoría de las operaciones de firma de clave pública consumen mucho cálculo, normalmente es más eficaz firmar (cifrar) un hash de mensaje que firmar el mensaje original. El procedimiento siguiente representa un escenario común, aunque simplificado:
- Bob y Alice comparten una clave secreta y están de acuerdo con una función MAC que se va a usar.
- Bob crea un mensaje y escribe el mensaje y la clave secreta en una función MAC para recuperar un valor MAC.
- Bob envía el mensaje [sin cifrar] y el valor MAC a Alice a través de una red.
- Alice usa la clave secreta y el mensaje como entrada para la función MAC. Compara el valor MAC generado con el valor MAC enviado por Bob. Si son iguales, el mensaje no se cambió en tránsito.
Tenga en cuenta que Alice envió un mensaje sin cifrar. Solo se cifró el hash. El procedimiento garantiza solo que el mensaje original no se modificó y, mediante la clave pública de Alice, que alguien con acceso a la clave privada de Alice firmó el hash del mensaje, presumiblemente Alice.
Puede usar la clase HashAlgorithmProvider para enumerar los algoritmos hash disponibles y crear un valor CryptographicHash.
Las firmas digitales son el equivalente de clave pública de códigos de autenticación de mensajes de clave privada (MAC). Mientras que los MAC usan claves privadas para permitir que un destinatario del mensaje compruebe que un mensaje no se ha modificado durante la transmisión, las firmas usan un par de claves pública o privada.
El objeto CryptographicHash se puede usar para aplicar hash repetidamente a distintos datos sin tener que volver a crear el objeto para cada uso. El método Append agrega nuevos datos a un búfer que se va a aplicar hash. El método GetValueAndReset aplica un hash a los datos y restablece el objeto para otro uso. Esto se muestra en el ejemplo siguiente.
public void SampleReusableHash()
{
// Create a string that contains the name of the hashing algorithm to use.
String strAlgName = HashAlgorithmNames.Sha512;
// Create a HashAlgorithmProvider object.
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(strAlgName);
// Create a CryptographicHash object. This object can be reused to continually
// hash new messages.
CryptographicHash objHash = objAlgProv.CreateHash();
// Hash message 1.
String strMsg1 = "This is message 1.";
IBuffer buffMsg1 = CryptographicBuffer.ConvertStringToBinary(strMsg1, BinaryStringEncoding.Utf16BE);
objHash.Append(buffMsg1);
IBuffer buffHash1 = objHash.GetValueAndReset();
// Hash message 2.
String strMsg2 = "This is message 2.";
IBuffer buffMsg2 = CryptographicBuffer.ConvertStringToBinary(strMsg2, BinaryStringEncoding.Utf16BE);
objHash.Append(buffMsg2);
IBuffer buffHash2 = objHash.GetValueAndReset();
// Hash message 3.
String strMsg3 = "This is message 3.";
IBuffer buffMsg3 = CryptographicBuffer.ConvertStringToBinary(strMsg3, BinaryStringEncoding.Utf16BE);
objHash.Append(buffMsg3);
IBuffer buffHash3 = objHash.GetValueAndReset();
// Convert the hashes to string values (for display);
String strHash1 = CryptographicBuffer.EncodeToBase64String(buffHash1);
String strHash2 = CryptographicBuffer.EncodeToBase64String(buffHash2);
String strHash3 = CryptographicBuffer.EncodeToBase64String(buffHash3);
}
Firmas digitales
Las firmas digitales son el equivalente de clave pública de códigos de autenticación de mensajes de clave privada (MAC). Mientras que los MAC usan claves privadas para permitir que un destinatario del mensaje compruebe que un mensaje no se ha modificado durante la transmisión, las firmas usan un par de claves pública o privada.
Dado que la mayoría de las operaciones de firma de clave pública consumen mucho cálculo, normalmente es más eficaz firmar (cifrar) un hash de mensaje que firmar el mensaje original. El remitente crea un hash de mensaje, lo firma y envía tanto la firma como el mensaje (sin cifrar). El destinatario calcula un hash sobre el mensaje, descifra la firma y compara la firma descifrada con el valor hash. Si coinciden, el destinatario puede ser bastante seguro de que el mensaje lo hizo, de hecho, proviene del remitente y no se modificó durante la transmisión.
La firma solo garantiza que el mensaje original no se modificó y, mediante el uso de la clave pública del remitente, que alguien con acceso a la clave privada firmó el hash del mensaje.
Puede usar un objeto AsymmetricKeyAlgorithmProvider para enumerar los algoritmos de firma disponibles y generar o importar un par de claves. Puede usar métodos estáticos en la clase CryptographicHash para firmar un mensaje o comprobar una firma.