1

i'm trying to to encrypt a buffer with rsa and then save the data in hex format to file. I'm using Crypto++ 5.6.5.

Loading keys (working):

try
{
    // Read RSA public
    FileSource fs1("public.pem", true);
    PEM_Load(fs1, pubKey);

    // Read RSA encrypted private
    FileSource fs2("private.pem", true);
    PEM_Load(fs2, privKey, "1234", 4);
}
catch(const Exception& ex)
{
    cout << "ERROR: RSA:" << ex.what() << endl;
    SystemLog_Print("RSA: Couldn't load keys");
}

Encrypt (ok?):

std::string RSA_Encrypt(unsigned char *buf, uint8_t len)
{
    AutoSeededRandomPool rng;
    std::string plain;
    std::string cipher, recovered;

    for(int i = 0; i < len; ++i) {
        plain.push_back(buf[i]);
    }

    // Encryption
    RSAES_OAEP_SHA_Encryptor e(pubKey);

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

    // Test Decryption
    RSAES_OAEP_SHA_Decryptor d(privKey);

    StringSource ss2(cipher, true, new PK_DecryptorFilter(rng, d, new StringSink(recovered)));

    if(memcmp(plain.data(), recovered.data(), plain.size()) != 0) {
        cout << "RSA Mismatch" << endl;
    }

    return cipher;
}

Now i'm stuck with writing the encrypted data to a file in readable HEX like:

AB123CDE456

Using stream operators like std::hex doesn't seem to work. Could you give me any advice how to do this?

Not working:

unsigned char *buf[] = "123456789";
file << std::hex << RSA_Encrypt(buf, 9);

Prints only some unreadable binary data;

