I'm using crypto/rsa
, and trying to find a way to properly save and load a key. Is there a correct way to create a []byte
from an rsa.PrivateKey
. If so, is there a way to properly do so for an rsa.PublicKey
?
Thank you all very much.
I'm using crypto/rsa
, and trying to find a way to properly save and load a key. Is there a correct way to create a []byte
from an rsa.PrivateKey
. If so, is there a way to properly do so for an rsa.PublicKey
?
Thank you all very much.
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.
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")
}
}
||
not &&
–
Waters Type: "PUBLIC KEY",
–
Antivenin 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:
PS: MarshalPKIXPublicKey also accepts ECDSA keys, ajust the pem header appropriately.
math/big.Int
implements the [encoding/gob.GobEncoder]
interface, the [encoding.TextMarshaler]
interface and the [encoding/json.Marshaler]
interface. I would say you could use these encoders/decoders.
Here is a verification code you may use,
func TestRsaSaveLoad(t *testing.T) {
pk0, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
t.Fatal(err.Error())
}
bytes, err := json.Marshal(pk0)
if err != nil {
t.Fatal(err.Error())
}
pk1 := &rsa.PrivateKey{}
err = json.Unmarshal(bytes, &pk1)
if err != nil {
t.Fatal(err.Error())
}
err = pk1.Validate()
if err != nil {
t.Fatal(err.Error())
}
if !pk0.Equal(pk1) {
t.Fatal("keys mismatch")
}
}
© 2022 - 2024 — McMap. All rights reserved.
x509.MarshalPKIXPublicKey
– Cortico