CNG를 사용하여 데이터 암호화

암호화 API의 주요 용도는 데이터를 암호화하고 암호 해독하는 것입니다. CNG를 사용하면 최소 수의 함수 호출을 사용하여 데이터를 암호화할 수 있으며 모든 메모리 관리를 수행할 수 있습니다. 많은 프로토콜 구현 세부 정보는 사용자에게 남아 있지만 CNG는 실제 데이터 암호화 및 암호 해독 작업을 수행하는 기본 형식을 제공합니다.

데이터 암호화

데이터를 암호화하려면 다음 단계를 수행합니다.

  1. BCRYPT_DES_ALGORITHM 같은 암호화를 지원하는 알고리즘 공급자를 엽니다.

  2. 데이터를 암호화할 키를 만듭니다. 다음 함수를 사용하여 키를 만들 수 있습니다.

    참고

    비대칭 키를 사용한 데이터 암호화 및 암호 해독은 대칭 키 암호화에 비해 계산 집약적입니다. 비대칭 키로 데이터를 암호화해야 하는 경우 대칭 키로 데이터를 암호화하고, 대칭 키를 비대칭 키로 암호화하고, 메시지와 함께 암호화된 대칭 키를 포함해야 합니다. 그러면 받는 사람이 대칭 키의 암호를 해독하고 대칭 키를 사용하여 데이터의 암호를 해독할 수 있습니다.

     

  3. 암호화된 데이터의 크기를 가져옵니다. 암호화 알고리즘, 패딩 구성표(있는 경우) 및 암호화할 데이터의 크기를 기반으로 합니다. BCryptEncrypt 함수를 사용하여 pbOutput 매개 변수에 대해 NULL을 전달하여 암호화된 데이터 크기를 가져올 수 있습니다. 다른 모든 매개 변수는 이 경우 사용되지 않는 pbInput 매개 변수를 제외하고 데이터가 실제로 암호화되는 경우와 동일해야 합니다.

  4. 동일한 버퍼로 데이터를 암호화하거나 데이터를 별도의 버퍼로 암호화할 수 있습니다.

    데이터를 암호화하려는 경우 BCryptEncrypt 함수에서 pbInput 및 pbOutput 매개 변수 모두에 대한 일반 텍스트 버퍼 포인터를 전달합니다. 암호화된 데이터 크기가 암호화되지 않은 데이터 크기보다 클 수 있으므로 일반 텍스트 버퍼는 일반 텍스트뿐만 아니라 암호화된 데이터를 저장할 수 있을 만큼 커야 합니다. 3단계에서 얻은 크기를 사용하여 일반 텍스트 버퍼를 할당할 수 있습니다.

    데이터를 별도의 버퍼로 암호화하려면 3단계에서 얻은 크기를 사용하여 암호화된 데이터에 대한 메모리 버퍼를 할당합니다.

  5. BCryptEncrypt 함수를 호출하여 데이터를 암호화합니다. 이 함수는 pbOutput 매개 변수에 제공된 위치에 암호화된 데이터를 씁니다.

  6. 필요에 따라 암호화된 데이터를 유지합니다.

  7. 모든 데이터가 암호화될 때까지 5단계와 6단계를 반복합니다.

데이터 암호화 예제

다음 예제에서는 고급 암호화 표준 대칭 암호화 알고리즘을 사용하여 CNG로 데이터를 암호화하는 방법을 보여 줍니다.

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) Microsoft. All rights reserved.
/*++

Abstract:
    Sample program for AES-CBC encryption using CNG

--*/

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>

#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)


#define DATA_TO_ENCRYPT  "Test Data"


const BYTE rgbPlaintext[] = 
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};

static const BYTE rgbIV[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};

static const BYTE rgbAES128Key[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};

void PrintBytes(
                IN BYTE     *pbPrintData,
                IN DWORD    cbDataLen)
{
    DWORD dwCount = 0;

    for(dwCount=0; dwCount < cbDataLen;dwCount++)
    {
        printf("0x%02x, ",pbPrintData[dwCount]);

        if(0 == (dwCount + 1 )%10) putchar('\n');
    }

}

