gpt4 book ai didi

c++ - AES-128 CBC 模式下加密流末尾的 Crypto++ 额外 block

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:03:41 28 4
gpt4 key购买 nike

我正在尝试在 CBC 模式下使用 AES-128 加密 320 字节的二进制数据,并将密码存储到一个文件中。输出文件应该是 320 字节,但我得到了 336 字节。这是我的代码:

#include <iostream>
#include <fstream>
#include <crypto++/aes.h>
#include <crypto++/modes.h>
#include <crypto++/base64.h>
#include <crypto++/sha.h>
#include <cryptopp/osrng.h>
#include <crypto++/filters.h>
#include <crypto++/files.h>

namespace CryptoPP
{
using byte = unsigned char;
}

void myAESTest()
{
std::string password = "testPassWord";

// hash the password string
// -------------------------------
CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];
CryptoPP::byte passHash[CryptoPP::SHA256::DIGESTSIZE];
CryptoPP::SHA256().CalculateDigest(passHash, (CryptoPP::byte*) password.data(), password.size());
std::memcpy(key, passHash, CryptoPP::AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+CryptoPP::AES::DEFAULT_KEYLENGTH, CryptoPP::AES::BLOCKSIZE);

// encrypt
// ---------------------------------
int chunkSize = 20*CryptoPP::AES::BLOCKSIZE;

CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, sizeof(key), iv);

std::ofstream testOut("./test.enc", std::ios::binary);
CryptoPP::FileSink outSink(testOut);

CryptoPP::byte message[chunkSize];

CryptoPP::StreamTransformationFilter stfenc(encryptor, new CryptoPP::Redirector(outSink));

for(int i = 0; i < chunkSize; i ++)
{
message[i] = (CryptoPP::byte)i;
}

stfenc.Put(message, chunkSize);
stfenc.MessageEnd();
testOut.close();

// decrypt
// ------------------------------------
// Because of some unknown reason increase chuksize by 1 block
// chunkSize+=16;

CryptoPP::byte cipher[chunkSize], decrypted[chunkSize];

CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV(key, sizeof(key), iv);

std::ifstream inFile("./test.enc", std::ios::binary);
inFile.read((char *)cipher, chunkSize);

CryptoPP::ArraySink decSink(decrypted, chunkSize);
CryptoPP::StreamTransformationFilter stfdec(decryptor, new CryptoPP::Redirector(decSink));

stfdec.Put(cipher, chunkSize);
stfdec.MessageEnd();
inFile.close();

for(int i = 0; i < chunkSize; i++)
{
std::cout << (int)decrypted[i] << ' ';
}
std::cout << std::endl;
}

int main(int argc, char* argv[])
{
myAESTest();
return 0;
}

我无法理解最后 16 个字节是如何生成的。如果我选择忽略解密中的最后 16 个字节,CryptoPP 会抛出 CryptoPP::InvalidCiphertext 错误:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found

最佳答案

I'm not able to understand how the last 16 bytes are generated. If I choose to ignore the last 16 bytes in the decryption, Crypto++ throws InvalidCiphertext error

最后 16 个字节是填充。填充由 StreamTransformationFilter 过滤器添加;见StreamTransformationFilter Class Reference在手册中。虽然不是很明显,DEFAULT_PADDINGPKCS_PADDING 用于 ECB_ModeCBC_Mode。对于 OFB_ModeCTR_Mode 等其他模式,它是 NO_PADDING

您只需为加密和解密过滤器指定NO_PADDING。但是,您必须确保明文和密文是 block 大小的倍数,对于 AES 是 16。

您可以通过切换到另一种模式(例如 CTR_Mode)来避开 block 大小限制。但是您必须非常小心 key 或 IV 重用,这可能会因您使用的密码派生方案而变得困难。

所以代替:

CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink));

使用:

CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink), NO_PADDING);

另见 CBC_Mode在 Crypto++ 维基上。您可能还对 Authenticated Encryption 感兴趣在维基上。


为此,您还可以:

#ifndef CRYPTOPP_NO_GLOBAL_BYTE
namespace CryptoPP
{
using byte = unsigned char;
}
#endif

CRYPTOPP_NO_GLOBAL_BYTEC++17 std::byte fixes 之后定义.如果未定义 CRYPTOPP_NO_GLOBAL_BYTE,则 byte 位于全局命名空间中(Crypto++ 5.6.5 及更早版本)。如果定义了 CRYPTOPP_NO_GLOBAL_BYTE,则 byte 位于 CryptoPP 命名空间(Crypto++ 6.0 及更高版本)中。


为此:

std::ofstream testOut("./test.enc", std::ios::binary);
FileSink outSink(testOut);

您还可以:

FileSink outSink("./test.enc");

为此:

SHA256().CalculateDigest(passHash, (byte*) password.data(), password.size());
std::memcpy(key, passHash, AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+AES::DEFAULT_KEYLENGTH, AES::BLOCKSIZE);

您可以考虑使用 HKDF 作为推导函数。使用一个密码,但使用两个不同的标签,以确保独立派生。一个标签可能是字符串 "AES key derivation version 1",另一个标签可能是 "AES iv derivation version 1"

该标签将用作 DeriveKeyinfo 参数。您只需要调用它两次,一次用于 key ,一次用于 iv。

unsigned int DeriveKey (byte *derived, size_t derivedLen,
const byte *secret, size_t secretLen,
const byte *salt, size_t saltLen,
const byte *info, size_t infoLen) const

secret 是密码。如果你有 salt 就使用它。否则 HKDF 使用默认盐。

另见 HKDF在 Crypto++ wiki 上。


最后,关于这个:

You can sidestep the blocksize restriction by switching to another mode like CTR_Mode. But then you have to be very careful of key or IV reuse, which may be difficult due to the password derivation scheme you are using.

您还可以考虑集成加密方案,例如 Elliptic Curve Integrated Encryption Scheme .是IND-CCA2 ,这是一个强大的安全概念。您需要的一切都打包到加密方案中。

在 ECIES 下,每个用户都会获得一个公钥/私钥对。然后,使用一个大的随机 secret 作为 AES key 、iv 和 mac key 的种子。明文经过加密和验证。最后,种子在用户的公钥下加密。密码还是用的,只是用来解密私钥。

关于c++ - AES-128 CBC 模式下加密流末尾的 Crypto++ 额外 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54791882/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com