How to read an RSA key from file - go

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-----

A combination of pem.Decode and x509.ParsePKCS1PrivateKey should do the trick:
package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
)
func main() {
pemString := `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDLets8+7M+iAQAqN/5BVyCIjhTQ4cmXulL+gm3v0oGMWzLupUS
v8KPA+Tp7dgC/DZPfMLaNH1obBBhJ9DhS6RdS3AS3kzeFrdu8zFHLWF53DUBhS92
5dCAEuJpDnNizdEhxTfoHrhuCmz8l2nt1pe5eUK2XWgd08Uc93h5ij098wIDAQAB
AoGAHLaZeWGLSaen6O/rqxg2laZ+jEFbMO7zvOTruiIkL/uJfrY1kw+8RLIn+1q0
wLcWcuEIHgKKL9IP/aXAtAoYh1FBvRPLkovF1NZB0Je/+CSGka6wvc3TGdvppZJe
rKNcUvuOYLxkmLy4g9zuY5qrxFyhtIn2qZzXEtLaVOHzPQECQQDvN0mSajpU7dTB
w4jwx7IRXGSSx65c+AsHSc1Rj++9qtPC6WsFgAfFN2CEmqhMbEUVGPv/aPjdyWk9
pyLE9xR/AkEA2cGwyIunijE5v2rlZAD7C4vRgdcMyCf3uuPcgzFtsR6ZhyQSgLZ8
YRPuvwm4cdPJMmO3YwBfxT6XGuSc2k8MjQJBAI0+b8prvpV2+DCQa8L/pjxp+VhR
Xrq2GozrHrgR7NRokTB88hwFRJFF6U9iogy9wOx8HA7qxEbwLZuhm/4AhbECQC2a
d8h4Ht09E+f3nhTEc87mODkl7WJZpHL6V2sORfeq/eIkds+H6CJ4hy5w/bSw8tjf
sz9Di8sGIaUbLZI2rd0CQQCzlVwEtRtoNCyMJTTrkgUuNufLP19RZ5FpyXxBO5/u
QastnN77KfUwdj3SJt44U/uh1jAIv4oSLBr8HYUkbnI8
-----END RSA PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
fmt.Println(key.N)
}
If what you are sitting on is a PKCS#8 encoded key, instead you would do something like the following:
func main() {
pemString := `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKhPSTDs4cpKfnMc
p86fCkpnuER7bGc+mGkhkw6bE+BnROfrDCFBSjrENLS5JcsenANQ1kYGt9iVW2fd
ZAWUdDoj+t7g6+fDpzY1BzPSUls421Dmu7joDPY8jSdMzFCeg7Lyj0I36bJJ7ooD
VPW6Q0XQcb8FfBiFPAKuY4elj/YDAgMBAAECgYBo2GMWmCmbM0aL/KjH/KiTawMN
nfkMY6DbtK9/5LjADHSPKAt5V8ueygSvI7rYSiwToLKqEptJztiO3gnls/GmFzj1
V/QEvFs6Ux3b0hD2SGpGy1m6NWWoAFlMISRkNiAxo+AMdCi4I1hpk4+bHr9VO2Bv
V0zKFxmgn1R8qAR+4QJBANqKxJ/qJ5+lyPuDYf5s+gkZWjCLTC7hPxIJQByDLICw
iEnqcn0n9Gslk5ngJIGQcKBXIp5i0jWSdKN/hLxwgHECQQDFKGmo8niLzEJ5sa1r
spww8Hc2aJM0pBwceshT8ZgVPnpgmITU1ENsKpJ+y1RTjZD6N0aj9gS9UB/UXdTr
HBezAkEAqkDRTYOtusH9AXQpM3zSjaQijw72Gs9/wx1RxOSsFtVwV6U97CLkV1S+
2HG1/vn3w/IeFiYGfZXLKFR/pA5BAQJAbFeu6IaGM9yFUzaOZDZ8mnAqMp349t6Q
DB5045xJxLLWsSpfJE2Y12H1qvO1XUzYNIgXq5ZQOHBFbYA6txBy/QJBAKDRQN47
6YClq9652X+1lYIY/h8MxKiXpVZVncXRgY6pbj4pmWEAM88jra9Wq6R77ocyECzi
XCqi18A/sl6ymWc=
-----END PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
parseResult, _ := x509.ParsePKCS8PrivateKey(block.Bytes)
key := parseResult.(*rsa.PrivateKey)
fmt.Println(key.N)
}

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

Retrieve public SSH key from private key file using Go

