Does somebody knows how to properly use AWS CloudHSM to sign ethereum transactions (Keccak256)?

  • 2
    yup I've done it with colleagues - will try and post later. – Woodstock Jul 25 '19 at 14:04
  • 1
    @Woodstock I'm interested in AWS CloudHSM if you have some links it will be helpful. – Ismael Jul 26 '19 at 17:56
  • 1
    OK created an answer – Woodstock Jul 28 '19 at 8:50


so when we (me and the ShaneT) implemented hardware signing (private key stored exclusively on AWS CloudHSM) there were a number of things we learned. Hopefully this gives you a guide on how you can achieve it. Unfortunately I can't directly share the code as it's my firm's IP.

Here is the actual tx we first made using AWS CloudHSM on mainnet :)


  • Most HSM products support a proprietary interface and a generic interface called PKCS#11. PKCS#11 is just a fancy term for a standard way to talk to a HSM.

  • AWS supports both prop and PKCS#11 interaction, you should use PKCS#11.

  • Keccak256 is irrelevant with regard to HSM, it's the fact that AWS supports the SECP256k1 curve that allows it to sign ETH transactions.

  • ECDSA (elliptic curve digital signature algo) is bigger than ETH or BTC. i.e. you may hit hurdles where AWS CloudHSM is returning valid ECDSA signatures, however they are invalid Ethereum transactions, such as if the HSM returns a sig with a high S value.

  • There exists many libs in Web3, however JS is the predominant language, don't fight it :)

Approximate guide to implementation:

  • Follow this guide meticulously in order to setup the infrastructure/HSM. If you skip even a little you are gonna get rekt. p.s. I used Amazon Linux 2, it's easier.

  • Use this (Graphene) lib for JS PKCS#11, I tried them all this is the one to use imo.

  • If you can use ecrecover with an r, s, and v value that results in the calculation of the correct Eth address you've done it.

Steps to Signature:

1) Open connection to HSM create a public private key pair using SECP256k1.

2) Retrieve the public key for the pair and calculate the corresponding ETH address.

3) You now have a Eth address for which the HSM owns the private key.

4) Create a tx like so:

  const txParams = {
    nonce: '0x' + nonce.toString(16),
    gasPrice: '0x09184e72a00',
    gasLimit: '0x27100',
    to: '0x4D8519890C77217A352d3cC978B0b74165154421', 
    value: web3.utils.toHex(web3.utils.toWei('0.01', 'ether')),
    chainId: 4

5) With a handle to the private key request signature, remember you're signing a hash of the payload:

  const sign = session.createSign('ECDSA', yourPrivateKey);
  const sig = sign.once(msgHash);

6) Remember, r and s are the first and second 32 bytes of the ECDSA sig, v is a calculated value and ethereum bound concept representing chainID.

  const rs = {
    r: sig.slice(0, 32),
    s: sig.slice(32, 64)

7) CRITICAL: Due to EIP-2 you must loop until you achieve a ECDSA sig with an s value on the right side of the curve, or else it will be a bad Eth tx.

i.e. where s < curve.n/2

if (s > curve.n / 2) id = id ^ 1; // Invert id if s of signature is over half the n

8) Assuming all is implemented correctly you should be able to use ecrecover to check the validity of the HSM created sig.

if (ethUtil.ecrecover(msgHash, 27, rsvOdd.r, rsvOdd.s).toString('hex') === yourETHPublicKeyString)

If that's all good, then you've done it! You can now submit your tx and it should be mined.

This sounds tricky but it's totally doable.

Questions, just shout.

| improve this answer | |
  • 1
    Rather than looping until you get an s which satisfies EIP-2, you can just take s = curve.n - s, and flip the v value. From EIP-2 "Allowing transactions with any s value with 0 < s < secp256k1n, as is currently the case, opens a transaction malleability concern, as one can take any transaction, flip the s value from s to secp256k1n - s, flip the v value (27 -> 28, 28 -> 27), and the resulting signature would still be valid." The point of EIP-2 is to mandate one of these two valid signatures. – Alex Coventry Aug 27 '19 at 20:14
  • I did exactly the same, but my ethaddress generating during signing is different every single time. Any idea why ? – user2805885 Dec 20 '19 at 7:28
  • Do you mean ecrecover returns a different address every time? Or, do you mean it’s one of two possible addresses? – Woodstock Dec 20 '19 at 8:13
  • ecrecover gives different address every single time. – user2805885 Dec 20 '19 at 9:37
  • 1
    Wow. Thanks alot for the input. I was able to resolve the issue.I am using Gemalto SafeNet Luna PCIe HSM and it worked. – user2805885 Dec 24 '19 at 2:19

I had implemented a solution based @Woodstock steps using SoftHSM and Graphene. The source code is available in https://github.com/wshbair/HSM2ETH

Hope this will help

| improve this answer | |
  • Did you try this with AWS Cloud HSM that uses Cavium? – lingraj mahanand Mar 17 at 14:56
  • Not yet, but I think as the AWS CloudHSM is accessed via PKCS#11 it should work. – Wazen Shbair Mar 18 at 0:22
  • @WazenShbair exactly! nice work, clean code. – Woodstock Apr 28 at 8:37

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.