【问题标题】:How to use crypto++ Blowfish correctly如何正确使用crypto++ Blowfish
【发布时间】:2014-05-07 07:44:31
【问题描述】:

我今天整天都在尝试找出如何从二进制文件中读取并解密它。

在我的文件中,前4个字节是描述文件格式,后32个字节是header,用Blowfish加密。

所以我写了这段代码来做到这一点:

string file = "C:\\test.bin";    

byte *header = new byte[32];

FILE *data = fopen(file.c_str(), "r");

if(data == NULL)
{
    return 1; //Error opening file!
}

char type[6];

type[5] = 0;

if(fread(type, sizeof(type) - 1, 1, data) < 1)
{
    return 2;
}

if(strcmp(type, "ABCD") != 0)
{
    return 3;
}

if(fread(header, sizeof(header), 1, data) < 1)
{
    return 2; //Error reading file!
}

vector<byte> key;

key.push_back(0xAA);
key.push_back(0xBB);
key.push_back(0xCC);
key.push_back(0xDD);
key.push_back(0xAA);
key.push_back(0xBB);
key.push_back(0xCC);
key.push_back(0xDD);

ECB_Mode<Blowfish>::Decryption decryption(key.data(), key.size());

byte out[32];

decryption.ProcessData(out, header, 32);

FILE *outer =  fopen("C:\\out.bin", "w");

fwrite (out, sizeof(byte), sizeof(out), outer);

但这并没有正确解密数据。

我做错了什么?

【问题讨论】:

  • 请在 SO:coliru.stacked-crooked.com/a/dfb51ef2402b3d80 上发布时使您的代码自包含(69 行代码,我的答案用 39 行代码替换它(实际上,更少,因为我的代码做得更多)) .
  • ECB_Mode 可能是一个糟糕的选择。选择EAXGCMCCM 等模式。 ECB 模式仅对一个密文块是安全的。超过一个块,该模式会泄漏信息。在您的代码中,您应该在C:\test.bin 的密文中看到重复。重复将出现在 8 字节边界上。
  • 对不起,我差点忘了...

标签: c++ binary blowfish crypto++


【解决方案1】:

这里有很多东西有点臭

  • fopen 应该使用 "rb""wb" 用于二进制模式
  • 您应该使用memcmp 而不是strcmp
  • 您没有验证 fread 实际读取了 4 个字节
  • 对于二进制数据,您应该更喜欢 unsigned char(与符号扩展和溢出时未定义行为有关的陷阱更少)
  • 如果您使用的是 C++,为什么首先要使用 cstdlib、cstdio 和 cstring?
  • 这是一个错误

    if(fread(header, sizeof(header), 1, data) < 1)
    

    sizeof (header) 在这里是 sizeof(byte*),而不是您所期望的 32

以下是对 c++ 风格代码的快速回顾:更新为我的真实往返测试添加了一个长度字段(见下文)。

decryptor.cpp:

#include <fstream>
#include <algorithm>
#include <iterator>
#include <crypto++/blowfish.h>
#include <crypto++/modes.h>

static std::vector<byte> const key { 's','e','c','r','e','t' };
static byte const SIGNATURE[] = "ABCD"; //{ 'A','B','C','D' };

int main()
{
    if (std::ifstream data {"test.bin", std::ios::binary})
    {
        char type[] = { 0, 0, 0, 0 };

        if (!data.read(type, 4))
        {
            return 2;
        }

        auto mismatch = std::mismatch(std::begin(SIGNATURE), std::end(SIGNATURE), std::begin(type));

        if (mismatch.first != std::end(SIGNATURE))
        {
            return 3;
        }

        uint32_t length = 0;
        if (!data.read(reinterpret_cast<char*>(&length), sizeof(length))) // TODO use portable byte-order
        {
            return 4;
        }

        std::vector<byte> const ciphertext { std::istreambuf_iterator<char>(data), {} };
        // to read 32 bytes: 
        // std::copy_n(std::istreambuf_iterator<char>(data), 32, std::back_inserter(ciphertext));

        assert(data.good() || data.eof());
        assert(ciphertext.size() >= length);
        assert(ciphertext.size() % CryptoPP::Blowfish::BLOCKSIZE == 0);

        CryptoPP::ECB_Mode<CryptoPP::Blowfish>::Decryption decryption(key.data(), key.size());

        std::vector<char> plaintext(ciphertext.size());

        decryption.ProcessData(reinterpret_cast<byte*>(plaintext.data()), ciphertext.data(), plaintext.size());
        plaintext.resize(length); // trim padding

        std::ofstream out("out.bin", std::ios::binary);
        out.write(plaintext.data(), plaintext.size());
    } else
    {
        return 1; //Error opening file
    }
}

我还没有文件可以用来测试它。

更新所以,我现在也制作了an encryptor.cpp

echo "Hello world" | ./encryptor

生成一个 40 字节的文件(sig + 长度 + 密文 = 4 + 4 + 32 = 40),采用 base64 格式:

base64 test.bin
QUJDRAwAAABCaDMrpG0WEYePd7fI0wsHAQoNkUl1CjIBCg2RSXUKMg==

现在,解密测试结果很好。请注意,我发现我需要确保对 BLOCKSIZE 进行填充,因此我添加了一个 length 字段来存储明文的实际大小,以避免在解密后出现尾随垃圾。

你可以通过做来查看往返

echo 'Bye world!!' | ./encryptor && ./decryptor && cat out.bin

解密后确实会打印回问候语。

注意特别TODO。你可能应该use StreamTransformationFilter which adds padding as required.

【讨论】:

  • 添加了经过全面测试的往返加密器/解密器。添加了一些额外的观察结果和 CryptoPP StreamTransformationFilter 的链接,以防您也处理有效负载!= 32 字节。
  • @salim_aliya 当然可以,因为我正是出于这个原因对其进行了测试。我可以尝试一下:您的编译器太旧而无法编译我的代码(或者您不提供-std=c++11)。或者,您没有注意到我的示例使用了不同的密钥,并且允许可变长度的有效负载。
  • 请您解释一下什么是密文以及它需要什么?编辑:对不起,我还没有刷新网站...:D
  • @salim 这里的重点是你可以看到哪里出了问题,什么构成了现代 C++ 代码。我们不是来为您重新实施您的计划。
  • 密文就是……密文。你称这个变量为header
猜你喜欢
  • 2014-06-11
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 2016-11-08
相关资源
最近更新 更多