GCM 中的 Crypto++ AES 正在生成类似的密文
Crypto++ AES in GCM is producing similar ciphertexts
我正在尝试实施正确且安全的准系统,从头到尾的 AES 加密。出于我的目的,我只想生成一些基准测试程序输出的密文,以模拟将写入什么样的密文,比如云存储服务器。在阅读了一些之后,我得出的结论是,就我的目的而言,AES 在 GCM 模式下生成的经过身份验证的加密代表了我在真实服务器上使用的密文。
从 Crypto++ Wiki 页面上的示例代码开始,我想出了以下代码片段来从文件中读取一些字符串并转储密文。问题是我得到的密文非常相似,许多输出的上半部分是相同的。我怀疑这是由于我 using/initializing AutoSeededRandomPool 的方式所致,但我对它的了解还不够,无法弄清楚如何修复它。看起来底层代码正在调用 OS (Ubuntu 16.04) 来生成熵,这让我相信调用之间可能没有足够的时间来更改此值。
在此先感谢您提供的任何帮助。
#include "osrng.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include "cryptlib.h"
#include "hex.h"
#include "filters.h"
#include "aes.h"
#include "gcm.h"
#include "secblock.h"
using namespace std;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::AutoSeededX917RNG;
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::AES;
using CryptoPP::GCM;
using CryptoPP::SecByteBlock;
// Single randomly seeded RNG
AutoSeededRandomPool rnd;
// Generate a string buffer for the input/output data
string plainText, cipherText, encoded;
// Generate a random key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
rnd.GenerateBlock(key, key.size());
// Generate an initial value vector (public but unique per msg)
SecByteBlock iv(AES::BLOCKSIZE);
rnd.GenerateBlock(iv, iv.size());
for (size_t i = 0; i < numLines; ++i)
{
getline(inputFileStream, plainText);
// Do encryption
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), iv, iv.size());
StringSource
(
plainText,
true,
new AuthenticatedEncryptionFilter
(
e,
new StringSink(cipherText)
)
);
encoded.clear();
StringSource
(
cipherText,
true,
new HexEncoder
(
new StringSink(encoded)
)
);
cout << "Cipher Text: " << encoded << endl;
}
这里是一个输出密文的例子:
密文:
9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6
密文:
9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6D8B9F0691B616C2E996B5E473860EE348C09A1F0FC
将 IV 生成移动到循环之外,有类似的行为:
密文:
25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87
密文:
25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87FA3272CFE669E235CE452FCEEA59CC8CA34554FF25
以下答案的附录:
在代码中,我忽略了在每次循环迭代时清除字符串 "cipherText"。显然,StringSink 操作只是将新的密文附加到密文的末尾,这就是它们相同的原因。感谢大家的帮助!
您正在 for 循环之外生成密钥和 IV。这意味着您将对要加密的行使用相同的密钥/IV 组合。 GCM 基本上是带有身份验证标签的 CTR 加密。这意味着明文中的相似位将产生完全相同的值。只要 IV 永不重复 .
,GCM 就是安全的
将 IV 代放在循环中,你应该没问题。请注意,对于 GCM,12 字节 IV 比 16 字节 IV 更有效,也可能更安全。随机 IV 通常放在密文前面(通过手动将它们写入 StringSink
)。
在您的原始代码中,您犯了两个错误。你打印出了密钥而不是 IV,这让你相信你的 RNG 是错误的。此外,您写入了 ciphertext
字符串变量而没有先清除它。这使得 StringSink
将下一个密文(和身份验证标记)附加到 ciphertext
字符串。所以你打印出来的时候第一密文和第二密文都得到了。
我正在尝试实施正确且安全的准系统,从头到尾的 AES 加密。出于我的目的,我只想生成一些基准测试程序输出的密文,以模拟将写入什么样的密文,比如云存储服务器。在阅读了一些之后,我得出的结论是,就我的目的而言,AES 在 GCM 模式下生成的经过身份验证的加密代表了我在真实服务器上使用的密文。
从 Crypto++ Wiki 页面上的示例代码开始,我想出了以下代码片段来从文件中读取一些字符串并转储密文。问题是我得到的密文非常相似,许多输出的上半部分是相同的。我怀疑这是由于我 using/initializing AutoSeededRandomPool 的方式所致,但我对它的了解还不够,无法弄清楚如何修复它。看起来底层代码正在调用 OS (Ubuntu 16.04) 来生成熵,这让我相信调用之间可能没有足够的时间来更改此值。
在此先感谢您提供的任何帮助。
#include "osrng.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include "cryptlib.h"
#include "hex.h"
#include "filters.h"
#include "aes.h"
#include "gcm.h"
#include "secblock.h"
using namespace std;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::AutoSeededX917RNG;
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::AES;
using CryptoPP::GCM;
using CryptoPP::SecByteBlock;
// Single randomly seeded RNG
AutoSeededRandomPool rnd;
// Generate a string buffer for the input/output data
string plainText, cipherText, encoded;
// Generate a random key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
rnd.GenerateBlock(key, key.size());
// Generate an initial value vector (public but unique per msg)
SecByteBlock iv(AES::BLOCKSIZE);
rnd.GenerateBlock(iv, iv.size());
for (size_t i = 0; i < numLines; ++i)
{
getline(inputFileStream, plainText);
// Do encryption
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), iv, iv.size());
StringSource
(
plainText,
true,
new AuthenticatedEncryptionFilter
(
e,
new StringSink(cipherText)
)
);
encoded.clear();
StringSource
(
cipherText,
true,
new HexEncoder
(
new StringSink(encoded)
)
);
cout << "Cipher Text: " << encoded << endl;
}
这里是一个输出密文的例子:
密文:
9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6
密文:
9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6D8B9F0691B616C2E996B5E473860EE348C09A1F0FC
将 IV 生成移动到循环之外,有类似的行为:
密文:
25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87
密文:
25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87FA3272CFE669E235CE452FCEEA59CC8CA34554FF25
以下答案的附录:
在代码中,我忽略了在每次循环迭代时清除字符串 "cipherText"。显然,StringSink 操作只是将新的密文附加到密文的末尾,这就是它们相同的原因。感谢大家的帮助!
您正在 for 循环之外生成密钥和 IV。这意味着您将对要加密的行使用相同的密钥/IV 组合。 GCM 基本上是带有身份验证标签的 CTR 加密。这意味着明文中的相似位将产生完全相同的值。只要 IV 永不重复 .
,GCM 就是安全的将 IV 代放在循环中,你应该没问题。请注意,对于 GCM,12 字节 IV 比 16 字节 IV 更有效,也可能更安全。随机 IV 通常放在密文前面(通过手动将它们写入 StringSink
)。
在您的原始代码中,您犯了两个错误。你打印出了密钥而不是 IV,这让你相信你的 RNG 是错误的。此外,您写入了 ciphertext
字符串变量而没有先清除它。这使得 StringSink
将下一个密文(和身份验证标记)附加到 ciphertext
字符串。所以你打印出来的时候第一密文和第二密文都得到了。