Use previously generated private key in ECIES

1.8k views Asked by At

I wan to encrypt /decrypt data using ECIES , I am using cryptopp for this.

AutoSeededRandomPool prng;

//get private key generated
ECIES<ECP>::Decryptor d0(prng, ASN1::secp256r1());
PrintPrivateKey(d0.GetKey());

//get public key 
ECIES<ECP>::Encryptor e0(d0);
PrintPublicKey(e0.GetKey());

//encrypt the message
string em0; // encrypted message
StringSource ss1 (message, true, new PK_EncryptorFilter(prng, e0, new StringSink(em0) ) );

//decrypt the message   
string dm0; // decrypted message
StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, d1, new StringSink(dm0) ) );

Everything else is fine but I want to do the above same thing using already generated 'private key' and not randomly generated 'private key' unlike the case above. How can I do this?

I have tried the following code but it just simply crashes

AutoSeededRandomPool prng;

std::string  privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";

CryptoPP::HexDecoder decoder;
decoder.Put((byte*)privatekeyString.data(), privatekeyString.size());
decoder.MessageEnd();

ECIES<ECP> ::Decryptor d0;
d0.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());

crash point

//load private key  
d0.AccessKey().Load(decoder);
PrintPrivateKey(d0.GetKey());

//get public key    
ECIES<ECP>::Encryptor e0(d0);
PrintPublicKey(e0.GetKey());

string em0; // encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, e0, new StringSink(em0) ) );
cout<<"encrypted msg: "<<em0<<"  and its length: "<<em0.length()<<endl;

string dm0; // decrypted message
StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, d0, new StringSink(dm0) ) );
cout <<"decrypted msg: "<< dm0<<"  and its length: "<<dm0.length() <<   endl;

Edit 2

In response to @jww answer I managed to decode the message with the private key as:

  try
  {
    AutoSeededRandomPool prng;

    std::string exponent="AsIAECwYD55qTnovWLW+hrwXlHg=";
    StringSource ss(exponent, true /*pumpAll*/, new CryptoPP::HexDecoder);


    Integer x;
    x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);
    // cout << "Exponent: " << std::hex << x << endl;

    ECIES<ECP>::Decryptor decryptor;
    decryptor.AccessKey().Initialize(ASN1::secp128r1(), x);

    bool valid = decryptor.AccessKey().Validate(prng, 3);
    if(!valid)
    {
        cout<<"Exponent is not valid for P-128"<<endl;
        return;
    }
      //  throw  Exception(CryptoPP::Exception::OTHER_ERROR, "Exponent is not valid for P-256");

    // Or: decryptor.AccessKey().ThrowIfInvalid(prng, 3);

    cout << "Exponent is valid for P-128" << endl;

    PrintPrivateKey(decryptor.GetKey());


    //get public key
    ECIES<ECP>::Encryptor encryptor(decryptor);
    PrintPublicKey(encryptor.GetKey());



    string em0; // encrypted message
    StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(em0) ) );
    cout<<"encrypted msg: "<<em0<<"  and its length: "<<em0.length()<<endl;

    string dm0; // decrypted message
    StringSource ss2 (em0, true, new PK_DecryptorFilter(prng, decryptor, new StringSink(dm0) ) );
    cout <<"decrypted msg: "<< dm0<<"  and its length: "<<dm0.length() << endl;

}
catch(const CryptoPP::Exception& ex)
{
    std::cerr << ex.what() << endl;
}

But when I try to encrypt the message using public key I got error

CryptoPP::CryptoMaterial::InvalidMaterial: CryptoMaterial: this object contains invalid values

Here is my code:

std::string     public_point="AsIAEFjzIcX+Kvhe8AmLoGUc8aYAEAwf5ecREGZ2u4RLxQuav/A=";
StringSource ss(public_point, true, new CryptoPP::HexDecoder);

ECIES<ECP>::Encryptor encryptor;
    encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());

ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "X: " << std::hex << point.x << endl;
cout << "Y: " << std::hex << point.y << endl;

encryptor.AccessKey().SetPublicElement(point);


encryptor.AccessKey().ThrowIfInvalid(prng, 3);

PrintPublicKey(encryptor.GetKey());



string em0; // encrypted message
StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(em0) ) );
cout<<"encrypted msg: "<<em0<<"  and its length: "<<em0.length()<<endl;
3

There are 3 answers

0
SandeepAggarwal On BEST ANSWER

As jww suggested I have successfully completed encryption and decryption. Below are the code snippets if anyone wants.

Decryption

