Search code examples
c++encryptioncryptographycrypto++

Encrypt a symmetric AES key with RSA in Crypto++


I'm trying to exchange an AES key between two parts.The AES key would be encrypted with receiver's RSA publicKey and then written to a file.

I'm using Crypto++ library,and here is the beginning of my program:

//generate key pair
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::RSAES_OAEP_SHA_Decryptor priv(rng, 4096);

RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);

//generate aes key (256bits)
SecByteBlock key(AES::MAX_KEYLENGTH);
rnd.GenerateBlock(key, key.size());

(You will recognize Crypto++ wiki example)

Then I start the encryption routine:

CryptoPP::SecByteBlock cipher(CryptoPP::AES::MAX_KEYLENGTH), decrypted_data(CryptoPP::AES::MAX_KEYLENGTH);

CryptoPP::RSAES_OAEP_SHA_Encryptor e(publicKey);

I don't know which one to use: CryptoPP:: ArraySource or CryptoPP::StringSource?


Solution

  • ... after I don't know what to use : CryptoPP::ArraySource ? CryptoPP::StringSource ?

    After this, use a PK_EncryptorFilter for the RSA encryptor; and use an ArraySource for the key you are trying to encrypt. An ArraySource is just a typedef for a StringSource, so you are really just using a StringSource.

    The code would look something like the following. I did not run the examples, so please fix the typos.

    SecByteBlock key(AES::MAX_KEYLENGTH);
    rng.GenerateBlock(key, key.size());
    ...
    
    RSAES_OAEP_SHA_Encryptor encryptor(publicKey);
    ...
    
    ArraySource as(key, key.size(), true, /* pump all data */
        new PK_EncryptorFilter(rng, encryptor,
            new FileSink("session-key.enc")));
    

    You can write it to a std::string with something like:

    std::string session_key;
    ArraySource as(key, key.size(), true, /* pump all data */
        new PK_EncryptorFilter(rng, encryptor,
            new StringSink(session_key)));
    

    You could also get fancy and use a ByteQueue. The Redirector breaks the ownership chain. Also see Redirector on the Crypto++ wiki.

    ByteQueue queue;
    ArraySource as(key, key.size(), true, /* pump all data */
        new PK_EncryptorFilter(rng, encryptor,
            new Redirector(queue)));
    

    ByteQueue are cool because they wrap SecByteBlock. Once in a ByteQueue you can move the bytes around with TransferTo and CopyTo:

    ByteQueue queue;
    ArraySource as(key, key.size(), true, /* pump all data */
        new PK_EncryptorFilter(rng, encryptor,
            new Redirector(queue)));
    
    // Copy bytes to a file
    FileSink sink1("session-key.enc");
    queue.CopyTo(sink1);
    
    // Copy bytes to std::out
    HexEncoder encoder(new FileSink(std::cout));
    queue.CopyTo(encoder);
    
    // Transfer bytes to a SecByteBlock.
    SecByteBlock block(queue.MaxRetrievable());
    ArraySink sink2(block, block.size());
    queue.TransferTo(sink2);
    // No bytes remain in the queue
    

    You might also be interested in Pipelines on the Crypto++ wiki. The stuff you see above are sources, filters and sinks used in a pipeline.


    If this is supposed to hold the encrypted symmetric key, then its too small:

    SecByteBlock cipher(AES::MAX_KEYLENGTH);
    

    Take a look at RSA Encryption Schemes on the Crypto++ wiki. cipher needs to be encryptor.CiphertextLength(AES::MAX_KEYLENGTH);, which is roughly the size of the RSA modulus minus about 50 bytes for the OAEP formatting and padding.


    (comment) > Now I am trying to add the part to decrypt it using the private key... Is this correct ?

    StringSource(readed_key, true,
        new PK_DecryptorFilter(rng, decryptor, new 
            FileSource("sessionkey.enc")));
    

    Well, the filter part is correct (the PK_DecryptorFilter part). In Crypto++, data flows from a source to a sink. So the general pattern is as shown below. Its called a Pipeline.

    Source(..., new Filter(..., new Filter(..., new Sink(...))));
    

    Usually you want something like the following. The Redirector breaks the ownership chain. Also see Redirector on the Crypto++ wiki.

    // decrypted, in-memory
    SecByteBlock session_key(AES::MAX_KEYLENGTH);
    ArraySink sink(session_key, session_key.size());
    
    FileSource fs("sessionkey.enc", true,
        new PK_DecryptorFilter(rng, decryptor, 
            new Redirector(sink)));
    

    (comment) > ... and then compare the original ...

    You can compare two byte buffers in nearly constant time with VerifyBufsEqual. Again, see the Crypto++ wiki.