omar
omar

Reputation: 53

How to marshal an ECC public or private key

Hello i'am trying to unmarshal object that contain ECC public key but i go umarshaling error saying that can't unmarshal object properly. i'am tring to do the followeing :

var privateKey *ecdsa.PrivateKey
var publicKey ecdsa.PublicKey
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
    log.Fatalf("Failed to generate ECDSA key: %s\n", err)
}
publicKey = privateKey.PublicKey

marshalledKey, err := json.Marshal(publicKey)
if err != nil {
    panic(err)
}

var unmarshalledKey ecdsa.PublicKey
err2 := json.Unmarshal(marshalledKey, &unmarshalledKey)
if err2 != nil {
    panic(err2)
}

and tthe error returned from (err2) is :

panic: json: cannot unmarshal object into Go struct field PublicKey.Curve of type elliptic.Curve

and there is no way in either eliptoc or 509x function to umarshal and the curve value is alwayes null

Upvotes: 1

Views: 1854

Answers (2)

smiletrl
smiletrl

Reputation: 396

For public key, it looks like we only need to save the point (X, Y), and the Curve type. If we know what curve is for the original private key, then we only need to save the public point into persistent storage.

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "encoding/json"
    "log"
    "math/big"
)

type P256PublicKey struct {
    X, Y *big.Int
}

func (p *P256PublicKey) Key() *ecdsa.PublicKey {
    return &ecdsa.PublicKey{
        Curve: elliptic.P256(),
        X:     p.X,
        Y:     p.Y,
    }
}

func main() {
    privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        panic(err)
    }
    pub := &privKey.PublicKey

    key := P256PublicKey{
        X: pub.X,
        Y: pub.Y,
    }

    // Get bytes and save
    bz, err := json.Marshal(key)
    if err != nil {
        panic(err)
    }
    nk := P256PublicKey{}

    err = json.Unmarshal(bz, &nk)
    if err != nil {
        panic(err)
    }

    newPub := key.Key()

    log.Printf("is equal: %v", newPub.Equal(pub))
}

More discussion at this issue https://github.com/golang/go/issues/33564#issuecomment-2249743482

Upvotes: 0

omar
omar

Reputation: 53

i found the answer finally and i will post it in case somone need it. all we need is just to creat another struct, lets name it "retrieve" and declare the value it wil read from the unmarshal. now the ECC public key contain 3 values "curve" and will append it as json:"Curve", "Y" ans will append it as json:"Y" and we will do the same for "X"

and it will work like this :

type retrieve struct {
    CurveParams *elliptic.CurveParams `json:"Curve"`
    MyX         *big.Int              `json:"X"`
    MyY         *big.Int              `json:"Y"`
}

//UnmarshalECCPublicKey extract ECC public key from marshaled objects
func UnmarshalECCPublicKey(object []byte) (pub ecdsa.PublicKey) {
    var public ecdsa.PublicKey
    rt := new(retrieve)

    errmarsh := json.Unmarshal(object, &rt)
    if errmarsh != nil {
        fmt.Println("err at UnmarshalECCPublicKey()")
        panic(errmarsh)
    }

    public.Curve = rt.Key.CurveParams
    public.X = rt.Key.MyX
    public.Y = rt.Key.MyY
    mapstructure.Decode(public, &pub)

    fmt.Println("Unmarshalled ECC public key : ", pub)
    return
}

we will pass the the marshalled public key to UnmarshalECCPublicKey() and it will return the correct unmarshaled obj

so the coorect use will be :

var privateKey *ecdsa.PrivateKey
var publicKey ecdsa.PublicKey
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
    log.Fatalf("Failed to generate ECDSA key: %s\n", err)
}
publicKey = privateKey.PublicKey

marshalledKey, err := json.Marshal(publicKey)
if err != nil {
    panic(err)
}

var unmarshalledKey ecdsa.PublicKey
unmarshalledKey = UnmarshalECCPublicKey(marshalledKey)

and that should do it, u will get the ECC public unmarshalled successfully.

Upvotes: 2

Related Questions