27

I would like to use the key pair of one of my Ethereum accounts to sign a piece of data. How can this be done?

14

There's a functionality from the JSON RPC API not yet ported to web3 to sign data directly with one RPC function call, without messing with keys and crypto. If you don't need to sign messages clientside (because this requires an rpc connection and the account needs to be unlocked), then you can use eth_sign.

eth_sign accepts two parameters, the address you unlocked in Geth, eth or pyethapp and the data you want to sign.

Here's an example request you can make with curl from your terminal, of course you can use your favorite programming language and libraries to make this request:

// 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"
}

The "result" bit in the json contains your signed message in hex.

0x2ac19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601
  • how can I unlock account using private key instead of account password @makvoid – Yogesh Karodiya Nov 6 '17 at 9:44
  • 1
    to answer your question you can use something like keythereum (github.com/ethereumjs/keythereum) to convert your plaintext (non encrypted) private key to the encrypted format geth/parity use. Then you can write/copy it to the keys directory (you can script this step) of your geth/parity installation and then follow my answer --- Otherwise you can use another library like ethereumjs-utils and use your private key to sign directly: github.com/ethereumjs/ethereumjs-util/blob/master/index.js#L339 (source) – makevoid Nov 7 '17 at 12:32
  • Please view my follow up question: ethereum.stackexchange.com/questions/36561/… – quantumpotato Jan 17 '18 at 2:51
  • How can i verify this hexed signed message in solidity? – Mr_Hmp Aug 21 '18 at 5:34
  • It's important to note (because no one mentions it) that eth_sign and therefore higher level calls like web3.eth.sign first hash the message before signing, which allows for a fixed length output, which can then be parsed into r/s/v for ecrecover. – GViz Nov 14 '18 at 23:59
13

First, you'll need the private key for your account. To generate a brand new key, I use elliptic.js:

import {ec as EC} from 'elliptic';

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

If you have a key in your Ethereum node, you can use keythereum to import it. This will also give you an elliptic.js key.

Once you have a key, you need a crypto library and some code to glue it all together. I use Crypto-JS in this code:

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));
}
  • Note: If an alternative to CryptoJS is usable, js-sha3 module on NPM can produce SHA-3 standard hashes, since CryptoJS.SHA3 hasn't been updated to the SHA-3 FIPS 202 standard. – eth Jan 26 '16 at 23:49
  • That's a good point. Ethereum's "SHA3" hashes are Keccak hashes. Crypto-JS is still out of date and incorrectly calls Keccak "SHA3," but it's probably the hash function you want. medium.com/@ConsenSys/… – Niran Babalola Jan 26 '16 at 23:54
5

As mentioned in this answer, if you have access to an RPC node or geth, the easiest way is to use the builtin eth_sign functionality.

There are various libraries for Javascript which let you do that manually if you are not afraid of writing some code. An example is shown by Niran.

If you have (or are happy using) the node.js environment, you can try out helpeth which is a command line tool for managing keys and signing:

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

Just use MyEtherWallet which support signing and verifying messages on your browser via: https://www.myetherwallet.com/signmsg.html

Just tested:

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

This issue came up recently for me so will provide the latest solution that worked out. Primarily using Ethereum tooling in JavaScript and verifying on-chain.

The working minimal test case may be found here: https://github.com/AdamJLemmon/ethereum-signing

Note: truffle version 4.1.14 and web3 version 0.20.6

Starting from the beginning a brand new account may be generated. The address and private key may be accessed directly from the new wallet:

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');

Then the data to be signed can be specified and the hash taken:

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

Finally, this data may be signed:

const sig = await generateSignature(hash, privateKey);

Where generateSignature is the following:

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);
}

I won't post the contract source here but it can be found within the linked test case above, and noted here: https://github.com/AdamJLemmon/ethereum-signing/blob/master/contracts/TestVerification.sol

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.