Save and load crypto/rsa PrivateKey to and from the disk

Here's code snippet that shows the import and export of both public and private keys. It's based on the other answers which were super helpful, as well as copy-pasta from the official docs.

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "errors"
    "fmt"
)

func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey) {
    privkey, _ := rsa.GenerateKey(rand.Reader, 4096)
    return privkey, &privkey.PublicKey
}

func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
    privkey_bytes := x509.MarshalPKCS1PrivateKey(privkey)
    privkey_pem := pem.EncodeToMemory(
            &pem.Block{
                    Type:  "RSA PRIVATE KEY",
                    Bytes: privkey_bytes,
            },
    )
    return string(privkey_pem)
}

func ParseRsaPrivateKeyFromPemStr(privPEM string) (*rsa.PrivateKey, error) {
    block, _ := pem.Decode([]byte(privPEM))
    if block == nil {
            return nil, errors.New("failed to parse PEM block containing the key")
    }

    priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
            return nil, err
    }

    return priv, nil
}

func ExportRsaPublicKeyAsPemStr(pubkey *rsa.PublicKey) (string, error) {
    pubkey_bytes, err := x509.MarshalPKIXPublicKey(pubkey)
    if err != nil {
            return "", err
    }
    pubkey_pem := pem.EncodeToMemory(
            &pem.Block{
                    Type:  "RSA PUBLIC KEY",
                    Bytes: pubkey_bytes,
            },
    )

    return string(pubkey_pem), nil
}

func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
    block, _ := pem.Decode([]byte(pubPEM))
    if block == nil {
            return nil, errors.New("failed to parse PEM block containing the key")
    }

    pub, err := x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
            return nil, err
    }

    switch pub := pub.(type) {
    case *rsa.PublicKey:
            return pub, nil
    default:
            break // fall through
    }
    return nil, errors.New("Key type is not RSA")
}

func main() {

    // Create the keys
    priv, pub := GenerateRsaKeyPair()

    // Export the keys to pem string
    priv_pem := ExportRsaPrivateKeyAsPemStr(priv)
    pub_pem, _ := ExportRsaPublicKeyAsPemStr(pub)

    // Import the keys from pem string
    priv_parsed, _ := ParseRsaPrivateKeyFromPemStr(priv_pem)
    pub_parsed, _ := ParseRsaPublicKeyFromPemStr(pub_pem)

    // Export the newly imported keys
    priv_parsed_pem := ExportRsaPrivateKeyAsPemStr(priv_parsed)
    pub_parsed_pem, _ := ExportRsaPublicKeyAsPemStr(pub_parsed)

    fmt.Println(priv_parsed_pem)
    fmt.Println(pub_parsed_pem)

    // Check that the exported/imported keys match the original keys
    if priv_pem != priv_parsed_pem || pub_pem != pub_parsed_pem {
            fmt.Println("Failure: Export and Import did not result in same Keys")
    } else {
            fmt.Println("Success")
    }
}

FYI: I polished up @David W 's answer and put it in a tiny Go Module. https://github.com/rokk4/gorsapemhelpers


You need some sort of format to marshal the key into. One format supported by the Go standard library can be found here: http://golang.org/pkg/crypto/x509/#MarshalPKCS1PrivateKey

func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte

The inverse function is http://golang.org/pkg/crypto/x509/#ParsePKCS1PrivateKey.

func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)

However, it is relatively standard to encode the marshaled key into a PEM file.

pemdata := pem.EncodeToMemory(
    &pem.Block{
        Type: "RSA PRIVATE KEY",
        Bytes: x509.MarshalPKCS1PrivateKey(key),
    },
)

You can find a full example here.


Since the public key part of your question wasn't answered and I just ran into the same problem and solved it, here it is:

Note the & in front of the Argument to MarshalPKIXPublicKey

Priv := rsa.GenerateKey(rand.Reader, 4096)

pubASN1, err := x509.MarshalPKIXPublicKey(&Priv.PublicKey)
if err != nil {
    // do something about it
}

pubBytes := pem.EncodeToMemory(&pem.Block{
    Type:  "RSA PUBLIC KEY",
    Bytes: pubASN1,
})

ioutil.WriteFile("key.pub", pubBytes, 0644)

Relevant reads:

  • MarshalPKIXPublicKey(pub interface{}) ([]byte, error) godoc
  • EncodeToMemory(b *Block) []byte godoc
  • Block godoc

PS: MarshalPKIXPublicKey also accepts ECDSA keys, ajust the pem header appropriately.