string decrypt(std::string encryptedMessage ,  std::string   privateKeyExponent)
{
    string decryptedMessage;
    try
    {
        AutoSeededRandomPool prng;

        //since the 'privateKeyExponent' is in base-64 format use Base64Decoder
        StringSource ss(privateKeyExponent, true /*pumpAll*/, new CryptoPP::Base64Decoder);

        Integer x;
        x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);

        ECIES<ECP>::Decryptor decryptor;

        //curve used is secp256k1
        //make decryptor's access key using decoded private exponent's value
        decryptor.AccessKey().Initialize(ASN1::secp256k1(), x);

        //check whether decryptor's access key is valid or not
        bool valid = decryptor.AccessKey().Validate(prng, 3);
        if(!valid)
           decryptor.AccessKey().ThrowIfInvalid(prng, 3);

        cout << "Exponent is valid for P-256k1" << endl;

        //decrypt the message using private key
        StringSource ss2 (encryptedMessage, true, new PK_DecryptorFilter(prng, decryptor, new StringSink(decryptedMessage) ) );
        cout <<"decrypted msg: "<< decryptedMessage<<"  and its length: "<<decryptedMessage.length() << endl;

    }
    catch(const CryptoPP::Exception& ex)
    {
        std::cerr << ex.what() << endl;
    }
    return decryptedMessage;
}

Encryption

string encrypt(std::string message ,  std::string  compressedPublicKeyPoint)
{
    string encryptedMessage;
    try
    {
        AutoSeededRandomPool prng;

        //public key is a point consisting of "public key point x" and "public key point y"
        //compressed public key also known as "public-point" formed using point-compression of public key


        //since the key is in base-64 format use Base64Decoder
        StringSource ss(compressedPublicKeyPoint, true, new CryptoPP::Base64Decoder);
     ECIES<ECP>::Encryptor encryptor;

        //curve used is secp256k1
        encryptor.AccessKey().AccessGroupParameters()
       .Initialize(ASN1::secp256k1());

        //get point on the used curve
        ECP::Point point;
        encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
        cout << "X: " << std::hex << point.x << endl;
        cout << "Y: " << std::hex << point.y << endl;

        //set encryptor's public element
        encryptor.AccessKey().SetPublicElement(point);

        //check whether the encryptor's access key thus formed is valid or not
        encryptor.AccessKey().ThrowIfInvalid(prng, 3);

        // encrypted message
        StringSource ss1(message, true, new PK_EncryptorFilter(prng, encryptor, new StringSink(encryptedMessage) ) );
        cout<<"encrypted msg: "<<encryptedMessage<<"  and its length: "<<encryptedMessage.length()<<endl;
    }
    catch(const CryptoPP::Exception& ex)
    {
        std::cerr << ex.what() << endl;
    }

    return encryptedMessage;
}
11
jww On

The problem I am having is you don't appear to know what you have, and the some of the parameters you are using are wrong when taken with the other parameters. So its pretty much a stab in the dark.


First, you should wrap the disk operations in a try/catch. I/O can always cause problems, so be sure to catch exceptions related to the iostream stuff. You should also catch the Crypto++ Exception related to key loading. That will handle the "crash" with no information.

So your code might look something like:

try
{
    // Read key from disk, load it into Crypto++ object
}
catch(const Exception& ex)
{
    cerr << "Caught Crypto++ exception " << ex.what() << endl;
}
catch(const std::runtime_error& ex)
{
    cerr << "Caught C++ runtime error " << ex.what() << endl;
}

Second, this looks like a private exponent, and not a private key:

std::string  privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";

And its too big to be in P-128. Maybe you should do something like:

try
{
    AutoSeededRandomPool prng;

    std::string exponent="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
    StringSource ss(exponent, true /*pumpAll*/, new HexDecoder);

    Integer x;
    x.Decode(ss, ss.MaxRetrievable(), Integer::UNSIGNED);        
    // cout << "Exponent: " << std::hex << x << endl;

    ECIES<ECP>::Decryptor decryptor;
    decryptor.AccessKey().Initialize(ASN1::secp256r1(), x);

    bool valid = decryptor.AccessKey().Validate(prng, 3);
    if(!valid)
        throw  Exception(Exception::OTHER_ERROR, "Exponent is not valid for P-256");

    // Or: decryptor.AccessKey().ThrowIfInvalid(prng, 3);

    cout << "Exponent is valid for P-256" << endl;        
}
catch(const Exception& ex)
{
    cerr << ex.what() << endl;
}

Or, you can:

ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp256r1());
decryptor.AccessKey().SetPrivateExponent(x);

If you add the following to the program above:

// Encode key, use OID versus domain paramters
string encoded;
HexEncoder encoder(new StringSink(encoded));

decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(encoder);

cout << "Private key: " << encoded << endl;

You will get the following for the private key:

$ ./ecies-test.exe
Exponent: 2c200102c180f9e6a4e7a2f58b5be86bc179478h
Private key: 3041020100301306072A8648CE3D020106082A8648CE3D030107042730250201010
42000000000000000000000000002C200102C180F9E6A4E7A2F58B5BE86BC179478

As you can see, the key is not "02C200102C180F9E6A4E7A2F58B5BE86BC179478".

The 12 leading 0's look suspicious to me. Though the exponent validates, you should verify the exponent and the field. The closest fit I could find is the curve secp160r2 (of course, curves like secp192k1 and secp224k1 work too).

The private key above is the hex encoding of ecies.priv.der shown below.


Third, this could be a public point in compressed form due to the leading 02.

std::string  privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";

If that is the case, then you are supposed to be able to do this, but I can't get it to decode the point (see Minimizing Key Size for Persistence on the wiki). x and y are 0 after the operation; maybe the problem is with the field:

std::string public_point="02C200102C180F9E6A4E7A2F58B5BE86BC179478";
StringSource ss(public_point, true, new HexDecoder);

ECIES<ECP>::Encryptor encryptor;
encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp128r1());

ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "X: " << std::hex << point.x << endl;
cout << "Y: " << std::hex << point.y << endl;

encryptor.AccessKey().SetPublicElement(point);
encryptor.AccessKey().ThrowIfInvalid(prng, 3);

Fourth, you should probably save the entire key, and not just the exponent. Here's a program for you that shows you how to save and load the keys. It also shows you how to perform encryption and decryption in one-liners.

/////////////////////////////////////////////////
// Part one - generate keys

ECIES<ECP>::Decryptor decryptor(prng, ASN1::secp256r1());
ECIES<ECP>::Encryptor encryptor(decryptor);

/////////////////////////////////////////////////
// Part two - save keys

FileSink fs1("ecies.priv.der", true /*binary*/);
decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(fs1);

FileSink fs2("ecies.pub.der", true /*binary*/);
encryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
encryptor.GetKey().Save(fs2);

/////////////////////////////////////////////////
// Part three - encrypt/decrypt

string message, encrypted, recovered;

if(argc >= 2 && argv[1] != NULL)
    message = argv[1];
else
    message = "Attack at dawn!";

StringSource ss1 (message,   true /*pumpAll*/, new PK_EncryptorFilter(prng, encryptor, new StringSink(encrypted)));
StringSource ss2 (encrypted, true /*pumpAll*/, new FileSink("ecies.encrypted.bin", true /*binary*/));
StringSource ss3 (encrypted, true /*pumpAll*/, new PK_DecryptorFilter(prng, decryptor, new StringSink(recovered)));

cout << recovered << endl;

Here is what a private key looks like from the test program above. Notice it has the field encoded into the structure so you don't have to guess at it (P-256 versus P-128 versus P-521).

