dpfl37651 2016-02-23 23:49
浏览 613
已采纳

如何从PKCS#12容器中提取私钥并将其保存为PKCS#8格式?

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 (
"golang.org/x/crypto/pkcs12"
"io/ioutil"
"fmt"
"encoding/pem"
"github.com/aws/aws-sdk-go/service/sns"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/aws"
"crypto/x509"
"crypto/rsa"
"errors"
"github.com/youmark/pkcs8"
)
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条回答 默认 最新

  • douhan9467 2016-03-01 03:30
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 springboot+vue 集成keycloak sso到阿里云
  • ¥15 win7系统进入桌面过一秒后突然黑屏
  • ¥30 backtrader对于期货交易的现金和资产计算的问题
  • ¥15 求C# .net4.8小报表工具
  • ¥15 安装虚拟机时出现问题
  • ¥15 Selenium+docker Chrome不能运行
  • ¥15 mac电脑,安装charles后无法正常抓包
  • ¥18 visio打开文件一直显示文件未找到
  • ¥15 请教一下,openwrt如何让同一usb储存设备拔插后设备符号不变?
  • ¥50 使用quartz框架进行分布式任务定时调度,启动了两个实例,但是只有一个实例参与调度,另外一个实例没有参与调度,不知道是为什么?请各位帮助看一下原因!!