无法使用 C 风格的函数调用验证签名
Posted
技术标签:
【中文标题】无法使用 C 风格的函数调用验证签名【英文标题】:Cannot verify signature using C-style function calls 【发布时间】:2018-03-26 15:48:30 【问题描述】:以下打印失败,我不明白为什么:
#include <cryptopp/eccrypto.h>
#include <cryptopp/oids.h>
#include <cryptopp/osrng.h>
#include <iostream>
using namespace std;
using namespace CryptoPP;
int main()
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::PrivateKey private_key;
ECDSA<ECP, SHA256>::PublicKey public_key;
private_key.Initialize( prng, ASN1::secp160r1() );
private_key.MakePublicKey(public_key);
ECDSA<ECP, SHA256>::Signer signer(private_key);
ECDSA<ECP, SHA256>::Verifier verifier(public_key);
signer.AccessKey().Initialize(prng, ASN1::secp160r1());
string signature(signer.MaxSignatureLength(), 0);
string message = "asdf";
auto signature_length = signer.SignMessage(
prng, (const byte*)message.data(),
message.size(), (byte*)signature.data());
signature.resize(signature_length);
bool verified = verifier.VerifyMessage(
(const byte*)message.data(), message.size(),
(const byte*)signature.data(), signature.size());
if (verified)
cout << "PASS" << endl;
else
cout << "FAIL" << endl;
它遵循 crypto++ wiki 中的说明:https://www.cryptopp.com/wiki/ECDSA#Message_Signing,并使用从用于签署同一消息的私钥派生的公钥进行验证。我应该切换到过滤器吗?
【问题讨论】:
请修改您的源代码以创建更明确的错误消息,然后使用修改后的源代码和错误消息更新您的帖子。 你建议我怎么做? VerifyMessage 返回 bool,所以我不能说验证失败的程度。 我不知道你正在使用这个包,所以我不能给你任何关于获取更具体的错误信息的建议。但是,正如您所看到的,其他人也认为您的帖子缺少任何人提供帮助所需的信息。大多数软件包都有一些机制,用于在发生故障时获取附加信息。errno
和 GetLastError()
是一个经典的例子。
如果签名确实不是由相同的密钥(对)创建的,则无法验证加密签名并不表示(使用)软件出现错误,因此缺少错误消息对于这个功能。使用它的功能和设置是否实际上是我在这里要问的。
【参考方案1】:
以下打印失败,我不明白为什么:
你很接近。查看wiki页面有一些问题。首先,这是未定义的行为(已在 wiki 上修复):
auto signature_length = signer.SignMessage(
prng, (const byte*)message.data(),
message.size(), (byte*)signature.data());
要获得非常量指针,您需要它(但这不是问题的原因):
auto signature_length = signer.SignMessage(
prng, (const byte*)&message[0],
message.size(), (byte*)&signature[0]);
其次,当您调用Initialize
两次时,您会破坏旧配置。 “重击”意味着您生成新参数。实际上,您覆盖了另一个私钥:
private_key.Initialize( prng, ASN1::secp160r1() );
...
signer.AccessKey().Initialize(prng, ASN1::secp160r1());
这不是很明显,但是采用prng
的Initialize
会生成一个新密钥。你想要一个Initialize
,不接受prng
:
private_key.Initialize( prng, ASN1::secp160r1() );
...
signer.AccessKey().Initialize(private_key);
第三,页面不清楚如何在Signers/Verifiers和PublicKey/PrivateKey之间移动。为了便于说明,这里有一些其他的方法:
cryptopp $ cat test.cxx
#include "eccrypto.h"
#include "oids.h"
#include "osrng.h"
#include <string>
#include <iostream>
int main()
using namespace CryptoPP;
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::Signer signer;
ECDSA<ECP, SHA256>::Verifier verifier;
signer.AccessKey().Initialize(prng, ASN1::secp160r1());
signer.AccessKey().MakePublicKey(verifier.AccessKey());
std::string signature(signer.MaxSignatureLength(), 0);
std::string message = "asdf";
auto signature_length = signer.SignMessage(
prng, (const byte*)&message[0],
message.size(), (byte*)&signature[0]);
signature.resize(signature_length);
bool verified = verifier.VerifyMessage(
(const byte*)&message[0], message.size(),
(const byte*)&signature[0], signature.size());
if (verified)
std::cout << "PASS" << std::endl;
else
std::cout << "FAIL" << std::endl;
return 0;
我在 Crypto++ 目录下工作,所以包含和命令行有点不同:
cryptopp$ g++ -I . test.cxx ./libcryptopp.a -o test.exe
cryptopp$ ./test.exe
PASS
如果您想同时使用 Signers/Verifiers 和 PublicKey/PrivateKey,请尝试以下操作:
cryptopp$ cat test.cxx
#include "eccrypto.h"
#include "oids.h"
#include "osrng.h"
#include <string>
#include <iostream>
int main()
using namespace CryptoPP;
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::Signer signer;
ECDSA<ECP, SHA256>::Verifier verifier;
ECDSA<ECP, SHA256>::PrivateKey& sKey = signer.AccessKey();
sKey.Initialize(prng, ASN1::secp160r1());
ECDSA<ECP, SHA256>::PublicKey& pKey = verifier.AccessKey();
sKey.MakePublicKey(pKey);
std::string signature(signer.MaxSignatureLength(), 0);
std::string message = "asdf";
auto signature_length = signer.SignMessage(
prng, (const byte*)&message[0],
message.size(), (byte*)&signature[0]);
signature.resize(signature_length);
bool verified = verifier.VerifyMessage(
(const byte*)&message[0], message.size(),
(const byte*)&signature[0], signature.size());
if (verified)
std::cout << "PASS" << std::endl;
else
std::cout << "FAIL" << std::endl;
return 0;
这看起来有点不寻常:
ECDSA<ECP, SHA256>::Signer signer;
...
signer.AccessKey().Initialize(prng, ASN1::secp160r1());
通常您使用secp160r1, SHA1
或secp256k1, SHA256
。这维护了整个系统的Security Levels。当您使用secp160r1, SHA256
时,由于secp160r1
,您将安全级别降低到大约80 位。
【讨论】:
感谢您提供全面的答案和安全提示。看起来 C++17 也允许非常量 data() 访问,因此它不再是未定义的行为:en.cppreference.com/w/cpp/string/basic_string/data以上是关于无法使用 C 风格的函数调用验证签名的主要内容,如果未能解决你的问题,请参考以下文章
使用 mbedtls 生成的 RSA 签名,无法使用 C# (bouncycastle) 应用程序进行验证