9

我正在尝试编写 Ruby 代码来检查我在此处找到的特定消息的椭圆曲线数字签名算法 (ECDSA) 签名。

问题是我不知道如何将公钥的八位字节字符串转换为OpenSSL::PKey::EC::Point对象。如果我在 C 中编写此代码,我只需将八位字节字符串传递给 OpenSSL 的o2i_ECPublicKey,它的功能与我想要的很接近,实际上已被参考实现使用。但是,我搜索了Ruby (MRI) 的源代码,它不包含对的调用,o2i_ECPublicKey所以我不知道如何在不编写 C 扩展的情况下从 Ruby 中使用该函数。

这是八位字节字符串,以十六进制表示。它只是一个 0x04 字节,后跟两个 32 字节整数,表示椭圆曲线上点的 x 和 y 坐标:

04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284

那么有人知道如何将该字符串转换为OpenSSL::PKey::EC::PointRuby 中的 in 吗?获得点对象后,我将在以下代码中使用它,我相信这将验证签名:

key = OpenSSL::PKey::EC.new('secp256k1')
key.public_key = point
result = key.dsa_verify_asn1(digest, signature)

更新:

感谢 Jay-Ar Polidario,我得到了它的工作。这是我使用 OpenSSL 验证签名的完整代码。我还编写了一个名为ecdsa的 gem ,并包含了显示如何使用我的 gem 来做同样事情的代码。

# coding: ASCII-8BIT

digest =
  "\xbf\x91\xfb\x0b\x4f\x63\x33\x77\x4a\x02\x2b\xd3\x07\x8e\xd6\xcc" \
  "\xd1\x76\xee\x31\xed\x4f\xb3\xf9\xaf\xce\xb7\x2a\x37\xe7\x87\x86"

signature_der_string =
  "\x30\x45" \
  "\x02\x21\x00" \
  "\x83\x89\xdf\x45\xf0\x70\x3f\x39\xec\x8c\x1c\xc4\x2c\x13\x81\x0f" \
  "\xfc\xae\x14\x99\x5b\xb6\x48\x34\x02\x19\xe3\x53\xb6\x3b\x53\xeb" \
  "\x02\x20" \
  "\x09\xec\x65\xe1\xc1\xaa\xee\xc1\xfd\x33\x4c\x6b\x68\x4b\xde\x2b" \
  "\x3f\x57\x30\x60\xd5\xb7\x0c\x3a\x46\x72\x33\x26\xe4\xe8\xa4\xf1"

public_key_octet_string =
  "\x04" \
  "\xfc\x97\x02\x84\x78\x40\xaa\xf1\x95\xde\x84\x42\xeb\xec\xed\xf5" \
  "\xb0\x95\xcd\xbb\x9b\xc7\x16\xbd\xa9\x11\x09\x71\xb2\x8a\x49\xe0" \
  "\xea\xd8\x56\x4f\xf0\xdb\x22\x20\x9e\x03\x74\x78\x2c\x09\x3b\xb8" \
  "\x99\x69\x2d\x52\x4e\x9d\x6a\x69\x56\xe7\xc5\xec\xbc\xd6\x82\x84"

# Verifying with openssl.
require 'openssl'
ec = OpenSSL::PKey::EC.new('secp256k1')
key_bn = OpenSSL::BN.new(public_key_octet_string, 2)  # 2 means binary
ec.public_key = OpenSSL::PKey::EC::Point.new(ec.group, key_bn)
result = ec.dsa_verify_asn1(digest, signature_der_string)
puts result  # => true

# Verifying with the new ECDSA gem I wrote, version 0.1.5
require 'ecdsa'
group = ECDSA::Group::Secp256k1
point = ECDSA::Format::PointOctetString.decode(public_key_octet_string, group)
signature = ECDSA::Format::SignatureDerString.decode(signature_der_string)
result = ECDSA.valid_signature?(point, digest, signature)
puts result  # => true

我认为 OpenSSL 让您将公钥暂时表示为单个 BN(大数字)很奇怪,因为它实际上是两个大数字。我的 gem 可以直接将八位字节字符串(在 SEC2 标准中定义)转换为ECDSA::Point对象。

4

2 回答 2

7

尝试以下(测试没有错误):

key =  '04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284'
key_bn = OpenSSL::BN.new(key, 16) #Input: 16=Hexa, Output: BigNumber
group = OpenSSL::PKey::EC::Group.new('secp256k1')

point = OpenSSL::PKey::EC::Point.new(group, key_bn)
#--> <OpenSSL::PKey::EC::Point:0x5288178>
于 2014-03-18T10:06:12.937 回答
2

OpenSSL 允许您使用组和 bignum 创建一个,所以我尝试了这个:

require 'openssl'
include OpenSSL
group = PKey::EC::Group.new('secp256k1')
bignum = BN.new('04fc9702847840...')
point = PKey::EC::Point.new(group, bignum)

但它提高了OpenSSL::PKey::EC::Point::Error: invalid encoding。我不太确定如何从这里对其进行故障排除,但希望这对您有所帮助。

于 2014-03-13T07:48:27.527 回答