c++ - 从内存导入 key 时 Crypto++ BERDecode 异常?

标签 c++ visual-c++ cryptography crypto++

我正在尝试从内存导入公钥,但失败并出现以下异常:“BER 解码错误”。一旦我使用 FileSource 从文件导入它,它就会成功。

为什么我无法从内存中解码它,但从文件中解码成功?我尝试了 Load()BERDecode() 函数。

namespace CryptoHelper
{
    byte key[292] =
    {
        0x30, 0x82, 0x01, 0x20, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
        0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0D, 0x00, 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01,
        0x00, 0xB6, 0x87, 0x31, 0x96, 0xE3, 0xAD, 0x61, 0x66, 0x30, 0x0D, 0xE6, 0x91, 0xED, 0x9D, 0x41,
        0x4E, 0xA2, 0x30, 0x10, 0xCD, 0x5C, 0x24, 0x90, 0xD2, 0x9A, 0xD9, 0xBD, 0xF9, 0x70, 0x14, 0xF9,
        0x57, 0x36, 0xE7, 0xE7, 0x9D, 0xCF, 0xFE, 0x85, 0x6F, 0x28, 0x81, 0x16, 0x10, 0x95, 0x80, 0x2B,
        0xCA, 0xA2, 0xB7, 0x88, 0x95, 0xF1, 0xCA, 0x5E, 0xEF, 0xCD, 0x3B, 0xEC, 0x30, 0xB9, 0xE3, 0xC6,
        0x88, 0xB3, 0xF4, 0xA2, 0x5D, 0xF3, 0xC7, 0x9C, 0x2A, 0xA7, 0x54, 0xAE, 0xBF, 0xEC, 0x7D, 0x14,
        0xE8, 0x63, 0xE7, 0x2C, 0x8E, 0xAE, 0x06, 0xFF, 0xA2, 0x21, 0x04, 0xF8, 0x1B, 0x3F, 0x94, 0x01,
        0xFC, 0xDE, 0x28, 0xA1, 0x6F, 0x05, 0x76, 0xBE, 0x81, 0x6B, 0xF0, 0x60, 0x19, 0xB3, 0x8E, 0x6A,
        0xBE, 0x39, 0x54, 0xC3, 0xF5, 0xA8, 0xC9, 0xC0, 0x3F, 0x30, 0x87, 0x48, 0x64, 0xBE, 0x8E, 0x8A,
        0xFF, 0x8D, 0xC8, 0x51, 0xCC, 0x5A, 0x99, 0xD5, 0xDF, 0x0B, 0x41, 0x8F, 0xFE, 0xAD, 0xBC, 0xD2,
        0x01, 0x79, 0x6F, 0x6E, 0xFC, 0x46, 0xEC, 0x73, 0xD4, 0xF7, 0xD4, 0xD4, 0x34, 0x1F, 0xCF, 0x1E,
        0xBE, 0xA9, 0xD5, 0xDF, 0x9B, 0x45, 0x15, 0xF4, 0xB5, 0x0F, 0xA1, 0x22, 0x06, 0xED, 0x5B, 0x21,
        0xC0, 0x8C, 0x1C, 0xC8, 0xF3, 0x47, 0x7A, 0x6D, 0x3D, 0x03, 0x87, 0xF7, 0x0D, 0xF9, 0x5C, 0xF0,
        0xA9, 0xD0, 0xD4, 0xAD, 0x89, 0x86, 0x08, 0xAE, 0xC2, 0x4B, 0x2B, 0x1C, 0xC1, 0xCA, 0xA7, 0xBD,
        0x37, 0x5D, 0x62, 0xD9, 0x0A, 0xF8, 0x1F, 0x12, 0x64, 0xBA, 0xD1, 0x43, 0x33, 0xA2, 0xFC, 0xAD,
        0xE0, 0x5A, 0x17, 0x7B, 0x86, 0xD1, 0x8F, 0xF8, 0x05, 0x5F, 0xB6, 0x32, 0x59, 0xAB, 0x70, 0x1C,
        0xD4, 0x30, 0x68, 0xA2, 0xC6, 0x68, 0xD7, 0x7B, 0x21, 0xE3, 0xE9, 0x4D, 0x4C, 0x54, 0x40, 0x95,
        0xEB, 0x02, 0x01, 0x11,
    };

    AutoSeededRandomPool rng;       // TODO: Check whether this is thread-safe
    ArraySink keySink(key, 292);    // TODO: Check whether this is thread-safe
    RSA::PublicKey rsaPublic;       // TODO: Check whether this is thread-safe

    void EncryptBuffer()
    {
        std::string plain = "RSA Encryption", cipher, recovered;

        FileSource input("rsapublic.dat", true);

        // This one works perfectly
        // rsaPublic.BERDecode(input);
        rsaPublic.Load(keySink);

        RSAES_OAEP_SHA_Encryptor e(rsaPublic);

        // Encrypt
        StringSource ss1(plain, true,
            new PK_EncryptorFilter(rng, e, new StringSink(cipher)
            ));

        return;
    }
}

