How to read an RSA key from file

Viewed 21379

I need to read in an RSA private key from a file to sign a JWT. I have found some examples on how to save a generated RSA key to disk but nothing showing how to build a key struct based on a pre-generated key from a file.

The key is generated like this:

openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt

Example key:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClHYNDPVSF‌​FmWF 
oKGTqd/n7Dt2+tGXh97KJjVLAqCBZZHlQJ534v2OzFjTgzuMNehD9Y6HnkYF‌​dkRb 
QzYi2YDROOzRl1bhyyWPA35OGf50r7LiNvSvNPNtswsCuK7ywOcH0yEMKSiW‌​4q5R 
GKYi42w961EcTQQPrfihavY+c2FYPv4+pXymzaIz9hGBPLHwaHq/QTAyHxPC‌​fkOo 
s/x3mxUVd7Ni2bz1VJGlyqcNEeU88wTAYMmv8oQ3y2NfKExtYn+W6TCDiq/+‌​ZkOp 
wacuAU0J7tCNgcXvkq39KH5uza2uSiTniye6uhlkvYWD3s9riIIiekTEiHk/‌​kkc6 
jMg8HN/7AgMBAAECggEBAJ12u8vQHV6esUrymaTdCG+BVmRtZpyA ... 
-----END RSA PRIVATE KEY-----
3 Answers

There is a helpful function ssh.ParseRawPrivateKey which already implements some of the required logic. I suggest building upon it, especially if your key is used as a SSH key, which in my case it is.

import (
    "os"
    "crypto/rsa"
    "golang.org/x/crypto/ssh"
    "io/ioutil"
)

func mustNot(err error) {
    if err != nil {
        panic(err)
    }
}

func loadRsaPrivateKey() *rsa.PrivateKey {
    bytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.ssh/id_rsa")
    mustNot(err)
    key, err := ssh.ParseRawPrivateKey(bytes)
    mustNot(err)
    return key.(*rsa.PrivateKey)
}

Here's a full example for both private and public pem files:

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "io/ioutil"
    "log"
)

func RSAConfigSetup(rsaPrivateKeyLocation, privatePassphrase, rsaPublicKeyLocation string) (*rsa.PrivateKey, error) {
    if rsaPrivateKeyLocation == "" {
        log.Println("No RSA Key given, generating temp one")
        return GenRSA(4096)
    }

    priv, err := ioutil.ReadFile(rsaPrivateKeyLocation)
    if err != nil {
        log.Println("No RSA private key found, generating temp one")
        return GenRSA(4096)
    }

    privPem, _ := pem.Decode(priv)

    if privPem.Type != "RSA PRIVATE KEY" {
        log.Println("RSA private key is of the wrong type", privPem.Type)
    }

    if x509.IsEncryptedPEMBlock(privPem) && privatePassphrase == "" {
        log.Println("Passphrase is required to open private pem file")
        return GenRSA(4096)
    }

    var privPemBytes []byte

    if privatePassphrase != "" {
        privPemBytes, err = x509.DecryptPEMBlock(privPem, []byte(privatePassphrase))
    } else {
        privPemBytes = privPem.Bytes
    }

    var parsedKey interface{}
    //PKCS1
    if parsedKey, err = x509.ParsePKCS1PrivateKey(privPemBytes); err != nil {
        //If what you are sitting on is a PKCS#8 encoded key
        if parsedKey, err = x509.ParsePKCS8PrivateKey(privPemBytes); err != nil { // note this returns type `interface{}`
            log.Println("Unable to parse RSA private key, generating a temp one", err)
            return GenRSA(4096)
        }
    }

    var privateKey *rsa.PrivateKey
    var ok bool
    privateKey, ok = parsedKey.(*rsa.PrivateKey)
    if !ok {
        log.Println("Unable to parse RSA private key, generating a temp one", err)
        return GenRSA(4096)
    }

    pub, err := ioutil.ReadFile(rsaPublicKeyLocation)
    if err != nil {
        log.Println("No RSA public key found, generating temp one")
        return GenRSA(4096)
    }
    pubPem, _ := pem.Decode(pub)
    if pubPem == nil {
        log.Println("Use `ssh-keygen -f id_rsa.pub -e -m pem > id_rsa.pem` to generate the pem encoding of your RSA public key - rsa public key not in pem format")
        return GenRSA(4096)
    }

    if pubPem.Type != "RSA PUBLIC KEY" {
        log.Println("RSA public key is of the wrong type", pubPem.Type)
        return GenRSA(4096)
    }

    if parsedKey, err = x509.ParsePKIXPublicKey(pubPem.Bytes); err != nil {
        log.Println("Unable to parse RSA public key, generating a temp one", err)
        return GenRSA(4096)
    }

    var pubKey *rsa.PublicKey
    if pubKey, ok = parsedKey.(*rsa.PublicKey); !ok {
        log.Println("Unable to parse RSA public key, generating a temp one", err)
        return GenRSA(4096)
    }

    privateKey.PublicKey = *pubKey

    return privateKey, nil
}

// GenRSA returns a new RSA key of bits length
func GenRSA(bits int) (*rsa.PrivateKey, error) {
    key, err := rsa.GenerateKey(rand.Reader, bits)
    return key, err
}
Related