How to sign the Tx data including ETH Keccak256
hashed addresses signature with secp256k1 (ECDSA)
keys and encode to DER
format in hex and verify in php?
I want to use only php libraries come with, openssl and others, to make secp256k1
private and public keys, sign, and convert to DER
format in hex
for making a sample of my cryptcoin.
Ethereum addresses are made by secp256k1 EDSA
keys, hash the 256 bits/64 character
pubic key made from the private key with Keccak256
(sha-3-256) hash algorithm to calculate hash, then take last 40 characters from the hash string in hex, add checksum, and add x0
prefix to the hashed string.
The example is shown in github.com/simplito/elliptic-php
repo as toDER()
function in elliptic-php/lib/EC/Signature.php
. The library is using BN-php
, but I want to use only php library, openssl
and others.
// 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()
functions can make new secp256k1
private and public keys and sign, but can not convert the public key to DER
format in hex
to make address by hashing keccak256
algorithm .
Signing Tx hash requires from/to ETH addresses in JSON string Tx data {address:{to:address1,from:address2},amount:0,timestamp:timestamp tx, hash:sha256hash, previousHash:hash, difficulty:2...}
and openssl_pkey_new() openssl_pkey_export()
made private and public keys are needed to make ETH address.
Since, openssl_pkey_new() openssl_pkey_export()
for OPENSSL_KEYTYPE_EC
return DER bin
keys, I need to convert them to hex.
I have tried openssl_pkey_new()
and openssl_sign()
functions but I can not convert to DER
format in hex
with simple function.
openssl_pkey_new()
function to make newsecp256k1
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;
And sign with the key.
openssl_sign($hashdata, $signature, $private_key);
Is there any way, with php libraries, to convert the keys to DER
in hex?
Unfortunately I think you're out of luck. First to clarify, for Ethereum you want the signature in DER and the key format can vary depending on your software, but for an Ethereum account address you do NOT want the key in DER. Specifically an address is computed by:
represent the publickey point as raw X,Y coordinates, with no metadata like DER or even a single byte like X9.62 and no encoding like base64 or hex
take a Keccak256 hash and take the last 20 bytes, which can be represented (e.g. displayed) in hex with 0x prefixed for a total of 42 characters
For step 1, the keys used by OpenSSL, and thus by php's builtin module, are PEM format, as you see in your example, which consists of an ASN.1 (DER or sometimes BER) body encoded in base64 with linebreaks and header/trailer lines. Specifically the publickey is in the format defined by 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 which reference X9.62 and SEC1. (Whew!) While these standards allow several options, OpenSSL in PHP always uses X9.62-uncompressed point format and DER encoding, so for this curve the data you need for Ethereum is simply the last 64 bytes of the DER decoded from the PEM:
$der=base64_decode(str_replace(array("-----BEGIN PUBLIC KEY-----\n","-----END PUBLIC KEY-----\n"),"",$pub_key));
$raw=substr($der,-64);
But step 2 is a problem. Although SHA3 and SHAKE as standardized in FIPS202 are instances of Keccak, the hash Ethereum uses is a different and nonstandard instance of Keccak, which OpenSSL does not implement, therefore neither does php by itself (without adding a library).