I have a private key file id_rsa (starts with -----BEGIN RSA PRIVATE KEY-----). With the tool ssh-keygen I am able to generate an SSH public key using the following command:
ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
The generated file will have the following content:
ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9j...
I am trying to achieve the same within my Go code. The code will be executed on AWS Lambda so I want to avoid executing system commands with os.exec since I don't have control over the underlying environment.
Given that I have a variable private_key, how can I extract the ssh public key from it?
This is my solution to extract the public key in OpenSSH format, using packages rsa, pem and x509 from the standard library and golang.org/x/crypto/ssh
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"os"
"golang.org/x/crypto/ssh"
)
func check(e error) {
if e != nil {
panic(e)
}
}
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 PublicPEMtoOpenSSH(rsaPubKey *rsa.PublicKey) (string, error) {
pub, err := ssh.NewPublicKey(rsaPubKey)
if err != nil {
return "", err
}
sshPubKey := ssh.MarshalAuthorizedKey(pub)
return string(sshPubKey), nil
}
func main() {
private_PEM, err := os.ReadFile("path/to/file.pem")
check(err)
priv_parsed, err := ParseRsaPrivateKeyFromPemStr(string(private_PEM))
check(err)
openssh_key, err := PublicPEMtoOpenSSH(&priv_parsed.PublicKey)
check(err)
print(openssh_key) // ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA...
}

How to check pkcs format of a private key using golang code?

I am trying find the encryption format of a private key whether it is pkcs1 or pkcs8. Is there any golang code that helps me identify it
func main() {
pemString := `-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,1ADE9ED351F5B8C7
z8nRX5jG2eUxcahgzF/Sn47DWXKs/y+pOcyYzTA3eBYwkqnjOzSqK9YbV8irAAZm
uwwoGBCjn7740nCo+pEYe+ECC4MJBrAl6uPcX6eL0ltj1En5gqFB6bPqFrVa8uuf
MDoSGJzIcDl9kuLK4xS0q9TE7K5ayrzfn7ecn6/KHjqgIdGtEsrZT1Ajhm6Quubs
PhCRTldky/o1LegEoooUm2s509EGX2PixAWfTS36Z3UENOSWAgHJCdfybDR7+jvP
lC9ZHXD/OVcgDVC+K3UjF7kV5HMD+CosuCwR6oXHEiKGv6X6rW46wyB17ul/zDA5
6MbQeEDDE3uEa+cpAGzeYwudCRttclsBlLKBDHv2DgjI+yoGU/GuwuV5Gd9y4fYw
5ZakGhezbm9W5g4XiuA3wTM9j4x2hNIB2Bexu8b9KlZwJnXZOY8glDcfdT5+kmQL
CY4LlqZQU/YXFLoGpChidQmIQhxzVsYRx46NK0l+hrrJJ2xGz+Cj5AhqomOjq69y
5AAjL3THAj1ys2dy54Ue1m1T4w12Oz+fAICVC2mb966821cUjkSzC2QVGqE+UdrS
C9qaMtBvKxumN/88Pl3SsO1tDpgSG4OTcr+Hyeh20fg3wVnFPZJZyWDVnv4cXrVs
SYAk7m3ngAvr+XOu8WOdOc8ouuiWlaFhKNpGC6MZT1FKCCD53VANeehdBbYngmrR
oZAuQBtS1aJpMpP2zcsXcexcuZrHSdVlaU1Cs3JykIXt1kmJDKAcyz1C8dRA23fI
QlP3Q+lbohQpWdS8hSDhetxUaCYHIYHJL3fp/0hsOWrd2oJ7ukNBEPIaEK9FnKHe
nrAp6spTrtCAFum6UAqiV6eoAyX2SwIJepPivgxcquA7rIeO/D4OZCHRsQtLMFPn
FG7KYJ0ZcAincs5AKX67hxrZBLt1VvjvqyifdhFEJoCI0vwza1Q9tFtrkJ4dXr5W
n5wnS8dSZnin9JWYCf1ltxVwPNxlii1jwYmIKM49D3qcmSsKsWNNrAW/nvrkOP0R
CasZKUGgpYu6cu0T1JYayZyb5gWjawDUkZpdd0E/RS/xk5qvJ6jrcu8QoiOo0s+f
EizdBvhiLuA6SNWHj/ksXf3seTTdHxDdG91iea5t+vK1ZJoJC9PTxB18ws6BALCH
3DGXBo1vdoz7HVTq0YeTwavzhY9l3UYPcNeM6+blEuy7S0p5OoavVevynKXp+/Ct
gsODMh6Xhz1kjPIDfvuA36HJiB84FGED6d2s+7MOTh/z7p5d1OfUMu+0IsyDR6Un
p17G82ihgZlE9MbJVnAGSpUPQd6gmtdFh7jgu2S7SYqph5P6s/aVlZsH36xVgdRG
3pdo+7qhuWulBwN9dsdSigy0CLAOiGJAU3Pxu1QdKiwE+/Qs6LmLRkLWbd4S42N2
Ur1xwXiBC8Qz35JZpE6+MyvR4jUybSC6IMhWPHrpQ2sVw6HFlRvPN78egO9zuO6v
UTqL30g3J5+SS+ulLm16xwRgBAiLBYgvDLpfsQB3G3rShOyiNvvOneGZ8FZIerpB
RVpv18PvAvhFICyUuxVkvTm6j1BObmMvoefPOUWeZHsE+Miu/N2UoQ==
-----END RSA PRIVATE KEY-----`
block, _ := pem.Decode([]byte(pemString))
_, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
fmt.Println(err)
fmt.Println("hi")
} else {
fmt.Println("bye")
}
}
asn1: structure error: length too large
hi
Just parse the key, err comes if mismatch.
func pkcs() {
var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex)
_, err := x509.ParsePKCS1PrivateKey(derBytes)
if err != nil {
log.Fatalln(err)
return
}
}
2022/08/10 15:59:08 x509: failed to parse private key (use ParsePKCS8PrivateKey instead for this key format)
EDIT:
For encrypted private key, need to decrypt first:
bytes, err := x509.DecryptPEMBlock(block, []byte("password"))
if err != nil {
log.Fatalln(err)
return
}
_, err = x509.ParsePKCS1PrivateKey(bytes)

