MAC, hash e firme

Questo articolo illustra come utilizzare i codici di autenticazione dei messaggi (MAC), gli hash e le firme nelle app UWP (Universal Windows Platform) per rilevare la manomissione dei messaggi.

Codici di autenticazione dei messaggi (MAC)

La crittografia aiuta a impedire a un individuo non autorizzato di leggere un messaggio, ma non impedisce a tale individuo di manometterlo. Un messaggio alterato, anche se l'alterazione non ha altro senso, può avere dei costi reali. Un codice di autenticazione del messaggio (MAC) aiuta a prevenire la manomissione dei messaggi. Ad esempio, si consideri il seguente scenario:

  • Bob e Alice condividono una chiave segreta e concordano una funzione MAC da utilizzare.
  • Bob crea un messaggio e inserisce il messaggio e la chiave segreta in una funzione MAC per recuperare un valore MAC.
  • Bob invia il messaggio [non crittografato] e il valore MAC ad Alice tramite una rete.
  • Alice utilizza la chiave segreta e il messaggio come input per la funzione MAC. Confronta il valore MAC generato con il valore MAC inviato da Bob. Se sono uguali, il messaggio non è stato modificato durante il trasporto.

Si noti che Eve, una terza persona che ascolta di nascosto la conversazione tra Bob e Alice, non può manipolare efficacemente il messaggio. Eve non ha accesso alla chiave privata e non può quindi creare un valore MAC che faccia apparire legittimo ad Alice il messaggio manomesso.

La creazione di un codice di autenticazione del messaggio garantisce solo che il messaggio originale non sia stato alterato e, utilizzando una chiave segreta condivisa, che l'hash del messaggio sia stato firmato da qualcuno con accesso a quella chiave privata.

È possibile utilizzare il MacAlgorithmProvider per enumerare gli algoritmi MAC disponibili e generare una chiave simmetrica. È possibile utilizzare metodi statici su CryptographicEngine classe per eseguire la crittografia necessaria che crea il valore MAC.

Le firme digitali sono l'equivalente a chiave pubblica dei codici di autenticazione dei messaggi a chiave privata (MAC). Sebbene i MAC utilizzino chiavi private per consentire al destinatario del messaggio di verificare che un messaggio non sia stato alterato durante la trasmissione, le firme utilizzano una coppia di chiavi privata/pubblica.

Questo codice di esempio mostra come utilizzare la classe MacAlgorithmProvider per creare un codice di autenticazione del messaggio con 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.");
            }
        }
    }
}

Hash

Una funzione hash crittografica accetta un blocco di dati arbitrariamente lungo e restituisce una stringa di bit di dimensione fissa. Le funzioni hash vengono generalmente utilizzate durante la firma dei dati. Poiché la maggior parte delle operazioni di firma a chiave pubblica richiedono un utilizzo intensivo del calcolo, in genere è più efficiente firmare (crittografare) l'hash di un messaggio piuttosto che firmare il messaggio originale. La seguente procedura rappresenta uno scenario comune, anche se semplificato:

  • Bob e Alice condividono una chiave segreta e concordano una funzione MAC da utilizzare.
  • Bob crea un messaggio e inserisce il messaggio e la chiave segreta in una funzione MAC per recuperare un valore MAC.
  • Bob invia il messaggio [non crittografato] e il valore MAC ad Alice tramite una rete.
  • Alice utilizza la chiave segreta e il messaggio come input per la funzione MAC. Confronta il valore MAC generato con il valore MAC inviato da Bob. Se sono uguali, il messaggio non è stato modificato durante il trasporto.

Si prega di notare che Alice ha inviato un messaggio non crittografato. Solo l'hash è stato crittografato. La procedura garantisce solo che il messaggio originale non sia stato alterato e, utilizzando la chiave pubblica di Alice, che l'hash del messaggio sia stato firmato da qualcuno con accesso alla chiave privata di Alice, presumibilmente Alice.

Puoi utilizzare la classe HashAlgorithmProviderper enumerare gli algoritmi hash disponibili e creare un valoreCryptographicHash.

Le firme digitali sono l'equivalente a chiave pubblica dei codici di autenticazione dei messaggi a chiave privata (MAC). Mentre i MAC utilizzino chiavi private per consentire al destinatario del messaggio di verificare che un messaggio non sia stato alterato durante la trasmissione, le firme utilizzano una coppia di chiavi privata/pubblica.

L'oggetto CryptographicHash può essere utilizzato per eseguire ripetutamente l'hashing di dati diversi senza dover ricreare l'oggetto per ogni utilizzo. Il metodo Appendaggiunge nuovi dati a un buffer da sottoporre ad hashing. Il metodo GetValueAndReset esegue l'hashing dei dati e reimposta l'oggetto per un altro utilizzo. Ciò è mostrato dal seguente esempio.

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);
}

Firme digitali

Le firme digitali sono l'equivalente a chiave pubblica dei codici di autenticazione dei messaggi a chiave privata (MAC). Mentre i MAC utilizzino chiavi private per consentire al destinatario del messaggio di verificare che un messaggio non sia stato alterato durante la trasmissione, le firme utilizzano una coppia di chiavi privata/pubblica.

Poiché la maggior parte delle operazioni di firma a chiave pubblica richiedono un utilizzo intensivo del calcolo, tuttavia in genere è più efficiente firmare (crittografare) l'hash di un messaggio piuttosto che firmare il messaggio originale. Il mittente crea un hash del messaggio, lo firma e invia sia la firma che il messaggio (non crittografato). Il destinatario calcola un hash sul messaggio, decrittografa la firma e confronta la firma decrittografata con il valore hash. Se corrispondono, il destinatario può essere abbastanza certo che il messaggio provenga effettivamente dal mittente e non sia stato alterato durante la trasmissione.

La firma garantisce solo che il messaggio originale non sia stato alterato e, utilizzando la chiave pubblica del mittente, che l'hash del messaggio sia stato firmato da qualcuno con accesso alla chiave privata.

È possibile utilizzare un oggettoAsymmetricKeyAlgorithmProvider per enumerare gli algoritmi di firma disponibili e generare o importare una coppia di chiavi. È possibile utilizzare metodi statici sulla classe CryptographicHash per firmare un messaggio o verificare una firma.