Given a private key: 3cd0560f5b27591916c643a0b7aa69d03839380a738d2e912990dcc573715d2c, And a tx hash: 456f9e1b6184d770f1a240da9a3c4458e55b6b4ba2244dd21404db30b3131b94, I would like to sign, using SSL and PHP. Converting the private key to a pem format (so SSL can use it), I generate the following base64 string: PNBWD1snWRkWxkOgt6pp0Dg5OApzjS6RKZDcxXNxXSw=. So a valid pem format should be:


However, it does not seem to be working. I'm using the following function: openssl_sign($hash, $signature, $private_key, OPENSSL_ALGO_SHA256).

But when I generate a random private key using SSL, for example: -----BEGIN EC PRIVATE KEY----- MHcCAQEEINhhp8dYz31X+KWq3u/gutJthvW2puUbB9AOIul/v2SOoAoGCCqGSM49 AwEHoUQDQgAE0jE7TIszCklw//SGyYW0+z38PUxwfkip9WS4UUn68f7D78D6ZQ/O dlPisC1fQZrUrFrik/wq7E4Xwrqs3n3b5w== -----END EC PRIVATE KEY-----

The function does work. What is wrong with the pem format that I'm generating using an existing private key?


when I verify your self created privkey, I get this error:

error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long

I couldn't see how you created your privkey, but the way to go is through the ASN.1 structure, and then base64 it. When using OpenSSL to sign, you must also make sure you are signing hex data, and not strings (this is explained in the answer of the link I provided in my comment).

The ASN1 structure for a privkey looks like this:

#   30  <-- declares the start of an ASN.1 sequence
#   74  <-- length of following sequence 
#   02  <-- declares the start of an integer
#   01  <-- length of integer in bytes (1 byte)
#   01  <-- value of integer (1)
#   04  <-- declares the start of an "octet string"
#   20  <-- length of string to follow (32 bytes)
#           3cd0560f5b27591916c643 ... a738d2e912990dcc573715d2c 
#           \--------------------------------------------------/
#            this is the private key 
#   a0   <-- declares the start of context-specific tag 0
#   07   <-- length of context-specific tag 
#   06   <-- declares the start of an object ID
#   05   <-- length of object ID to follow 
#   2b 81 04 00 0a <-- the object ID of the curve secp256k1
#   a1   <-- declares the start of context-specific tag 1
#   44   <-- declares the length of context-sepcifc tag (68 bytes)
#   03   <-- declares the start of a bit string
#   42   <-- length of bit string to follow (66 bytes)
#   00   <-- ??
#            04 f1 44 f0 dc 00 80 af d2 b7 3f 13 37 6c ... 05 49 cd 83 f4 58 56 1e
#            \-------------------------------------------------------------------/
#             this is the public key

Using your privkey, I derived the pukey, and get this pem format:

### use pre defined ASN.1 strings to concatenate PEM privkey
  a pre_string : 30740201010420
  the privkey  : 3cd0560f5b27591916c643a0b7aa69d03839380a738d2e912990dcc573715d2c
  a mid_string : a00706052b8104000aa144034200
  the pubkey   : 04BF350D2821375158A608B51E3E898E507FE47F2D2E8C774DE4A9A7EDECF74ED

### base64 privkey file and put some nice surroundings

using asn1parse, the result looks like this:

openssl asn1parse -in privkey.pem
    0:d=0  hl=2 l= 116 cons: SEQUENCE          
    2:d=1  hl=2 l=   1 prim: INTEGER           :01
    5:d=1  hl=2 l=  32 prim: OCTET STRING      [HEX DUMP]:3CD0560F5B27591916C643A0B7AA69D03839380A738D2E912990DCC573715D2C
   39:d=1  hl=2 l=   7 cons: cont [ 0 ]        
   41:d=2  hl=2 l=   5 prim: OBJECT            :secp256k1
   48:d=1  hl=2 l=  68 cons: cont [ 1 ]        
   50:d=2  hl=2 l=  66 prim: BIT STRING 

putting all together:

# convert from chars to hex
printf $( echo 456f9e1b6184d770f1a240da9a3c4458e55b6b4ba2244dd21404db30b3131b94 | sed 's/[[:xdigit:]]\{2\}/\\x&/g' ) > tmp_utx.hex

echo "The private key in HEX format: "
echo 3cd0560f5b27591916c643a0b7aa69d03839380a738d2e912990dcc573715d2c  
# The private key in PEM format:
echo "-----BEGIN EC PRIVATE KEY-----
-----END EC PRIVATE KEY-----" > privkey.pem

# the signing process 
#  prepare pubkey:
    openssl ec -in privkey.pem -pubout -out pubkey.pem
#  sign:
    openssl dgst -sign privkey.pem  -sha256 -hex tmp_utx.hex
    openssl dgst -sign privkey.pem  -sha256 tmp_utx.hex > tmp_sig.hex
#  verify:
    openssl dgst -verify pubkey.pem -sha256 -signature tmp_sig.hex tmp_utx.hex

echo " "


Verified OK

| improve this answer | |
  • Thank you very much for your detailed response! It's very helpful. I thought all I need is to convert the hex string (my btc private key) to binary and then base64 it. One thing I did not understand is why do I need my public key in order to generate a .pem private key? – user2298995 Dec 27 '17 at 7:10
  • By the way, even with the base64 privkey you provided above, the openssl_sign function still does not seem to accept it as a valid key. – user2298995 Dec 27 '17 at 7:41
  • 1
    I further extended my answer, with the "raw" openssl commands. Below the secton "utting all together". This is as much as I can do... I don't know how to realize in PHP. – pebwindkraft Dec 27 '17 at 8:00
  • 1
    How do you derive the public key from the private key? – Gavin S. Yancey Nov 27 '19 at 23:09
  • I don't :-) Openssl does it for me, at line "# prepare pubkey:", it uses openssl, the input is the privkey, output is public key. – pebwindkraft Nov 28 '19 at 16:40

For Javascript/Node, I highly recommend this library:



It does exactly what you need.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.