OpenSSL无法验证ECDSA签名C++,C#验证正确
OpenSSL cannot verify ECDSA signature C++, C# verifies correctly
我正在传递以下内容(digest/hash 是 SHA1):
hash = HexToBytes("9E712647173B435CF691537A76C2F1423E4A18ED");
signature = Base64ToBytes("ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==");
pubKey16 = "04C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A";//Actual key lacks 04 at front but I'm guessing OpenSSL needs this?
到这个函数:
int Misc::verify_signature(unsigned char* hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
//Load our public key
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
//Set S
if (NULL == BN_bin2bn(&signature[0] + 32, 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
int sig_size = i2d_ECDSA_SIG(ec_sig, NULL);
unsigned char *sig_bytes =(unsigned char *) malloc(sig_size);
unsigned char *p;
printf("Orig Sig Size: %d\n", sig_size);
p = sig_bytes;
int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
printf("New Sig Size: %d\n", new_sig_size);
int verify_status = ECDSA_do_verify(hash, 20, ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if(verify_status==-1)handleErrors();
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}
但我无法验证 OpenSSL 中的签名(verify_success 为 0),即使完全相同的数据在 C# 中验证成功。
关于我做错的原因或地方有什么想法吗?
C#中的public键是:
4543533120000000C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A
我假设 4543533120000000 是 .NET 特定的东西,所以我只是将 04 添加到其余部分。
这是用于验证签名的 C# 代码,它成功地验证了(dataBytes 的 SHA1 在两个程序中是相同的)..
HashAlgorithm hashMan2 = new SHA1Managed();
byte[] dataBytes = hashMan2.ComputeHash(Encoding.ASCII.GetBytes("H4sIAAAAAAAEADPQMQBCQzBJDsSm0xCMDTFUYYpQAjFNAIsAAOvFhT3RAAAA"));
String sig = "ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==";
byte[] readPublicKey2 = Convert.FromBase64String("RUNTMSAAAADC0Khow19HUgi2wzpY1KwnUZDxqdWARFb/B8QmBXFu90j7T9JGFj6FHb6alCVpdB9UNBp8hfOUsgd3q3/lJglq");
Console.WriteLine("Public key file is read as:");
Console.WriteLine(Convert.ToBase64String(readPublicKey2));
using (ECDsaCng ecsdKey = new ECDsaCng(CngKey.Import(readPublicKey2, CngKeyBlobFormat.EccPublicBlob)))
{
if (ecsdKey.VerifyData(dataBytes, Convert.FromBase64String(sig)))
{
Console.WriteLine("Data and Signature have been verified.");
}
else
{
Console.WriteLine("Data and Signature could not be verified!");
}
}
感谢任何帮助。
我发现了这个问题,我使用的是 .NET 函数 ECDSA.SignData,但这实际上在输入之前对数据进行了哈希处理(使用 ECDsaCng.HashAlgorithm),我假设它正在将哈希作为一个输入,但正确的函数是 ECDSA.SignHash,我已经切换到 SignHash 并且我的新 signature/message 验证正确。 (请注意,这可能因您的 .NET 版本而异,请务必检查您的版本的 API)
如果它对某人有帮助,这是我的草稿功能(这里还有一些 extra/unneeded 的东西可能对你有帮助):
int Misc::verify_signature(std::vector<unsigned char> hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
printf("Raw PubKey Bytes: \n");
for (unsigned char t : pubKeyVC) {
printf("%d\n", t);
}
printf("Raw PubKey Length:%d \n", pubKeyVC.size());
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
////Try to pad S
//std::vector<unsigned char> sPadded = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
//sPadded.insert(sPadded.begin(), '0');
//sPadded.insert(sPadded.begin(), '0');
//Set S
if (NULL == BN_bin2bn(&signature[32], 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
std::vector<unsigned char> rValue = std::vector<unsigned char>(&signature[0], &signature[0] + 32);
std::vector<unsigned char> sValue = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
std::vector<unsigned char> derEncoded = std::vector<unsigned char>();
derEncoded.push_back(0x30);
//Push payload length into this position later
//Seperator
derEncoded.push_back(0x02);
//Length of rValue
if (rValue.at(0) >= 0x80) {
derEncoded.push_back(rValue.size() + 1);
}
else {
derEncoded.push_back(rValue.size());
}
//Push rValue bytes in
int c = 0;
for (unsigned char b : rValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Seperator
derEncoded.push_back(0x02);
//Length of sValue
if (sValue.at(0) >= 0x80) {
derEncoded.push_back(sValue.size() + 1);
}
else {
derEncoded.push_back(sValue.size());
}
//Push sValue bytes in
c = 0;
for (unsigned char b : sValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Insert payload length in
int len = derEncoded.size() - 1;
derEncoded.insert(derEncoded.begin() + 1, len);
printf("Encoded Sig Len: %d\n", derEncoded.size());
printf("Encoded Sig64: %s\n", Misc::base64_encode_d(&derEncoded).c_str());
//unsigned char *p = (unsigned char*)malloc(ECDSA_size(eckey));
//int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
//printf("New Sig Size: %d\n", new_sig_size);
//for (int x = 0; x < new_sig_size; x++) {
// printf("%d\n", p[x]);
//}
//Dump DER encoded sig
//printf("DER encoded signature\n");
//const unsigned char* pp = (unsigned char*) malloc(new_sig_size);
//d2i_ECDSA_SIG(&ec_sig, &pp, new_sig_size);
//std::vector<unsigned char> ppVC = std::vector<unsigned char>(pp, pp+new_sig_size);
//printf("Base64: %s\n", Misc::base64_encode_d(&ppVC).c_str());
//ECDSA_SIG *signature = ECDSA_do_sign(hash, 20, eckey);
//ECDSA_size(eckey);
int verify_status = ECDSA_verify(0, hash.data(), hash.size(), derEncoded.data(), derEncoded.size(), eckey);//ECDSA_do_verify(hash.data(), hash.size(), ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if (verify_status == -1)
{
handleErrors();
}
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}
我正在传递以下内容(digest/hash 是 SHA1):
hash = HexToBytes("9E712647173B435CF691537A76C2F1423E4A18ED");
signature = Base64ToBytes("ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==");
pubKey16 = "04C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A";//Actual key lacks 04 at front but I'm guessing OpenSSL needs this?
到这个函数:
int Misc::verify_signature(unsigned char* hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
//Load our public key
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
//Set S
if (NULL == BN_bin2bn(&signature[0] + 32, 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
int sig_size = i2d_ECDSA_SIG(ec_sig, NULL);
unsigned char *sig_bytes =(unsigned char *) malloc(sig_size);
unsigned char *p;
printf("Orig Sig Size: %d\n", sig_size);
p = sig_bytes;
int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
printf("New Sig Size: %d\n", new_sig_size);
int verify_status = ECDSA_do_verify(hash, 20, ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if(verify_status==-1)handleErrors();
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}
但我无法验证 OpenSSL 中的签名(verify_success 为 0),即使完全相同的数据在 C# 中验证成功。
关于我做错的原因或地方有什么想法吗?
C#中的public键是:
4543533120000000C2D0A868C35F475208B6C33A58D4AC275190F1A9D5804456FF07C42605716EF748FB4FD246163E851DBE9A942569741F54341A7C85F394B20777AB7FE526096A
我假设 4543533120000000 是 .NET 特定的东西,所以我只是将 04 添加到其余部分。
这是用于验证签名的 C# 代码,它成功地验证了(dataBytes 的 SHA1 在两个程序中是相同的)..
HashAlgorithm hashMan2 = new SHA1Managed();
byte[] dataBytes = hashMan2.ComputeHash(Encoding.ASCII.GetBytes("H4sIAAAAAAAEADPQMQBCQzBJDsSm0xCMDTFUYYpQAjFNAIsAAOvFhT3RAAAA"));
String sig = "ASLQ3wguSDkJCfFWE3kvBfp7BDNjdajl2ezIetR6DsiacFVASvEAw9v6S3IM0LnaqAV2BTe7eBcRmef/qb2/Hw==";
byte[] readPublicKey2 = Convert.FromBase64String("RUNTMSAAAADC0Khow19HUgi2wzpY1KwnUZDxqdWARFb/B8QmBXFu90j7T9JGFj6FHb6alCVpdB9UNBp8hfOUsgd3q3/lJglq");
Console.WriteLine("Public key file is read as:");
Console.WriteLine(Convert.ToBase64String(readPublicKey2));
using (ECDsaCng ecsdKey = new ECDsaCng(CngKey.Import(readPublicKey2, CngKeyBlobFormat.EccPublicBlob)))
{
if (ecsdKey.VerifyData(dataBytes, Convert.FromBase64String(sig)))
{
Console.WriteLine("Data and Signature have been verified.");
}
else
{
Console.WriteLine("Data and Signature could not be verified!");
}
}
感谢任何帮助。
我发现了这个问题,我使用的是 .NET 函数 ECDSA.SignData,但这实际上在输入之前对数据进行了哈希处理(使用 ECDsaCng.HashAlgorithm),我假设它正在将哈希作为一个输入,但正确的函数是 ECDSA.SignHash,我已经切换到 SignHash 并且我的新 signature/message 验证正确。 (请注意,这可能因您的 .NET 版本而异,请务必检查您的版本的 API)
如果它对某人有帮助,这是我的草稿功能(这里还有一些 extra/unneeded 的东西可能对你有帮助):
int Misc::verify_signature(std::vector<unsigned char> hash, std::vector<unsigned char> signature, char* cPubKey16) {
printf("Signature length: %d\n", signature.size());
int function_status = -1;
EC_KEY *eckey = NULL;
EC_POINT *pub_key;
const EC_GROUP *ecgroup;
SSL_library_init();
SSL_load_error_strings();
std::string pubKeyS(cPubKey16);
std::vector<unsigned char> pubKeyVC = Misc::hexToBytes(pubKeyS);
printf("Raw PubKey Bytes: \n");
for (unsigned char t : pubKeyVC) {
printf("%d\n", t);
}
printf("Raw PubKey Length:%d \n", pubKeyVC.size());
const unsigned char* pubKeyVCp = pubKeyVC.data();
const unsigned char** pubKeyVCpp = &pubKeyVCp;
//NID_secp256k1 is not r1 which is what .NET uses
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
eckey = o2i_ECPublicKey(&eckey, pubKeyVCpp, pubKeyVC.size());
if (!EC_KEY_check_key(eckey)) {
printf("EC_KEY_check_key failed:\n");
printf("%s\n", ERR_error_string(ERR_get_error(), NULL));
}
else {
printf("Public key verified OK\n");
}
//Create the properly formatted signature
ECDSA_SIG* ec_sig = ECDSA_SIG_new();
//Split signature into R and S value
//Set R
if (NULL == BN_bin2bn(&signature[0], 32, (ec_sig->r))) {
printf("Failed to set R value in EC Signature\n");
function_status = -1;
}
printf("post r :%s\n", BN_bn2hex(ec_sig->r));
////Try to pad S
//std::vector<unsigned char> sPadded = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
//sPadded.insert(sPadded.begin(), '0');
//sPadded.insert(sPadded.begin(), '0');
//Set S
if (NULL == BN_bin2bn(&signature[32], 32, (ec_sig->s))) {
printf("Failed to set S value in EC Signature\n");
function_status = -1;
}
printf("post s :%s\n", BN_bn2hex(ec_sig->s));
//Encode the signature
std::vector<unsigned char> rValue = std::vector<unsigned char>(&signature[0], &signature[0] + 32);
std::vector<unsigned char> sValue = std::vector<unsigned char>(&signature[32], &signature[32] + 32);
std::vector<unsigned char> derEncoded = std::vector<unsigned char>();
derEncoded.push_back(0x30);
//Push payload length into this position later
//Seperator
derEncoded.push_back(0x02);
//Length of rValue
if (rValue.at(0) >= 0x80) {
derEncoded.push_back(rValue.size() + 1);
}
else {
derEncoded.push_back(rValue.size());
}
//Push rValue bytes in
int c = 0;
for (unsigned char b : rValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Seperator
derEncoded.push_back(0x02);
//Length of sValue
if (sValue.at(0) >= 0x80) {
derEncoded.push_back(sValue.size() + 1);
}
else {
derEncoded.push_back(sValue.size());
}
//Push sValue bytes in
c = 0;
for (unsigned char b : sValue) {
if (b >= 0x80 && c == 0) {
derEncoded.push_back(0);
}
derEncoded.push_back(b);
c++;
}
//Insert payload length in
int len = derEncoded.size() - 1;
derEncoded.insert(derEncoded.begin() + 1, len);
printf("Encoded Sig Len: %d\n", derEncoded.size());
printf("Encoded Sig64: %s\n", Misc::base64_encode_d(&derEncoded).c_str());
//unsigned char *p = (unsigned char*)malloc(ECDSA_size(eckey));
//int new_sig_size = i2d_ECDSA_SIG(ec_sig, &p);
//printf("New Sig Size: %d\n", new_sig_size);
//for (int x = 0; x < new_sig_size; x++) {
// printf("%d\n", p[x]);
//}
//Dump DER encoded sig
//printf("DER encoded signature\n");
//const unsigned char* pp = (unsigned char*) malloc(new_sig_size);
//d2i_ECDSA_SIG(&ec_sig, &pp, new_sig_size);
//std::vector<unsigned char> ppVC = std::vector<unsigned char>(pp, pp+new_sig_size);
//printf("Base64: %s\n", Misc::base64_encode_d(&ppVC).c_str());
//ECDSA_SIG *signature = ECDSA_do_sign(hash, 20, eckey);
//ECDSA_size(eckey);
int verify_status = ECDSA_verify(0, hash.data(), hash.size(), derEncoded.data(), derEncoded.size(), eckey);//ECDSA_do_verify(hash.data(), hash.size(), ec_sig, eckey);
printf("Verify status: %d\n", verify_status);
const int verify_success = 1;
if (verify_success != verify_status)
{
if (verify_status == -1)
{
handleErrors();
}
printf("Failed to verify EC Signature\n");
function_status = -1;
}
else
{
printf("Verifed EC Signature\n");
function_status = 1;
}
//EC_GROUP_free(ecgroup);//Might fail as Ecgroup is constant TODO
EC_KEY_free(eckey);
return function_status;
}