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?
5 Answers
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 Commented Nov 6, 2017 at 9:44
-
1to 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)– makevoidCommented Nov 7, 2017 at 12:32
-
Please view my follow up question: ethereum.stackexchange.com/questions/36561/…– user22075Commented Jan 17, 2018 at 2:51
-
-
1It's important to note (because no one mentions it) that
eth_sign
and therefore higher level calls likeweb3.eth.sign
first hash the message before signing, which allows for a fixed length output, which can then be parsed into r/s/v forecrecover
. Commented Nov 14, 2018 at 23:59
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 ♦Commented Jan 26, 2016 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/… Commented Jan 26, 2016 at 23:54
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
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"
}
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