Šifrování dat pomocí CNG
Primárním použitím libovolného rozhraní API kryptografie je šifrování a dešifrování dat. CNG umožňuje šifrovat data pomocí minimálního počtu volání funkcí a umožňuje provádět veškerou správu paměti. I když mnoho podrobností implementace protokolu zůstává uživateli, CNG poskytuje primitiva, která provádějí skutečné úlohy šifrování a dešifrování dat.
- Šifrování dat
- příklad šifrování dat
- Dešifrování dat
Šifrování dat
Pokud chcete šifrovat data, proveďte následující kroky:
Otevřete zprostředkovatele algoritmu, který podporuje šifrování, například BCRYPT_DES_ALGORITHM.
Vytvořte klíč pro šifrování dat. Klíč lze vytvořit pomocí některé z následujících funkcí:
- BCryptGenerateKeyPair nebo BCryptImportKeyPair pro asymetrické poskytovatele.
- BCryptGenerateSymmetricKey nebo BCryptImportKey pro symetrické zprostředkovatele.
Poznámka
Šifrování a dešifrování dat s asymetrickým klíčem je ve srovnání s šifrováním symetrického klíče výpočetně náročné. Pokud potřebujete šifrovat data asymetrickým klíčem, měli byste data zašifrovat symetrickým klíčem, zašifrovat symetrický klíč asymetrickým klíčem a zahrnout šifrovaný symetrický klíč do zprávy. Příjemce pak může symetrický klíč dešifrovat a použít symetrický klíč k dešifrování dat.
Získejte velikost šifrovaných dat. Vychází z šifrovacího algoritmu, schématu odsazení (pokud existuje) a velikosti dat, která se mají šifrovat. Šifrovanou velikost dat můžete získat pomocí funkce BCryptEncrypt a předáním null pro parametr pbOutput. Všechny ostatní parametry musí být stejné jako při skutečném šifrování dat s výjimkou parametru pbInput, který se v tomto případě nepoužívá.
Data můžete buď zašifrovat pomocí stejné vyrovnávací paměti, nebo je zašifrovat do samostatné vyrovnávací paměti.
Pokud chcete šifrovat data na místě, předejte ukazatel vyrovnávací paměti s prostým textem jak pro parametr pbInput, tak i pro parametr pbOutput ve funkci BCryptEncrypt. Je možné, že zašifrovaná velikost dat bude větší než nešifrovaná velikost dat, takže vyrovnávací paměť prostého textu musí být dostatečně velká pro uložení šifrovaných dat, nejen prostého textu. Použijte velikost získanou v kroku 3 k přidělení paměťového prostoru pro prostý text.
Pokud chcete data zašifrovat do samostatné vyrovnávací paměti, přidělte šifrovaným datům vyrovnávací paměť pomocí velikosti získané v kroku 3.
Voláním funkce BCryptEncrypt data zašifrujte. Tato funkce zapíše šifrovaná data do umístění zadaného v parametru pbOutput.
Podle potřeby uchovejte šifrovaná data.
Opakujte kroky 5 a 6, dokud nebudou šifrována všechna data.
Příklad šifrování dat
Následující příklad ukazuje, jak šifrovat data pomocí CNG pomocí pokročilého šifrovacího standardu symetrického šifrovacího algoritmu.
// 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);
}
}
Dešifrování dat
Pokud chcete dešifrovat data, proveďte následující kroky:
Otevřete poskytovatele algoritmu, který například podporuje šifrování, BCRYPT_DES_ALGORITHM.
Získejte klíč, pomocí kterého byla data zašifrována, a tento klíč použijte k získání identifikátoru klíče.
Získejte velikost dešifrovaných dat. To je založeno na šifrovacím algoritmu, schématu odsazení (pokud existuje) a velikosti dat, která se mají dešifrovat. Velikost šifrovaných dat můžete získat pomocí funkce BCryptDecrypt a předáním null parametru pbOutput. Parametry určující schéma odsazení a inicializační vektor (IV) musí být stejné jako při šifrování dat s výjimkou parametru pbInput, který se v tomto případě nepoužívá.
Přidělte vyrovnávací paměť pro dešifrovaná data.
Data můžete buď dešifrovat na místě pomocí stejné vyrovnávací paměti, nebo je dešifrovat do samostatné vyrovnávací paměti.
Pokud chcete dešifrovat data na místě, předejte ukazatel na vyrovnávací paměť šifrovaného textu pro parametry pbInput i pbOutput ve funkci BCryptDecrypt.
Pokud chcete data dešifrovat do samostatné vyrovnávací paměti, přidělte pro dešifrovaná data vyrovnávací paměť pomocí velikosti získané v kroku 3.
Voláním funkce BCryptDecrypt dešifrujte data. Parametry, které určují schéma doplňování a IV, musí být stejné jako při šifrování dat. Tato funkce zapíše dešifrovaná data do umístění zadaného v parametru pbOutput.
Uložte dešifrovaná data, jak je potřeba.
Opakujte kroky 5 a 6, dokud nebudou dešifrována všechna data.