OpenSSL SHA256 signature

I have a private key in a file -----BEGIN PRIVATE KEY-----\nxxx-----END PRIVATE KEY-----\n
I am trying to perform the below which is in Ruby in Go and got stuck as Go accepts only pem file. Kindly suggest.
azure_certificate_private_key = OpenSSL::PKey.read(private_key_file)
base64_signature = Base64.strict_encode64(private_key.sign(OpenSSL::Digest::SHA256.new, headerPayloadConcatenated))
I have tried as,
signBytes, err := ioutil.ReadFile(privKeyPath)
signer, err := ParsePrivateKey(signBytes)
no key found error
package main
import (
"encoding/pem"
"crypto/x509"
"io/ioutil"
)
func main() {
signBytes, err := ioutil.ReadFile(privKeyPath)
if err != nil {
panic(err) //or do whatever with err
}
keyRSA := parseRSAKey(signBytes)
//keyRSA is a *rsa.PrivateKey
//....use the key
}
func parseRSAKey(b []byte) *rsa.PrivateKey {
block, _ := pem.Decode(b)
if block == nil {
panic("no PEM block")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
return key
}
this should work to parse a RSA private key.

How to create RSA private key with passphrase in Go?

How to create RSA private key with passphrase in Go?
I read the documentation for the crypto package but I was unable to piece together a solution from it.
First step, generate a private key.
Second step, convert it to PEM format. Third step, encrypt the PEM.
All can be done using Golang's standard library, which is very complete. The code is nothing difficult so I put it here. All there is to it is to know which functions to use.
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
)
func PrivateKeyToEncryptedPEM(bits int, pwd string) ([]byte, error) {
// Generate the key of length bits
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
// Convert it to pem
block := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
// Encrypt the pem
if pwd != "" {
block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(pwd), x509.PEMCipherAES256)
if err != nil {
return nil, err
}
}
return pem.EncodeToMemory(block), nil
}

How do I extract the private key from a PKCS#12 container and save it in PKCS#8 format?

