【问题标题】:How to sign ECDSA signature and encode to DER format in hex in php?如何在 php 中以十六进制格式签署 ECDSA 签名并编码为 DER 格式?
【发布时间】:2021-11-07 22:39:52
【问题描述】:

如何使用secp256k1 (ECDSA) 密钥对包括ETH Keccak256 哈希地址签名的Tx 数据进行签名,并以十六进制编码为DER 格式并在php 中验证?

我只想使用附带的 php 库、openssl 和其他库来制作secp256k1 私钥和公钥、签名并转换为hex 中的DER 格式,以制作我的cryptcoin 样本。

以太坊地址由secp256k1 EDSA 密钥生成,用Keccak256 (sha-3-256) 哈希算法对由私钥生成的256 bits/64 character 公钥进行哈希计算以计算哈希,然后从哈希中取出最后40 个字符十六进制字符串,添加校验和,并将 x0 前缀添加到哈希字符串。

该示例在github.com/simplito/elliptic-php repo 中显示为toDER() 函数在elliptic-php/lib/EC/Signature.php。该库正在使用BN-php,但我只想使用php库,openssl等。

// https://github.com/simplito/elliptic-php ECDSA sample
<?php
use Elliptic\EC;
// Create and initialize EC context
// (better do it once and reuse it)
$ec = new EC('secp256k1');

// Generate keys
$key = $ec->genKeyPair();

// Sign message (can be hex sequence or array)
$msg = 'ab4c3451';
$signature = $key->sign($msg);

// Export DER encoded signature to hex string
$derSign = $signature->toDER('hex');

openssl_pkey_new()openssl_sign() 函数可以生成新的secp256k1 私钥和公钥并签名,但不能将hex 中的公钥转换为DER 格式以通过散列@987654351 生成地址@算法。

签署 Tx 哈希需要 JSON 字符串中的来自/到 ETH 地址 Tx 数据 {address:{to:address1,from:address2},amount:0,timestamp:timestamp tx, hash:sha256hash, previousHash:hash, difficulty:2...}openssl_pkey_new() openssl_pkey_export() 需要制作私钥和公钥才能制作 ETH 地址。

由于openssl_pkey_new() openssl_pkey_export() 用于OPENSSL_KEYTYPE_EC 返回DER bin 键,我需要将它们转换为十六进制。

我尝试过openssl_pkey_new()openssl_sign() 函数,但我无法通过简单函数转换为hex 中的DER 格式。

openssl_pkey_new() 函数创建新的secp256k1 密钥对,只需 显示在Generating the 256bit ECDSA private key question

$config = [
    "config" => getenv('OPENSSL_CONF'),
    'private_key_type' => OPENSSL_KEYTYPE_EC,
    'curve_name' => 'secp256k1'
];
$res = openssl_pkey_new($config);
if (!$res) {
    echo 'ERROR: Fail to generate private key. -> ' . openssl_error_string();
    exit;
}
// Generate Private Key
openssl_pkey_export($res, $priv_key, NULL, $config);
// Get The Public Key
$key_detail = openssl_pkey_get_details($res);
$pub_key = $key_detail["key"];

echo "priv_key:<br>".$priv_key;
echo "<br><br>pub_key:<br>".$pub_key;

Link

并用钥匙签名。

openssl_sign($hashdata, $signature, $private_key);

有什么办法可以通过 php 库将密钥转换为十六进制的DER

【问题讨论】:

  • openssl_sign for EC 创建 DER binary;要将二进制转换为十六进制,请参见 stackoverflow.com/questions/14674834/… 。我不知道您所说的用 \n 分割块或删除标题是什么意思;这些听起来像是 PEM 密钥 上的可能操作,它不是签名,也不是十六进制。
  • 它根本没有选项。格式取决于算法;对于 RSA,它由 PKCS1/RFC8017 定义的;对于 DSA 和 ECDSA,它 DER(两个 INT 的 SEQ)在几个地方定义;对于 EdDSA,它由 8032 定义。
  • 好的,谢谢。我将ECDSA 用于DER
  • 推荐的内置 bin2hex (在评论和答案中)和该链接的最高投票答案中的显式 strToHex 函数为我工作(在 7.4 中,在更正您的代码使 private_key 匹配 priv_key)。你一定是做错了什么,但由于你没有表现出你在做什么,我不知道问题出在哪里。
  • 现在您谈论的是密钥,而不是签名和以太坊地址,这不是您的问题。 Stackexchange 的设计目的不是作为聊天论坛或进行讨论或依赖于 cmets 中的添加,这些内容经常被删除;请阅读“欢迎”页面,即“旅游”。请把你的问题放在你的问题中。但是请考虑一下,以太坊地址根本不使用私钥或 DER,并且它们的输入不是十六进制的,有时它们的输出。

标签: php private-key elliptic-curve ecdsa der


【解决方案1】:

好的,现在我知道你想要什么了。我已经调查过了,不幸的是,我认为你运气不好。首先要澄清的是,对于以太坊,您需要 DER 中的 签名,并且密钥格式可能因您的软件而异,但对于以太坊帐户 地址,您不需要密钥德。具体来说,地址是通过以下方式计算的:

  1. 将公钥点表示为原始 X、Y 坐标,没有像 DER 这样的元数据,甚至没有像 X9.62 这样的单个字节,也没有像 base64 或 hex 这样的编码

  2. 获取 Keccak256 哈希并获取最后 20 个字节,可以用 0x 前缀的十六进制表示(例如显示),总共 42 个字符

对于第 1 步,OpenSSL 以及 php 的内置模块使用的密钥是 PEM 格式,如您在示例中所见,它由以 base64 编码的 ASN.1(DER 或有时是 BER)主体和换行符组成和标题/拖车线。具体来说,公钥采用RFC7468 section 13 定义的格式,其内容是X.509 和PKIX 定义的SubjectPublicKeyInfo 结构,即RFC5280 4.1.2.7,其特定算法的内容在其他标准中定义;这里的案例,X9 风格的 EC,在 RFC3279 2.3.5 中定义,并由引用 X9.62 和 SEC1 的 RFC5480 2.2 简化。 (哇!)虽然这些标准允许多种选择,但 PHP 中的 OpenSSL 始终使用 X9.62 未压缩点格式和 DER 编码,因此对于这条曲线,您需要的以太坊数据只是从 PEM 解码的 DER 的最后 64 个字节:

$der=base64_decode(str_replace(array("-----BEGIN PUBLIC KEY-----\n","-----END PUBLIC KEY-----\n"),"",$pub_key));
$raw=substr($der,-64);

但是第 2 步是个问题。尽管在 FIPS202 中标准化的 SHA3 和 SHAKE 是 Keccak 的实例,但以太坊使用的哈希是 Keccak 的不同且非标准的实例,OpenSSL 没有实现,因此 php 本身也没有(不添加库)。

【讨论】:

  • 好的。我发现 Asn1 已解码并获得密钥,而 keccak256 则获得地址。谢谢。
猜你喜欢
  • 1970-01-01
  • 2021-12-01
  • 2017-01-16
  • 2015-10-02
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 2021-01-12
  • 2022-07-08
相关资源
最近更新 更多