c++ - ECDSA签名验证: Go vs OpenSSL

标签 c++ go openssl cryptography ecdsa

我正在尝试使用公钥验证哈希的 ECDSA 签名。我编写了一个小型 Go 程序,成功地做到了这一点,但我无法将其移植到 C++。

这是我的输入数据:

  • 公钥:MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDd9VXmpHjV4voFO+0ZoFPlRr5icZXquxsr9EkaOUO9B7Wl1DAgGI0EKCm1++Bl2Od32xZFeFuG07OTpTMVOCPA==
  • 哈希值:562d6ddfb3ceb5abb12d97bc35c4963d249f55b7c75eda618d365492ee98d469
  • 签名:304502204d6d070117d445f4c2fcdbd4df037a1c8cfee2a166353c2e562cd5efd06e914d022100bb06439ded1478bd19022519dc06a84ba18ea4bf30ea9eb9ea9 0f8b66dad12c7

这是我的工作程序:

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);
}

最佳答案

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 实际上包含消息digest。所以 msghashdigest 将是一个更好的名称。在进行加密时,变量命名的精确非常重要。

关于c++ - ECDSA签名验证: Go vs OpenSSL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70973923/

相关文章:

c++ - 使用 OpenCV 2.4.3 的双线性插值的 VideoCapture 旋转

c# - 手工翻译代码的技巧

c++ - 什么事情(或在什么情况下)可以使 C++ 比 C 慢?

go - 访问函数内部结构的指针值

python - 扭曲地将证书传递给 ssl 处理程序

c++ - 我如何检测 std::thread 被终止为异步?

optimization - 如何在不实际创建结构的情况下获得结构的 reflect.Type 实例?

arrays - 为什么不能超过 *[]Struct?

c - 具有ECDHE key 和证书的服务器不起作用

openssl - 在 Bash 中验证 PEM 证书有错误,但返回代码正常