首页 > 解决方案 > 如何在 php 中以十六进制格式签署 ECDSA 签名并编码为 DER 格式?

问题描述

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

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

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

该示例在github.com/simplito/elliptic-phprepo 中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私钥和公钥并签名,但不能将公钥转换为DER格式hex以通过哈希keccak256算法制作地址。

签署 Tx 哈希需要 JSON 字符串 Tx 数据中的往返 ETH 地址,{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(),但我无法使用简单的功能转换为DER格式。hex

openssl_pkey_new()生成新secp256k1密钥对的函数,如生成 256 位 ECDSA 私钥问题中所示。

$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;

关联

并用钥匙签名。

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

有什么方法可以使用 php 库将密钥转换为DER十六进制?

标签: phpprivate-keyelliptic-curveecdsader

解决方案


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

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

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

对于第 1 步,OpenSSL 以及 php 的内置模块使用的密钥是 PEM 格式,正如您在示例中看到的那样,它由以 base64 编码的 ASN.1(DER 或有时是 BER)主体组成,带有换行符和 header/拖车线。具体来说,公钥采用RFC7468 第 13 节定义的格式,其内容是 X.509 和 PKIX 定义的 SubjectPublicKeyInfo 结构,即RFC5280 4.1.2.7,其特定算法的内容在其他标准中定义;这里的案例,X9 风格的 EC,在RFC3279 2.3.5中定义并由RFC5480 2.2简化参考 X9.62 和 SEC1。(哇!)虽然这些标准允许多种选择,但 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 本身也没有(不添加库)。


推荐阅读