¿Cómo puedo firmar un dato con la clave privada de una dirección de Ethereum?

Me gustaría usar el par de claves de una de mis cuentas de Ethereum para firmar un dato. ¿Cómo se puede hacer esto?

Respuestas (5)

Hay una funcionalidad de la API JSON RPC que aún no se ha transferido a web3 para firmar datos directamente con una llamada de función RPC , sin tener que jugar con claves y criptografía. Si no necesita firmar mensajes en el lado del cliente (porque esto requiere una conexión rpc y la cuenta debe desbloquearse), puede usar eth_sign .

eth_sign acepta dos parámetros, la dirección que desbloqueaste en Geth, eth o pyethapp y los datos que deseas firmar.

Aquí hay una solicitud de ejemplo que puede realizar curldesde su terminal, por supuesto, puede usar su lenguaje de programación y bibliotecas favoritas para realizar esta solicitud:

// parameters:
// address: 0xd1ade25ccd3d550a7eb532ac759cac7be09c2719 (needs to be unlocked)
// message: "Schoolbus"

// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sign","params":["0xd1ade25ccd3d550a7eb532ac759cac7be09c2719", "Schoolbus"],"id":1}'

// Result
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601"
}

El bit de "resultado" en el json contiene su mensaje firmado en hexadecimal.

0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601
¿Cómo puedo desbloquear una cuenta usando una clave privada en lugar de la contraseña de la cuenta @makvoid?
para responder a su pregunta, puede usar algo como keythereum ( github.com/ethereumjs/keythereum ) para convertir su clave privada de texto sin formato (no encriptada) al formato encriptado geth/parity use. Luego puede escribirlo/copiarlo en el directorio de claves (puede escribir este paso) de su instalación de geth/parity y luego seguir mi respuesta --- De lo contrario, puede usar otra biblioteca como ethereumjs-utils y usar su clave privada para firmar directamente : github.com/ethereumjs/ethereumjs-util/blob/master/index.js#L339 (fuente)
Consulte mi pregunta de seguimiento: ethereum.stackexchange.com/questions/36561/…
¿Cómo puedo verificar este mensaje firmado hexadecimal en Solidity?
Es importante tener en cuenta (porque nadie lo menciona) que eth_sign, por lo tanto, las llamadas de nivel superior como web3.eth.signprimero hash el mensaje antes de firmar, lo que permite una salida de longitud fija, que luego se puede analizar en r/s/v para ecrecover.

Primero, necesitará la clave privada de su cuenta. Para generar una nueva clave, uso elliptic.js:

import {ec as EC} from 'elliptic';

const ec = new EC('secp256k1');
const keypair = ec.genKeyPair();

Si tiene una clave en su nodo Ethereum, puede usarla keythereumpara importarla. Esto también le dará una elliptic.jsclave.

Una vez que tenga una clave, necesita una biblioteca criptográfica y algún código para unirlo todo. Yo uso Crypto-JSen este código:

import BigNumber from 'bignumber.js';
import CryptoJS from 'crypto-js';

type WordArray = object;

// https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v/38909#38909
const UNCOMPRESSED_PUBKEY_HEADER = 27;


/**
 * Sign the given hex-encoded bytes.
 */
function signHex(keypair, hex) {
  const signature = keypair.sign(hex);
  return {
    v: UNCOMPRESSED_PUBKEY_HEADER + signature.recoveryParam,
    r: new BigNumber(signature.r.toString(16), 16),
    s: new BigNumber(signature.s.toString(16), 16),
  };
}

/**
 * Sign the hash of a message. If the message is a string, it is encoded
 * as UTF-8 bytes. As a result, hex-encoded strings are not valid input. They
 * must be parsed into WordArrays first.
 */
