github.com/emmansun/gmsm@v0.29.1/sm2/sm2_envelopedkey.go (about)

     1  package sm2
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/rand"
     7  	"crypto/x509/pkix"
     8  	"encoding/asn1"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  
    13  	"github.com/emmansun/gmsm/cipher"
    14  	"github.com/emmansun/gmsm/sm4"
    15  	"golang.org/x/crypto/cryptobyte"
    16  	cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
    17  )
    18  
    19  var (
    20  	oidSM4    = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104}
    21  	oidSM4ECB = asn1.ObjectIdentifier{1, 2, 156, 10197, 1, 104, 1}
    22  )
    23  
    24  // MarshalEnvelopedPrivateKey, returns sm2 key pair protected data with ASN.1 format:
    25  //
    26  //	SM2EnvelopedKey ::= SEQUENCE {
    27  //	  symAlgID                AlgorithmIdentifier,
    28  //	  symEncryptedKey         SM2Cipher,
    29  //	  sm2PublicKey            SM2PublicKey,
    30  //	  sm2EncryptedPrivateKey  BIT STRING,
    31  //	}
    32  //
    33  // This implementation follows GB/T 35276-2017, uses SM4 cipher to encrypt sm2 private key.
    34  // Please note the standard did NOT clarify if the ECB mode requires padding or not.
    35  //
    36  // This function can be used in CSRResponse.encryptedPrivateKey, reference GM/T 0092-2020 
    37  // Specification of certificate request syntax based on SM2 cryptographic algorithm.
    38  func MarshalEnvelopedPrivateKey(rand io.Reader, pub *ecdsa.PublicKey, tobeEnveloped *PrivateKey) ([]byte, error) {
    39  	// encrypt sm2 private key
    40  	size := (tobeEnveloped.Curve.Params().N.BitLen() + 7) / 8
    41  	if tobeEnveloped.D.BitLen() > size*8 {
    42  		return nil, errors.New("sm2: invalid private key")
    43  	}
    44  	plaintext := tobeEnveloped.D.FillBytes(make([]byte, size))
    45  
    46  	key := make([]byte, sm4.BlockSize)
    47  	if _, err := io.ReadFull(rand, key); err != nil {
    48  		return nil, err
    49  	}
    50  	block, err := sm4.NewCipher(key)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	mode := cipher.NewECBEncrypter(block)
    55  
    56  	encryptedPrivateKey := make([]byte, len(plaintext))
    57  	mode.CryptBlocks(encryptedPrivateKey, plaintext)
    58  
    59  	// encrypt the symmetric key
    60  	encryptedKey, err := EncryptASN1(rand, pub, key)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	symAlgID := pkix.AlgorithmIdentifier{
    66  		Algorithm:  oidSM4ECB,
    67  		Parameters: asn1.NullRawValue,
    68  	}
    69  	symAlgIDBytes, _ := asn1.Marshal(symAlgID)
    70  
    71  	// marshal the result
    72  	var b cryptobyte.Builder
    73  	b.AddASN1(cryptobyte_asn1.SEQUENCE, func(b *cryptobyte.Builder) {
    74  		b.AddBytes(symAlgIDBytes)
    75  		b.AddBytes(encryptedKey)
    76  		b.AddASN1BitString(elliptic.Marshal(tobeEnveloped.Curve, tobeEnveloped.X, tobeEnveloped.Y))
    77  		b.AddASN1BitString(encryptedPrivateKey)
    78  	})
    79  	return b.Bytes()
    80  }
    81  
    82  // ParseEnvelopedPrivateKey, parses and decrypts the enveloped SM2 private key.
    83  // This methed just supports SM4 cipher now.
    84  func ParseEnvelopedPrivateKey(priv *PrivateKey, enveloped []byte) (*PrivateKey, error) {
    85  	// unmarshal the asn.1 data
    86  	var (
    87  		symAlgId                              pkix.AlgorithmIdentifier
    88  		encryptedPrivateKey, pub              asn1.BitString
    89  		inner, symEncryptedKey, symAlgIdBytes cryptobyte.String
    90  	)
    91  	input := cryptobyte.String(enveloped)
    92  	if !input.ReadASN1(&inner, cryptobyte_asn1.SEQUENCE) ||
    93  		!input.Empty() ||
    94  		!inner.ReadASN1Element(&symAlgIdBytes, cryptobyte_asn1.SEQUENCE) ||
    95  		!inner.ReadASN1Element(&symEncryptedKey, cryptobyte_asn1.SEQUENCE) ||
    96  		!inner.ReadASN1BitString(&pub) ||
    97  		!inner.ReadASN1BitString(&encryptedPrivateKey) ||
    98  		!inner.Empty() {
    99  		return nil, errors.New("sm2: invalid asn1 format enveloped key")
   100  	}
   101  
   102  	if _, err := asn1.Unmarshal(symAlgIdBytes, &symAlgId); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if !(symAlgId.Algorithm.Equal(oidSM4) || symAlgId.Algorithm.Equal(oidSM4ECB)) {
   107  		return nil, fmt.Errorf("sm2: unsupported symmetric cipher <%v>", symAlgId.Algorithm)
   108  	}
   109  
   110  	// parse public key
   111  	pubKey, err := NewPublicKey(pub.RightAlign())
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	// decrypt symmetric cipher key
   117  	key, err := priv.Decrypt(rand.Reader, symEncryptedKey, nil)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	// decrypt sm2 private key
   123  	block, err := sm4.NewCipher(key)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	mode := cipher.NewECBDecrypter(block)
   128  	bytes := encryptedPrivateKey.RightAlign()
   129  	plaintext := make([]byte, len(bytes))
   130  	mode.CryptBlocks(plaintext, bytes)
   131  	// Do we need to check length in order to be compatible with some implementations with padding?
   132  	sm2Key, err := NewPrivateKey(plaintext)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	if !sm2Key.PublicKey.Equal(pubKey) {
   137  		return nil, errors.New("sm2: mismatch key pair in enveloped data")
   138  	}
   139  
   140  	return sm2Key, nil
   141  }