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

如何从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.

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

报告相同问题?

悬赏问题

  • ¥30 Matlab打开默认名称带有/的光谱数据
  • ¥50 easyExcel模板 动态单元格合并列
  • ¥15 res.rows如何取值使用
  • ¥15 在odoo17开发环境中,怎么实现库存管理系统,或独立模块设计与AGV小车对接?开发方面应如何设计和开发?请详细解释MES或WMS在与AGV小车对接时需完成的设计和开发
  • ¥15 CSP算法实现EEG特征提取,哪一步错了?
  • ¥15 游戏盾如何溯源服务器真实ip?需要30个字。后面的字是凑数的
  • ¥15 vue3前端取消收藏的不会引用collectId
  • ¥15 delphi7 HMAC_SHA256方式加密
  • ¥15 关于#qt#的问题:我想实现qcustomplot完成坐标轴
  • ¥15 下列c语言代码为何输出了多余的空格