4

I'm write Client/Server encrypted program and want to send ECIES public key. For this I must serialize public key to file, read file to byte array, send this byte array. In other side: receive byte array, write it to file, deserialize public key from file. So, I'm written some test project, for try do it separately from great system, and (when all this module will work successfully) just insert it in my project. Code of this project are:

class EncoderRSA
{
    public:
        EncoderRSA();
        void keyGeneration();
        std::string encrypt(std::string plainText);
        std::string decrypt(std::string cypher);
        void setRsaPublicKey(char *publicKeyInCharArray);
        char *getRsaPublicKey();
    private:
        AutoSeededRandomPool prng;  // Pseudo Random Number Generator
        ECIES<ECP>::Decryptor rsaDecryptor;
        ECIES<ECP>::Encryptor rsaEncryptor;
};

And, strictly speaking, prime (for problem) methods:

char *EncoderRSA::getRsaPublicKey() {
    std::string file = "publicKey.txt";

    //Save public key in file
    FileSink sink(file.c_str());
    this->rsaEncryptor.GetPublicKey().Save(sink);

    //Read file with public key into the buffer
    std::ifstream infile (file.c_str(),std::ifstream::binary);

    if (!infile.is_open()) {
        std::cout << "Can't open file to write" << std::endl;
        exit(1);
    }

    // get size of file
    infile.seekg (0,infile.end);
    long size = infile.tellg();
    infile.seekg (0);

    // allocate memory for file content
    char* buffer = new char[size];
    infile.read (buffer,size);
    infile.close();

    return buffer;
}

void EncoderRSA::setRsaPublicKey(char *publicKeyInCharArray) {
    std::string file = "publicKey.txt";

    int size = strlen(publicKeyInCharArray);

    //Write received public key in file
    std::ofstream outfile (file.c_str(),std::ofstream::binary);

    if (!outfile.is_open()) {
        std::cout << "Can't open file to write" << std::endl;
        exit(1);
    }

    outfile.write (publicKeyInCharArray,size);
    outfile.close();

    // release dynamically-allocated memory
    delete[] publicKeyInCharArray;

    //Load public key from file
    FileSource source(file.c_str(), true);
    this->rsaEncryptor.AccessPublicKey().Load(source);
}

Code of main.cpp:

int main() {
    char *buffer = keysEncoder.getRsaPublicKey();
    cout << "buffer: " << buffer << endl;
    //...
    //send buffer
    //receive buffer from other side
    //..
    keysEncoder.setRsaPublicKey(buffer);

    string decoded = keysEncoder.decrypt(cypher);
    cout << "decoded: " << decoded << endl;

    return 0;
}

But it crashed with error:

terminate called after throwing an instance of 'CryptoPP::BERDecoderErr'
wait(): BER decode error
Aborted (core dumped)

Process returned 134 (0x86)    execution time: 2.891

Why?

jww
  • 83,594
  • 69
  • 338
  • 732
V. Panchenko
  • 696
  • 7
  • 29
  • 1
    You don't check if any of the file operations are successful. What if `open` or `seekg` return an error code? – Bo Persson Sep 19 '15 at 08:06
  • @Bo Persson, Thank you :) But, I do it intentionally - it is not final version, I just want to show you a problem. Problem not in it. – V. Panchenko Sep 19 '15 at 08:46
  • 2
    Why are you using temporary files? Use CryptoPP's `StringSink` and `StringSource` if you want to encode and decode to/from memory. However, if you are using files, have you checked the contents of the file? You may try the handy ASN.1 decoding tool at https://lapo.it/asn1js – buc Sep 19 '15 at 10:28

1 Answers1

1

I removed references to RSA since it looks like you are using ECIES. Good job on that.


terminate called after throwing an instance of 'CryptoPP::BERDecoderErr'

Obviously, you need to setup a try/catch:

try
{
    ...
}
catch(const BERDecoderErr& ex)
{
    cerr << ex.what() << endl;
}

char *Encoder::getPublicKey() {
    ...
    char* buffer = ...
    return buffer;
}

The ASN.1/DER encoding will likely have an embedded NULL, so you can't operate on it using traditional C-strings.

This should probably return a std::string so the output is not truncated at the first NULL character:

// get size of file
infile.seekg (0,infile.end);
long size = infile.tellg();
infile.seekg (0);

// allocate memory for file content
string buffer(size, '0');
infile.read (&buffer[0], size);
infile.close();

return buffer;

Another way to perform the read from the file can be found at Read whole ASCII file into C++ std::string:

std::ifstream infile (file.c_str(), std::ifstream::binary);
std::string buffer((std::istreambuf_iterator<char>(infile)),
                    std::istreambuf_iterator<char>());

Another way is to ensure the NULL's are not present in the output and input:

string Encoder::getPublicKey() {
    string encodedKey;
    HexEncoder sink(new StringSink(encodedKey));
    Encryptor.GetPublicKey().Save(sink);
    return encodedKey;
}

And:

void Encoder::setPublicKey(const string& encodedKey) {
    StringSource source(encodedKey, new HexDecoder());
    Encryptor.AccessPublicKey().Load(source);
}

The code above uses StringSource and StringSink, so it operates on stuff in-memory. If you really want the on-disk intermediate files, then use a FileSource and FileSink.

Community
  • 1
  • 1
jww
  • 83,594
  • 69
  • 338
  • 732
  • Thank you so much!) I'm trying to do it right now :) – V. Panchenko Sep 20 '15 at 07:10
  • @V.Panchenko - if you are on Linux, then `sed` is your friend. `sed -i "s|rsaDecryptor|eciesDecryptor|g" *.h *.cpp` can be used to replace text. If on OS X, then you will use the same command, but with `sed -i "" ...` (you need the empty quoted string). – jww Sep 20 '15 at 08:05
  • Yes, I'm on Linux, but I want write cross-platform code, so I should focus on Windows too ) – V. Panchenko Sep 20 '15 at 08:11