Progga
  • 343
  • 1
  • 3
  • 11
  • What do you mean it doesn't seem to work. Show your results and what you expect, as in a [mcve] – Passer By Jun 09 '17 at 09:36
  • This doesn't work: file << std::hex << RSA_Encrypt(...); Only binary junk is printed to file – Progga Jun 09 '17 at 09:38
  • No, [edit] it in the question, into a [mcve] – Passer By Jun 09 '17 at 09:39
  • The `std::hex` I/O manipulator is intended to modify the output of integer numbers (as text). In your case, it does nothing at best (as you want to write binary data). You have to write an own "formatter" but it's rather simple... – Scheff's Cat Jun 09 '17 at 10:06
  • Also see [Convert Hex string to bytes in crypto++](https://stackoverflow.com/q/17816312/608639), [Decoding Hex Encoded Value with Crypto++](https://stackoverflow.com/q/17306752/608639), [Decoding Hex Encoded Value with Crypto++](https://stackoverflow.com/q/17306752/608639), [Get hexadecimal encrypted string in AES 256 Crypto++](https://stackoverflow.com/q/21896874/608639), etc. – jww Jun 10 '17 at 01:01

3 Answers3

1

OK, for anyone interested... I found a generic hex formatter here: Integer to hex string in C++

I slightly modified it like this:

template< typename T >
std::string int2hex(T i)
{
  std::stringstream stream;
  stream << std::setfill ('0') << std::setw(sizeof(T)*2)
         << std::hex << (int32_t)i;
  return stream.str();
}

Now i call my routines like this:

buf = RSA_Encrypt(data, 32);

// Write hash to sig file
for(unsigned int i = 0 ; i < buf.size() ; ++i) {
    uint8_t val = buf[i];
    file << int2hex(val);
}

Now i get HEX chars in my file.

Progga
  • 343
  • 1
  • 3
  • 11
  • The Crypto++ library has most of the encoders you need. Checkout [HexEncoder](https://www.cryptopp.com/wiki/HexEncoder), [HexDecoder](https://www.cryptopp.com/wiki/HexDecoder), [Base32Encoder](https://www.cryptopp.com/wiki/Base32Encoder), [Base64Encoder](https://www.cryptopp.com/wiki/Base64Encoder), [Base64URLEncoder](https://www.cryptopp.com/wiki/Base64URLEncoder), [Base64URLDecoder](https://www.cryptopp.com/wiki/Base64URLDecoder), etc... – jww Jun 09 '17 at 13:08
0

A hex output function could look like this:

void writeHex(std::ostream &out, const char *data, size_t len)
{
  char digits[] = "0123456789ABCDEF";
  for (size_t i = 0; i < len; ++i) {
    unsigned byte = (unsigned)data[i];
    out << digits[byte >> 4] << digits[byte & 0xf];
  }
}

With a small sample to test it:

#include <iostream>

void writeHex(std::ostream &out, const char *data, size_t len)
{
  char digits[] = "0123456789ABCDEF";
  for (size_t i = 0; i < len; ++i) {
    unsigned byte = (unsigned)data[i];
    out << digits[byte >> 4] << digits[byte & 0xf];
  }
}

int main()
{
  // sample data
  char data[] =
    "This is some test data:\n"
    "\x00\x01\x02\x03\0x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
    "\x10\x11\x12\x13\0x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
    "\x20\x21\x22\x23\0x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
    "\x30\x31\x32\x33\0x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f";
  // test writeHex()
  writeHex(std::cout, data, sizeof data);
  std::cout << std::endl;
  // done
  return 0;
}

Compiled and tested with VS2013 on Windows 10 (64 bit):

5468697320697320736F6D65207465737420646174613A0A000102030078303405060708090A0B0C0D0E0F101112130078313415161718191A1B1C1D1E1F202122230078323425262728292A2B2C2D2E2F303132330078333435363738393A3B3C3D3E3F00

The human-readable text at the begin of my test data[] can be checked using an ASCII table. I simply searched for "000102" and saw the "0A" (for \n) before. The "00" at the end of output is for the string 0-terminator (which is considered by sizeof also).

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
0

Now i'm stuck with writing the encrypted data to a file in readable HEX like:

AB123CDE456

Add a HexEncoder and use a FileSink in the pipeline:

StringSource ss(plain, true, new PK_EncryptorFilter(rng, enc, new HexEncoder(new FileSink("file.enc"))));  

With the change above, the data is hex encoded as it travels through the pipeline.

Later, when you are ready to read the data, you use a FileSource and add a HexDecoder in the pipeline. Ad the decoder is added before the decryptor, not afterwards like when encrypting.

FileSource fs("file.enc", true, new HexDecoder, new PK_DecryptorFilter(rng, dec, new StringSink(recovered))));  

You should probably avoid this because its not a constant time compare:

if(memcmp(plain.data(), recovered.data(), plain.size()) != 0) {
    cout << "RSA Mismatch" << endl;
}

Use VerifyBufsEqual instead:

bool equal = VerifyBufsEqual(plain.data(), recovered.data(), plain.size());

VerifyBufsEqual requires same size buffers, so maybe something like:

bool equal = (plain.size() == recovered.size());
size_t size = STDMIN(plain.size(), recovered.size());
equal = VerifyBufsEqual(plain.data(), recovered.data(), size) && equal;

This may help...

Instead of using an intermediate std::string:

std::string RSA_Encrypt(unsigned char *buf, uint8_t len)
{
    ...    
    for(int i = 0; i < len; ++i) {
        plain.push_back(buf[i]);
    }  
    ...    
    StringSource ss(plain, true, new PK_EncryptorFilter(rng, enc, new StringSink(cipher)));    
    ...
}

You can use the buf and len instead:

std::string RSA_Encrypt(unsigned char *buf, uint8_t len)
{ 
    ...    
    ArraySource as(buf, len, true, new PK_EncryptorFilter(rng, enc, new StringSink(cipher)));    
    ...
}

An ArraySource is really a typedef of a StringSource using a constructor overload.

jww
  • 97,681
  • 90
  • 411
  • 885