英文:
ECDSA signature verification: Go vs OpenSSL
问题
我正在尝试使用公钥验证一个哈希的ECDSA签名。我已经编写了一个小的Go程序成功地完成了这个任务,但是我无法将其移植到C++中。
这是我的输入数据:
- 公钥:
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
- 哈希:
562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469
- 签名:
304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7
这是我工作正常的Go程序:
package main
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
)
func main() {
key_, _ := base64.StdEncoding.DecodeString("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==")
key, _ := x509.ParsePKIXPublicKey(pkey_)
msg, _ := hex.DecodeString("562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469")
sig, _ := hex.DecodeString("304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7")
valid := ecdsa.VerifyASN1(key.(*ecdsa.PublicKey), msg[:], sig)
if !valid {
panic("key invalid")
}
}
这是我有问题的C++程序,在最后一个断言处失败。我想知道为什么会这样:
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
std::string decode_hex_str(std::string const &hex) {
std::string str;
for (std::size_t i { 0 }; i < hex.size(); i += 2) {
auto byte { hex.substr(i,2) };
str.push_back(std::strtol(byte.c_str(), nullptr, 16));
}
return str;
}
int main() {
std::string key_ {
R"(
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
-----END PUBLIC KEY-----
)"
};
std::string msg { decode_hex_str("562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469") };
std::string sig { decode_hex_str("304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7") };
auto bio { BIO_new_mem_buf(key_.data(), key_.length()) };
assert(bio);
auto key { PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr) };
EVP_MD_CTX *mdctx { nullptr };
EVP_PKEY *pkey { nullptr };
int status;
bool verified;
mdctx = EVP_MD_CTX_new();
assert(mdctx);
pkey = EVP_PKEY_new();
assert(pkey);
assert(EVP_PKEY_assign_EC_KEY(pkey, key) == 1);
assert(EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, pkey) == 1);
assert(EVP_DigestVerifyUpdate(mdctx, msg.data(), msg.length()) == 1);
status = EVP_DigestVerifyFinal(mdctx, reinterpret_cast<unsigned char const *>(&sig.data()), sig.length());
switch (status) {
case 0:
verified = false;
break;
case 1:
verified = true;
break;
default:
assert(false);
}
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(pkey);
assert(verified);
}
英文:
I'm trying to verify an ECDSA signature for a hash using a public key. I've written a small Go program that successfully does this but I've been unable to port it to C++.
Here is my input data:
- Public key:
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
- Hash:
562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469
- Signature:
304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7
Here is my working go program:
package main
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
)
func main() {
key_, _ := base64.StdEncoding.DecodeString("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==")
key, _ := x509.ParsePKIXPublicKey(pkey_)
msg, _ := hex.DecodeString("562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469")
sig, _ := hex.DecodeString("304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7")
valid := ecdsa.VerifyASN1(key.(*ecdsa.PublicKey), msg[:], sig)
if !valid {
panic("key invalid")
}
}
And here is my broken C++ program that fails at the very last assertion. I'd like to know why that is:
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
std::string decode_hex_str(std::string const &hex) {
std::string str;
for (std::size_t i { 0 }; i < hex.size(); i += 2) {
auto byte { hex.substr(i,2) };
str.push_back(std::strtol(byte.c_str(), nullptr, 16));
}
return str;
}
int main() {
std::string key_ {
R"(
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
-----END PUBLIC KEY-----
)"
};
std::string msg { decode_hex_str("562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469") };
std::string sig { decode_hex_str("304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7") };
auto bio { BIO_new_mem_buf(key_.data(), key_.length()) };
assert(bio);
auto key { PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr) };
EVP_MD_CTX *mdctx { nullptr };
EVP_PKEY *pkey { nullptr };
int status;
bool verified;
mdctx = EVP_MD_CTX_new();
assert(mdctx);
pkey = EVP_PKEY_new();
assert(pkey);
assert(EVP_PKEY_assign_EC_KEY(pkey, key) == 1);
assert(EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha256(), nullptr, pkey) == 1);
assert(EVP_DigestVerifyUpdate(mdctx, msg.data(), msg.length()) == 1);
status = EVP_DigestVerifyFinal(mdctx, reinterpret_cast<unsigned char const *>(sig.data()), sig.length());
switch (status) {
case 0:
verified = false;
break;
case 1:
verified = true;
break;
default:
assert(false);
}
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(pkey);
assert(verified);
}
答案1
得分: 2
EVP_DigestVerify{Init/Update/Final}
执行两个操作:
- 它们对输入数据进行哈希处理
- 然后它们验证计算得到的哈希与签名是否匹配
而ecdsa.VerifyASN1
只执行上述步骤中的第二步,已经将哈希作为输入。
执行这个操作的OpenSSL函数是ECDSA_verify
。
所以你可以这样做:
BIO* bio = BIO_new_mem_buf(key_.data(), key_.length());
EC_KEY* key = PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr);
int verified = ECDSA_verify(
0,
(unsigned char const*)msghash.data(), msghash.length(),
(unsigned char const*)sig.data(), sig.length(),
key
);
std::cout << verified << std::endl;
应该输出 1
。
请注意,你代码中的变量命名有些混淆 - 变量 msg
实际上包含的是消息的摘要。因此,msghash
或 digest
是更好的命名。在进行加密操作时,变量命名的准确性很重要。
英文:
EVP_DigestVerify{Init/Update/Final}
do two things:
- They hash the input data
- Then they verify the computed hash against the signature
ecdsa.VerifyASN1
on the other hand does only the second step from the above, already taking in the hash as input.
The OpenSSL function that does that is ECDSA_verify
.
So you can do something like:
BIO* bio = BIO_new_mem_buf(key_.data(), key_.length());
EC_KEY* key = PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr);
int verified = ECDSA_verify(
0,
(unsigned char const*)msghash.data(), msghash.length(),
(unsigned char const*)sig.data(), sig.length(),
key
);
std::cout << verified << std::endl;
Should print 1
.
Note that the variable naming in your code is confusing - the variable msg
actually contains the message digest. So msghash
or digest
would be a better name for it. It is important to be precise in variable naming when doing crypto.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论