Firma de transacción con SSL: clave privada para PEM

Dada una clave privada: 3cd0560f5b27591916c643a0b7aa69d03839380a738d2e912990dcc573715d2cy un hash tx: 456f9e1b6184d770f1a240da9a3c4458e55b6b4ba2244dd21404db30b3131b94me gustaría firmar, usando SSL y PHP. Al convertir la clave privada a un formato pem (para que SSL pueda usarla), genero la siguiente cadena base64: PNBWD1snWRkWxkOgt6pp0Dg5OApzjS6RKZDcxXNxXSw=. Entonces, un formato pem válido debería ser:

-----BEGIN EC PRIVATE KEY----- PNBWD1snWRkWxkOgt6pp0Dg5OApzjS6RKZDcxXNxXSw= -----END EC PRIVATE KEY-----

Sin embargo, no parece estar funcionando. Estoy usando la siguiente función: openssl_sign($hash, $signature, $private_key, OPENSSL_ALGO_SHA256).

Pero cuando genero una clave privada aleatoria usando SSL, por ejemplo: -----BEGIN EC PRIVATE KEY----- MHcCAQEEINhhp8dYz31X+KWq3u/gutJthvW2puUbB9AOIul/v2SOoAoGCCqGSM49 AwEHoUQDQgAE0jE7TIszCklw//SGyYW0+z38PUxwfkip9WS4UUn68f7D78D6ZQ/O dlPisC1fQZrUrFrik/wq7E4Xwrqs3n3b5w== -----END EC PRIVATE KEY-----

La función funciona. ¿Qué tiene de malo el formato pem que estoy generando usando una clave privada existente?

Tuve el mismo problema y lo describí aquí: bitcoin.stackexchange.com/questions/46455/…
En realidad, miré este elemento de intercambio de pila hace unas horas. Lamentablemente no respondió mi pregunta.

Respuestas (2)

cuando verifico tu clave privada creada por ti mismo, aparece este error:

error: 0D07207B: rutinas de codificación asn1: ASN1_get_object: encabezado demasiado largo

No pude ver cómo creaste tu clave privada, pero el camino a seguir es a través de la estructura ASN.1, y luego base64. Al usar OpenSSL para firmar, también debe asegurarse de que está firmando datos hexadecimales y no cadenas (esto se explica en la respuesta del enlace que proporcioné en mi comentario).

La estructura ASN1 para una clave privada se ve así:

# ASN.1 STRUCTURE FOR PRIVATE KEY:
#   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

Usando su clave privada, derivé el pukey y obtuve este formato pem:

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

### base64 privkey file and put some nice surroundings
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIDzQVg9bJ1kZFsZDoLeqadA4OTgKc40ukSmQ3MVzcV0soAcGBSuBBAAK
oUQDQgAEvzUNKCE3UVimCLUePomOUH/kfy0ujHdN5Kmn7ez3TtokJDy5ksVnOgf6
WzpmzY46zvKAnQ44Cgx5Kdqx5dVDiw==
-----END EC PRIVATE KEY-----

usando asn1parse, el resultado se ve así:

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 

juntando todo:

#!/bin/sh 
###############################################
# 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-----
MHQCAQEEIDzQVg9bJ1kZFsZDoLeqadA4OTgKc40ukSmQ3MVzcV0soAcGBSuBBAAK
oUQDQgAEvzUNKCE3UVimCLUePomOUH/kfy0ujHdN5Kmn7ez3TtokJDy5ksVnOgf6
WzpmzY46zvKAnQ44Cgx5Kdqx5dVDiw==
-----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 " "

devoluciones:

Verificado OK

¡Muchas gracias por su detallada respuesta! Es muy útil. Pensé que todo lo que necesitaba era convertir la cadena hexadecimal (mi clave privada btc) a binaria y luego base64. Una cosa que no entendí es ¿por qué necesito mi clave pública para generar una clave privada .pem?
Por cierto, incluso con la clave privada base64 que proporcionó anteriormente, la función openssl_sign todavía no parece aceptarla como una clave válida.
Extendí aún más mi respuesta, con los comandos "en bruto" de openssl. Debajo de la sección "juntar todos". Esto es todo lo que puedo hacer... No sé cómo realizarlo en PHP.
¿Cómo se deriva la clave pública de la clave privada?
Yo no :-) Openssl lo hace por mí, en la línea "# prepare pubkey:", usa openssl, la entrada es la clave privada, la salida es la clave pública.

Para Javascript/Node, recomiendo esta biblioteca:

https://www.npmjs.com/package/key-codificador

https://github.com/blockstack/key-codificador-js/

Hace exactamente lo que necesitas.