2

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

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?

smith john
  • 33
  • 9
  • `openssl_sign` for EC creates DER _binary_; to convert binary to hex see https://stackoverflow.com/questions/14674834/php-convert-string-to-hex-and-hex-to-string . I have no clue what you mean by chunk splitting with \n, or removing header; those sound like possible operations on the _PEM key_, which is not a signature and not in hex. – dave_thompson_085 Sep 12 '21 at 07:16
  • It has no _options_ at all. The format depends on the algorithm; for RSA it _is_ defined by PKCS1/RFC8017; for DSA and ECDSA it _is_ DER (SEQ of two INT) defined in several places; for EdDSA it is defined by 8032. – dave_thompson_085 Sep 13 '21 at 05:19
  • OK thank you. I use `ECDSA` for `DER`. – smith john Sep 13 '21 at 06:20
  • Both builtin bin2hex as recommended (in a comment and an answer) and the explicit strToHex function in the topvoted answer at that link **work for me** (in 7.4, after correcting your code so private_key matches priv_key). You must be doing something wrong, but since you don't show what you are doing, I have no clue what the problem could be. – dave_thompson_085 Sep 14 '21 at 08:17
  • Now you're talking about the key, not the signature, and Ethereum address, which was not in your question. Stackexchange is designed NOT to be a chat forum or have discussions or depend on additions in comments, which are often deleted; please read the 'welcome' page aka 'tour'. Please put your question in your question. But consider that Ethereum addresses do not use the privatekey, or DER, at all and do not have their input in hex only sometimes their output. – dave_thompson_085 Sep 17 '21 at 09:30
  • I need a public key and ETH addresses to sign Tx hash. The sample from Elliptic\EC library automatically assign the keys and the ETH address goes to the JSON obj string. {address:ETHaddress, amount:n, timestamp:timestamp tx} in $msg section in the example. – smith john Sep 17 '21 at 12:34

1 Answers1

1

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:

  1. 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

  2. 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).

desertnaut
  • 57,590
  • 26
  • 140
  • 166
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70