在给定曲线和私有指数的情况下创建 ECDSA 私钥?

Posted

技术标签:

【中文标题】在给定曲线和私有指数的情况下创建 ECDSA 私钥?【英文标题】:Creation of ECDSA private key given curve and private exponent? 【发布时间】:2014-12-01 15:50:43 【问题描述】:

我是 cryptopp 新手,并且一直在为 ECDSA 签名创建私钥而苦苦挣扎。

我有一个十六进制编码的私有指数E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB。这被存储为一个字符串。

我想用它来使用 ECDSA 对文本块进行签名。我的代码看起来有点像这样

string Sig::genSignature(const string& privKeyIn, const string& messageIn)

   AutoSeededRandomPool prng;
   ECDSA<ECP, SHA256>::PrivateKey privateKey;
   privateKey.AccessGroupParameters().Initialize(ASN1::secp256r1());
   privateKey.Load(StringSource(privKeyIn, true, NULL).Ref());
   ECDSA<ECP, SHA256>::Signer signer(privateKey);

   // Determine maximum size, allocate a string with that size
   size_t siglen = signer.MaxSignatureLength();
   string signature(siglen, 0x00);

   // Sign, and trim signature to actual size
   siglen = signer.SignMessage(prng, (const byte *) messageIn.data(), (size_t) messageIn.length(),       (byte*)signature.data());
   signature.resize(siglen);
   cout << signature.data() << endl;
   return signature;

当我尝试执行 privateKey.load(...) 时,此代码在 Visual Studio 中生成以下错误

First-chance exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.
Unhandled exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.

我猜我在做一些有点愚蠢的事情......任何帮助都会很棒???

PS 我在使用 ECDH 生成 GMAC 时遇到了类似的问题,但通过将密钥保存为 SECByteBlock 来解决这个问题,但这种“技巧”在这种情况下似乎不起作用。

【问题讨论】:

“我在使用 ECDH 生成 GMAC 时遇到了类似的问题......” - 我不清楚 ECDH 和 GMAC 之间的交集在哪里。您可能想在Code Review Stack Exchange 上要求进行代码审查。 ECDH 用于创建共享密钥,然后用于创建用于 GMAC 生成的 AES 密钥。 【参考方案1】:

DLLTest.exe:Microsoft C++ 异常:CryptoPP::BERDecodeErr ...

你有一个私人指数,而没有一个私人密钥。所以你应该打电话给Load。这导致 Crypto++ BERDecodeErr 异常。

答案在ECDSA wiki page 上有详细说明,但并不明显。给定曲线和指数,您需要执行以下操作来初始化privateKey::

string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
exp.insert(0, "0x");

Integer x(exp.c_str());
privateKey.Initialize(ASN1::secp256r1(), x);

添加 "0x" 可确保 Integer 类正确解析 ASCII 字符串。您还可以将"h" 字符附加到字符串。你可以在StringToInteger函数的第2960行附近的Integer.cpp处看到Integer类的解析代码。


这是做同样事情的另一种方法:

string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";

HexDecoder decoder;
decoder.Put((byte*)exp.data(), exp.size());
decoder.MessageEnd();

Integer x;
x.Decode(decoder, decoder.MaxRetrievable());

privateKey.Initialize(ASN1::secp256r1(), x);

HexDecoder 将为您执行 ASCII 到二进制的转换。 HexDecoder 持有的缓冲区随后将由 Integer 使用其 Decode (BufferedTransformation &amp;bt, size_t inputLen, Signedness=UNSIGNED) 方法使用。


这是使用HexDecoder 的另一种方式(Crypto++ 有时与脚本语言一样糟糕 :)...

string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
StringSource ss(exp, true /*punpAll*/, new HexDecoder);

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

privateKey.Initialize(ASN1::secp256r1(), x);

初始化密钥后,您应该对其进行验证:

bool result = privateKey.Validate( prng, 3 );
if( !result )  /* Handle error */ 

这将输出二进制数据:

cout << signature.data() << endl;

如果您想要可打印/可读的内容,请通过 Crypto++ HexEncoder 运行它。

【讨论】:

感谢您的回答!!这很有效..我将为其他人发布我的最终代码..我想知道您是否可以帮助解决与公钥有关的类似问题? ***.com/questions/27315687/… @jww ,如果是私钥字符串而不是私钥指数字符串,那么如何生成私钥?我已经在这里发布了我的问题***.com/questions/30860777/…。请回复。【参考方案2】:

供以后寻找此内容的其他人使用

string genSignature(const string& privKeyIn, const string& messageIn)

    CryptoPP::Integer secretNumber(genSecretNumber(privKeyIn, messageIn));
    AutoSeededRandomPool secretNumberGenerator;

    if (encryptBase::debug)
    
        cout << "secret number: " << secretNumber << endl;
    

    SecByteBlock message(convertHexStrToSecByteBlock(messageIn));
    ECDSA<ECP, SHA256>::PrivateKey privateKey;
    string exp(privKeyIn);
    exp.insert(0, "0x");
    Integer x(exp.c_str());
    privateKey.Initialize(ASN1::secp256r1(), x);
    AutoSeededRandomPool prng;
    if (!privateKey.Validate(prng, 3))
    
        cout << "unable to verify key" << endl;
        return "failed to verify key";
    

    ECDSA<ECP, SHA256>::Signer signer(privateKey);
    size_t siglen = signer.MaxSignatureLength();
    string signature(siglen, 0x00);
    siglen = signer.SignMessage(secretNumberGenerator, message.BytePtr(), message.size(),     (byte*)signature.data());
    signature.resize(siglen);

    string encoded;
    HexEncoder encoder;
    encoder.Put((byte *) signature.data(), signature.size());
    encoder.MessageEnd();
    word64 size = encoder.MaxRetrievable();
    if (size)
    
        encoded.resize(size);
        encoder.Get((byte*)encoded.data(), encoded.size());
    

    return encoded;

【讨论】:

你不需要secretNumberGenerator,因为你有prng。您可以使用HexEncoder encoder(new StringSink(encoded)) 并删除代码以检索十六进制编码值。由于附加了StringSinkHexEncoder 会将结果放入字符串中。您仍然需要致电encoder.MessageEnd() 感谢您的建议。我会尽快更新代码。上面的代码不是最终版本,因为在“真实”应用程序中,密码不是随机的。它使用自定义类返回预先计算的值。

以上是关于在给定曲线和私有指数的情况下创建 ECDSA 私钥?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用椭圆曲线私钥和 ECDSA 算法签署证书?

如何在 C# 中加载私有 ecdsa 密钥 es256?

算法2_非对称加密算法之ECDSA(椭圆曲线数字签名算法)

使用带有曲线 secp224k1 的私钥签署 ECDSA

golang 椭圆曲线加密使用ecdsa

生成 256 位 ECDSA 私钥