MAC、雜湊以及簽章MACs, hashes, and signatures

本文討論如何在通用 Windows 平台 (UWP) app 中使用訊息驗證碼 (MAC)、雜湊及簽章來偵測訊息是否遭竄改。This article discusses how message authentication codes (MACs), hashes, and signatures can be used in Universal Windows Platform (UWP) apps to detect message tampering.

訊息驗證碼 (MAC)Message authentication codes (MACs)

加密有助於防止未授權的人員讀取訊息,但加密無法防止該人員篡改訊息。Encryption helps prevent an unauthorized individual from reading a message, but it does not prevent that individual from tampering with the message. 即使竄改不會產生任何不良影響且無意義,被竄改的訊息還是會產生實際成本。An altered message, even if the alteration results in nothing but nonsense, can have real costs. 訊息驗證碼 (MAC) 有助於防止訊息遭篡改。A message authentication code (MAC) helps prevent message tampering. 例如,請考慮下列案例:For example, consider the following scenario:

  • Bob 與 Alice 共用祕密金鑰並同意使用 MAC 函數。Bob and Alice share a secret key and agree on a MAC function to use.
  • Bob 建立訊息,然後將訊息與祕密金鑰輸入 MAC 函數來擷取 MAC 值。Bob creates a message and inputs the message and the secret key into a MAC function to retrieve a MAC value.
  • Bob 透過 [ 網路將未加密的 ] 訊息和 MAC 值傳送給 Alice。Bob sends the [unencrypted] message and the MAC value to Alice over a network.
  • Alice 使用祕密金鑰與訊息做為 MAC 函數的輸入。Alice uses the secret key and the message as input to the MAC function. 然後將產生的 MAC 值與 Bob 傳送的 MAC 值相比較。She compares the generated MAC value to the MAC value sent by Bob. 若結果相同,表示訊息在傳輸時未遭到變更。If they are the same, the message was not changed in transit.

請注意,竊聽 Bob 與 Alice 之間交談的第三者 Eve,無法有效操縱該訊息。Note that Eve, a third party eavesdropping on the conversation between Bob and Alice, cannot effectively manipulate the message. Eve 無法存取私密金鑰,因此也無法建立 MAC 值來讓 Alice 以為遭竄改的訊息是合法訊息。Eve does not have access to the private key and cannot, therefore, create a MAC value which would make the tampered message appear legitimate to Alice.

建立訊息驗證碼只可確保原始訊息不被竄改,而且還可透過使用共用的祕密金鑰,確保訊息雜湊是由可存取私密金鑰的人員所簽署。Creating a message authentication code ensures only that the original message was not altered and, by using a shared secret key, that the message hash was signed by someone with access to that private key.

您可以使用 MacAlgorithmProvider 來列舉可用的 MAC 演算法以及產生對稱金鑰。You can use the MacAlgorithmProvider to enumerate the available MAC algorithms and generate a symmetric key. 您可以在 CryptographicEngine 類別使用靜態方法,執行必要的加密來建立 MAC 值。You can use static methods on the CryptographicEngine class to perform the necessary encryption that creates the MAC value.

數位簽章是等同於私密金鑰訊息驗證碼 (MAC) 的公開金鑰。Digital signatures are the public key equivalent of private key message authentication codes (MACs). 雖然 MAC 使用私密金鑰讓訊息收件者驗證訊息在傳輸期間未遭竄改,但簽章使用的是私密/公開金鑰組。Although MACs use private keys to enable a message recipient to verify that a message has not been altered during transmission, signatures use a private/public key pair.

下列範例程式碼說明如何使用 MacAlgorithmProvider 類別來建立雜湊訊息驗證碼 (HMAC)。This example code shows how to use the MacAlgorithmProvider class to create a hashed message authentication code (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.");
            }
        }
    }
}

雜湊Hashes

