如何在 php 中签署 ECDSA 签名并以十六进制编码为 DER 格式?

How to sign ECDSA signature and encode to DER format in hex in php?

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

我只想使用 php 附带的库、openssl 和其他库来制作 secp256k1 私有密钥和 public 密钥、签名并转换为 DER 格式在 hex 制作我的加密币样本。

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

示例显示在 github.com/simplito/elliptic-php repo as toDER() function in elliptic-php/lib/EC/Signature.php. The library is using 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() and openssl_sign() 函数可以生成新的 secp256k1 私钥和 public 密钥并签名,但不能将 public 密钥转换为 [=16] =] 格式 hex 通过散列 keccak256 算法生成地址 .

签名 Tx 散列需要 from/to 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() 设为私有并且需要 public 密钥才能生成 ETH地址。

因为 openssl_pkey_new() openssl_pkey_export() 对于 OPENSSL_KEYTYPE_EC return DER bin 键,我需要将它们转换为十六进制。

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

openssl_pkey_new() function to make new secp256k1 key pairs, just shown in 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 吗?

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

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

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

对于第 1 步,OpenSSL 使用的密钥以及 php 的内置模块使用的密钥是 PEM 格式,如您在示例中所见,它由 ASN.1(DER 或有时BER) 正文以 base64 编码,带有换行符和 header/trailer 行。具体来说,公钥采用 RFC7468 section 13 whose content is the SubjectPublicKeyInfo structure defined by X.509 and PKIX i.e. RFC5280 4.1.2.7 whose contents for a particular algorithm is defined in other standards; the case here, X9-style EC, is defined in RFC3279 2.3.5 and simplified by RFC5480 2.2 定义的格式,引用 X9.62 和 SEC1。 (哇!)虽然这些标准允许多种选择,但 PHP 中的 OpenSSL 始终使用 X9.62 未压缩点格式和 DER 编码,因此对于这条曲线,以太坊所需的数据只是 DER 的最后 64 个字节从 PEM 解码:

$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 本身也没有(不添加库)。