【问题标题】:Different results when encrypting with Common Crypto and Crypto++ with AES使用 Common Crypto 和 Crypto++ 使用 AES 加密时的不同结果
【发布时间】:2015-03-29 18:13:45
【问题描述】:

使用 Apple 的 Common Crypto 和 Crypto++ 使用相同的密钥加密相同的文件(二进制数据)时,我得到不同的结果。我使用的算法是 AES。

这是 Objective C 中使用 Common Crypto 的代码:

void FileUtil::writeToFileEncrypt(string fileName, const void *data, int size, string key, int *sizeOut)
{
    int numBytesEncrypted = 0;
    char keyPtr[kCCKeySizeAES256+1];

    if (key.length() > 32)
    {
        key = key.substr(0, 32);
    }

    memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

    if (key.length() < 32)
    {
        for (int i = key.length(); i < 32; i++)
        {
            keyPtr[i] = '0';
        }
    }

    size_t bufferSize = size + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                      keyPtr, kCCKeySizeAES256,
                                      NULL /* initialization vector (optional) */,
                                      data, size, /* input */
                                      buffer, bufferSize, /* output */
                                      &numBytesEncrypted);

    if (cryptStatus == kCCSuccess) {
        cout << "encrypt success" << endl;
    }

    ofstream myfile;
    myfile.open (fileName.c_str(), ios::out | ios::binary);
    myfile.write((const char *)buffer, numBytesEncrypted);
    myfile.close();

    free(buffer);

    *sizeOut = numBytesEncrypted;
}

这是使用 Crypto++ 的 C++ 代码

void EncryptUtils::encrypt(string fileName, unsigned char *chars, string key, int *length, int dataLength)
{
    unsigned char *iv = (unsigned char *)"0000000000000000";

    char keyPtr[32 + 1];

    if (key.length() > 32)
    {
        key = key.substr(0, 32);
    }

    memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

    if (key.length() < 32)
    {
        for (int i = key.length(); i < 32; i++)
        {
            keyPtr[i] = '0';
        }
    }
    keyPtr[32] = '\0';

    CryptoPP::AES::Encryption aesEncryption((unsigned char *)keyPtr, 32);
    CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

    int newBufSize = (sizeof(unsigned char *) * dataLength) + 32;
    unsigned char *newBuf = (unsigned char *)malloc(newBufSize);
    CryptoPP::ArraySink *arraySink = new CryptoPP::ArraySink(newBuf, newBufSize);

    CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, arraySink, CryptoPP::StreamTransformationFilter::PKCS_PADDING);
    stfEncryptor.Put(reinterpret_cast<const unsigned char*>(chars), (unsigned int)dataLength);
    stfEncryptor.MessageEnd();

    *length = arraySink->TotalPutLength();

    ofstream myfile;
    myfile.open (fileName.c_str(), ios::out | ios::binary);
    myfile.write((const char *)newBuf, *length);
    myfile.close();
}

我需要让它们都产生相同的结果。有什么我忽略的吗?

【问题讨论】:

  • 如果你使用 C++,你为什么使用malloc?你应该使用new;或使用auto_ptrvectorarray 等标准容器。
  • 你说得对,我不应该使用 malloc。我使用 malloc 是出于习惯,因为我来自 C 背景并且是 C++ 新手。

标签: c++ objective-c encryption crypto++ commoncrypto


【解决方案1】:
  1. “Objective-C”版本不是用 Objective-C 编写的,而是用 C++ 编写的。使用的实际加密CCCrypt 是纯“C”。

  2. “Objective-C”版本没有提供iv,因此默认为全零。 C++ 版本提供了iv 的 ASCII“0”字符,这与全零数据不同。这可能是错误。

  3. 在加密调用之前和之后为每个包括密钥、iv、数据输入和数据输出提供输入和输出十六进制数据转储。

【讨论】:

  • 静脉注射是主要问题。感谢您的回复。
【解决方案2】:

这是错误的:

memcpy(keyPtr, key.c_str(), sizeof(keyPtr));

它尝试复制 33 个字节。我认为你需要这样的东西:

size_t ksize = std::min(sizeof(keyPtr), key.size());
assert(ksize == 16 || ksize == 24 || ksize == 32);

-----

总的来说,设计是错误的:

keyPtr[32] = '\0';

密钥是二进制的,而不是 ASCII 字符串。如果您使用密码,那么它仍然是错误的,因为您应该使用 KDF 将密码消化成合适的密钥。

-----

通用 Crypto 代码缺少 IV,并且可能不会使用与 Crypto++ 示例相同的填充。

-----

AES 的使用似乎是错误的。看来您正在以 ECB 模式操作密码,因此它仅提供机密性如果数据小于块大小。它受到重新排序和操纵,因此不提供真实性保证。你应该使用Authenticated Encryption,如EAXGCMCCM 模式。

由于 Common Crypto 很糟糕(它不提供经过身份验证的加密模式),而 Crypto++ 提供了您需要的一切(并且几乎可以在任何地方运行),您应该使用 Crypto++ 和经过身份验证的加密模式。

【讨论】:

  • Common Crypto 默认为 CBC,为了使用 ECB,必须显式设置该选项,但并非如此。请注意,虽然一个糟糕的编码选择 '\0' 是 0 而不是 '0'。不幸的是,Common Crypto 不支持 EAX、GCM 或 CCM 模式,除非这些模式是必需的,Common Crypto 是最好的解决方案,它由 Apple 提供,工作正常,在 iOS 社区中很好理解,不需要编译 Crypto++。
  • @ Zaph - 感谢 Common Crypto 更正。 Crypto++ 允许一组源代码,因此我不确定使用仅在一个平台上运行的 Apple 库重新实现是否有好处。一种实现似乎优于 4x 实现(Crypto++、iOS、Android、Windows)。另外,我们知道 Apple 的实现存在未修复的错误。它会导致 OpenSSL 和 ECDHE/ECDSA 出现很多问题。
  • 请指出有关“Apple 的实现有未修复的错误”的信息,我有兴趣看到它们。如果 Crypto++ 有一个可用于 php 的版本,那将是一个巨大的优势,因为 php 填充不是 PKCS#7 和 PITA。
  • @Zaph - 例如,参见 OpenSSL wiki 上的 SSL_OP_SAFARI_ECDHE_ECDSA_BUG。不要忘记他们的Goto Fail bug。我什至与他们一起打开错误报告,以发现他们的安全设备存在缺陷。它们尚未公开。
  • Goto Fail 不在 Common Crypto 中,SSL_OP_SAFARI_ECDHE_ECDSA_BUG 也不在。查看 Crypto++ wiki 似乎需要 GCC,它不再随 Xcode 一起提供。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 2019-02-22
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2022-09-27
相关资源
最近更新 更多