I want to be able to send iOS APNS push notifications using AWS SNS with the aws golang SDK. I've created a p12 file following this instructions: https://support-aws.s3.amazonaws.com/Exporting-APNS-Cert-Keychain-Mac.pdf
now in order to get the private key and cert I need to implement the following openssl equivalent commands:
openssl pkcs12 -in MyCertificates.p12 -out MyCer.pem -clcerts -nokeys
openssl pkcs12 -in MyCertificates.p12 -out MyKey.pem -nocerts -nodes
openssl pkcs8 -topk8 -inform pem -in MyKey.pem -outform pem -nocrypt -out MyKeyCorrectFormat.pem
I can't find a way to do this in golang, any help will be appreciated. What seems to be the issue is converting the private key to pkcs8 format.
EDIT:
This is what I have been trying to do (in order to compile you need to change the first import in github.com/youmark/pkcs8 to golang.org/x/crypto/pbkdf2) :
import (
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"github.com/youmark/pkcs8"
"golang.org/x/crypto/pkcs12"
)
func main() {
b, err := ioutil.ReadFile("myP12File.p12")
if err != nil {
fmt.Println(err)
return
}
password := "123456"
_, pKey, err := Decode(b, password)
pKeyPkcs8, err := pkcs8.ConvertPrivateKeyToPKCS8(pKey, passwordBytes)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(pKeyPkcs8))
}
// Decode and verify an in memory .p12 certificate (DER binary format).
func Decode(p12 []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
// decode an x509.Certificate to verify
privateKey, cert, err := pkcs12.Decode(p12, password)
if err != nil {
return nil, nil, err
}
if err := verify(cert); err != nil {
return nil, nil, err
}
// assert that private key is RSA
priv, ok := privateKey.(*rsa.PrivateKey)
if !ok {
return nil, nil, errors.New("expected RSA private key type")
}
return cert, priv, nil
}
// verify checks if a certificate has expired
func verify(cert *x509.Certificate) error {
_, err := cert.Verify(x509.VerifyOptions{})
if err == nil {
return nil
}
switch e := err.(type) {
case x509.CertificateInvalidError:
switch e.Reason {
case x509.Expired:
return ErrExpired
default:
return err
}
case x509.UnknownAuthorityError:
// Apple cert isn't in the cert pool
// ignoring this error
return nil
default:
return err
}
}
// Certificate errors
var (
ErrExpired = errors.New("certificate has expired or is not yet valid")
)
What I get when printing the converted key is gibberish, so I guess there is something wrong with my decoding process somewhere.
I think you're there. You've converted the key to PKCS#8 format, but it's displaying as gibberish because it's printed in binary DER form. The key just needs to be encoded in pem format.
One way to test this is by creating your own pkcs#12 file containing a self signed certificate & key. A benefit is you can vary the expiry duration to exercise your certificate expiry error handling:
go run generate_cert.go -ca -duration 30m -host gooble.com
It generates key.pem and cert.pem. Combine key & cert:
cat key.pem cert.pem > both.pem
Bundle into pkcs#12:
openssl pkcs12 -export -in both.pem -out bundle.12 -nodes -password pass:123456
Here bundle.12 is a pkcs#12 file in binary DER form containing a certificate and private key, protected by a password.
Run the go program (see source below) to extract the certificate and key:
go run pk.go -in bundle.12 -outkey key8.pem -outcert outcert.pem -password 123456
The extracted certificate is identical to the original certificate. The extracted private key is similar to the original private key, but now in pkcs#8 format.
You can extract the original rsa key from the pkcs8 file. The original key.pem is identical to key.final.pem:
openssl rsa -in key8.pem -out key.final.pem
You could also verify the extracted pkcs#8 private key has the same modulus as the original certificate:
openssl x509 -in cert.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
openssl rsa -in key8.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
Note that the extracted pkcs#8 private key is unencrypted; that may not be what you want depending on how the key's going to be used.
Here's a slightly modified version of the go program (pk.go):
package main
import (
"crypto/x509"
"encoding/pem"
"errors"
"flag"
"github.com/youmark/pkcs8"
"golang.org/x/crypto/pkcs12"
"io/ioutil"
"log"
"os"
)
var (
in = flag.String("in", "", "pkcs#12 input file (private key and certificate only)")
password = flag.String("password", "", "to unlock the pkcs#12 bundle")
outkey = flag.String("outkey", "", "output filename of private key in pkcs#8 PEM format")
outcert = flag.String("outcert", "", "output filename of certificate in PEM format")
)
func main() {
flag.Parse()
if *in == "" || *password == "" || *outkey == "" || *outcert == "" {
flag.Usage()
os.Exit(1)
}
data, err := ioutil.ReadFile(*in)
if err != nil {
log.Fatal(err)
}
privateKey, certificate, err := pkcs12.Decode(data, *password)
if err != nil {
log.Fatal(err)
}
if err := verify(certificate); err != nil {
log.Fatal(err)
}
keyBytes, err := pkcs8.ConvertPrivateKeyToPKCS8(privateKey)
if err != nil {
log.Fatal(err)
}
//could write private key as binary DER encoded (instead of pem below)
//_, err = ioutil.WriteFile(*outkey,keyBytes,0644)
//write private key as pem
keyFile, err := os.Create(*outkey)
if err != nil {
log.Fatal(err)
}
defer keyFile.Close()
err = pem.Encode(keyFile, &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
if err != nil {
log.Fatal(err)
}
certFile, err := os.Create(*outcert)
if err != nil {
log.Fatal(err)
}
defer certFile.Close()
err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certificate.Raw})
if err != nil {
log.Fatal(err)
}
}
// verify checks if a certificate has expired
func verify(cert *x509.Certificate) error {
_, err := cert.Verify(x509.VerifyOptions{})
if err == nil {
return nil
}
switch e := err.(type) {
case x509.CertificateInvalidError:
switch e.Reason {
case x509.Expired:
return ErrExpired
default:
return err
}
case x509.UnknownAuthorityError:
// Apple cert isn't in the cert pool
// ignoring this error
return nil
default:
return err
}
}
// Certificate errors
var (
ErrExpired = errors.New("certificate has expired or is not yet valid")
)
Hope that helps.

Resources