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?
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 curl
desde 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
eth_sign
, por lo tanto, las llamadas de nivel superior como web3.eth.sign
primero 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 keythereum
para importarla. Esto también le dará una elliptic.js
clave.
Una vez que tenga una clave, necesita una biblioteca criptográfica y algún código para unirlo todo. Yo uso Crypto-JS
en 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));
}
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_sign
funcionalidad 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.js
entorno, 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.14
yweb3 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 generateSignature
esta 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
q9f