我在解密密文(在 C# 端)时陷入困境,该密文是在 C++ 端使用 Microsoft 的 BCRYPT.h 加密的。我收到异常消息 Org.BouncyCastle.Crypto.InvalidCipherTextException: GCM 中的 MAC 检查失败。
据我所知,这与 MACTag 不匹配或任何其他参数不匹配有关。我已经验证我使用的是相同的值,但它仍然不起作用。
以下是我正在使用的流程:
请找到我正在使用的以下代码:
在 C++ 端(我已将行格式化为粗体,这些行在 C# 端用作数据)
void Encrypt()
{
BCRYPT_ALG_HANDLE hAlgorithmAes = NULL;
BCRYPT_KEY_HANDLE hKeyAes = NULL;
ULONG cbKeyObjectLength = 0;
ULONG ulWritten = 0;
UCHAR* pbKeyObjectAes = 0;
UCHAR iv[12] = { 0 };
UCHAR tag[16] = { 0 };
NTSTATUS status = BCryptOpenAlgorithmProvider(
&hAlgorithmAes,
BCRYPT_AES_ALGORITHM,
NULL, 0
);
if (!BCRYPT_SUCCESS(status)) {
printf("Failed to get algorithm provider for AES..status : %08x\n", status);
//goto cleanup;
}
status = BCryptSetProperty(
hAlgorithmAes,
BCRYPT_CHAINING_MODE,
(BYTE*)BCRYPT_CHAIN_MODE_GCM,
sizeof(BCRYPT_CHAIN_MODE_GCM),
0
);
if (!BCRYPT_SUCCESS(status)) {
printf("Failed to set property for AES..status : %08x\n", status);
//goto cleanup;
}
BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths;
status = BCryptGetProperty(hAlgorithmAes, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &ulWritten, 0);
if (!BCRYPT_SUCCESS(status))
{
}
DWORD blockLength = 0;
status = BCryptGetProperty(hAlgorithmAes, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &ulWritten, 0);
if (!BCRYPT_SUCCESS(status))
{
}
const std::vector<BYTE> keyAes = MakeRandomBytes(blockLength);
**std::string keyUsedAtCSharpSide = base64_encode_new(&keyAes[0], keyAes.size());**
status = BCryptGenerateSymmetricKey(hAlgorithmAes, &hKeyAes, 0, 0, (PUCHAR)&keyAes[0], keyAes.size(), 0);
if (!BCRYPT_SUCCESS(status))
{
}
const size_t GCM_NONCE_SIZE = 12;
const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE);
**std::string ivNonceUsedAtCSharpSide = base64_encode_new(&origNonce[0], origNonce.size());**
BYTE origData2[] = "test";
DWORD origDataSize2 = sizeof(origData2);
const std::vector<BYTE> origData = MakePatternBytes(18);
// Encrypt data as a whole
PBYTE encrypted = (PBYTE)HeapAlloc(GetProcessHeap(), 0, blockLength);
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();
status = BCryptEncrypt
(
hKeyAes,
// &encrypted[0], encrypted.size(),
origData2, origDataSize2,
&authInfo,
0, 0,
&encrypted[0], blockLength,
&ulWritten, 0
);
}
**std::string tagIdUsedAtCSharpSide = base64_encode_new(&authTag[0], authTag.size());**
**std::string encryptedStringUsedAtCSharpSide = base64_encode_new(encrypted, blockLength);**
}
在 C# 端:
private static byte[] AesDecrypt()
{
//Below Data is taken from the c++ side and hardcoded here
byte[] aesKey = Convert.FromBase64String("KSO+hOFs1q5SkEnx8bvp6w==");
byte[] iv = Convert.FromBase64String("s6bbPIcMPpkkXg0c");
byte[] encryptedData = Convert.FromBase64String("q/KR75wAAAAAAAAAAAAAAA==");
int macBitSize = 96;
byte[] decryptedMessage = null;
try
{
CryptoServicesRegistrar.SetApprovedOnlyMode(true);
var key = new FipsAes.Key(aesKey);
IAeadCipherService cipherService = CryptoServicesRegistrar.CreateService(key);
var algorithmDetails = FipsAes.Gcm.WithIV(iv).WithMacSize(macBitSize);
IAeadCipherBuilder<IParameters<FipsAlgorithm>> aeadDecryptorBuilder = cipherService.CreateAeadDecryptorBuilder(algorithmDetails);
var decryptor = aeadDecryptorBuilder.BuildAeadCipher(AeadUsage.AAD_FIRST, new MemoryInputStream(encryptedData));
//Below Data is taken from the c++ side and hardcoded here
byte[] aadAuthData =Convert.FromBase64String("HX0BeEQNYbrpjA23");// Strings.ToByteArray( This message was sent 29th Feb at 11.00am - does not repeat");
decryptor.AadStream.Write(aadAuthData, 0, aadAuthData.Length);
/**/
using (var stream = decryptor.Stream)
{
decryptedMessage = Streams.ReadAll(stream);
}
}
catch
{
throw;
}
return decryptedMessage;
}
请指导我解决此错误。
您的实现中存在一些小错误,这可能是由于缺乏文档阅读和算法输出的非标准方式造成的。我已经调试了您的代码片段,并找到了以下松散点,我认为这些松散点在跨平台和多种算法实现时很常见:
pbTag参数将具有身份验证标签。 (authTag在你的情况下)
pbOutput参数将包含加密数据。 (在您的情况下加密)
加密数据字节+身份验证标签字节
您可以在 C# 端执行类似下面的操作(或者可以在 C++ 端本身执行类似的操作)。
var tag = Convert.FromBase64String("YadCQ6V4c3nOUdniqlzymw ==");
encryptedData = Convert.FromBase64String("q/KR7/U=");
byte[] data = new byte[tag.Length + encryptedData.Length];
encryptedData.CopyTo(data, 0);
tag.CopyTo(data, encryptedData.Length);
3.正如@dave_thompson_85在评论中所指出的,使用pbResult(在您的情况下是ulWritten)参数,即加密字节数以获取加密字节。
`base64_encode_new(encrypted, ulWritten);`
使用
BYTE origData2[] = "test";
会在字节错误中添加额外的内容,这将导致标签不匹配。将其更新为其他内容(如下所示)并在任何其他地方确保这一点。
std::string origData2("test");
您对AAD和AuthTag感到困惑,请了解一下。如果您在加密期间未指定 AAD,则也不应在解密期间指定它。从 AesDecrypt 方法中删除以下两行。
byte[] aadAuthData =Convert.FromBase64String("HX0BeEQNYbrpjA23");// Strings.ToByteArray("This message was sent 29th Feb at 11.00am - does not repeat");
decryptor.AadStream.Write(aadAuthData, 0, aadAuthData.Length);
另外 如果您希望使用自己的 AAD,则在加密期间,您必须在 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO 结构中分配 pbAuthData、cbAuthData 参数。 (BC侧也应添加相同的AAD。)