最佳答案

How come I can't decode it from memory, but succeed to decode from file? I tried both Load() and BERDecode() function.

你很接近。使用 ArraySource 而不是 ArraySink 来加载 key 。数据从源流向接收器。另请参阅ArraySourceArraySink在 Crypto++ wiki 上。

您还需要Load,因为 key 具有SubjectPublicKeyInfo 前导码。另请参阅Keys and Formats在 Crypto++ wiki 上。

$ cat test.cxx
#include "cryptlib.h"
#include "filters.h"
#include "osrng.h"
#include "files.h"
#include "rsa.h"
#include "hex.h"

#include <iostream>
#include <string>

using CryptoPP::byte;
extern const byte key[292];

int main (int argc, char* argv[])
{
    using namespace CryptoPP;
    AutoSeededRandomPool rng;
    ArraySource keySource(key, 292, true);

    RSA::PublicKey rsaPublic;
    rsaPublic.Load(keySource);
    RSAES_OAEP_SHA_Encryptor enc(rsaPublic);

    std::string plain = "RSA Encryption", cipher, recovered;    
    StringSource ss1(plain, true,
        new PK_EncryptorFilter(rng, enc, new StringSink(cipher)
    ));

    std::cout << "Encrypted: ";
    StringSource(cipher, true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    return 0;
}

const byte key[292] =
{
    0x30, 0x82, 0x01, 0x20, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
    0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0D, 0x00, 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01,
    0x00, 0xB6, 0x87, 0x31, 0x96, 0xE3, 0xAD, 0x61, 0x66, 0x30, 0x0D, 0xE6, 0x91, 0xED, 0x9D, 0x41,
    0x4E, 0xA2, 0x30, 0x10, 0xCD, 0x5C, 0x24, 0x90, 0xD2, 0x9A, 0xD9, 0xBD, 0xF9, 0x70, 0x14, 0xF9,
    0x57, 0x36, 0xE7, 0xE7, 0x9D, 0xCF, 0xFE, 0x85, 0x6F, 0x28, 0x81, 0x16, 0x10, 0x95, 0x80, 0x2B,
    0xCA, 0xA2, 0xB7, 0x88, 0x95, 0xF1, 0xCA, 0x5E, 0xEF, 0xCD, 0x3B, 0xEC, 0x30, 0xB9, 0xE3, 0xC6,
    0x88, 0xB3, 0xF4, 0xA2, 0x5D, 0xF3, 0xC7, 0x9C, 0x2A, 0xA7, 0x54, 0xAE, 0xBF, 0xEC, 0x7D, 0x14,
    0xE8, 0x63, 0xE7, 0x2C, 0x8E, 0xAE, 0x06, 0xFF, 0xA2, 0x21, 0x04, 0xF8, 0x1B, 0x3F, 0x94, 0x01,
    0xFC, 0xDE, 0x28, 0xA1, 0x6F, 0x05, 0x76, 0xBE, 0x81, 0x6B, 0xF0, 0x60, 0x19, 0xB3, 0x8E, 0x6A,
    0xBE, 0x39, 0x54, 0xC3, 0xF5, 0xA8, 0xC9, 0xC0, 0x3F, 0x30, 0x87, 0x48, 0x64, 0xBE, 0x8E, 0x8A,
    0xFF, 0x8D, 0xC8, 0x51, 0xCC, 0x5A, 0x99, 0xD5, 0xDF, 0x0B, 0x41, 0x8F, 0xFE, 0xAD, 0xBC, 0xD2,
    0x01, 0x79, 0x6F, 0x6E, 0xFC, 0x46, 0xEC, 0x73, 0xD4, 0xF7, 0xD4, 0xD4, 0x34, 0x1F, 0xCF, 0x1E,
    0xBE, 0xA9, 0xD5, 0xDF, 0x9B, 0x45, 0x15, 0xF4, 0xB5, 0x0F, 0xA1, 0x22, 0x06, 0xED, 0x5B, 0x21,
    0xC0, 0x8C, 0x1C, 0xC8, 0xF3, 0x47, 0x7A, 0x6D, 0x3D, 0x03, 0x87, 0xF7, 0x0D, 0xF9, 0x5C, 0xF0,
    0xA9, 0xD0, 0xD4, 0xAD, 0x89, 0x86, 0x08, 0xAE, 0xC2, 0x4B, 0x2B, 0x1C, 0xC1, 0xCA, 0xA7, 0xBD,
    0x37, 0x5D, 0x62, 0xD9, 0x0A, 0xF8, 0x1F, 0x12, 0x64, 0xBA, 0xD1, 0x43, 0x33, 0xA2, 0xFC, 0xAD,
    0xE0, 0x5A, 0x17, 0x7B, 0x86, 0xD1, 0x8F, 0xF8, 0x05, 0x5F, 0xB6, 0x32, 0x59, 0xAB, 0x70, 0x1C,
    0xD4, 0x30, 0x68, 0xA2, 0xC6, 0x68, 0xD7, 0x7B, 0x21, 0xE3, 0xE9, 0x4D, 0x4C, 0x54, 0x40, 0x95,
    0xEB, 0x02, 0x01, 0x11
};

该计划的结果是:

$ ./test.exe
Encrypted: 004EE30C8B91F07056FAB43D6DB17DA0A193B19856BB725898301B5717B929066BC37
37F587063ECC4EA81A3ED06219C24335B3997A612C2B0BFAE17F555D6B05D759A1BE3D812B3E6018
6E114E13B6CD7D145D4DBB125D7B56F7640875F16854C911F4552272FBD3E3437E6C3CD6F6059FB5
C2ED50E62B65EAC9B78645E86C2EFC606FCAD2F823CA19F846C6F1837DDA4AA81CAA73108B30F8DC
9107FF442708CD97BF1800BA4E7FE60D3F5B08376B0BD9A41021FC5812FA4B0F1B2A08F504C2B622
A8684D189AD2BCDD85E647AAE2023D923F2F3B2531F315F97E0135489B282DB6F3C05F78233AC810
E184028743B943B07FFFEA93CC6A69511BF113888F2

如果添加以下内容,则可以将 key 写入文件:

FileSink fs("rsa.der");
fs.Put(key, sizeof(key));
fs.MessageEnd();

或者:

ArraySource(key, sizeof(key), true, new FileSink("rsa.der"));

然后,您可以使用 Gutmann 的 dumpasn1 检查 key 。请注意,您有一个 RSA subjectPublicKeyInfo。当您有SubjectPublicKeyInfo 或PrivateKeyInfo 时,您可以使用Load()Save()。 (并使用 BERDecodeDEREncode 加载和保存不带 *Info header 的公钥或私钥) .

$ dumpasn1 rsa.der
  0 288: SEQUENCE {
  4  13:   SEQUENCE {
  6   9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 17   0:     NULL
       :     }
 19 269:   BIT STRING, encapsulates {
 24 264:     SEQUENCE {
 28 257:       INTEGER
       :         00 B6 87 31 96 E3 AD 61 66 30 0D E6 91 ED 9D 41
       :         4E A2 30 10 CD 5C 24 90 D2 9A D9 BD F9 70 14 F9
       :         57 36 E7 E7 9D CF FE 85 6F 28 81 16 10 95 80 2B
       :         CA A2 B7 88 95 F1 CA 5E EF CD 3B EC 30 B9 E3 C6
       :         88 B3 F4 A2 5D F3 C7 9C 2A A7 54 AE BF EC 7D 14
       :         E8 63 E7 2C 8E AE 06 FF A2 21 04 F8 1B 3F 94 01
       :         FC DE 28 A1 6F 05 76 BE 81 6B F0 60 19 B3 8E 6A
       :         BE 39 54 C3 F5 A8 C9 C0 3F 30 87 48 64 BE 8E 8A
       :                 [ Another 129 bytes skipped ]
289   1:       INTEGER 17
       :       }
       :     }
       :   }

0 warnings, 0 errors.


AutoSeededRandomPool prng;      // TODO: Check whether this is thread-safe
ArraySink keySink(key, 292);    // TODO: Check whether this is thread-safe
RSA::PublicKey rsaPublic;       // TODO: Check whether this is thread-safe

关于评论...所有 Crypto++ 对象在类级别都是线程安全的,这意味着它们不共享数据。 README 中对此进行了说明。 :

Crypto++ is thread safe at the class level. This means you can use Crypto++ safely in a multithreaded application, but you must provide synchronization when multiple threads access a common Crypto++ object.

这意味着,如果您有两个线程使用同一个对象,那么您需要提供锁。例如,使用 prng 意味着生成器的内部状态将会改变。没有内部锁,您必须序列化对 prng 的访问。

关于随机数生成器,Wei Dai(该库的原作者)建议为每个线程使用单独的生成器。来自 Use of AutoSeededRandomPool在邮件列表上:

I suggest one instance per thread so you don't have to worry about synchronizing access to it. One instance per use is fine also if that is more convenient. The extra system overhead is probably unnoticeable in most situations.

关于c++ - 从内存导入 key 时 Crypto++ BERDecode 异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57509819/

相关文章:

c++ - 处理数据包的设计模式

C++ : How to make a specific binary (executable) for each trait?

c++ - 现在 Visual C++ 动态运行时库是 Windows 操作系统的一部分吗?

windows - 在 Windows 上编译用 Zig 编写的 CPython 扩展

c++ - 将 .lib 项目转换为 exe 一个

visual-c++ - Visual Studio 的 MFC 版本

java - Android AES/CBC/PKCS5Padding 加密

ios - `arc4random` 函数族是线程安全的吗?

java - 对文件使用相同的 AES 算法的错误填充异常?

c++ - 为什么对模板化基类的函数调用不起作用?