github.com/emmansun/gmsm@v0.29.1/cfca/pkcs12_sm2.go (about)

     1  // Package cfca handles cfca issued key and certificate
     2  package cfca
     3  
     4  import (
     5  	"crypto/cipher"
     6  	"encoding/asn1"
     7  	"errors"
     8  	"fmt"
     9  	"math/big"
    10  
    11  	"github.com/emmansun/gmsm/padding"
    12  	"github.com/emmansun/gmsm/pkcs"
    13  	"github.com/emmansun/gmsm/sm2"
    14  	"github.com/emmansun/gmsm/sm3"
    15  	"github.com/emmansun/gmsm/sm4"
    16  	"github.com/emmansun/gmsm/smx509"
    17  )
    18  
    19  // CFCA私有格式,在SADK中把它定义为PKCS12_SM2
    20  
    21  type cfcaKeyPairData struct {
    22  	Version      int `asn1:"default:1"`
    23  	EncryptedKey keyData
    24  	Certificate  certData
    25  }
    26  
    27  // 被加密的私钥数据
    28  type keyData struct {
    29  	ContentType      asn1.ObjectIdentifier
    30  	Algorithm        asn1.ObjectIdentifier
    31  	EncryptedContent asn1.RawValue
    32  }
    33  
    34  // 对应的证书
    35  type certData struct {
    36  	ContentType asn1.ObjectIdentifier
    37  	Content     asn1.RawContent
    38  }
    39  
    40  var (
    41  	oidSM2Data = asn1.ObjectIdentifier{1, 2, 156, 10197, 6, 1, 4, 2, 1}
    42  	oidSM4     = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104} // SADK中认为这就是SM4_CBC,不知道是不是历史原因
    43  	oidSM4CBC  = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 2}
    44  )
    45  
    46  // ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
    47  func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) {
    48  	var keys cfcaKeyPairData
    49  	if _, err := asn1.Unmarshal(data, &keys); err != nil {
    50  		return nil, nil, err
    51  	}
    52  	if !keys.Certificate.ContentType.Equal(oidSM2Data) {
    53  		return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.Certificate.ContentType)
    54  	}
    55  	if !keys.EncryptedKey.ContentType.Equal(oidSM2Data) {
    56  		return nil, nil, fmt.Errorf("cfca: unsupported content type oid <%v>", keys.EncryptedKey.ContentType)
    57  	}
    58  	if !keys.EncryptedKey.Algorithm.Equal(oidSM4) && !keys.EncryptedKey.Algorithm.Equal(oidSM4CBC) {
    59  		return nil, nil, fmt.Errorf("cfca: unsupported algorithm <%v>", keys.EncryptedKey.Algorithm)
    60  	}
    61  	ivkey := sm3.Kdf(password, 32)
    62  	marshalledIV, err := asn1.Marshal(ivkey[:16])
    63  	if err != nil {
    64  		return nil, nil, err
    65  	}
    66  	pk, err := pkcs.SM4CBC.Decrypt(ivkey[16:], &asn1.RawValue{FullBytes: marshalledIV}, keys.EncryptedKey.EncryptedContent.Bytes)
    67  	if err != nil {
    68  		return nil, nil, err
    69  	}
    70  	d := new(big.Int).SetBytes(pk) // here we do NOT check if the d is in (0, N) or not
    71  	// Create private key from *big.Int
    72  	prvKey := new(sm2.PrivateKey)
    73  	prvKey.Curve = sm2.P256()
    74  	prvKey.D = d
    75  	prvKey.PublicKey.X, prvKey.PublicKey.Y = prvKey.ScalarBaseMult(prvKey.D.Bytes())
    76  
    77  	cert, err := smx509.ParseCertificate(keys.Certificate.Content)
    78  	if err != nil {
    79  		return nil, nil, err
    80  	}
    81  
    82  	if !prvKey.PublicKey.Equal(cert.PublicKey) {
    83  		return nil, nil, errors.New("cfca: public key and private key do not match")
    84  	}
    85  	return prvKey, cert, nil
    86  }
    87  
    88  // MarshalSM2 encodes sm2 private key and related certificate to cfca defined format
    89  func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
    90  	if len(password) == 0 {
    91  		return nil, errors.New("cfca: invalid password")
    92  	}
    93  	ivkey := sm3.Kdf(password, 32)
    94  	block, err := sm4.NewCipher(ivkey[16:])
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	mode := cipher.NewCBCEncrypter(block, ivkey[:16])
    99  	pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
   100  	plainText := pkcs7.Pad(key.D.Bytes())
   101  	ciphertext := make([]byte, len(plainText))
   102  	mode.CryptBlocks(ciphertext, plainText)
   103  
   104  	ciphertext, err = asn1.Marshal(ciphertext)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	keys := cfcaKeyPairData{
   110  		Version: 1,
   111  		EncryptedKey: keyData{
   112  			ContentType:      oidSM2Data,
   113  			Algorithm:        oidSM4,
   114  			EncryptedContent: asn1.RawValue{FullBytes: ciphertext},
   115  		},
   116  		Certificate: certData{
   117  			ContentType: oidSM2Data,
   118  			Content:     cert.Raw,
   119  		},
   120  	}
   121  
   122  	return asn1.Marshal(keys)
   123  }