1 Star 1 Fork 0

zhouhuo-dev/gmsm

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
pkcs12.go 16.48 KB
一键复制 编辑 原始数据 按行查看 历史
周霍 提交于 2020-06-22 16:34 +08:00 . 修改导入包数据
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package go-pkcs12 implements some of PKCS#12.
//
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
// and referenced documents. It is intended for decoding P12/PFX-stored
// certificates and keys for use with the crypto/tls package.
package pkcs12
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"errors"
"io/ioutil"
"gitee.com/zhouhuodev/gmsm/sm2"
)
var (
oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
)
type pfxPdu struct {
Version int
AuthSafe contentInfo
MacData macData `asn1:"optional"`
}
type contentInfo struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawValue `asn1:"tag:0,explicit,optional"`
}
type encryptedData struct {
Version int
EncryptedContentInfo encryptedContentInfo
}
type encryptedContentInfo struct {
ContentType asn1.ObjectIdentifier
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedContent []byte `asn1:"tag:0,optional"`
}
func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
return i.ContentEncryptionAlgorithm
}
func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data }
type safeBag struct {
Id asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"tag:0,explicit"`
Attributes []pkcs12Attribute `asn1:"set,optional"`
}
type pkcs12Attribute struct {
Id asn1.ObjectIdentifier
Value asn1.RawValue `asn1:"set"`
}
type encryptedPrivateKeyInfo struct {
AlgorithmIdentifier pkix.AlgorithmIdentifier
EncryptedData []byte
}
func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
return i.AlgorithmIdentifier
}
func (i encryptedPrivateKeyInfo) Data() []byte {
return i.EncryptedData
}
func (i *encryptedPrivateKeyInfo) SetData(data []byte) {
i.EncryptedData = data
}
// PEM block types
const (
certificateType = "CERTIFICATE"
privateKeyType = "PRIVATE KEY"
)
// unmarshal calls asn1.Unmarshal, but also returns an error if there is any
// trailing data after unmarshaling.
func unmarshal(in []byte, out interface{}) error {
trailing, err := asn1.Unmarshal(in, out)
if err != nil {
return err
}
if len(trailing) != 0 {
return errors.New("go-pkcs12: trailing data found")
}
return nil
}
// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, ErrIncorrectPassword
}
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
blocks := make([]*pem.Block, 0, len(bags))
for _, bag := range bags {
block, err := convertBag(&bag, encodedPassword)
if err != nil {
return nil, err
}
blocks = append(blocks, block)
}
return blocks, nil
}
func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
block := &pem.Block{
Headers: make(map[string]string),
}
for _, attribute := range bag.Attributes {
k, v, err := convertAttribute(&attribute)
if err != nil {
return nil, err
}
block.Headers[k] = v
}
switch {
case bag.Id.Equal(oidCertBag):
block.Type = certificateType
certsData, err := decodeCertBag(bag.Value.Bytes)
if err != nil {
return nil, err
}
block.Bytes = certsData
case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
block.Type = privateKeyType
key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
if err != nil {
return nil, err
}
switch key := key.(type) {
case *rsa.PrivateKey:
block.Bytes = x509.MarshalPKCS1PrivateKey(key)
case *ecdsa.PrivateKey:
block.Bytes, err = x509.MarshalECPrivateKey(key)
if err != nil {
return nil, err
}
default:
return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
}
default:
return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
}
return block, nil
}
func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
isString := false
switch {
case attribute.Id.Equal(oidFriendlyName):
key = "friendlyName"
isString = true
case attribute.Id.Equal(oidLocalKeyID):
key = "localKeyId"
case attribute.Id.Equal(oidMicrosoftCSPName):
// This key is chosen to match OpenSSL.
key = "Microsoft CSP Name"
isString = true
default:
return "", "", errors.New("go-pkcs12: unknown attribute with OID " + attribute.Id.String())
}
if isString {
if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
return "", "", err
}
if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
return "", "", err
}
} else {
var id []byte
if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
return "", "", err
}
value = hex.EncodeToString(id)
}
return key, value, nil
}
// DecodeAll extracts all certificates and a private key from pfxData.
func DecodeAll(pfxData []byte, password string) (privateKey interface{}, certificate []*sm2.Certificate, err error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, nil, err
}
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
if err != nil {
return nil, nil, err
}
// if len(bags) != 2 {
// err = errors.New("go-pkcs12: expected exactly two safe bags in the PFX PDU")
// return
// }
for _, bag := range bags {
switch {
case bag.Id.Equal(oidCertBag):
if certificate != nil {
err = errors.New("go-pkcs12: expected exactly one certificate bag")
}
certsData, err := decodeCertBag(bag.Value.Bytes)
if err != nil {
return nil, nil, err
}
certs, err := sm2.ParseCertificates(certsData)
if err != nil {
return nil, nil, err
}
if len(certs) != 1 {
err = errors.New("go-pkcs12: expected exactly one certificate in the certBag")
return nil, nil, err
}
certificate = append(certificate, certs[0])
case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
if privateKey != nil {
err = errors.New("go-pkcs12: expected exactly one key bag")
}
if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
return nil, nil, err
}
}
}
if certificate == nil {
return nil, nil, errors.New("go-pkcs12: certificate missing")
}
if privateKey == nil {
return nil, nil, errors.New("go-pkcs12: private key missing")
}
return
}
// Decode extracts a certificate and private key from pfxData. This function
// assumes that there is only one certificate and only one private key in the
// pfxData.
func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, nil, err
}
bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
if err != nil {
return nil, nil, err
}
// if len(bags) != 2 {
// err = errors.New("go-pkcs12: expected exactly two safe bags in the PFX PDU")
// return
// }
for _, bag := range bags {
switch {
case bag.Id.Equal(oidCertBag):
if certificate != nil {
err = errors.New("go-pkcs12: expected exactly one certificate bag")
}
certsData, err := decodeCertBag(bag.Value.Bytes)
if err != nil {
return nil, nil, err
}
certs, err := x509.ParseCertificates(certsData)
if err != nil {
return nil, nil, err
}
if len(certs) != 1 {
err = errors.New("go-pkcs12: expected exactly one certificate in the certBag")
return nil, nil, err
}
certificate = certs[0]
case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
if privateKey != nil {
err = errors.New("go-pkcs12: expected exactly one key bag")
}
if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
return nil, nil, err
}
}
}
if certificate == nil {
return nil, nil, errors.New("go-pkcs12: certificate missing")
}
if privateKey == nil {
return nil, nil, errors.New("go-pkcs12: private key missing")
}
return
}
func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
pfx := new(pfxPdu)
if err := unmarshal(p12Data, pfx); err != nil {
return nil, nil, errors.New("go-pkcs12: error reading P12 data: " + err.Error())
}
if pfx.Version != 3 {
return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
}
if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
return nil, nil, NotImplementedError("only password-protected PFX is implemented")
}
// unmarshal the explicit bytes in the content for type 'data'
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
return nil, nil, err
}
if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
return nil, nil, errors.New("go-pkcs12: no MAC in data")
}
if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
// some implementations use an empty byte array
// for the empty string password try one more
// time with empty-empty password
password = nil
err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
}
if err != nil {
return nil, nil, err
}
}
var authenticatedSafe []contentInfo
if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
return nil, nil, err
}
if len(authenticatedSafe) != 2 {
return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
}
for _, ci := range authenticatedSafe {
var data []byte
switch {
case ci.ContentType.Equal(oidDataContentType):
if err := unmarshal(ci.Content.Bytes, &data); err != nil {
return nil, nil, err
}
case ci.ContentType.Equal(oidEncryptedDataContentType):
var encryptedData encryptedData
if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
return nil, nil, err
}
if encryptedData.Version != 0 {
return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
}
if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
return nil, nil, err
}
default:
return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
}
var safeContents []safeBag
if err := unmarshal(data, &safeContents); err != nil {
return nil, nil, err
}
bags = append(bags, safeContents...)
}
return bags, password, nil
}
// Encode produces pfxData containing one private key, an end-entity certificate, and any number of CA certificates.
// It emulates the behavior of OpenSSL's PKCS12_create: it creates two SafeContents: one that's encrypted with RC2
// and contains the certificates, and another that is unencrypted and contains the private key shrouded with 3DES.
// The private key bag and the end-entity certificate bag have the LocalKeyId attribute set to the SHA-1 fingerprint
// of the end-entity certificate.
func Encode(privateKey interface{}, certificate *sm2.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) {
encodedPassword, err := bmpString(password)
if err != nil {
return nil, err
}
var pfx pfxPdu
pfx.Version = 3
var certFingerprint = sha1.Sum(certificate.Raw)
var localKeyIdAttr pkcs12Attribute
localKeyIdAttr.Id = oidLocalKeyID
localKeyIdAttr.Value.Class = 0
localKeyIdAttr.Value.Tag = 17
localKeyIdAttr.Value.IsCompound = true
if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil {
return nil, err
}
var certBags []safeBag
var certBag *safeBag
if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil {
return nil, err
}
certBags = append(certBags, *certBag)
for _, cert := range caCerts {
if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil {
return nil, err
}
certBags = append(certBags, *certBag)
}
var keyBag safeBag
keyBag.Id = oidPKCS8ShroundedKeyBag
keyBag.Value.Class = 2
keyBag.Value.Tag = 0
keyBag.Value.IsCompound = true
if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(privateKey, encodedPassword); err != nil {
return nil, err
}
keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr)
// Construct an authenticated safe with two SafeContents.
// The first SafeContents is encrypted and contains the cert bags.
// The second SafeContents is unencrypted and contains the shrouded key bag.
var authenticatedSafe [2]contentInfo
if authenticatedSafe[0], err = makeSafeContents(certBags, encodedPassword); err != nil {
return nil, err
}
if authenticatedSafe[1], err = makeSafeContents([]safeBag{keyBag}, nil); err != nil {
return nil, err
}
var authenticatedSafeBytes []byte
if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil {
return nil, err
}
// compute the MAC
pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1
pfx.MacData.MacSalt = make([]byte, 8)
if _, err = rand.Read(pfx.MacData.MacSalt); err != nil {
return nil, err
}
pfx.MacData.Iterations = 1
if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil {
return nil, err
}
pfx.AuthSafe.ContentType = oidDataContentType
pfx.AuthSafe.Content.Class = 2
pfx.AuthSafe.Content.Tag = 0
pfx.AuthSafe.Content.IsCompound = true
if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil {
return nil, err
}
if pfxData, err = asn1.Marshal(pfx); err != nil {
return nil, errors.New("go-pkcs12: error writing P12 data: " + err.Error())
}
return
}
func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) {
certBag = new(safeBag)
certBag.Id = oidCertBag
certBag.Value.Class = 2
certBag.Value.Tag = 0
certBag.Value.IsCompound = true
if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil {
return nil, err
}
certBag.Attributes = attributes
return
}
func makeSafeContents(bags []safeBag, password []byte) (ci contentInfo, err error) {
var data []byte
if data, err = asn1.Marshal(bags); err != nil {
return
}
if password == nil {
ci.ContentType = oidDataContentType
ci.Content.Class = 2
ci.Content.Tag = 0
ci.Content.IsCompound = true
if ci.Content.Bytes, err = asn1.Marshal(data); err != nil {
return
}
} else {
randomSalt := make([]byte, 8)
if _, err = rand.Read(randomSalt); err != nil {
return
}
var algo pkix.AlgorithmIdentifier
algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC
if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil {
return
}
var encryptedData encryptedData
encryptedData.Version = 0
encryptedData.EncryptedContentInfo.ContentType = oidDataContentType
encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo
if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil {
return
}
ci.ContentType = oidEncryptedDataContentType
ci.Content.Class = 2
ci.Content.Tag = 0
ci.Content.IsCompound = true
if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil {
return
}
}
return
}
func SM2P12Encrypt(certificate *sm2.Certificate, pwd string, priv *sm2.PrivateKey, fileName string) error {
pfxDataNew, err := Encode(priv, certificate, nil, pwd)
if err != nil {
return err
}
err = ioutil.WriteFile(fileName, pfxDataNew, 0666)
return err
}
func SM2P12Decrypt(fileName string, pwd string) (*sm2.Certificate, *sm2.PrivateKey, error) {
pfxData, _ := ioutil.ReadFile(fileName)
pv, cer, err := DecodeAll(pfxData, pwd)
if err != nil {
return nil, nil, err
}
switch k := pv.(type) {
case *ecdsa.PrivateKey:
switch k.Curve {
case sm2.P256Sm2():
sm2pub := &sm2.PublicKey{
Curve: k.Curve,
X: k.X,
Y: k.Y,
}
sm2Pri := &sm2.PrivateKey{
PublicKey: *sm2pub,
D: k.D,
}
if !k.IsOnCurve(k.X, k.Y) {
return nil, nil, errors.New("error while validating SM2 private key: %v")
}
return cer[0], sm2Pri, nil
}
default:
return nil, nil, errors.New("unexpected type for p12 private key")
}
return nil, nil, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/zhouhuodev/gmsm.git
git@gitee.com:zhouhuodev/gmsm.git
zhouhuodev
gmsm
gmsm
71cb6201d680

搜索帮助