【问题标题】:Use previously generated private key in ECIES在 ECIES 中使用先前生成的私钥
【发布时间】:2015-08-31 20:52:51
【问题描述】:

我想使用 ECIES 加密/解密数据,我正在为此使用 cryptopp。

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) ) );

其他一切都很好,但我想使用已经生成的“私钥”而不是随机生成的“私钥”来做上述同样的事情,这与上面的情况不同。我该怎么做?

我尝试了以下代码,但它只是崩溃了

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());

崩溃点

//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;

编辑 2

为了回应@jww 的回答,我设法用私钥将消息解码为:

  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;
}

但是当我尝试使用公钥加密消息时出现错误

CryptoPP::CryptoMaterial::InvalidMaterial: CryptoMaterial: 此对象包含无效值

这是我的代码:

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;

【问题讨论】:

  • “我尝试了以下代码,但它只是崩溃了......” - 什么是崩溃或错误?请说明您从何处获得私钥,以及他们告诉您的内容。
  • 在您的编辑中,您需要使用Base64Decoder,而不是HexDecoder
  • @jww 如何使用 iv(initialization vector) 将 nonce 添加到加密/解密部分?
  • 您不能/不设置 IV。 ECIES 提供密钥封装机制 (KEM) 所需的一切。实际上,在您的 EC 密钥下加密了一个大种子。种子材料被解密然后消化。然后使用种子生成密钥流。然后,密钥流与纯文本(加密)或密文(解密)进行异或运算。您能做的最多的事情就是为该过程中使用的 PRNG 播种。
  • @jww 我们必须一次又一次地使用相同的令牌,这就是为什么我们必须以某种方式包含 nonce。包含它的最佳方式是什么?

标签: c++ public-key-encryption crypto++ elliptic-curve


【解决方案1】:

我遇到的问题是您似乎不知道自己拥有什么,并且您使用的某些参数与其他参数一起使用时是错误的。所以它几乎是在黑暗中刺伤。


首先,您应该将磁盘操作包装在try/catch 中。 I/O 总是会导致问题,所以一定要捕获与iostream 相关的异常。您还应该捕获与密钥加载相关的 Crypto++ 异常。这将处理没有信息的“崩溃”。

所以你的代码可能看起来像:

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;
}

第二,这看起来像是一个私有指数,而不是一个私有密钥:

std::string  privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";

而且它太大了,不能在P-128 中。也许你应该这样做:

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;
}

或者,您可以:

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

如果在上面的程序中添加以下内容:

// 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;

您将获得以下私钥:

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

如您所见,密钥不是 "02C200102C180F9E6A4E7A2F58B5BE86BC179478"

前 12 个 0 对我来说看起来很可疑。虽然指数验证,但您应该验证指数和字段。我能找到的最接近的曲线是 secp160r2 曲线(当然,像 secp192k1secp224k1 这样的曲线也可以)。

上面的私钥是下图ecies.priv.der的十六进制编码。


第三,由于前导02,这可能是压缩形式的公共点。

std::string  privatekeyString="02C200102C180F9E6A4E7A2F58B5BE86BC179478";

如果是这种情况,那么你应该能够做到这一点,但我无法让它解码这一点(参见 wiki 上的Minimizing Key Size for Persistence)。 xy运算后为0;也许问题出在该领域:

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);

第四,您可能应该保存整个密钥,而不仅仅是指数。这是一个为您展示如何保存和加载密钥的程序。它还向您展示了如何在单行中执行加密和解密。

/////////////////////////////////////////////////
// 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;

这是上面测试程序中私钥的样子。请注意,它已将字段编码到结构中,因此您不必猜测它(P-256P-128P-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
       :       }
       :     }
       :   }

还有公钥:

$ 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++ 在 ECIES 上有一个 wiki 页面。见Elliptic Curve Integrated Encryption Scheme。他们还有 Bouncy Castle 互操作解决方法。


您也可以对密钥进行 PEM 编码,但您需要一个补丁来执行此操作,因为它不是库的一部分。有关补丁,请参阅 Crypto++ wiki 上的 PEM Pack

【讨论】:

  • 非常感谢您提供如此详细的答案。我所做的是我以十六进制格式而不是 base_64 格式提供了密钥。我使用的密钥是“AsIAECwYD55qTnovWLW+hrwXlHg=".
  • 但我无法使用公钥使其工作。我已经编辑了上面的代码,该代码用于解密但不适用于使用公钥加密
  • @Sandeep - 不,这也不起作用:std::string key="AsIAECwYD55qTnovWLW+hrwXlHg="; StringSource ss(key, true, new Base64Decoder); for P-128
  • 我使用了 HexDecoder 而不是 Base64Decoder,但密钥本身在 base_64 中,它为我工作。我不知道,我很困惑。让我考虑一下。
  • 正如你所建议的,我使用了 Base64Decoder,但它不适用于 P-128,但适用于 P-256,这很不寻常,因为我从服务器获取此私钥,而在服务器端我们使用的是 P- 128 曲线。
【解决方案2】:

我将添加另一个答案,向您展示如何序列化私有指数和公共点,以防您遇到公共点问题。它还向您展示了如何Save PrivateKeyInfoSubjectPublicKeyInfo

它产生类似于下面的输出。您将需要Base64URLEncoder 的补丁。它不是图书馆的一部分。

$ ./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

使用上面的私有指数和公共点,以下工作正常:

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;

【讨论】:

    【解决方案3】:

    正如jww 建议的那样,我已经成功完成了加密和解密。 如果有人愿意,下面是代码 sn-ps。

    解密

    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;
    }
    

    加密

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-16
      • 1970-01-01
      • 2011-03-25
      • 2018-10-07
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      • 2021-07-18
      • 1970-01-01
      相关资源
      最近更新 更多