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")
}