Rei*_*eek 12 c windows cng aes-gcm
使用Windows CNG API,我能够在GCM模式下使用AES对身份验证的各个数据块进行加密和解密.我现在想要连续加密和解密多个缓冲区.
根据CNG的文档,支持以下方案:
如果加密或解密的输入分散在多个缓冲区中,则必须将调用链接到BCryptEncrypt和BCryptDecrypt函数.通过在dwFlags成员中设置BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG标志来指示链接.
如果我理解正确,这意味着我可以BCryptEncrypt在多个缓冲区上顺序调用,最后获取组合缓冲区的验证标记.类似地,我可以BCryptDecrypt在多个缓冲区上顺序调用,同时将实际的身份验证检查推迟到结束.我不能让它工作,看起来像值dwFlags被忽略.每当我使用BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG,我得到的返回值0xc000a002,这等于STATUS_AUTH_TAG_MISMATCH为定义ntstatus.h.
即使参数pbIV标记为in/out,参数指向的元素pbIV也不会被修改BCryptEncrypt().这是预期的吗?我还查看了指针所指向pbNonce的BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO结构中的字段pPaddingInfo,但是那个字段也没有被修改.我也尝试"手动"推进IV,根据计数器方案自己修改内容,但这也没有帮助.
成功链接BCryptEncrypt和/或BCryptDecrypt功能的正确程序是什么?
Cod*_*ard 11
我设法让它发挥作用.似乎问题出在MSDN中,它应该提到设置BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG而不是BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG.
#include <windows.h>
#include <assert.h>
#include <vector>
#include <Bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
std::vector<BYTE> MakePatternBytes(size_t a_Length)
{
std::vector<BYTE> result(a_Length);
for (size_t i = 0; i < result.size(); i++)
{
result[i] = (BYTE)i;
}
return result;
}
std::vector<BYTE> MakeRandomBytes(size_t a_Length)
{
std::vector<BYTE> result(a_Length);
for (size_t i = 0; i < result.size(); i++)
{
result[i] = (BYTE)rand();
}
return result;
}
int _tmain(int argc, _TCHAR* argv[])
{
NTSTATUS bcryptResult = 0;
DWORD bytesDone = 0;
BCRYPT_ALG_HANDLE algHandle = 0;
bcryptResult = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, 0, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptOpenAlgorithmProvider");
bcryptResult = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptSetProperty(BCRYPT_CHAINING_MODE)");
BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths;
bcryptResult = BCryptGetProperty(algHandle, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &bytesDone, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_AUTH_TAG_LENGTH)");
DWORD blockLength = 0;
bcryptResult = BCryptGetProperty(algHandle, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &bytesDone, 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_BLOCK_LENGTH)");
BCRYPT_KEY_HANDLE keyHandle = 0;
{
const std::vector<BYTE> key = MakeRandomBytes(blockLength);
bcryptResult = BCryptGenerateSymmetricKey(algHandle, &keyHandle, 0, 0, (PUCHAR)&key[0], key.size(), 0);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGenerateSymmetricKey");
}
const size_t GCM_NONCE_SIZE = 12;
const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE);
const std::vector<BYTE> origData = MakePatternBytes(256);
// Encrypt data as a whole
std::vector<BYTE> encrypted = origData;
std::vector<BYTE> authTag(authTagLengths.dwMinLength);
{
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = (PUCHAR)&origNonce[0];
authInfo.cbNonce = origNonce.size();
authInfo.pbTag = &authTag[0];
authInfo.cbTag = authTag.size();
bcryptResult = BCryptEncrypt
(
keyHandle,
&encrypted[0], encrypted.size(),
&authInfo,
0, 0,
&encrypted[0], encrypted.size(),
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptEncrypt");
assert(bytesDone == encrypted.size());
}
// Decrypt data in two parts
std::vector<BYTE> decrypted = encrypted;
{
DWORD partSize = decrypted.size() / 2;
std::vector<BYTE> macContext(authTagLengths.dwMaxLength);
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = (PUCHAR)&origNonce[0];
authInfo.cbNonce = origNonce.size();
authInfo.pbTag = &authTag[0];
authInfo.cbTag = authTag.size();
authInfo.pbMacContext = &macContext[0];
authInfo.cbMacContext = macContext.size();
// IV value is ignored on first call to BCryptDecrypt.
// This buffer will be used to keep internal IV used for chaining.
std::vector<BYTE> contextIV(blockLength);
// First part
authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
bcryptResult = BCryptDecrypt
(
keyHandle,
&decrypted[0*partSize], partSize,
&authInfo,
&contextIV[0], contextIV.size(),
&decrypted[0*partSize], partSize,
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
assert(bytesDone == partSize);
// Second part
authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
bcryptResult = BCryptDecrypt
(
keyHandle,
&decrypted[1*partSize], partSize,
&authInfo,
&contextIV[0], contextIV.size(),
&decrypted[1*partSize], partSize,
&bytesDone, 0
);
assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
assert(bytesDone == partSize);
}
// Check decryption
assert(decrypted == origData);
// Cleanup
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*dge 10
@Codeguard 的回答让我完成了我正在从事的项目,这让我首先找到了这个问题/答案;然而,我仍然遇到了一些问题。下面是我遵循的过程,并指出了棘手的部分。您可以在上面的链接中查看实际代码:
BCryptOpenAlgorithmProvider使用 打开算法提供程序BCRYPT_AES_ALGORITHM。BCryptSetProperty设为。BCRYPT_CHAINING_MODEBCRYPT_CHAIN_MODE_GCMBCryptGetProperty获取BCRYPT_OBJECT_LENGTHBCrypt 库用于加密/解密操作的分配。根据您的实施情况,您可能还需要:
BCryptGetProperty确定BCRYPT_BLOCK_SIZE和分配 IV 的暂存空间。Windows API 在每次调用时都会更新 IV,并且调用者负责为该使用提供内存。BCryptGetProperty确定BCRYPT_AUTH_TAG_LENGTH并为尽可能大的标记分配暂存空间。与 IV 一样,调用者负责提供这个空间,API 每次都会更新该空间。BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO结构体:
BCRYPT_INIT_AUTH_MODE_INFO()pbNonceandcbNonce字段。请注意,对于第一次调用BCryptEncrypt/ BCryptDecrypt,IV 作为输入被忽略,并且该字段用作“IV”。但是,IV 参数将由第一次调用更新并由后续调用使用,因此仍必须为其提供空间。此外,对于所有对/ 的调用,pbNonce和cbNonce字段必须保持设置状态(即使它们在第一次调用后未使用),否则这些调用将会抱怨。BCryptEncryptBCryptDecryptpbAuthData和cbAuthData. 在我的项目中,我在第一次调用BCryptEncrypt/之前设置这些字段,并在之后BCryptDecrypt立即将它们重置为NULL/ 。0您可以在这些调用期间传递NULL/0作为输入和输出参数。pbTag和cbTag. pbTag可以直到检索或检查标签时NULL最终调用BCryptEncrypt/为止,但必须设置,否则/会抱怨。BCryptDecryptcbTagBCryptEncryptBCryptDecryptpbMacContext和cbMacContext. 这些指向BCryptEncrypt/的暂存空间BCryptDecrypt,用于跟踪 tag/mac 的当前状态。cbAAD并cbData到0. API 使用这些字段,因此您可以随时读取它们,但在最初将它们设置为0.dwFlags为BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG. 初始化后,应使用|=或对此字段进行更改&=。Windows 还在该字段中设置了调用者需要注意不要更改的标志。BCryptGenerateSymmetricKey导入用于加密/解密的密钥。请注意,您需要提供与BCRYPT_OBJECT_LENGTH此调用关联的内存,以供BCryptEncrypt/BCryptDecrypt在操作期间使用。BCryptEncrypt/BCryptDecrypt联系您的 AAD(如果有);此调用不需要提供输入或输出空间。cbAAD(如果调用成功,您可以看到结构字段中反映的 AAD 大小BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO。)
pbAuthData和cbAuthData来反映 AAD。BCryptEncrypt或BCryptDecrypt.pbAuthData并cbAuthData返回到NULL和0。BCryptEncrypt/ BCryptDecrypt“N-1”次
dwFlags调用的参数设置为 以外的任何参数0。BCryptEncrypt/ (有或没有明文/密文输入/输出)。BCryptDecrypt对于此调用,输入的大小不必是算法块大小的倍数。dwFlags仍设置为0.
pbTag将结构的字段设置BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO为存储生成的标签的位置或要验证的标签的位置,具体取决于操作是加密还是解密。BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG从结构dwFlags的字段中删除。BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO&=BCryptDestroyKeyBCryptCloseAlgorithmProvider此时,明智的做法是消除与 相关的空间BCRYPT_OBJECT_LENGTH。
| 归档时间: |
|
| 查看次数: |
4569 次 |
| 最近记录: |