24

I am using the ecdsa.GenerateKey method to generate a private/public key pair in Go. I would like to store the private key in a file on the users computer, and load it whenever the program starts. There is a method elliptic.Marshal that marshals the public key, but nothing for the private key. Should I simply roll my own, or is there a recommended way to store the private key?

Sam Lee
  • 9,913
  • 15
  • 48
  • 56
  • 2
    Other programs that use asymmetric crypto (such as OpenSSH) usually store the private key in a file accessible only to the user (and root on linux / unix systems, Administrator on Windows). On Linux / Unix this is usually achieved with something like `chmod 600 ~/private.key`. More secure systems will use key provider services or systems to provide the private key from a completely separate system. These bring a bunch of their own caveats and cautions with them. – Intermernet Jan 26 '14 at 07:13

3 Answers3

43

Here is a code sample that demonstrates encoding and decoding of keys in Go. It helps to know that you need to connect couple of steps. Crypto algorithm is the fist step, in this case ECDSA key. Then you need standard encoding, x509 is most commontly used standard. Finally you need a file format, PEM is again commonly used one. This is currently most commonly used combination, but feel free to substitute any other algoriths or encoding.

func encode(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (string, string) {
    x509Encoded, _ := x509.MarshalECPrivateKey(privateKey)
    pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded})

    x509EncodedPub, _ := x509.MarshalPKIXPublicKey(publicKey)
    pemEncodedPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: x509EncodedPub})

    return string(pemEncoded), string(pemEncodedPub)
}

func decode(pemEncoded string, pemEncodedPub string) (*ecdsa.PrivateKey, *ecdsa.PublicKey) {
    block, _ := pem.Decode([]byte(pemEncoded))
    x509Encoded := block.Bytes
    privateKey, _ := x509.ParseECPrivateKey(x509Encoded)

    blockPub, _ := pem.Decode([]byte(pemEncodedPub))
    x509EncodedPub := blockPub.Bytes
    genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)
    publicKey := genericPublicKey.(*ecdsa.PublicKey)

    return privateKey, publicKey
}

func test() {
    privateKey, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
    publicKey := &privateKey.PublicKey

    encPriv, encPub := encode(privateKey, publicKey)

    fmt.Println(encPriv)
    fmt.Println(encPub)

    priv2, pub2 := decode(encPriv, encPub)

    if !reflect.DeepEqual(privateKey, priv2) {
        fmt.Println("Private keys do not match.")
    }
    if !reflect.DeepEqual(publicKey, pub2) {
        fmt.Println("Public keys do not match.")
    }
}
mikijov
  • 1,552
  • 24
  • 37
  • Might be a newbie question -- why do these keys need to be encoded? I created my keypair, printed them out to stdout (the underlying fields are `big.Int`). Then to load, I initialized `ecdsa.PrivateKey|PublicKey` structs and populated their fields, but that didn't work. However when I encoded like your solution suggests, it worked. – Brian Luong Nov 04 '21 at 18:06
  • "printing big.Int to stdout" is a type of encoding. You came up with your own. Difference is basically that the standard encodings work everywhere and yours is specific to Go and big.Int. While you can use yours, using steps above is easy, so once you set it up you are standard and not inventing your own things. – mikijov Nov 09 '21 at 03:44
10

I believe the standard format for those keys is to use the X.509 ASN.1 DER representation. See http://golang.org/pkg/crypto/x509/#MarshalECPrivateKey and http://golang.org/pkg/crypto/x509/#ParseECPrivateKey.

Russ Cox
  • 1,851
  • 13
  • 12
0

I adapted a really quick and dirty way to do it, as suggested by one of the geth team in late '15 in my library https://github.com/DaveAppleton/ether_go

it is a far simpler solution (but puts keys in plain sight)

Dave Appleton
  • 465
  • 1
  • 6
  • 18