void __cdecl wmain(
                   int                      argc, 
                   __in_ecount(argc) LPWSTR *wargv)
{

    BCRYPT_ALG_HANDLE       hAesAlg                     = NULL;
    BCRYPT_KEY_HANDLE       hKey                        = NULL;
    NTSTATUS                status                      = STATUS_UNSUCCESSFUL;
    DWORD                   cbCipherText                = 0, 
                            cbPlainText                 = 0,
                            cbData                      = 0, 
                            cbKeyObject                 = 0,
                            cbBlockLen                  = 0,
                            cbBlob                      = 0;
    PBYTE                   pbCipherText                = NULL,
                            pbPlainText                 = NULL,
                            pbKeyObject                 = NULL,
                            pbIV                        = NULL,
                            pbBlob                      = NULL;

    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(wargv);


    // Open an algorithm handle.
    if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
                                                &hAesAlg,
                                                BCRYPT_AES_ALGORITHM,
                                                NULL,
                                                0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    // Calculate the size of the buffer to hold the KeyObject.
    if(!NT_SUCCESS(status = BCryptGetProperty(
                                        hAesAlg, 
                                        BCRYPT_OBJECT_LENGTH, 
                                        (PBYTE)&cbKeyObject, 
                                        sizeof(DWORD), 
                                        &cbData, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Allocate the key object on the heap.
    pbKeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbKeyObject);
    if(NULL == pbKeyObject)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

   // Calculate the block length for the IV.
    if(!NT_SUCCESS(status = BCryptGetProperty(
                                        hAesAlg, 
                                        BCRYPT_BLOCK_LENGTH, 
                                        (PBYTE)&cbBlockLen, 
                                        sizeof(DWORD), 
                                        &cbData, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Determine whether the cbBlockLen is not longer than the IV length.
    if (cbBlockLen > sizeof (rgbIV))
    {
        wprintf (L"**** block length is longer than the provided IV length\n");
        goto Cleanup;
    }

    // Allocate a buffer for the IV. The buffer is consumed during the 
    // encrypt/decrypt process.
    pbIV= (PBYTE) HeapAlloc (GetProcessHeap (), 0, cbBlockLen);
    if(NULL == pbIV)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    memcpy(pbIV, rgbIV, cbBlockLen);

    if(!NT_SUCCESS(status = BCryptSetProperty(
                                hAesAlg, 
                                BCRYPT_CHAINING_MODE, 
                                (PBYTE)BCRYPT_CHAIN_MODE_CBC, 
                                sizeof(BCRYPT_CHAIN_MODE_CBC), 
                                0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptSetProperty\n", status);
        goto Cleanup;
    }

                

     // Generate the key from supplied input key bytes.
    if(!NT_SUCCESS(status = BCryptGenerateSymmetricKey(
                                        hAesAlg, 
                                        &hKey, 
                                        pbKeyObject, 
                                        cbKeyObject, 
                                        (PBYTE)rgbAES128Key, 
                                        sizeof(rgbAES128Key), 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n", status);
        goto Cleanup;
    }

  
    // Save another copy of the key for later.
    if(!NT_SUCCESS(status = BCryptExportKey(
                                        hKey,
                                        NULL,
                                        BCRYPT_OPAQUE_KEY_BLOB,
                                        NULL,
                                        0,
                                        &cbBlob,
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }


    // Allocate the buffer to hold the BLOB.
    pbBlob = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbBlob);
    if(NULL == pbBlob)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if(!NT_SUCCESS(status = BCryptExportKey(
                                        hKey, 
                                        NULL, 
                                        BCRYPT_OPAQUE_KEY_BLOB,
                                        pbBlob, 
                                        cbBlob, 
                                        &cbBlob, 
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }
 
    cbPlainText = sizeof(rgbPlaintext);
    pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);
    if(NULL == pbPlainText)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));
    
    //
    // Get the output buffer size.
    //
    if(!NT_SUCCESS(status = BCryptEncrypt(
                                        hKey, 
                                        pbPlainText, 
                                        cbPlainText,
                                        NULL,
                                        pbIV,
                                        cbBlockLen,
                                        NULL, 
                                        0, 
                                        &cbCipherText, 
                                        BCRYPT_BLOCK_PADDING)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    pbCipherText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbCipherText);
    if(NULL == pbCipherText)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    // Use the key to encrypt the plaintext buffer.
    // For block sized messages, block padding will add an extra block.
    if(!NT_SUCCESS(status = BCryptEncrypt(
                                        hKey, 
                                        pbPlainText, 
                                        cbPlainText,
                                        NULL,
                                        pbIV,
                                        cbBlockLen, 
                                        pbCipherText, 
                                        cbCipherText, 
                                        &cbData, 
                                        BCRYPT_BLOCK_PADDING)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    // Destroy the key and reimport from saved BLOB.
    if(!NT_SUCCESS(status = BCryptDestroyKey(hKey)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptDestroyKey\n", status);
        goto Cleanup;
    }    
    hKey = 0;

    if(pbPlainText)
    {
        HeapFree(GetProcessHeap(), 0, pbPlainText);
    }

    pbPlainText = NULL;

    // We can reuse the key object.
    memset(pbKeyObject, 0 , cbKeyObject);

    
    // Reinitialize the IV because encryption would have modified it.
    memcpy(pbIV, rgbIV, cbBlockLen);


    if(!NT_SUCCESS(status = BCryptImportKey(
                                        hAesAlg,
                                        NULL,
                                        BCRYPT_OPAQUE_KEY_BLOB,
                                        &hKey,
                                        pbKeyObject,
                                        cbKeyObject,
                                        pbBlob,
                                        cbBlob,
                                        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n", status);
        goto Cleanup;
    }
       

    //
    // Get the output buffer size.
    //
    if(!NT_SUCCESS(status = BCryptDecrypt(
                                    hKey, 
                                    pbCipherText, 
                                    cbCipherText, 
                                    NULL,
                                    pbIV,
                                    cbBlockLen,
                                    NULL, 
                                    0, 
                                    &cbPlainText, 
                                    BCRYPT_BLOCK_PADDING)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptDecrypt\n", status);
        goto Cleanup;
    }

    pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);
    if(NULL == pbPlainText)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if(!NT_SUCCESS(status = BCryptDecrypt(
                                    hKey, 
                                    pbCipherText, 
                                    cbCipherText, 
                                    NULL,
                                    pbIV, 
                                    cbBlockLen,
                                    pbPlainText, 
                                    cbPlainText, 
                                    &cbPlainText, 
                                    BCRYPT_BLOCK_PADDING)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptDecrypt\n", status);
        goto Cleanup;
    }


    if (0 != memcmp(pbPlainText, (PBYTE)rgbPlaintext, sizeof(rgbPlaintext))) 
    {
        wprintf(L"Expected decrypted text comparison failed.\n");
        goto Cleanup;
    } 

    wprintf(L"Success!\n");


Cleanup:

    if(hAesAlg)
    {
        BCryptCloseAlgorithmProvider(hAesAlg,0);
    }

    if (hKey)    
    {
        BCryptDestroyKey(hKey);
    }

    if(pbCipherText)
    {
        HeapFree(GetProcessHeap(), 0, pbCipherText);
    }

    if(pbPlainText)
    {
        HeapFree(GetProcessHeap(), 0, pbPlainText);
    }

    if(pbKeyObject)
    {
        HeapFree(GetProcessHeap(), 0, pbKeyObject);
    }

    if(pbIV)
    {
        HeapFree(GetProcessHeap(), 0, pbIV);
    }

}

데이터 해독

데이터 암호를 해독하려면 다음 단계를 수행합니다.

  1. BCRYPT_DES_ALGORITHM 같은 암호화를 지원하는 알고리즘 공급자를 엽니다.

  2. 데이터가 암호화된 키를 가져오고 해당 키를 사용하여 키에 대한 핸들을 가져옵니다.

  3. 암호 해독된 데이터의 크기를 가져옵니다. 암호화 알고리즘, 패딩 구성표(있는 경우) 및 암호 해독할 데이터의 크기를 기반으로 합니다. BCryptDecrypt 함수를 사용하여 pbOutput 매개 변수에 대해 NULL을 전달하여 암호화된 데이터 크기를 가져올 수 있습니다. 패딩 구성표 및 IV( 초기화 벡터 )를 지정하는 매개 변수는 이 경우에 사용되지 않는 pbInput 매개 변수를 제외하고 데이터가 암호화된 경우와 동일해야 합니다.

  4. 암호 해독된 데이터에 대한 메모리 버퍼를 할당합니다.

  5. 동일한 버퍼를 사용하여 현재 위치에 있는 데이터의 암호를 해독하거나 데이터를 별도의 버퍼로 암호 해독할 수 있습니다.

    현재 위치에서 데이터의 암호를 해독하려면 BCryptDecrypt 함수에서 pbInputpbOutput 매개 변수 모두에 대한 암호 텍스트 버퍼 포인터를 전달합니다.

    데이터를 별도의 버퍼로 암호 해독하려면 3단계에서 얻은 크기를 사용하여 암호 해독된 데이터에 대한 메모리 버퍼를 할당합니다.

  6. BCryptDecrypt 함수를 호출하여 데이터의 암호를 해독합니다. 패딩 구성표와 IV를 지정하는 매개 변수는 데이터가 암호화되었을 때와 동일해야 합니다. 이 함수는 pbOutput 매개 변수에 제공된 위치에 암호 해독된 데이터를 씁니다.

  7. 필요에 따라 암호 해독된 데이터를 유지합니다.

  8. 모든 데이터가 암호 해독될 때까지 5단계와 6단계를 반복합니다.