英文:
How do I extract the private key from a PKCS#12 container and save it in PKCS#8 format?
问题
我想要使用AWS SNS和AWS golang SDK发送iOS APNS推送通知。我已经按照这个指南创建了一个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")
)
当我打印转换后的密钥时,得到的是乱码,所以我猜我的解码过程中出了问题。
英文:
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.
答案1
得分: 5
我认为你已经到达了目标。你已经将密钥转换为PKCS#8格式,但由于以二进制DER形式打印出来,所以显示的是无意义的字符。密钥只需要以PEM格式进行编码。
测试的一种方法是创建一个包含自签名证书和密钥的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")
)
希望对你有所帮助。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论