如何从 PKCS#12 容器中提取私钥并将其保存为 PKCS#8 格式?
How do I extract the private key from a PKCS#12 container and save it in PKCS#8 format?
我希望能够使用 AWS SNS 和 aws 发送 iOS APNS 推送通知]golang SDK。我按照以下说明创建了一个 p12 文件:https://support-aws.s3.amazonaws.com/Exporting-APNS-Cert-Keychain-Mac.pdf
现在为了获得私钥和证书,我需要执行以下 openssl 等效命令:
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
我无法在 golang 中找到执行此操作的方法,我们将不胜感激。问题似乎是将私钥转换为 pkcs8 格式。
编辑:
这就是我一直在尝试做的事情(为了编译你需要将 github.com/youmark/pkcs8 中的第一个导入更改为 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")
)
我在打印转换后的密钥时得到的是乱码,所以我猜我的解码过程某处有问题。
我想你在那里。您已将密钥转换为 PKCS#8 格式,但它显示为乱码,因为它是以二进制 DER 格式打印的。密钥只需要以 pem 格式编码。
一种测试方法是创建您自己的包含 self signed certificate & key 的 pkcs#12 文件。一个好处是您可以改变到期持续时间来执行您的证书到期错误处理:
go run generate_cert.go -ca -duration 30m -host gooble.com
它生成key.pem和cert.pem。合并密钥和证书:
cat key.pem cert.pem > both.pem
打包到 pkcs#12:
openssl pkcs12 -export -in both.pem -out bundle.12 -nodes -password pass:123456
这里的 bundle.12 是二进制 DER 格式的 pkcs#12 文件,包含证书和私钥,受密码保护。
运行 go 程序(参见下面的源代码)提取证书和密钥:
go run pk.go -in bundle.12 -outkey key8.pem -outcert outcert.pem -password 123456
提取的证书与原始证书相同。提取出来的私钥和原来的私钥很像,只是现在是pkcs#8格式。
您可以从pkcs8 文件中提取原始rsa 密钥。原来的key.pem等同于key.final.pem:
openssl rsa -in key8.pem -out key.final.pem
您还可以验证提取的 pkcs#8 私钥与原始证书具有相同的模数:
openssl x509 -in cert.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
openssl rsa -in key8.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
注意提取的pkcs#8私钥是未加密的;这可能不是您想要的,具体取决于密钥的使用方式。
这是 go 程序 (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")
)
希望对您有所帮助。
我希望能够使用 AWS SNS 和 aws 发送 iOS APNS 推送通知]golang SDK。我按照以下说明创建了一个 p12 文件:https://support-aws.s3.amazonaws.com/Exporting-APNS-Cert-Keychain-Mac.pdf 现在为了获得私钥和证书,我需要执行以下 openssl 等效命令:
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
我无法在 golang 中找到执行此操作的方法,我们将不胜感激。问题似乎是将私钥转换为 pkcs8 格式。
编辑:
这就是我一直在尝试做的事情(为了编译你需要将 github.com/youmark/pkcs8 中的第一个导入更改为 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")
)
我在打印转换后的密钥时得到的是乱码,所以我猜我的解码过程某处有问题。
我想你在那里。您已将密钥转换为 PKCS#8 格式,但它显示为乱码,因为它是以二进制 DER 格式打印的。密钥只需要以 pem 格式编码。
一种测试方法是创建您自己的包含 self signed certificate & key 的 pkcs#12 文件。一个好处是您可以改变到期持续时间来执行您的证书到期错误处理:
go run generate_cert.go -ca -duration 30m -host gooble.com
它生成key.pem和cert.pem。合并密钥和证书:
cat key.pem cert.pem > both.pem
打包到 pkcs#12:
openssl pkcs12 -export -in both.pem -out bundle.12 -nodes -password pass:123456
这里的 bundle.12 是二进制 DER 格式的 pkcs#12 文件,包含证书和私钥,受密码保护。
运行 go 程序(参见下面的源代码)提取证书和密钥:
go run pk.go -in bundle.12 -outkey key8.pem -outcert outcert.pem -password 123456
提取的证书与原始证书相同。提取出来的私钥和原来的私钥很像,只是现在是pkcs#8格式。
您可以从pkcs8 文件中提取原始rsa 密钥。原来的key.pem等同于key.final.pem:
openssl rsa -in key8.pem -out key.final.pem
您还可以验证提取的 pkcs#8 私钥与原始证书具有相同的模数:
openssl x509 -in cert.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
openssl rsa -in key8.pem -noout -modulus
Modulus=AEB5770C4DA8D...05E12398BE1
注意提取的pkcs#8私钥是未加密的;这可能不是您想要的,具体取决于密钥的使用方式。
这是 go 程序 (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")
)
希望对您有所帮助。