function signMessageHash(keypair, message) {
  const hash: WordArray = CryptoJS.SHA3(message, {outputLength: 256});
  return signHex(keypair, hash.toString(CryptoJS.enc.Hex));
}
Nota: si se puede usar una alternativa a CryptoJS, el módulo js-sha3 en NPM puede producir hashes estándar SHA-3, ya que CryptoJS.SHA3 no se ha actualizado al estándar SHA-3 FIPS 202.
Ese es un buen punto. Los hashes "SHA3" de Ethereum son hashes de Keccak. Crypto-JS todavía está desactualizado y llama incorrectamente a Keccak "SHA3", pero probablemente sea la función hash que desea. medium.com/@ConsenSys/…

Como se menciona en esta respuesta , si tiene acceso a un nodo RPC o geth, la forma más fácil es usar la eth_signfuncionalidad integrada.

Hay varias bibliotecas para Javascript que le permiten hacerlo manualmente si no tiene miedo de escribir código. Niran muestra un ejemplo .

Si tiene (o está feliz de usar) el node.jsentorno, puede probar helpeth , que es una herramienta de línea de comandos para administrar claves y firmar:

$ helpeth --password 'Use --password-prompt instead for security' --keyfile UTC--2016-03-17T19-06-57.064Z--15f2f3e0f2d74ea7b185fc12f24cb4f402cc96d0 signMessage 'Hello World'
Input message: Hello World
Message hash (Keccak): 0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba
The signature: 0x167760997a69e225c0668e6761cd20cac70f3a6ace29fe2d287c3003daf6972b10d158a47e8f064cf982a3defdf236247c41249dbfb0fb81f0d126c26a94971d01

Simplemente use MyEtherWallet, que admite la firma y verificación de mensajes en su navegador a través de: https://www.myetherwallet.com/signmsg.html

Recién probado:

{  
  "address":"0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf",
  "msg":"Signing a Message with the first best available private key for 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf. | 20 APR 2017 09:46:48",
  "sig":"0x85589aa36c3d5e1080e17220ae33768c42054b519cf90934bffd92b341dc1b6e4f2f632e21cf3ad104db746a02c8126a2cbb4232ee5c5b7d40085e598e5460351c"
}

Este problema surgió recientemente para mí, por lo que proporcionaré la última solución que funcionó. Principalmente usando las herramientas de Ethereum en JavaScript y verificando en cadena.

El caso de prueba mínimo funcional se puede encontrar aquí: https://github.com/AdamJLemmon/ethereum-signing

Nota: truffle version 4.1.14yweb3 version 0.20.6

Desde el principio se puede generar una cuenta nueva. Se puede acceder a la dirección y la clave privada directamente desde la nueva billetera:

const bip39 = require('bip39');
const hdkey = require('ethereumjs-wallet/hdkey');

const mnemonic = bip39.generateMnemonic();
const hdwallet = hdkey.fromMasterSeed(bip39.mnemonicToSeed(mnemonic));
const path = "m/44'/60'/0'/0/0";
const wallet = hdwallet.derivePath(path).getWallet();
const address = `0x${wallet.getAddress().toString('hex')}`;
const privateKey = wallet.getPrivateKey().toString('hex');

Luego se pueden especificar los datos a firmar y tomar el hash:

const messageToSign = 'adamjlemmon';
const hash = web3.sha3(messageToSign);

Finalmente, estos datos pueden ser firmados:

const sig = await generateSignature(hash, privateKey);

Donde generateSignatureesta lo siguiente:

const ethUtil = require("ethereumjs-util");

module.exports = (dataToSign, privateKey) => {
  const msg = Buffer.from(dataToSign.replace("0x", ""), "hex");
  const msgHash = ethUtil.hashPersonalMessage(msg);
  const sig = ethUtil.ecsign(msgHash, new Buffer(privateKey, 'hex'));
  return ethUtil.toRpcSig(sig.v, sig.r, sig.s);
}

No publicaré la fuente del contrato aquí, pero se puede encontrar en el caso de prueba vinculado anteriormente y se indica aquí: https://github.com/AdamJLemmon/ethereum-signing/blob/master/contracts/TestVerification.sol