Get public key of any ethereum account

  • Can I somehow get the public key of an ethereum account knowing just the corresponding ethereum address (e.g. 0x54dbb737eac5007103e729e9ab7ce64a6850a310)?

  • tayvano

    tayvano Correct answer

    4 years ago

    You can if and only if a transaction has been sent from the account. When you send a tx, you sign the transaction and it includes these v r and s values. You parse these from the signed tx and then pass these v r and s values and the hash of the transaction back into a function and it'll spit out the public key. This is actually how you get the from address of a transaction.

    You can do so yourself using a tool like ethereumjs-utils:

    /**
     * ECDSA public key recovery from signature
     * @param {Buffer} msgHash
     * @param {Number} v
     * @param {Buffer} r
     * @param {Buffer} s
     * @return {Buffer} publicKey
     */
    exports.ecrecover = function (msgHash, v, r, s) {
      var signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64)
      var recovery = v - 27
      if (recovery !== 0 && recovery !== 1) {
        throw new Error('Invalid signature v value')
      }
      var senderPubKey = secp256k1.recover(msgHash, signature, recovery)
      return secp256k1.publicKeyConvert(senderPubKey, false).slice(1)
    }
    

    As another real-world scenario, ethereumjs-tx uses this function to verify the signature:

    /**
    * Determines if the signature is valid
    * @return {Boolean}
    */
    verifySignature () {
      const msgHash = this.hash(false)
      // All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
      if (this._homestead && new BN(this.s).cmp(N_DIV_2) === 1) {
        return false
      }
    
      try {
        let v = ethUtil.bufferToInt(this.v)
        if (this._chainId > 0) {
          v -= this._chainId * 2 + 8
        }
        this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s)
      } catch (e) {
        return false
      }
    
      return !!this._senderPubKey
    }
    

    For more information on v r and s:

    v, r, and s are parameters that can be parsed from the signature. Here's a good example from the ethereumjs utils library:

     var sig = secp256k1.sign(msgHash, privateKey)
      var ret = {}
      ret.r = sig.signature.slice(0, 32)
      ret.s = sig.signature.slice(32, 64)
      ret.v = sig.recovery + 27
    

    Note how you can parse each value from a given signature.

    source

    Very helpful. I had to look why an "extra" call `secp256k1.publicKeyConvert` is needed and the reason appears to be to get the decompressed public key, since `secp256k1.recover` returns a compressed public key.

    What do the values s, r, v stand for?

    What is the msgHash? Is that the hash of the message sent as part of the transaction or the actual hash of the transaction itself?

    How are you going to parse v,r and if you don't have the private key of the end user?

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM