我尝试在BcryptOpenAlgorithmProvider 上使用BCRYPT_ALG_HANDLE_HMAC,正如我所料,当使用BCRYPT_AES_GMAC_ALGORITHM 和BCRYPT_ALG_HANDLE_HMAC 时,我得到了NOT_SUPPORTED。
BCRYPT_AES_GMAC_ALGORITHM 似乎坏了。不要使用它。
通过BCRYPT_AES_ALGORITHM 使用 AES/GCM。不要加密任何数据。仅对 aad 进行身份验证。生成的标签是 AAD 上的 GMAC。
如何在 BCrypt 中使用 AES-GMAC 和密钥?
创建 GMAC 的步骤是:
- 获取 AES 算法
- 设置 GCM 模式
- 输入密码
- 设置随机数
- 验证广告
“aad” 是“额外认证的数据”。它不同于传统上加密的纯文本数据。
以下是 Bcrypt 和 Crypto++ 对 aad 的 MAC 程序。由于您不加密数据,因此对 BCryptEncrypt 的调用如下所示:
status = BCryptEncrypt(
hKey,
NULL, 0,
(PVOID)&aadInfo,
NULL, 0,
NULL, 0,
&ulWritten,
0
);
注意:SP800-38D 指定最大随机数为 264-1,但 Microsoft 将随机数限制为 12 个字节。如果您尝试使用 16 字节,即 AES 块大小,则结果为 0xc000000d 或 STATUS_INVALID_PARAMETER。 Microsoft 文档未能记录这些限制。
这是 Microsoft Bcrypt 程序。
#include <Windows.h>
#include <bcrypt.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <memory>
#include <stdexcept>
#pragma comment (lib, "bcrypt.lib")
std::string NtStatusToString(const CHAR* operation, NTSTATUS status)
{
std::ostringstream oss;
oss << operation << ", 0x" << std::hex << status;
switch(status)
{
case 0xc0000000:
oss << " (STATUS_SUCCESS)";
break;
case 0xC0000008:
oss << " (STATUS_INVALID_HANDLE)";
break;
case 0xc000000d:
oss << " (STATUS_INVALID_PARAMETER)";
break;
case 0xc00000bb:
oss << " (STATUS_NOT_SUPPORTED)";
break;
case 0xC0000225:
oss << " (STATUS_NOT_FOUND)";
break;
}
return oss.str();
}
std::string ArrayToHexString(const UCHAR arr[], size_t size)
{
std::ostringstream oss;
for (size_t i=0; i<size; ++i)
{
oss << std::hex << std::setw(2) << std::setfill('0');
oss << (unsigned int)arr[i];
}
return oss.str();
}
int main(int argc, char* argv[])
{
BCRYPT_ALG_HANDLE hAlgorithm = 0;
BCRYPT_KEY_HANDLE hKey = 0;
UCHAR* pbKeyObject = 0;
ULONG cbKeyObjectLength = 0;
// SP800-38D specifies max nonce of 2^64-1, but
// Microsoft limits the nonce to 12 bytes.
UCHAR key[16] = {0};
UCHAR iv[12] = {0};
UCHAR tag[16] = {0};
// The data to be GMAC'd. It is not encrypted.
std::string aad("Not so secret additionally authenticated data");
try
{
NTSTATUS status = 0;
ULONG ulWritten = 0;
////////////////////////////////////////
status = BCryptOpenAlgorithmProvider(
&hAlgorithm,
BCRYPT_AES_ALGORITHM,
NULL, 0
);
if (!BCRYPT_SUCCESS(status))
throw std::runtime_error(
NtStatusToString("BCryptOpenAlgorithmProvider", status));
status = BCryptSetProperty(
hAlgorithm,
BCRYPT_CHAINING_MODE,
(UCHAR*)BCRYPT_CHAIN_MODE_GCM,
sizeof(BCRYPT_CHAIN_MODE_GCM),
0
);
if (!BCRYPT_SUCCESS(status))
throw std::runtime_error(
NtStatusToString("BCryptSetProperty (BCRYPT_CHAINING_MODE)", status));
////////////////////////////////////////
status = BCryptGetProperty(
hAlgorithm,
BCRYPT_OBJECT_LENGTH,
(PUCHAR)&cbKeyObjectLength,
sizeof(cbKeyObjectLength),
&ulWritten,
0
);
if (!BCRYPT_SUCCESS(status))
throw std::runtime_error(
NtStatusToString("BCryptGetProperty (BCRYPT_OBJECT_LENGTH)", status));
pbKeyObject = new UCHAR[cbKeyObjectLength];
if (!pbKeyObject)
throw std::runtime_error("pbKeyObject");
////////////////////////////////////////
status = BCryptGenerateSymmetricKey(
hAlgorithm,
&hKey,
pbKeyObject,
cbKeyObjectLength,
key,
sizeof(key),
0
);
if (!BCRYPT_SUCCESS(status))
throw std::runtime_error(
NtStatusToString("BCryptGenerateSymmetricKey", status));
////////////////////////////////////////
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aadInfo;
BCRYPT_INIT_AUTH_MODE_INFO(aadInfo);
aadInfo.pbNonce = iv;
aadInfo.cbNonce = sizeof(iv);
// Awful API design; non-const pointer.
aadInfo.pbAuthData = reinterpret_cast<UCHAR*>(&aad[0]);
aadInfo.cbAuthData = static_cast<ULONG>(aad.size());
aadInfo.cbAAD = static_cast<ULONG>(aad.size());
aadInfo.pbTag = tag;
aadInfo.cbTag = sizeof(tag);
////////////////////////////////////////
status = BCryptEncrypt(
hKey,
NULL, 0,
(PVOID)&aadInfo,
NULL, 0,
NULL, 0,
&ulWritten,
0
);
if (!BCRYPT_SUCCESS(status))
throw std::runtime_error(
NtStatusToString("BCryptEncrypt", status));
std::cout << "Message: " << aad << std::endl;
std::cout << "GMAC: " << ArrayToHexString(tag, sizeof(tag));
}
catch (const std::exception& ex)
{
std::cerr << "Exception: " << ex.what() << std::endl;
}
if (hKey)
BCryptDestroyKey(hKey);
if (hAlgorithm)
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
// Destroy after handles
if (pbKeyObject)
delete [] pbKeyObject;
return 0;
}
这是结果。
>cl.exe /DWINVER=0x0600 /TP /GR /EHsc bcrypt-gmac.cpp /link
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24210 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
bcrypt-gmac.cpp
Microsoft (R) Incremental Linker Version 14.00.24210.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:bcrypt-gmac.exe
bcrypt-gmac.obj
>.\bcrypt-gmac.exe
Message: Not so secret additionally authenticated data
GMAC: 3a1158d288cd796899f0366cdf594020
这是Crypto++ 的代码。您可以在Botan 或OpenSSL 中执行相同操作。
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "hex.h"
#include "aes.h"
#include "gcm.h"
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
byte key[16] = {0};
byte iv[12] = {0};
byte tag[16] = {0};
std::string aad("Not so secret additionally authenticated data");
try
{
GCM< AES >::Encryption enc;
enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
// AuthenticatedEncryptionFilter defines two
// channels: DEFAULT_CHANNEL and AAD_CHANNEL
// DEFAULT_CHANNEL is encrypted and authenticated,
// AAD_CHANNEL is authenticated.
AuthenticatedEncryptionFilter ef(enc,
new ArraySink(tag, sizeof(tag)),
false, 16 /* Tag size, MAC_AT_END */
); // AuthenticatedEncryptionFilter
// Authenticated data *must* be pushed before
// Confidential/Authenticated data. Otherwise
// we must catch the BadState exception
ef.ChannelPut(AAD_CHANNEL, (const byte*)aad.data(), aad.size());
ef.ChannelMessageEnd(AAD_CHANNEL);
// Confidential data comes after authenticated data.
// This is a limitation due to CCM mode, not GCM mode.
//ef.ChannelPut(DEFAULT_CHANNEL, pdata.data(), pdata.size());
//ef.ChannelMessageEnd(DEFAULT_CHANNEL);
// Signal end of message
ef.MessageEnd();
std::cout << "Message: " << aad << std::endl;
std::cout << "GMAC: ";
StringSource(tag, sizeof(tag), true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
}
catch(CryptoPP::Exception& ex)
{
std::cerr << "Exception: " << ex.what() << std::endl;
}
return 0;
}
这是结果。
$ g++ test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
Message: Not so secret additionally authenticated data
GMAC: 3A1158D288CD796899F0366CDF594020