$ dumpasn1 ecies.priv.der 
  0  65: SEQUENCE {
  2   1:   INTEGER 0
  5  19:   SEQUENCE {
  7   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 16   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
 26  39:   OCTET STRING, encapsulates {
 28  37:     SEQUENCE {
 30   1:       INTEGER 1
 33  32:       OCTET STRING
       :         00 00 00 00 00 00 00 00 00 00 00 00 02 C2 00 10
       :         2C 18 0F 9E 6A 4E 7A 2F 58 B5 BE 86 BC 17 94 78
       :       }
       :     }
       :   }

And the public key:

$ dumpasn1 ecies.pub.der 
  0  89: SEQUENCE {
  2  19:   SEQUENCE {
  4   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 13   8:     OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
       :     }
 23  66:   BIT STRING
       :     04 08 9B D2 1C 3A DC 08 8B 1F F1 D0 F4 97 A0 87
       :     FE 4F 78 EA E2 B8 30 B8 E7 06 37 68 27 4C 71 CD
       :     63 C3 E2 90 66 64 2B 1C F6 79 00 36 AF 72 4C 61
       :     69 FA E9 06 00 9A 15 32 0B 85 B5 88 B2 C5 88 46
       :     5E
       :   }

Crypto++ has a wiki page on ECIES. See Elliptic Curve Integrated Encryption Scheme. They also have Bouncy Castle interop workarounds.


You can also PEM encode the keys, but you need a patch to do it because its not part of the library. For the patch, see PEM Pack on the Crypto++ wiki.

0
jww On

I'm going to add another answer to show you how to serialize private exponents and public points in case you had trouble with the public points. It also shows you how to Save the PrivateKeyInfo and SubjectPublicKeyInfo.

Its produces output similar to below. You will need a patch for the Base64URLEncoder. its not part of the library.

$ ./ecies-test.exe
Private exponent
  Hex: 57E91FA3EF48706D07E56D8CB566204A4416B833EFB9687D75A37D572EC42277
  Base64: V+kfo+9IcG0H5W2MtWYgSkQWuDPvuWh9daN9Vy7EInc=
  Base64 (URL safe): V-kfo-9IcG0H5W2MtWYgSkQWuDPvuWh9daN9Vy7EInc=
Pubic point
  Hex: 037142DE6143B6AD44C74135FE71222AC1406F541E53CB635112DE4928EC94763C
  Base64: A3FC3mFDtq1Ex0E1/nEiKsFAb1QeU8tjURLeSSjslHY8
  Base64 (URL safe): A3FC3mFDtq1Ex0E1_nEiKsFAb1QeU8tjURLeSSjslHY8
Private key (PrivateKeyInfo)
  3059301306072A8648CE3D020106082A8648CE3D030107034200047142DE6143B6AD44C74135FE71
  222AC1406F541E53CB635112DE4928EC94763CFA903D9282691AE47A2D718297465EF44E905A89ED
  2D4553ED1DF906A6E2383B
Public key (SubjectPublicKeyInfo)
  3041020100301306072A8648CE3D020106082A8648CE3D03010704273025020101042057E91FA3EF
  48706D07E56D8CB566204A4416B833EFB9687D75A37D572EC42277

With the private exponent and public point above, the following works just fine:

string pub_point("A7EDDUXAA4/6kOZ8H+firJ95YtKZvDrPFmyVoisyBfuW");
StringSource ss(pub_point, true, new Base64Decoder);

ECIES<ECP>::Encryptor encryptor;
encryptor.AccessKey().AccessGroupParameters().Initialize(ASN1::secp256r1());

ECP::Point point;
encryptor.GetKey().GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());

encryptor.AccessKey().SetPublicElement(point);
encryptor.AccessKey().ThrowIfInvalid(prng, 3);

ECIES<ECP>::Decryptor decryptor;
decryptor.AccessKey().Initialize(prng, ASN1::secp256r1());

const Integer& priv_exp = decryptor.GetKey().GetPrivateExponent();
SecByteBlock x(priv_exp.MinEncodedSize());
priv_exp.Encode(x, x.size());

string s1, s2, s3;    
HexEncoder f1(new StringSink(s1));
Base64Encoder f2(new StringSink(s2));
Base64URLEncoder f3(new StringSink(s3));

ChannelSwitch cs1;
cs1.AddDefaultRoute(f1);
cs1.AddDefaultRoute(f2);
cs1.AddDefaultRoute(f3);

ArraySource as1(x, x.size(), true /*pumpAll*/, new Redirector(cs1));

cout << "Private exponent" << endl;
cout << "  Hex: " << s1 << endl;
cout << "  Base64: " << s2 << endl;
cout << "  Base64 (URL safe): " << s3 << endl;

//////////////////////////////////////////

ECIES<ECP>::Encryptor encryptor(decryptor);
ECP::Point pub_point = encryptor.GetKey().GetGroupParameters().ExponentiateBase(priv_exp);
SecByteBlock y(encryptor.GetKey().GetGroupParameters().GetCurve().EncodedPointSize(true /*compressed*/));
encryptor.GetKey().GetGroupParameters().GetCurve().EncodePoint(y, pub_point, true /*compressed*/);

string s4, s5, s6;    
HexEncoder f4(new StringSink(s4));
Base64Encoder f5(new StringSink(s5));
Base64URLEncoder f6(new StringSink(s6));

ChannelSwitch cs2;
cs2.AddDefaultRoute(f4);
cs2.AddDefaultRoute(f5);
cs2.AddDefaultRoute(f6);

ArraySource as2(y, y.size(), true /*pumpAll*/, new Redirector(cs2));

cout << "Pubic point" << endl;
cout << "  Hex: " << s4 << endl;
cout << "  Base64: " << s5 << endl;
cout << "  Base64 (URL safe): " << s6 << endl;

//////////////////////////////////////////

string s10, s11;
HexEncoder hex1(new StringSink(s10));
HexEncoder hex2(new StringSink(s11));

encryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
encryptor.GetKey().Save(hex1);
decryptor.AccessKey().AccessGroupParameters().SetEncodeAsOID(true);
decryptor.GetKey().Save(hex2);

cout << "Private key" << endl;
cout << s10 << endl;

cout << "Public key" << endl;
cout << s11 << endl;