密碼編譯雜湊函數使用任意長度的資料區塊並傳回固定大小位元字串。A cryptographic hash function takes an arbitrarily long block of data and returns a fixed-size bit string. 簽章資料時通常是使用雜湊函數。Hash functions are typically used when signing data. 由於大部分的公開金鑰簽章作業需要大量的運作,因此通常簽署 (加密) 郵件雜湊比簽署原始郵件更有效率。Because most public key signature operations are computationally intensive, it is typically more efficient to sign (encrypt) a message hash than it is to sign the original message. 下列程序代表雖然簡化但卻常用的案例:The following procedure represents a common, albeit simplified, scenario:

  • Bob 與 Alice 共用祕密金鑰並同意使用 MAC 函數。Bob and Alice share a secret key and agree on a MAC function to use.
  • Bob 建立訊息,然後將訊息與祕密金鑰輸入 MAC 函數來擷取 MAC 值。Bob creates a message and inputs the message and the secret key into a MAC function to retrieve a MAC value.
  • Bob 透過 [ 網路將未加密的 ] 訊息和 MAC 值傳送給 Alice。Bob sends the [unencrypted] message and the MAC value to Alice over a network.
  • Alice 使用祕密金鑰與訊息做為 MAC 函數的輸入。Alice uses the secret key and the message as input to the MAC function. 然後將產生的 MAC 值與 Bob 傳送的 MAC 值相比較。She compares the generated MAC value to the MAC value sent by Bob. 若結果相同,表示訊息在傳輸時未遭到變更。If they are the same, the message was not changed in transit.

請注意,Alice 傳送的是未加密的郵件。Note that Alice sent an unencrypted message. 僅加密了雜湊。Only the hash was encrypted. 這個程序僅確保原始郵件未經過修改,而且,使用 Alice 的公開金鑰,郵件雜湊會由具有 Alice 私密金鑰存取權的人來簽署,可能就是 Alice 本人。The procedure ensures only that the original message was not altered and, by using Alice's public key, that the message hash was signed by someone with access to Alice's private key, presumably Alice.

您可以使用 HashAlgorithmProvider 類別來列舉可用的雜湊演算法及建立 CryptographicHash 值。You can use the HashAlgorithmProvider class to enumerate the available hash algorithms and create a CryptographicHash value.

數位簽章是等同於私密金鑰訊息驗證碼 (MAC) 的公開金鑰。Digital signatures are the public key equivalent of private key message authentication codes (MACs). 相對而言,MAC 使用私密金鑰讓郵件收件者驗證郵件在傳輸期間沒有被修改,而簽章則是使用私密/公開金鑰組。Whereas MACs use private keys to enable a message recipient to verify that a message has not been altered during transmission, signatures use a private/public key pair.

CryptographicHash 物件可以用來重複雜湊不同的資料,而不用在每次使用時都要重新建立物件。The CryptographicHash object can be used to repeatedly hash different data without having to re-create the object for each use. Append 方法會將新資料加入要雜湊的緩衝區。The Append method adds new data to a buffer to be hashed. GetValueAndReset 方法會進行資料拼湊並重設物件以供下次使用。The GetValueAndReset method hashes the data and resets the object for another use. 如下列範例所示。This is shown by the following example.

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

數位簽章Digital signatures

數位簽章是等同於私密金鑰訊息驗證碼 (MAC) 的公開金鑰。Digital signatures are the public key equivalent of private key message authentication codes (MACs). 相對而言,MAC 使用私密金鑰讓郵件收件者驗證郵件在傳輸期間沒有被修改,而簽章則是使用私密/公開金鑰組。Whereas MACs use private keys to enable a message recipient to verify that a message has not been altered during transmission, signatures use a private/public key pair.

不過,由於大部分的公開金鑰簽章作業需要大量的運作,因此通常簽署 (加密) 郵件雜湊比簽署原始郵件更有效率。Because most public key signature operations are computationally intensive, however, it is typically more efficient to sign (encrypt) a message hash than it is to sign the original message. 寄件者建立郵件雜湊,簽署它,然後傳送簽章及 (未加密) 郵件兩者。The sender creates a message hash, signs it, and sends both the signature and the (unencrypted) message. 收件者透過郵件計算雜湊,解密簽章,然後將解密的簽章與雜湊值進行比較。The recipient calculates a hash over the message, decrypts the signature, and compares the decrypted signature to the hash value. 如果這兩者相符,收件者即可確定郵件確實是來自寄件者,而且傳輸期間未經過修改。If they match, the recipient can be fairly certain that the message did, in fact, come from the sender and was not altered during transmission.

簽署僅確保原始郵件未經過修改,而且,使用寄件者的公開金鑰,郵件雜湊會由具有私密金鑰存取權的人來簽署。Signing ensures only that the original message was not altered and, by using the sender's public key, that the message hash was signed by someone with access to the private key.

您可以使用 AsymmetricKeyAlgorithmProvider 物件來列舉可用的簽章演算法及產生或匯入金鑰組。You can use an AsymmetricKeyAlgorithmProvider object to enumerate the available signature algorithms and generate or import a key pair. 您可以在 CryptographicHash 類別上使用靜態方法來簽署郵件或驗證簽章。You can use static methods on the CryptographicHash class to sign a message or verify a signature.