ECDSA签名验证:Go vs OpenSSL

huangapple go评论85阅读模式
英文:

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 (
&quot;crypto/ecdsa&quot;
&quot;crypto/x509&quot;
&quot;encoding/base64&quot;
&quot;encoding/hex&quot;
)
func main() {
key_, _ := base64.StdEncoding.DecodeString(&quot;MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==&quot;)
key, _ := x509.ParsePKIXPublicKey(pkey_)
msg, _ := hex.DecodeString(&quot;562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469&quot;)
sig, _ := hex.DecodeString(&quot;304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7&quot;)
valid := ecdsa.VerifyASN1(key.(*ecdsa.PublicKey), msg[:], sig)
if !valid {
panic(&quot;key invalid&quot;)
}
}

And here is my broken C++ program that fails at the very last assertion. I'd like to know why that is:

#include &lt;cassert&gt;
#include &lt;cstdlib&gt;
#include &lt;string&gt;
#include &lt;stdexcept&gt;
#include &lt;openssl/bio.h&gt;
#include &lt;openssl/ec.h&gt;
#include &lt;openssl/err.h&gt;
#include &lt;openssl/evp.h&gt;
#include &lt;openssl/pem.h&gt;
std::string decode_hex_str(std::string const &amp;hex) {
std::string str;
for (std::size_t i { 0 }; i &lt; 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&quot;(
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
-----END PUBLIC KEY-----
)&quot;
};
std::string msg { decode_hex_str(&quot;562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469&quot;) };
std::string sig { decode_hex_str(&quot;304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea90f8b66dad12c7&quot;) };
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&lt;unsigned char const *&gt;(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 &lt;&lt; verified &lt;&lt; std::endl;

应该输出 1


请注意,你代码中的变量命名有些混淆 - 变量 msg 实际上包含的是消息的摘要。因此,msghashdigest 是更好的命名。在进行加密操作时,变量命名的准确性很重要。

英文:

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 &lt;&lt; verified &lt;&lt; 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.

huangapple
  • 本文由 发表于 2022年2月3日 23:25:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/70973923.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定