MAC, 해시 및 서명

이 문서는 Universal Windows Platform(UWP) 앱에서 MAC(메시지 인증 코드), 해시 및 서명을 사용하여 메시지 변조를 감지하는 방법을 설명합니다.

메시지 인증 코드(MAC)

암호화는 권한이 없는 개인이 메시지를 읽는 것을 방지하는 데 도움이 되지만, 해당 개인이 메시지를 변조하는 것을 방지하지는 않습니다. 변경된 메시지로 인해 넌센스만 발생하더라도 실제 비용이 들 수 있습니다. MAC(메시지 인증 코드)를 사용하면 메시지 변조를 방지할 수 있습니다. 예를 들어 다음의 시나리오를 고려할 수 있습니다.

  • Bob과 Alice는 비밀 키를 공유하고, 사용하려는 MAC 함수에 동의합니다.
  • Bob은 메시지를 만들고 메시지와 비밀 키를 MAC 함수에 입력하여 MAC 값을 검색합니다.
  • Bob은 네트워크를 통해 [암호화되지 않은] 메시지와 MAC 값을 Alice에게 보냅니다.
  • Alice는 비밀 키와 메시지를 MAC 함수에 대한 입력으로 사용합니다. 생성된 MAC 값을 Bob이 보낸 MAC 값과 비교합니다. 동시에, 전송 중에 메시지가 변경되지 않았습니다.

Bob과 Alice의 대화를 도청하는 제3자인 Eve는 메시지를 효과적으로 조작할 수 없습니다. Eve는 프라이빗 키에 액세스할 수 없으므로, 변조된 메시지가 Alice에게 합법적인 것처럼 보이게 하는 MAC 값을 만들 수 없습니다.

메시지 인증 코드를 만들면 원래 메시지가 변경되지 않고 공유 비밀 키를 사용하여 해당 프라이빗 키에 액세스할 수 있는 사람이 메시지 해시에 서명했음만 보장합니다.

MacAlgorithmProvider를 사용하여 사용 가능한 MAC 알고리즘을 열거하고 대칭 키를 생성할 수 있습니다. CryptographicEngine 클래스에서 정적 메서드를 사용하여 MAC 값을 만드는 데 필요한 암호화를 수행할 수 있습니다.

디지털 서명은 MAC(프라이빗 키 메시지 인증 코드)와 동일한 퍼블릭 키입니다. MAC는 프라이빗 키를 사용하여 메시지 수신자가 전송 중에 메시지가 변경되지 않은지 확인할 수 있지만, 서명은 프라이빗/공개 키 쌍을 사용합니다.

이 예시 코드는 MacAlgorithmProvider 클래스를 사용하여 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.");
            }
        }
    }
}

해시

암호화 해시 함수는 임의로 긴 데이터 블록을 사용하고 고정 크기 비트 문자열을 반환합니다. 해시 함수는 일반적으로 데이터에 서명할 때 사용됩니다. 대부분의 퍼블릭 키 서명 작업은 계산 집약적이므로 일반적으로 원본 메시지에 서명하는 것보다 메시지 해시에 서명(암호화)하는 것이 더 효율적입니다. 다음의 절차는 간소화된 일반적인 시나리오를 나타냅니다.

  • Bob과 Alice는 비밀 키를 공유하고, 사용하려는 MAC 함수에 동의합니다.
  • Bob은 메시지를 만들고 메시지와 비밀 키를 MAC 함수에 입력하여 MAC 값을 검색합니다.
  • Bob은 네트워크를 통해 [암호화되지 않은] 메시지와 MAC 값을 Alice에게 보냅니다.
  • Alice는 비밀 키와 메시지를 MAC 함수에 대한 입력으로 사용합니다. 생성된 MAC 값을 Bob이 보낸 MAC 값과 비교합니다. 동시에, 전송 중에 메시지가 변경되지 않았습니다.

Alice가 암호화되지 않은 메시지를 보냈다는 점을 염두에 두세요. 해시만 암호화되었습니다. 이 절차는 원래 메시지가 변경되지 않았는지, Alice의 공개 키를 사용하여 Alice의 프라이빗 키에 액세스할 수 있는 사람(아마 Alice)이 메시지 해시에 서명했는지만 확인합니다.

HashAlgorithmProvider 클래스를 사용하여 사용 가능한 해시 알고리즘을 열거하고 CryptographicHash 값을 만들 수 있습니다.

디지털 서명은 MAC(프라이빗 키 메시지 인증 코드)와 동일한 퍼블릭 키입니다. MAC가 프라이빗 키를 사용하여 메시지 수신자가 전송 중에 메시지가 변경되지 않은지 확인할 수 있는 반면에, 서명은 프라이빗/퍼블릭 키 쌍을 사용합니다.

CryptographicHash 개체는 사용할 때마다 개체를 다시 만들지 않고도 다른 데이터를 반복적으로 해시하는 데 사용할 수 있습니다. Append 메서드는 해시할 버퍼에 새 데이터를 추가합니다. GetValueAndReset 메서드는 데이터를 해시하고 개체를 다른 용도로 다시 설정합니다. 다음의 예시로 이를 확인할 수 있습니다.

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

디지털 서명

디지털 서명은 MAC(프라이빗 키 메시지 인증 코드)와 동일한 퍼블릭 키입니다. MAC가 프라이빗 키를 사용하여 메시지 수신자가 전송 중에 메시지가 변경되지 않은지 확인할 수 있는 반면에, 서명은 프라이빗/퍼블릭 키 쌍을 사용합니다.

그러나 대부분의 퍼블릭 키 서명 작업은 계산 집약적이므로, 일반적으로 원본 메시지에 서명하는 것보다 메시지 해시에 서명(암호화)하는 것이 더 효율적입니다. 보낸 사람은 메시지 해시를 만들고 서명하며, 서명과 암호화되지 않은 메시지를 모두 보냅니다. 받는 사람은 메시지에 대한 해시를 계산하고, 서명의 암호를 해독하며, 암호 해독된 서명을 해시 값과 비교합니다. 일치하는 경우 받는 사람은 메시지가 보낸 사람에게서 전송되고 전송 중에 변경되지 않았음을 상당히 확신할 수 있습니다.

서명은 원본 메시지가 변경되지 않고 보낸 사람의 퍼블릭 키를 사용하여 프라이빗 키에 액세스할 수 있는 사람이 메시지 해시에 서명했음을 보장합니다.

AsymmetricKeyAlgorithmProvider 개체를 사용하여 사용 가능한 서명 알고리즘을 열거하고 키 쌍을 생성하거나 가져올 수 있습니다. CryptographicHash 클래스에서 정적 메서드를 사용하여 메시지에 서명하거나 서명을 확인할 수 있습니다.