github.com/emmansun/gmsm@v0.29.1/pkcs7/envelope.go (about)

     1  package pkcs7
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/x509/pkix"
     8  	"encoding/asn1"
     9  	"errors"
    10  
    11  	"github.com/emmansun/gmsm/pkcs"
    12  	"github.com/emmansun/gmsm/sm2"
    13  	"github.com/emmansun/gmsm/smx509"
    14  )
    15  
    16  type EnvelopedData struct {
    17  	ed                   envelopedData
    18  	key                  []byte
    19  	contentType          asn1.ObjectIdentifier
    20  	encryptedContentType asn1.ObjectIdentifier
    21  }
    22  
    23  type envelopedData struct {
    24  	Version              int
    25  	RecipientInfos       []recipientInfo `asn1:"set"`
    26  	EncryptedContentInfo encryptedContentInfo
    27  }
    28  
    29  type recipientInfo struct {
    30  	Version                int
    31  	IssuerAndSerialNumber  issuerAndSerial
    32  	KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
    33  	EncryptedKey           []byte
    34  }
    35  
    36  type encryptedContentInfo struct {
    37  	ContentType                asn1.ObjectIdentifier
    38  	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
    39  	EncryptedContent           asn1.RawValue `asn1:"tag:0,optional"`
    40  }
    41  
    42  func (data envelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo {
    43  	for _, recp := range data.RecipientInfos {
    44  		if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
    45  			return &recp
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  // GetRecipients returns the list of recipients (READONLY) for the enveloped data
    52  func (data envelopedData) GetRecipients() ([]IssuerAndSerial, error) {
    53  	var recipients []IssuerAndSerial
    54  	for _, recp := range data.RecipientInfos {
    55  		recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber))
    56  	}
    57  	return recipients, nil
    58  }
    59  
    60  func (data envelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
    61  	return &data.EncryptedContentInfo
    62  }
    63  
    64  // ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
    65  // content with an unsupported algorithm.
    66  var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, AES-GCM, SM4-CBC and SM4-GCM supported")
    67  
    68  // Encrypt creates and returns an envelope data PKCS7 structure with encrypted
    69  // recipient keys for each recipient public key.
    70  //
    71  // # The algorithm used to perform encryption is determined by the argument cipher
    72  //
    73  // TODO(fullsailor): Add support for encrypting content with other algorithms
    74  func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
    75  	ed, err := NewEnvelopedData(cipher, content)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	for _, recipient := range recipients {
    80  		if err := ed.AddRecipient(recipient, 0, func(cert *smx509.Certificate, key []byte) ([]byte, error) {
    81  			return encryptKey(key, cert, false)
    82  		}); err != nil {
    83  			return nil, err
    84  		}
    85  	}
    86  	return ed.Finish()
    87  }
    88  
    89  // EncryptSM creates and returns an envelope data PKCS7 structure with encrypted
    90  // recipient keys for each recipient public key.
    91  // The OIDs use GM/T 0010 - 2012 set and the encrypted key use ASN.1 format.
    92  //
    93  // The algorithm used to perform encryption is determined by the argument cipher
    94  func EncryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
    95  	return encryptSM(cipher, content, recipients, false)
    96  }
    97  
    98  // EncryptCFCA creates and returns an envelope data PKCS7 structure with encrypted
    99  // recipient keys for each recipient public key.
   100  // The OIDs use GM/T 0010 - 2012 set and the encrypted key use C1C2C3 format and without 0x4 prefix.
   101  //
   102  // The algorithm used to perform encryption is determined by the argument cipher
   103  func EncryptCFCA(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
   104  	return encryptSM(cipher, content, recipients, true)
   105  }
   106  
   107  func encryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate, isLegacyCFCA bool) ([]byte, error) {
   108  	ed, err := NewSM2EnvelopedData(cipher, content)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	for _, recipient := range recipients {
   113  		if err := ed.AddRecipient(recipient, 1, func(cert *smx509.Certificate, key []byte) ([]byte, error) {
   114  			return encryptKey(key, cert, isLegacyCFCA)
   115  		}); err != nil {
   116  			return nil, err
   117  		}
   118  	}
   119  	return ed.Finish()
   120  }
   121  
   122  // NewEnvelopedData creates a new EnvelopedData structure with the provided cipher and content.
   123  func NewEnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) {
   124  	var key []byte
   125  	var err error
   126  
   127  	// Create key
   128  	key = make([]byte, cipher.KeySize())
   129  	if _, err = rand.Read(key); err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	id, ciphertext, err := cipher.Encrypt(rand.Reader, key, content)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	ed := &EnvelopedData{}
   138  	ed.contentType = OIDEnvelopedData
   139  	ed.encryptedContentType = OIDData
   140  	ed.key = key
   141  	ed.ed = envelopedData{
   142  		Version:              0,
   143  		EncryptedContentInfo: newEncryptedContent(ed.encryptedContentType, id, marshalEncryptedContent(ciphertext)),
   144  	}
   145  	return ed, nil
   146  }
   147  
   148  // NewSM2EnvelopedData creates a new EnvelopedData structure with the provided cipher and content.
   149  // The OIDs use GM/T 0010 - 2012 set.
   150  func NewSM2EnvelopedData(cipher pkcs.Cipher, content []byte) (*EnvelopedData, error) {
   151  	var key []byte
   152  	var err error
   153  
   154  	// Create key
   155  	key = make([]byte, cipher.KeySize())
   156  	if _, err = rand.Read(key); err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	id, ciphertext, err := cipher.Encrypt(rand.Reader, key, content)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	ed := &EnvelopedData{}
   165  	ed.contentType = SM2OIDEnvelopedData
   166  	ed.encryptedContentType = SM2OIDData
   167  	ed.key = key
   168  	ed.ed = envelopedData{
   169  		Version:              1,
   170  		EncryptedContentInfo: newEncryptedContent(ed.encryptedContentType, id, marshalEncryptedContent(ciphertext)),
   171  	}
   172  	return ed, nil
   173  }
   174  
   175  // AddRecipient adds a recipient to the EnvelopedData structure.
   176  func (ed *EnvelopedData) AddRecipient(cert *smx509.Certificate, version int, encryptKeyFunc func(cert *smx509.Certificate, key []byte) ([]byte, error)) error {
   177  	encrypted, err := encryptKeyFunc(cert, ed.key)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	ias, err := cert2issuerAndSerial(cert)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	var keyEncryptionAlgorithm asn1.ObjectIdentifier = OIDEncryptionAlgorithmRSA
   186  	if cert.SignatureAlgorithm == smx509.SM2WithSM3 {
   187  		keyEncryptionAlgorithm = OIDKeyEncryptionAlgorithmSM2
   188  	}
   189  
   190  	info := recipientInfo{
   191  		Version:               version,
   192  		IssuerAndSerialNumber: ias,
   193  		KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   194  			Algorithm: keyEncryptionAlgorithm,
   195  		},
   196  		EncryptedKey: encrypted,
   197  	}
   198  	ed.ed.RecipientInfos = append(ed.ed.RecipientInfos, info)
   199  	return nil
   200  }
   201  
   202  // Finish creates the final PKCS7 structure.
   203  func (ed *EnvelopedData) Finish() ([]byte, error) {
   204  	innerContent, err := asn1.Marshal(ed.ed)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	// Prepare outer payload structure
   210  	wrapper := contentInfo{
   211  		ContentType: ed.contentType,
   212  		Content:     asn1.RawValue{Class: asn1.ClassContextSpecific, Tag: 0, IsCompound: true, Bytes: innerContent},
   213  	}
   214  	return asn1.Marshal(wrapper)
   215  }
   216  
   217  func newEncryptedContent(contentType asn1.ObjectIdentifier, alg *pkix.AlgorithmIdentifier, ciphertext asn1.RawValue) encryptedContentInfo {
   218  	return encryptedContentInfo{
   219  		ContentType:                contentType,
   220  		ContentEncryptionAlgorithm: *alg,
   221  		EncryptedContent:           ciphertext,
   222  	}
   223  }
   224  
   225  func marshalEncryptedContent(content []byte) asn1.RawValue {
   226  	asn1Content, _ := asn1.Marshal(content)
   227  	return asn1.RawValue{Tag: 0, Class: asn1.ClassContextSpecific, Bytes: asn1Content, IsCompound: true}
   228  }
   229  
   230  func encryptKey(key []byte, recipient *smx509.Certificate, isCFCA bool) ([]byte, error) {
   231  	if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok {
   232  		return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
   233  	}
   234  	if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() {
   235  		if isCFCA {
   236  			encryptedKey, err := sm2.Encrypt(rand.Reader, pub, key, sm2.NewPlainEncrypterOpts(sm2.MarshalUncompressed, sm2.C1C2C3))
   237  			if err != nil {
   238  				return nil, err
   239  			}
   240  			return encryptedKey[1:], nil
   241  		} else {
   242  			return sm2.EncryptASN1(rand.Reader, pub, key)
   243  		}
   244  	}
   245  	return nil, errors.New("pkcs7: only supports RSA/SM2 key")
   246  }