Compartilhar via


MACs, hashes e assinaturas

Este artigo discute como os MACs (códigos de autenticação de mensagens), hashes e assinaturas podem ser usados em aplicativos WinUI para detectar a adulteração de mensagens.

Códigos de autenticação de mensagem (MACs)

A criptografia ajuda a impedir que um indivíduo não autorizado leia uma mensagem, mas não impede que esse indivíduo viole a mensagem. Uma mensagem alterada, mesmo que a alteração resulte em nada além de absurdo, pode ter custos reais. Um MAC (código de autenticação de mensagem) ajuda a evitar a adulteração de mensagens. Por exemplo, considere o seguinte cenário:

  • Bob e Alice compartilham uma chave secreta e concordam com uma função MAC a ser usada.
  • Bob cria uma mensagem e insira a mensagem e a chave secreta em uma função MAC para recuperar um valor MAC.
  • Bob envia a mensagem [não criptografada] e o valor MAC para Alice em uma rede.
  • Alice usa a chave secreta e a mensagem como entrada para a função MAC. Ela compara o valor de MAC gerado com o valor MAC enviado por Bob. Se forem iguais, a mensagem não foi alterada em trânsito.

Observe que Eve, uma interceptadora de terceiros na conversa entre Bob e Alice, não pode manipular a mensagem efetivamente. Eve não tem acesso à chave privada e não pode, portanto, criar um valor MAC que faria com que a mensagem adulterada parecesse legítima para Alice.

A criação de um código de autenticação de mensagem garante apenas que a mensagem original não foi alterada e, usando uma chave secreta compartilhada, que o hash da mensagem foi assinado por alguém com acesso a essa chave privada.

Você pode usar o MacAlgorithmProvider para enumerar os algoritmos MAC disponíveis e gerar uma chave simétrica. Você pode usar métodos estáticos na classe CryptographicEngine para executar a criptografia necessária que cria o valor MAC.

As assinaturas digitais são o equivalente à chave pública de códigos de autenticação de mensagens de chave privada (MACs). Embora os MACs usem chaves privadas para habilitar um destinatário de mensagem para verificar se uma mensagem não foi alterada durante a transmissão, as assinaturas usam um par de chaves privada/pública.

Este código de exemplo mostra como usar a classe MacAlgorithmProvider para criar um HMAC (código de autenticação de mensagem hash).

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

Hashes

Uma função de hash criptográfica usa um bloco arbitrariamente longo de dados e retorna uma cadeia de caracteres de bits de tamanho fixo. Normalmente, as funções de hash são usadas ao assinar dados. Como a maioria das operações de assinatura de chave pública são computacionalmente intensivas, normalmente é mais eficiente assinar (criptografar) um hash de mensagem do que assinar a mensagem original. O procedimento a seguir representa um cenário comum, embora simplificado:

  • Alice tem um par de chaves pública/privada e deseja enviar uma mensagem assinada para Bob.
  • Alice cria uma mensagem e calcula um hash da mensagem usando uma função de hash.
  • Alice assina o hash usando sua chave privada e envia a mensagem [não criptografada] e a assinatura para Bob através de uma rede.
  • Bob calcula um hash da mensagem recebida usando a mesma função de hash. Em seguida, ele usa a chave pública de Alice para descriptografar a assinatura e a compara com o hash calculado. Se forem iguais, a mensagem não foi modificada durante o trânsito e veio de Alice.

Observe que Alice enviou uma mensagem não criptografada. Somente o hash foi criptografado. O procedimento garante apenas que a mensagem original não foi alterada e, usando a chave pública de Alice, que o hash da mensagem foi assinado por alguém com acesso à chave privada de Alice, presumivelmente Alice.

Você pode usar a classe HashAlgorithmProvider para enumerar os algoritmos de hash disponíveis e criar um valor CryptographicHash .

As assinaturas digitais são o equivalente à chave pública de códigos de autenticação de mensagens de chave privada (MACs). Enquanto os MACs usam chaves privadas para habilitar um destinatário de mensagem para verificar se uma mensagem não foi alterada durante a transmissão, as assinaturas usam um par de chaves privada/pública.

O objeto CryptographicHash pode ser usado para hash repetidamente de dados diferentes sem precisar recriar o objeto para cada uso. O método Append adiciona novos dados a um buffer para ser hasheado. O método GetValueAndReset faz hash nos dados e redefine o objeto para um novo uso. Isso é mostrado pelo exemplo a seguir.

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

Assinaturas digitais

As assinaturas digitais são o equivalente à chave pública de códigos de autenticação de mensagens de chave privada (MACs). Enquanto os MACs usam chaves privadas para habilitar um destinatário de mensagem para verificar se uma mensagem não foi alterada durante a transmissão, as assinaturas usam um par de chaves privada/pública.

Como a maioria das operações de assinatura de chave pública são computacionalmente intensivas, no entanto, normalmente é mais eficiente assinar (criptografar) um hash de mensagem do que assinar a mensagem original. O remetente cria um hash de mensagem, assina e envia a assinatura e a mensagem (não criptografada). O destinatário calcula um hash sobre a mensagem, descriptografa a assinatura e compara a assinatura descriptografada com o valor de hash. Se corresponderem, o destinatário poderá ter certeza de que a mensagem veio, de fato, do remetente e não foi alterada durante a transmissão.

A assinatura garante apenas que a mensagem original não foi alterada e, usando a chave pública do remetente, que o hash da mensagem foi assinado por alguém com acesso à chave privada.

Você pode usar um objeto AsymmetricKeyAlgorithmProvider para enumerar os algoritmos de assinatura disponíveis e gerar ou importar um par de chaves. Você pode usar métodos estáticos na classe CryptographicHash para assinar uma mensagem ou verificar uma assinatura.