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
?
... 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.