With ECDSA we create a signature with a private key, and then prove with a public key. A special feature of ECDSA is that we can recover the public key from the signature. Overall we have a signature value of \((r,s,v)\) and can use these values to recover the public key. In this case we will create a random private key, and then derive the public key. Next we will generate an ECDSA signature for a given data value, and then recover the public key using two methods (SigToPub and Ecrecover) from github.com/ethereum/go-ethereum/crypto.
Recovering The Public Key From ECDSA |
Code
The code used is:
package main // Taken from example here: https://stackoverflow.com/questions/51111605/how-do-i-recover-ecdsa-public-key-correctly-from-hashed-message-and-signature-in import ( "bytes" "crypto/rand" "encoding/hex" "fmt" "os" "github.com/ethereum/go-ethereum/crypto" ) func randomHex(n int) (string, error) { bytes := make([]byte, n) if _, err := rand.Read(bytes); err != nil { return "", err } return hex.EncodeToString(bytes), nil } func main() { argCount := len(os.Args[1:]) msg := "hello" if argCount > 0 { msg = os.Args[1] } data := []byte(msg) getAddr, _ := randomHex(32) privateKey, _ := crypto.HexToECDSA(getAddr) fmt.Printf("Private key: %s\n", getAddr) fmt.Printf("Message to sign: %s\n", msg) publicKey := privateKey.PublicKey publicKeyBytes := crypto.FromECDSAPub(&publicKey) hash := crypto.Keccak256Hash(data) fmt.Printf("Hash: %x\n", hash.Bytes()) fmt.Printf("\n=== Now using Ecrecover ===\n") signature, _ := crypto.Sign(hash.Bytes(), privateKey) fmt.Printf("ECDSA Signature: %x\n", signature) fmt.Printf(" R: %x\n", signature[0:32]) // 32 bytes fmt.Printf(" S: %x\n", signature[32:64]) // 32 bytes fmt.Printf(" V: %x\n", signature[64:]) sigPublicKey, _ := crypto.Ecrecover(hash.Bytes(), signature) fmt.Printf("\nOriginal public key: %x\n", publicKeyBytes) fmt.Printf("Recovered public key: %x\n", sigPublicKey) rtn := bytes.Equal(sigPublicKey, publicKeyBytes) if rtn { fmt.Printf("Public keys match\n\n") } fmt.Printf("\n=== Now using FromECDSAPub ===\n") sigPublicKeyECDSA, _ := crypto.SigToPub(hash.Bytes(), signature) sigPublicKeyBytes := crypto.FromECDSAPub(sigPublicKeyECDSA) rtn = bytes.Equal(sigPublicKeyBytes, publicKeyBytes) fmt.Printf("Original public key: %x\n", publicKeyBytes) fmt.Printf("Recovered public key: %x\n", sigPublicKeyBytes) if rtn { fmt.Printf("Public keys match\n\n") } signatureNoRecoverID := signature[:len(signature)-1] verified := crypto.VerifySignature(publicKeyBytes, hash.Bytes(), signatureNoRecoverID) fmt.Println(verified) }
A sample run:
NPrivate key: 00bb19aec0b23e3b0a221fe5c67cd7fe5ec05f882d7d79235b1a0640d3021a4f Message to sign: hello123 Hash: dfd5f2c67870019364f62f07afa806ad52c0999d603f679ea6f7e38dac856291 === Now using Ecrecover === ECDSA Signature: 86507e60b4cc0613bc27b5f1ec39cb2eb4b1f0b5859061196a6bfa15d440c0f00250acb9693c9621facd5436a07d07e7ed07f140008baf1cf5aec12d8e30048201 R: 86507e60b4cc0613bc27b5f1ec39cb2eb4b1f0b5859061196a6bfa15d440c0f0 S: 0250acb9693c9621facd5436a07d07e7ed07f140008baf1cf5aec12d8e300482 V: 01 Original public key: 04dfcfd60cd4d6a918398bf8174cf617097d004ac7c721b529ed9931f3239863a71f66d29e7b4de01b0ac3b0be3a0aa572da8b32cdc3ec9a81be94a5a7a3f62ba1 Recovered public key: 04dfcfd60cd4d6a918398bf8174cf617097d004ac7c721b529ed9931f3239863a71f66d29e7b4de01b0ac3b0be3a0aa572da8b32cdc3ec9a81be94a5a7a3f62ba1 Public keys match === Now using FromECDSAPub === Original public key: 04dfcfd60cd4d6a918398bf8174cf617097d004ac7c721b529ed9931f3239863a71f66d29e7b4de01b0ac3b0be3a0aa572da8b32cdc3ec9a81be94a5a7a3f62ba1 Recovered public key: 04dfcfd60cd4d6a918398bf8174cf617097d004ac7c721b529ed9931f3239863a71f66d29e7b4de01b0ac3b0be3a0aa572da8b32cdc3ec9a81be94a5a7a3f62ba1 Public keys match