...

Source file src/github.com/tjfoc/gmsm/pkcs12/pkcs12.go

Documentation: github.com/tjfoc/gmsm/pkcs12

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package go-pkcs12 implements some of PKCS#12.
     6  //
     7  // This implementation is distilled from https://tools.ietf.org/html/rfc7292
     8  // and referenced documents. It is intended for decoding P12/PFX-stored
     9  // certificates and keys for use with the crypto/tls package.
    10  package pkcs12
    11  
    12  import (
    13  	"crypto/ecdsa"
    14  	"crypto/rand"
    15  	"crypto/rsa"
    16  	"crypto/sha1"
    17  	"crypto/x509"
    18  	"crypto/x509/pkix"
    19  	"encoding/asn1"
    20  	"encoding/hex"
    21  	"encoding/pem"
    22  	"errors"
    23  	"github.com/tjfoc/gmsm/sm2"
    24  x 	"github.com/tjfoc/gmsm/x509"
    25  	"io/ioutil"
    26  )
    27  
    28  var (
    29  	oidDataContentType          = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
    30  	oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
    31  
    32  	oidFriendlyName     = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
    33  	oidLocalKeyID       = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
    34  	oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
    35  )
    36  
    37  type pfxPdu struct {
    38  	Version  int
    39  	AuthSafe contentInfo
    40  	MacData  macData `asn1:"optional"`
    41  }
    42  
    43  type contentInfo struct {
    44  	ContentType asn1.ObjectIdentifier
    45  	Content     asn1.RawValue `asn1:"tag:0,explicit,optional"`
    46  }
    47  
    48  type encryptedData struct {
    49  	Version              int
    50  	EncryptedContentInfo encryptedContentInfo
    51  }
    52  
    53  type encryptedContentInfo struct {
    54  	ContentType                asn1.ObjectIdentifier
    55  	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
    56  	EncryptedContent           []byte `asn1:"tag:0,optional"`
    57  }
    58  
    59  func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
    60  	return i.ContentEncryptionAlgorithm
    61  }
    62  
    63  func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
    64  
    65  func (i *encryptedContentInfo) SetData(data []byte) { i.EncryptedContent = data }
    66  
    67  type safeBag struct {
    68  	Id         asn1.ObjectIdentifier
    69  	Value      asn1.RawValue     `asn1:"tag:0,explicit"`
    70  	Attributes []pkcs12Attribute `asn1:"set,optional"`
    71  }
    72  
    73  type pkcs12Attribute struct {
    74  	Id    asn1.ObjectIdentifier
    75  	Value asn1.RawValue `asn1:"set"`
    76  }
    77  
    78  type encryptedPrivateKeyInfo struct {
    79  	AlgorithmIdentifier pkix.AlgorithmIdentifier
    80  	EncryptedData       []byte
    81  }
    82  
    83  func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
    84  	return i.AlgorithmIdentifier
    85  }
    86  
    87  func (i encryptedPrivateKeyInfo) Data() []byte {
    88  	return i.EncryptedData
    89  }
    90  
    91  func (i *encryptedPrivateKeyInfo) SetData(data []byte) {
    92  	i.EncryptedData = data
    93  }
    94  
    95  // PEM block types
    96  const (
    97  	certificateType = "CERTIFICATE"
    98  	privateKeyType  = "PRIVATE KEY"
    99  )
   100  
   101  // unmarshal calls asn1.Unmarshal, but also returns an error if there is any
   102  // trailing data after unmarshaling.
   103  func unmarshal(in []byte, out interface{}) error {
   104  	trailing, err := asn1.Unmarshal(in, out)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	if len(trailing) != 0 {
   109  		return errors.New("go-pkcs12: trailing data found")
   110  	}
   111  	return nil
   112  }
   113  
   114  // ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
   115  func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
   116  	encodedPassword, err := bmpString(password)
   117  	if err != nil {
   118  		return nil, ErrIncorrectPassword
   119  	}
   120  
   121  	bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
   122  
   123  	blocks := make([]*pem.Block, 0, len(bags))
   124  	for _, bag := range bags {
   125  		block, err := convertBag(&bag, encodedPassword)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		blocks = append(blocks, block)
   130  	}
   131  
   132  	return blocks, nil
   133  }
   134  
   135  func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
   136  	block := &pem.Block{
   137  		Headers: make(map[string]string),
   138  	}
   139  
   140  	for _, attribute := range bag.Attributes {
   141  		k, v, err := convertAttribute(&attribute)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  		block.Headers[k] = v
   146  	}
   147  
   148  	switch {
   149  	case bag.Id.Equal(oidCertBag):
   150  		block.Type = certificateType
   151  		certsData, err := decodeCertBag(bag.Value.Bytes)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		block.Bytes = certsData
   156  	case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
   157  		block.Type = privateKeyType
   158  
   159  		key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
   160  		if err != nil {
   161  			return nil, err
   162  		}
   163  
   164  		switch key := key.(type) {
   165  		case *rsa.PrivateKey:
   166  			block.Bytes = x509.MarshalPKCS1PrivateKey(key)
   167  		case *ecdsa.PrivateKey:
   168  			block.Bytes, err = x509.MarshalECPrivateKey(key)
   169  			if err != nil {
   170  				return nil, err
   171  			}
   172  		default:
   173  			return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
   174  		}
   175  	default:
   176  		return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
   177  	}
   178  	return block, nil
   179  }
   180  
   181  func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
   182  	isString := false
   183  
   184  	switch {
   185  	case attribute.Id.Equal(oidFriendlyName):
   186  		key = "friendlyName"
   187  		isString = true
   188  	case attribute.Id.Equal(oidLocalKeyID):
   189  		key = "localKeyId"
   190  	case attribute.Id.Equal(oidMicrosoftCSPName):
   191  		// This key is chosen to match OpenSSL.
   192  		key = "Microsoft CSP Name"
   193  		isString = true
   194  	default:
   195  		return "", "", errors.New("go-pkcs12: unknown attribute with OID " + attribute.Id.String())
   196  	}
   197  
   198  	if isString {
   199  		if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
   200  			return "", "", err
   201  		}
   202  		if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
   203  			return "", "", err
   204  		}
   205  	} else {
   206  		var id []byte
   207  		if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
   208  			return "", "", err
   209  		}
   210  		value = hex.EncodeToString(id)
   211  	}
   212  
   213  	return key, value, nil
   214  }
   215  
   216  // DecodeAll extracts all certificates and a private key from pfxData.
   217  func DecodeAll(pfxData []byte, password string) (privateKey interface{}, certificate []*x.Certificate, err error) {
   218  	encodedPassword, err := bmpString(password)
   219  	if err != nil {
   220  		return nil, nil, err
   221  	}
   222  
   223  	bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
   224  	if err != nil {
   225  		return nil, nil, err
   226  	}
   227  
   228  	//	if len(bags) != 2 {
   229  	//		err = errors.New("go-pkcs12: expected exactly two safe bags in the PFX PDU")
   230  	//		return
   231  	//	}
   232  
   233  	for _, bag := range bags {
   234  		switch {
   235  		case bag.Id.Equal(oidCertBag):
   236  			if certificate != nil {
   237  				err = errors.New("go-pkcs12: expected exactly one certificate bag")
   238  			}
   239  
   240  			certsData, err := decodeCertBag(bag.Value.Bytes)
   241  			if err != nil {
   242  				return nil, nil, err
   243  			}
   244  			certs, err := x.ParseCertificates(certsData)
   245  			if err != nil {
   246  				return nil, nil, err
   247  			}
   248  			if len(certs) != 1 {
   249  				err = errors.New("go-pkcs12: expected exactly one certificate in the certBag")
   250  				return nil, nil, err
   251  			}
   252  			certificate = append(certificate, certs[0])
   253  
   254  		case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
   255  			if privateKey != nil {
   256  				err = errors.New("go-pkcs12: expected exactly one key bag")
   257  			}
   258  
   259  			if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
   260  				return nil, nil, err
   261  			}
   262  		}
   263  	}
   264  
   265  	if certificate == nil {
   266  		return nil, nil, errors.New("go-pkcs12: certificate missing")
   267  	}
   268  	if privateKey == nil {
   269  		return nil, nil, errors.New("go-pkcs12: private key missing")
   270  	}
   271  
   272  	return
   273  }
   274  
   275  // Decode extracts a certificate and private key from pfxData. This function
   276  // assumes that there is only one certificate and only one private key in the
   277  // pfxData.
   278  func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
   279  	encodedPassword, err := bmpString(password)
   280  	if err != nil {
   281  		return nil, nil, err
   282  	}
   283  
   284  	bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
   285  	if err != nil {
   286  		return nil, nil, err
   287  	}
   288  
   289  	//	if len(bags) != 2 {
   290  	//		err = errors.New("go-pkcs12: expected exactly two safe bags in the PFX PDU")
   291  	//		return
   292  	//	}
   293  
   294  	for _, bag := range bags {
   295  		switch {
   296  		case bag.Id.Equal(oidCertBag):
   297  			if certificate != nil {
   298  				err = errors.New("go-pkcs12: expected exactly one certificate bag")
   299  			}
   300  
   301  			certsData, err := decodeCertBag(bag.Value.Bytes)
   302  			if err != nil {
   303  				return nil, nil, err
   304  			}
   305  			certs, err := x509.ParseCertificates(certsData)
   306  			if err != nil {
   307  				return nil, nil, err
   308  			}
   309  			if len(certs) != 1 {
   310  				err = errors.New("go-pkcs12: expected exactly one certificate in the certBag")
   311  				return nil, nil, err
   312  			}
   313  			certificate = certs[0]
   314  
   315  		case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
   316  			if privateKey != nil {
   317  				err = errors.New("go-pkcs12: expected exactly one key bag")
   318  			}
   319  
   320  			if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
   321  				return nil, nil, err
   322  			}
   323  		}
   324  	}
   325  
   326  	if certificate == nil {
   327  		return nil, nil, errors.New("go-pkcs12: certificate missing")
   328  	}
   329  	if privateKey == nil {
   330  		return nil, nil, errors.New("go-pkcs12: private key missing")
   331  	}
   332  
   333  	return
   334  }
   335  
   336  func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
   337  	pfx := new(pfxPdu)
   338  	if err := unmarshal(p12Data, pfx); err != nil {
   339  		return nil, nil, errors.New("go-pkcs12: error reading P12 data: " + err.Error())
   340  	}
   341  
   342  	if pfx.Version != 3 {
   343  		return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
   344  	}
   345  
   346  	if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
   347  		return nil, nil, NotImplementedError("only password-protected PFX is implemented")
   348  	}
   349  
   350  	// unmarshal the explicit bytes in the content for type 'data'
   351  	if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
   352  		return nil, nil, err
   353  	}
   354  
   355  	if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
   356  		return nil, nil, errors.New("go-pkcs12: no MAC in data")
   357  	}
   358  
   359  	if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
   360  		if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
   361  			// some implementations use an empty byte array
   362  			// for the empty string password try one more
   363  			// time with empty-empty password
   364  			password = nil
   365  			err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
   366  		}
   367  		if err != nil {
   368  			return nil, nil, err
   369  		}
   370  	}
   371  
   372  	var authenticatedSafe []contentInfo
   373  	if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
   374  		return nil, nil, err
   375  	}
   376  
   377  	if len(authenticatedSafe) != 2 {
   378  		return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
   379  	}
   380  
   381  	for _, ci := range authenticatedSafe {
   382  		var data []byte
   383  
   384  		switch {
   385  		case ci.ContentType.Equal(oidDataContentType):
   386  			if err := unmarshal(ci.Content.Bytes, &data); err != nil {
   387  				return nil, nil, err
   388  			}
   389  		case ci.ContentType.Equal(oidEncryptedDataContentType):
   390  			var encryptedData encryptedData
   391  			if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
   392  				return nil, nil, err
   393  			}
   394  			if encryptedData.Version != 0 {
   395  				return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
   396  			}
   397  			if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
   398  				return nil, nil, err
   399  			}
   400  		default:
   401  			return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
   402  		}
   403  
   404  		var safeContents []safeBag
   405  		if err := unmarshal(data, &safeContents); err != nil {
   406  			return nil, nil, err
   407  		}
   408  		bags = append(bags, safeContents...)
   409  	}
   410  
   411  	return bags, password, nil
   412  }
   413  
   414  // Encode produces pfxData containing one private key, an end-entity certificate, and any number of CA certificates.
   415  // It emulates the behavior of OpenSSL's PKCS12_create: it creates two SafeContents: one that's encrypted with RC2
   416  // and contains the certificates, and another that is unencrypted and contains the private key shrouded with 3DES.
   417  // The private key bag and the end-entity certificate bag have the LocalKeyId attribute set to the SHA-1 fingerprint
   418  // of the end-entity certificate.
   419  func Encode(privateKey interface{}, certificate *x.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error) {
   420  	encodedPassword, err := bmpString(password)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	var pfx pfxPdu
   426  	pfx.Version = 3
   427  
   428  	var certFingerprint = sha1.Sum(certificate.Raw)
   429  	var localKeyIdAttr pkcs12Attribute
   430  	localKeyIdAttr.Id = oidLocalKeyID
   431  	localKeyIdAttr.Value.Class = 0
   432  	localKeyIdAttr.Value.Tag = 17
   433  	localKeyIdAttr.Value.IsCompound = true
   434  	if localKeyIdAttr.Value.Bytes, err = asn1.Marshal(certFingerprint[:]); err != nil {
   435  		return nil, err
   436  	}
   437  
   438  	var certBags []safeBag
   439  	var certBag *safeBag
   440  	if certBag, err = makeCertBag(certificate.Raw, []pkcs12Attribute{localKeyIdAttr}); err != nil {
   441  		return nil, err
   442  	}
   443  	certBags = append(certBags, *certBag)
   444  
   445  	for _, cert := range caCerts {
   446  		if certBag, err = makeCertBag(cert.Raw, []pkcs12Attribute{}); err != nil {
   447  			return nil, err
   448  		}
   449  		certBags = append(certBags, *certBag)
   450  	}
   451  
   452  	var keyBag safeBag
   453  	keyBag.Id = oidPKCS8ShroundedKeyBag
   454  	keyBag.Value.Class = 2
   455  	keyBag.Value.Tag = 0
   456  	keyBag.Value.IsCompound = true
   457  	if keyBag.Value.Bytes, err = encodePkcs8ShroudedKeyBag(privateKey, encodedPassword); err != nil {
   458  		return nil, err
   459  	}
   460  	keyBag.Attributes = append(keyBag.Attributes, localKeyIdAttr)
   461  
   462  	// Construct an authenticated safe with two SafeContents.
   463  	// The first SafeContents is encrypted and contains the cert bags.
   464  	// The second SafeContents is unencrypted and contains the shrouded key bag.
   465  	var authenticatedSafe [2]contentInfo
   466  	if authenticatedSafe[0], err = makeSafeContents(certBags, encodedPassword); err != nil {
   467  		return nil, err
   468  	}
   469  	if authenticatedSafe[1], err = makeSafeContents([]safeBag{keyBag}, nil); err != nil {
   470  		return nil, err
   471  	}
   472  
   473  	var authenticatedSafeBytes []byte
   474  	if authenticatedSafeBytes, err = asn1.Marshal(authenticatedSafe[:]); err != nil {
   475  		return nil, err
   476  	}
   477  
   478  	// compute the MAC
   479  	pfx.MacData.Mac.Algorithm.Algorithm = oidSHA1
   480  	pfx.MacData.MacSalt = make([]byte, 8)
   481  	if _, err = rand.Read(pfx.MacData.MacSalt); err != nil {
   482  		return nil, err
   483  	}
   484  	pfx.MacData.Iterations = 1
   485  	if err = computeMac(&pfx.MacData, authenticatedSafeBytes, encodedPassword); err != nil {
   486  		return nil, err
   487  	}
   488  
   489  	pfx.AuthSafe.ContentType = oidDataContentType
   490  	pfx.AuthSafe.Content.Class = 2
   491  	pfx.AuthSafe.Content.Tag = 0
   492  	pfx.AuthSafe.Content.IsCompound = true
   493  	if pfx.AuthSafe.Content.Bytes, err = asn1.Marshal(authenticatedSafeBytes); err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	if pfxData, err = asn1.Marshal(pfx); err != nil {
   498  		return nil, errors.New("go-pkcs12: error writing P12 data: " + err.Error())
   499  	}
   500  	return
   501  }
   502  
   503  func makeCertBag(certBytes []byte, attributes []pkcs12Attribute) (certBag *safeBag, err error) {
   504  	certBag = new(safeBag)
   505  	certBag.Id = oidCertBag
   506  	certBag.Value.Class = 2
   507  	certBag.Value.Tag = 0
   508  	certBag.Value.IsCompound = true
   509  	if certBag.Value.Bytes, err = encodeCertBag(certBytes); err != nil {
   510  		return nil, err
   511  	}
   512  	certBag.Attributes = attributes
   513  	return
   514  }
   515  
   516  func makeSafeContents(bags []safeBag, password []byte) (ci contentInfo, err error) {
   517  	var data []byte
   518  	if data, err = asn1.Marshal(bags); err != nil {
   519  		return
   520  	}
   521  
   522  	if password == nil {
   523  		ci.ContentType = oidDataContentType
   524  		ci.Content.Class = 2
   525  		ci.Content.Tag = 0
   526  		ci.Content.IsCompound = true
   527  		if ci.Content.Bytes, err = asn1.Marshal(data); err != nil {
   528  			return
   529  		}
   530  	} else {
   531  		randomSalt := make([]byte, 8)
   532  		if _, err = rand.Read(randomSalt); err != nil {
   533  			return
   534  		}
   535  
   536  		var algo pkix.AlgorithmIdentifier
   537  		algo.Algorithm = oidPBEWithSHAAnd40BitRC2CBC
   538  		if algo.Parameters.FullBytes, err = asn1.Marshal(pbeParams{Salt: randomSalt, Iterations: 2048}); err != nil {
   539  			return
   540  		}
   541  
   542  		var encryptedData encryptedData
   543  		encryptedData.Version = 0
   544  		encryptedData.EncryptedContentInfo.ContentType = oidDataContentType
   545  		encryptedData.EncryptedContentInfo.ContentEncryptionAlgorithm = algo
   546  		if err = pbEncrypt(&encryptedData.EncryptedContentInfo, data, password); err != nil {
   547  			return
   548  		}
   549  
   550  		ci.ContentType = oidEncryptedDataContentType
   551  		ci.Content.Class = 2
   552  		ci.Content.Tag = 0
   553  		ci.Content.IsCompound = true
   554  		if ci.Content.Bytes, err = asn1.Marshal(encryptedData); err != nil {
   555  			return
   556  		}
   557  	}
   558  	return
   559  }
   560  func SM2P12Encrypt(certificate *x.Certificate, pwd string, priv *sm2.PrivateKey, fileName string) error {
   561  	pfxDataNew, err := Encode(priv, certificate, nil, pwd)
   562  	if err != nil {
   563  		return err
   564  	}
   565  	err = ioutil.WriteFile(fileName, pfxDataNew, 0666)
   566  	return err
   567  }
   568  func SM2P12Decrypt(fileName string, pwd string) (*x.Certificate, *sm2.PrivateKey, error) {
   569  	pfxData, _ := ioutil.ReadFile(fileName)
   570  	pv, cer, err := DecodeAll(pfxData, pwd)
   571  	if err != nil {
   572  		return nil, nil, err
   573  	}
   574  	switch k := pv.(type) {
   575  	case *ecdsa.PrivateKey:
   576  		switch k.Curve {
   577  		case sm2.P256Sm2():
   578  			sm2pub := &sm2.PublicKey{
   579  				Curve: k.Curve,
   580  				X:     k.X,
   581  				Y:     k.Y,
   582  			}
   583  			sm2Pri := &sm2.PrivateKey{
   584  				PublicKey: *sm2pub,
   585  				D:         k.D,
   586  			}
   587  			if !k.IsOnCurve(k.X,k.Y) {
   588  				return nil, nil, errors.New("error while validating SM2 private key: %v")
   589  			}
   590  			return cer[0], sm2Pri, nil
   591  		}
   592  	default:
   593  		return nil, nil, errors.New("unexpected type for p12 private key")
   594  	}
   595  	return nil,nil,nil
   596  }
   597  

View as plain text