重新加密加密数据文件生成解密输出

Re-encrypting the encrypted data file generates a decrypted output

我使用 Crypto++ 库编写了加密函数,当文件加密第一次完成时函数运行正确。如果再次传递同一个加密文件进行加密,则生成包含加密和解密数据的输出。

bool EncryptDataFile(const char* inputFile, const char* outputFile)
{
  try
  {
    std::vector<byte> key = HexDecoding(PASSCODE);
    std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);

    GCM<AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());

    FileSource fs(inputFile, true,
        new AuthenticatedEncryptionFilter(encryptor,
            new FileSink(outputFile), false, TAG_SIZE));
  }

  catch(...)
  {
    return false;
  }

  return true;
}

Input.txt:

Privacy and Security

Output1.txt - 第一次加密输出:

{)ªei ?ñìCzN[hç&Ää€|Ùrñ½…
Ä

输入"Output1.txt",输出"Output2.txt" - 二次加密:

Privacy and Security]®Ÿwþñ úeS„£Fpä40WL ,ÈR¯M 

它揭示了原始数据。不确定这里缺少什么。

If the same encrypted file is passed again for encryption, generates the output which includes encrypted and decrypted data.

如果我解析正确,那么您在加密方案中说的是 m ≅ Enc(Enc(m)) 而不是 c = Enc(Enc(m))。这就是为什么你应该避免设计自己的方案的原因之一。

这可能在几种情况下发生,例如当 re-using 密钥和 iv.

时,在计数器模式下使用流密码或分组密码

您应该为每条消息或加密操作使用不同的安全上下文。放弃一些手,这意味着为每个消息或加密操作更改密钥或 iv。


std::vector<byte> key = HexDecoding(PASSCODE);
std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);

这可能是您的问题。您需要为每个消息或加密操作使用不同的安全上下文。


以下是修复方法。您使用密钥派生函数为每个加密派生不同的安全参数。在下面的代码中,32 字节的密钥被分成两个 16 字节的密钥。这同样适用于 iv。第一次加密使用key+0iv+0;第二次加密使用 key+16iv+16.

cryptopp$ cat test.cxx
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "aes.h"
#include "gcm.h"
#include "hex.h"
#include "hkdf.h"
#include "sha.h"

#include <string>
#include <iostream>

int main(int argc, char* argv[])
{
    using namespace CryptoPP;

    std::string password = "super secret password";
    SecByteBlock key(32), iv(32);

    HKDF<SHA256> hkdf;
    hkdf.DeriveKey(key, key.size(),
                   (const byte*)password.data(), password.size(),
                   NULL, 0,  // salt
                   (const byte*)"key derivation", 14);

    hkdf.DeriveKey(iv, iv.size(),
                   (const byte*)password.data(), password.size(),
                   NULL, 0,  // salt
                   (const byte*)"iv derivation", 13);


    std::string m = "Yoda said, Do or do not. There is no try.";
    std::string c1, c2;

    GCM<AES>::Encryption encryptor;

    encryptor.SetKeyWithIV(key, 16, iv, 16);
    StringSource(m, true, new AuthenticatedEncryptionFilter(
                 encryptor, new StringSink(c1)));

    encryptor.SetKeyWithIV(key+16, 16, iv+16, 16);
    StringSource(c1, true, new AuthenticatedEncryptionFilter(
                 encryptor, new StringSink(c2)));    

    std::cout << "Hex(m):" << std::endl;
    StringSource(m, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    std::cout << "Hex(Enc(m)):" << std::endl;
    StringSource(c1, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    std::cout << "Hex(Enc(Enc(m))):" << std::endl;
    StringSource(c2, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return 0;
}

这里是一个运行的程序:

cryptopp$ ./test.exe
Hex(m):
596F646120736169642C20446F206F7220646F206E6F742E205468657265206973206E6F20747279
2E
Hex(Enc(m)):
D4A9063DE7400E90627DE90D16346DC5A99740C55F6FEE092A99071F55F1BDB25A72B7422126CCC4
09B5B5C0076E39EBF7256D5DC3151A738D
Hex(Enc(Enc(m))):
83A459F2D4A1627624AF162590465AC705C8AC0F4D915E4A4A9D300156C5F9E042CAA47903353F0A
A1FAE408D5747DD223AC4F9AEF3C320EEF7E79E08AB2C6FBEAE7A3A5B4978C45C7

我认为你的方案还有一些问题。例如,如果您多次加密消息 "Attack at dawn!",那么每次 运行 都会得到相同的密文。是泄露信息,缺乏密文不可分辨性

我认为你应该避免你的方案,并使用 Elliptic Curve Integrated Encryption Scheme (ECIES). It avoids most of the latent problems in your scheme, and achieves IND-CCA2.

ECIES 的缺点是,您必须管理 public/private 密钥对。不过,这并不是什么大缺点。您已经在管理密码和 iv,因此从密码更改为私钥并不需要更多工作。