How do I convert from an *ecdsa.PublicKey to crypto/ecdh *ecdh.PublicKey?

150 Views Asked by At

This is my old code.

    PKCS8GCSprvkey, _ := x509.ParsePKCS8PrivateKey([]byte(privateKeyFile))
    GCSprvkey, _ := PKCS8GCSprvkey.(*ecdsa.PrivateKey)
 

    GCSpubkey, _ := os.ReadFile("public_key.txt")
    Clientkeybuffer := ciphertext[0:len(GCSpubkey)]
    parseClientpub, _ := x509.ParsePKIXPublicKey(Clientkeybuffer)
    Clientpubkey, _ := parseClientpub.(*ecdsa.PublicKey)
 
    GCSsecret, _ := GCSprvkey.PublicKey.Curve.ScalarMult(Clientpubkey.X, Clientpubkey.Y, GCSprvkey.D.Bytes())
    GCSsecretstring := GCSsecret.Text(16)

But the method

GCSprvkey.PublicKey.Curve.ScalarMult(Clientpubkey.X, Clientpubkey.Y, GCSprvkey.D.Bytes())

.ScalarMult() is deprecated and it says "this is a low-Level unsafe API. For ECDH, use the crypto/ecdh package. Most of uses of ScalarMult can be replaced by a call to the ECDH methods of NIST curves in crypto/ECDH." . I'm not sure how to use the crypto/ecdh of golang to achieve the same thing. I tried to load my public key file into an *ecdh.PublicKey but I keep getting invalid public key error.

I've tried to convert the ecdsa.PublicKey to ecdh.PublicKey but it failed.

1

There are 1 best solutions below

0
chuckx On

The short answer is that in the ecdsa package, there are methods which convert each key type to their counterparts in the the ecdh package. This extra conversion step is mentioned in the ecdh documentation.

From ecdh.PrivateKey:

For NIST curves, they then need to be converted with ecdsa.PrivateKey.ECDH after parsing.

From ecdh.PublicKey:

For NIST curves, they then need to be converted with ecdsa.PublicKey.ECDH after parsing.


Taking your code example, here's how you'd parse the keys using x509, convert them to ecdh keys, and then compute a shared secret.

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "fmt"
    "log"
)

func main() {
    k1PrivateDER, k2PublicDER := mustGenerateKeyPair()

    privateKey, err := x509.ParsePKCS8PrivateKey(k1PrivateDER)
    if err != nil {
        log.Fatalf("Failed to parse private key: %v", err)
    }
    ecdsaPrivateKey, ok := privateKey.(*ecdsa.PrivateKey)
    if !ok {
        log.Fatalf("Private key is not an ECDSA key.")
    }
    ecdhPrivateKey, err := ecdsaPrivateKey.ECDH()
    if err != nil {
        log.Fatalf("Failed to convert to ECDH key: %v", err)
    }

    publicKey, err := x509.ParsePKIXPublicKey(k2PublicDER)
    if err != nil {
        log.Fatalf("Failed to parse public key: %v", err)
    }
    ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        log.Fatalf("Public key is not an ECDSA key.")
    }
    ecdhPublicKey, err := ecdsaPublicKey.ECDH()
    if err != nil {
        log.Fatalf("Failed to convert to ECDH key: %v", err)
    }

    secret, err := ecdhPrivateKey.ECDH(ecdhPublicKey)
    if err != nil {
        log.Fatalf("Failed to compute ECDH shared secret: %v", err)
    }

    fmt.Printf("secret = %x\n", secret)
}

func mustGenerateKeyPair() (k1PrivateDER, k2PublicDER []byte) {
    k1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    k1PrivateDER, err = x509.MarshalPKCS8PrivateKey(k1)
    if err != nil {
        log.Fatal(err)
    }

    k2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    k2PublicDER, err = x509.MarshalPKIXPublicKey(k2.Public())
    if err != nil {
        log.Fatal(err)
    }

    return k1PrivateDER, k2PublicDER
}

Output:

secret = 4c1858f80818886fd2978d4c1198aa59aa70865702367bfe8326bb20c4837424

Go Playground