Go introduced a new library called Elliptic Curve Diffie Hellman(crypto/ecdh) in v1.20.
Let’s see how to use this library to exchange encrypted data between two entities without sharing the secret that was used to encrypt the data.
Generate public key on the client Link to heading
clientCurve := ecdh.P256()
clientPrivKey, err := clientCurve.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Error: %v", err)
}
clientPubKey := clientPrivKey.PublicKey()
Generate public key on the server Link to heading
serverCurve := ecdh.P256()
serverPrivKey, err := serverCurve.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Error: %v", err)
}
serverPubKey := serverPrivKey.PublicKey()
The clientPubkey and serverPubKey can be shared over the network as plain text.
Generate Secret using the public key Link to heading
Client generates the clientSecret using the server’s public key.
clientSecret, err := clientPrivKey.ECDH(serverPubKey)
if err != nil {
t.Fatalf("Error: %v", err)
}
Server generates the serverSecret using the client’s public key
serverSecret, err := serverPrivKey.ECDH(clientPubKey)
if err != nil {
t.Fatalf("Error: %v", err)
}
Sharing Encrypted Data Link to heading
Using the clientSecret the client can encrypt data and share over the network while the server will be able to decrypt the data using the serverSecret.
What was wonderful about this is the fact that the secret in itself was not shared over the network.
Conclusion Link to heading
Diffie-Hellman key exchange algorithm is used more often than we realize. It is a very nice addition to the Go standard library.
Full Code Link to heading
package main
import (
"bytes"
"crypto/ecdh"
"crypto/rand"
"log"
)
func main() {
clientCurve := ecdh.P256()
clientPrivKey, err := clientCurve.GenerateKey(rand.Reader)
if err != nil {
log.Fatalf("Error: %v", err)
}
clientPubKey := clientPrivKey.PublicKey()
serverCurve := ecdh.P256()
serverPrivKey, err := serverCurve.GenerateKey(rand.Reader)
if err != nil {
log.Fatalf("Error: %v", err)
}
serverPubKey := serverPrivKey.PublicKey()
clientSecret, err := clientPrivKey.ECDH(serverPubKey)
if err != nil {
log.Fatalf("Error: %v", err)
}
serverSecret, err := serverPrivKey.ECDH(clientPubKey)
if err != nil {
log.Fatalf("Error: %v", err)
}
if !bytes.Equal(clientSecret, serverSecret) {
log.Fatalf("The secrets do not match")
}
log.Printf("The secrets match")
}