Criptografando dados com CNG
O principal uso de qualquer API de criptografia é criptografar e descriptografar dados. O CNG permite criptografar dados usando um número mínimo de chamadas de função e permite que você execute todo o gerenciamento de memória. Embora muitos dos detalhes da implementação do protocolo sejam deixados para o usuário, o CNG fornece os primitivos que executam as tarefas reais de criptografia e descriptografia de dados.
Criptografando dados
Para criptografar dados, execute as seguintes etapas:
Abra um provedor de algoritmos que dê suporte à criptografia, como BCRYPT_DES_ALGORITHM.
Crie uma chave para criptografar os dados. Uma chave pode ser criada usando qualquer uma das seguintes funções:
- BCryptGenerateKeyPair ou BCryptImportKeyPair para provedores assimétricos.
- BCryptGenerateSymmetricKey ou BCryptImportKey para provedores simétricos.
Observação
A criptografia de dados e a descriptografia com uma chave assimétrica são computacionalmente intensivas em comparação com a criptografia de chave simétrica. Se precisar criptografar dados com uma chave assimétrica, criptografe os dados com uma chave simétrica, criptografe a chave simétrica com uma chave assimétrica e inclua a chave simétrica criptografada com a mensagem. Em seguida, o destinatário pode descriptografar a chave simétrica e usar a chave simétrica para descriptografar os dados.
Obtenha o tamanho dos dados criptografados. Isso se baseia no algoritmo de criptografia, no esquema de preenchimento (se houver) e no tamanho dos dados a serem criptografados. Você pode obter o tamanho dos dados criptografados usando a função BCryptEncrypt , passando NULL para o parâmetro pbOutput . Todos os outros parâmetros devem ser os mesmos de quando os dados são realmente criptografados, exceto para o parâmetro pbInput , que não é usado nesse caso.
Você pode criptografar os dados em vigor com o mesmo buffer ou criptografar os dados em um buffer separado.
Se você quiser criptografar os dados em vigor, passe o ponteiro de buffer de texto sem formatação para os parâmetros pbInput e pbOutput na função BCryptEncrypt . É possível que o tamanho dos dados criptografados seja maior do que o tamanho dos dados não criptografados, portanto, o buffer de texto não criptografado deve ser grande o suficiente para conter os dados criptografados, não apenas o texto sem formatação. Você pode usar o tamanho obtido na etapa 3 para alocar o buffer de texto não criptografado.
Se você quiser criptografar os dados em um buffer separado, aloque um buffer de memória para os dados criptografados usando o tamanho obtido na etapa 3.
Chame a função BCryptEncrypt para criptografar os dados. Essa função gravará os dados criptografados no local fornecido no parâmetro pbOutput .
Persista os dados criptografados conforme necessário.
Repita as etapas 5 e 6 até que todos os dados sejam criptografados.
Exemplo de criptografia de dados
O exemplo a seguir mostra como criptografar dados com CNG usando o algoritmo de criptografia simétrica padrão de criptografia avançada.
// 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);
}
}
Descriptografando dados
Para descriptografar dados, execute as seguintes etapas:
Abra um provedor de algoritmos que dê suporte à criptografia, como BCRYPT_DES_ALGORITHM.
Obtenha a chave com a qual os dados foram criptografados e use essa chave para obter um identificador para a chave.
Obtenha o tamanho dos dados descriptografados. Isso se baseia no algoritmo de criptografia, no esquema de preenchimento (se houver) e no tamanho dos dados a serem descriptografados. Você pode obter o tamanho dos dados criptografados usando a função BCryptDecrypt , passando NULL para o parâmetro pbOutput . Os parâmetros que especificam o esquema de preenchimento e o vetor de inicialização (IV) devem ser os mesmos de quando os dados foram criptografados, exceto para o parâmetro pbInput , que não é usado nesse caso.
Aloque um buffer de memória para os dados descriptografados.
Você pode descriptografar os dados em vigor usando o mesmo buffer ou descriptografar os dados em um buffer separado.
Se você quiser descriptografar os dados em vigor, passe o ponteiro de buffer de texto cifrado para os parâmetros pbInput e pbOutput na função BCryptDecrypt .
Se você quiser descriptografar os dados em um buffer separado, aloque um buffer de memória para os dados descriptografados usando o tamanho obtido na etapa 3.
Chame a função BCryptDecrypt para descriptografar os dados. Os parâmetros que especificam o esquema de preenchimento e o IV devem ser os mesmos de quando os dados foram criptografados. Essa função gravará os dados descriptografados no local fornecido no parâmetro pbOutput .
Persista os dados descriptografados conforme necessário.
Repita as etapas 5 e 6 até que todos os dados sejam descriptografados.