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

     1  package pkcs7
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/rand"
     6  	"crypto/x509/pkix"
     7  	"encoding/asn1"
     8  	"errors"
     9  
    10  	"github.com/emmansun/gmsm/pkcs"
    11  	"github.com/emmansun/gmsm/sm2"
    12  	"github.com/emmansun/gmsm/smx509"
    13  )
    14  
    15  // It is recommended to use a sequential combination of the signed-data and the enveloped-data content types instead of using the signed-and-enveloped-data content type,
    16  // since the signed-and-enveloped-data content type does not have authenticated or unauthenticated attributes,
    17  // and does not provide enveloping of signer information other than the signature.
    18  type signedEnvelopedData struct {
    19  	Version                    int                        `asn1:"default:1"`
    20  	RecipientInfos             []recipientInfo            `asn1:"set"`
    21  	DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
    22  	EncryptedContentInfo       encryptedContentInfo
    23  	Certificates               rawCertificates        `asn1:"optional,tag:0"`
    24  	CRLs                       []pkix.CertificateList `asn1:"optional,tag:1"`
    25  	SignerInfos                []signerInfo           `asn1:"set"`
    26  }
    27  
    28  func (data signedEnvelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo {
    29  	for _, recp := range data.RecipientInfos {
    30  		if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
    31  			return &recp
    32  		}
    33  	}
    34  	return nil
    35  }
    36  
    37  // GetRecipients returns the list of recipients (READONLY) for the enveloped data
    38  func (data signedEnvelopedData) GetRecipients() ([]IssuerAndSerial, error) {
    39  	var recipients []IssuerAndSerial
    40  	for _, recp := range data.RecipientInfos {
    41  		recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber))
    42  	}
    43  	return recipients, nil
    44  }
    45  
    46  func (data signedEnvelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
    47  	return &data.EncryptedContentInfo
    48  }
    49  
    50  func parseSignedEnvelopedData(data []byte) (*PKCS7, error) {
    51  	var sed signedEnvelopedData
    52  	if _, err := asn1.Unmarshal(data, &sed); err != nil {
    53  		return nil, err
    54  	}
    55  	certs, err := sed.Certificates.Parse()
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	return &PKCS7{
    61  		Certificates: certs,
    62  		CRLs:         sed.CRLs,
    63  		Signers:      sed.SignerInfos,
    64  		raw:          sed}, nil
    65  }
    66  
    67  type VerifyFunc func() error
    68  
    69  // DecryptAndVerifyOnlyOne decrypts encrypted content info for the only recipient private key
    70  // and verifies the signature.
    71  func (p7 *PKCS7) DecryptAndVerifyOnlyOne(pkey crypto.PrivateKey, verifyFunc VerifyFunc) ([]byte, error) {
    72  	sed, ok := p7.raw.(signedEnvelopedData)
    73  	if !ok {
    74  		return nil, errors.New("pkcs7: it's NOT SignedAndEvelopedData")
    75  	}
    76  	if len(sed.RecipientInfos) != 1 {
    77  		return nil, errors.New("pkcs7: more than one recipients or no receipient")
    78  	}
    79  	defer func() {
    80  		p7.Content = nil
    81  	}()
    82  	plaintext, err := p7.decryptSED(&sed, &sed.RecipientInfos[0], pkey)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	if verifyFunc != nil {
    87  		p7.Content = plaintext
    88  		if err = verifyFunc(); err != nil {
    89  			return nil, err
    90  		}
    91  	}
    92  	return plaintext, nil
    93  }
    94  
    95  // DecryptAndVerify decrypts encrypted content info for recipient cert and private key
    96  // and verifies the signature.
    97  func (p7 *PKCS7) DecryptAndVerify(cert *smx509.Certificate, pkey crypto.PrivateKey, verifyFunc VerifyFunc) ([]byte, error) {
    98  	sed, ok := p7.raw.(signedEnvelopedData)
    99  	if !ok {
   100  		return nil, errors.New("pkcs7: it's NOT SignedAndEvelopedData")
   101  	}
   102  	recipient := sed.GetRecipient(cert)
   103  	if recipient == nil {
   104  		return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
   105  	}
   106  	defer func() {
   107  		p7.Content = nil
   108  	}()
   109  	plaintext, err := p7.decryptSED(&sed, recipient, pkey)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	if verifyFunc != nil {
   114  		p7.Content = plaintext
   115  		if err = verifyFunc(); err != nil {
   116  			return nil, err
   117  		}
   118  	}
   119  	return plaintext, nil
   120  }
   121  
   122  func (p7 *PKCS7) decryptSED(sed *signedEnvelopedData, recipient *recipientInfo, pkey crypto.PrivateKey) ([]byte, error) {
   123  	switch pkey := pkey.(type) {
   124  	case crypto.Decrypter:
   125  		// Generic case to handle anything that provides the crypto.Decrypter interface.
   126  		contentKey, err := pkey.Decrypt(rand.Reader, recipient.EncryptedKey, nil)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		return sed.GetEncryptedContentInfo().decrypt(contentKey)
   131  	default:
   132  		return nil, ErrUnsupportedAlgorithm
   133  	}
   134  }
   135  
   136  // SignedAndEnvelopedData is an opaque data structure for creating signed and enveloped data payloads
   137  type SignedAndEnvelopedData struct {
   138  	sed            signedEnvelopedData
   139  	certs          []*smx509.Certificate
   140  	data, cek      []byte
   141  	contentTypeOid asn1.ObjectIdentifier
   142  	digestOid      asn1.ObjectIdentifier
   143  }
   144  
   145  // NewSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7 SignedAndEnvelopedData structure
   146  // that is ready to be signed via AddSigner and encrypted via AddRecipient. The digest algorithm is set to SHA1 by default
   147  // and can be changed by calling SetDigestAlgorithm.
   148  func NewSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvelopedData, error) {
   149  	var key []byte
   150  	var err error
   151  
   152  	// Create key
   153  	key = make([]byte, cipher.KeySize())
   154  	_, err = rand.Read(key)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	id, ciphertext, err := cipher.Encrypt(rand.Reader, key, data)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	sed := signedEnvelopedData{
   165  		Version: 1, // 0 or 1?
   166  		EncryptedContentInfo: encryptedContentInfo{
   167  			ContentType:                OIDData,
   168  			ContentEncryptionAlgorithm: *id,
   169  			EncryptedContent:           marshalEncryptedContent(ciphertext),
   170  		},
   171  	}
   172  	return &SignedAndEnvelopedData{sed: sed, data: data, cek: key, digestOid: OIDDigestAlgorithmSHA1, contentTypeOid: OIDSignedEnvelopedData}, nil
   173  }
   174  
   175  // NewSMSignedAndEnvelopedData takes data and cipher and initializes a new PKCS7(SM) SignedAndEnvelopedData structure
   176  // that is ready to be signed via AddSigner and encrypted via AddRecipient. The digest algorithm is set to SM3 by default.
   177  func NewSMSignedAndEnvelopedData(data []byte, cipher pkcs.Cipher) (*SignedAndEnvelopedData, error) {
   178  	sd, err := NewSignedAndEnvelopedData(data, cipher)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	sd.contentTypeOid = SM2OIDSignedEnvelopedData
   183  	sd.digestOid = OIDDigestAlgorithmSM3
   184  	sd.sed.EncryptedContentInfo.ContentType = SM2OIDData
   185  	return sd, nil
   186  }
   187  
   188  // SetDigestAlgorithm sets the digest algorithm to be used in the signing process.
   189  //
   190  // This should be called before adding signers
   191  func (saed *SignedAndEnvelopedData) SetDigestAlgorithm(d asn1.ObjectIdentifier) {
   192  	saed.digestOid = d
   193  }
   194  
   195  // AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.
   196  func (saed *SignedAndEnvelopedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey) error {
   197  	var parents []*smx509.Certificate
   198  	return saed.AddSignerChain(ee, pkey, parents)
   199  }
   200  
   201  func (saed *SignedAndEnvelopedData) AddSignerChain(ee *smx509.Certificate, pkey crypto.PrivateKey, parents []*smx509.Certificate) error {
   202  	// Following RFC 2315, 9.2 SignerInfo type, the distinguished name of
   203  	// the issuer of the end-entity signer is stored in the issuerAndSerialNumber
   204  	// section of the SignedData.SignerInfo, alongside the serial number of
   205  	// the end-entity.
   206  	var ias issuerAndSerial
   207  	ias.SerialNumber = ee.SerialNumber
   208  	if len(parents) == 0 {
   209  		// no parent, the issuer is the end-entity cert itself
   210  		ias.IssuerName = asn1.RawValue{FullBytes: ee.RawIssuer}
   211  	} else {
   212  		err := verifyPartialChain(ee, parents)
   213  		if err != nil {
   214  			return err
   215  		}
   216  		// the first parent is the issuer
   217  		ias.IssuerName = asn1.RawValue{FullBytes: parents[0].RawSubject}
   218  	}
   219  	saed.sed.DigestAlgorithmIdentifiers = append(saed.sed.DigestAlgorithmIdentifiers,
   220  		pkix.AlgorithmIdentifier{Algorithm: saed.digestOid},
   221  	)
   222  	hasher, err := getHashForOID(saed.digestOid)
   223  	if err != nil {
   224  		return err
   225  	}
   226  
   227  	signatureOid, err := getOIDForEncryptionAlgorithm(pkey, saed.digestOid)
   228  	if err != nil {
   229  		return err
   230  	}
   231  	key, ok := pkey.(crypto.Signer)
   232  	if !ok {
   233  		return errors.New("pkcs7: private key does not implement crypto.Signer")
   234  	}
   235  
   236  	var signOpt crypto.SignerOpts
   237  	var tobeSigned []byte
   238  
   239  	if _, isSM2 := pkey.(sm2.Signer); isSM2 {
   240  		signOpt = sm2.DefaultSM2SignerOpts
   241  		tobeSigned = saed.data
   242  	} else {
   243  		signOpt = hasher
   244  		h := newHash(hasher, saed.digestOid)
   245  		h.Write(saed.data)
   246  		tobeSigned = h.Sum(nil)
   247  	}
   248  	signature, err := key.Sign(rand.Reader, tobeSigned, signOpt)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	signer := signerInfo{
   253  		DigestAlgorithm:           pkix.AlgorithmIdentifier{Algorithm: saed.digestOid},
   254  		DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: signatureOid},
   255  		IssuerAndSerialNumber:     ias,
   256  		EncryptedDigest:           signature,
   257  		Version:                   1,
   258  	}
   259  	saed.certs = append(saed.certs, ee)
   260  	if len(parents) > 0 {
   261  		saed.certs = append(saed.certs, parents...)
   262  	}
   263  	saed.sed.SignerInfos = append(saed.sed.SignerInfos, signer)
   264  	return nil
   265  }
   266  
   267  // AddCertificate adds the certificate to the payload. Useful for parent certificates
   268  func (saed *SignedAndEnvelopedData) AddCertificate(cert *smx509.Certificate) {
   269  	saed.certs = append(saed.certs, cert)
   270  }
   271  
   272  // AddRecipient adds a recipient to the payload
   273  func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error {
   274  	encryptedKey, err := encryptKey(saed.cek, recipient, false) //TODO: check if CFCA has such function
   275  	if err != nil {
   276  		return err
   277  	}
   278  	ias, err := cert2issuerAndSerial(recipient)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	var keyEncryptionAlgorithm asn1.ObjectIdentifier = OIDEncryptionAlgorithmRSA
   283  	if recipient.SignatureAlgorithm == smx509.SM2WithSM3 {
   284  		keyEncryptionAlgorithm = OIDKeyEncryptionAlgorithmSM2
   285  	}
   286  	info := recipientInfo{
   287  		Version:               1,
   288  		IssuerAndSerialNumber: ias,
   289  		KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   290  			Algorithm: keyEncryptionAlgorithm,
   291  		},
   292  		EncryptedKey: encryptedKey,
   293  	}
   294  	saed.sed.RecipientInfos = append(saed.sed.RecipientInfos, info)
   295  	return nil
   296  }
   297  
   298  // Finish marshals the content and its signers
   299  func (saed *SignedAndEnvelopedData) Finish() ([]byte, error) {
   300  	saed.sed.Certificates = marshalCertificates(saed.certs)
   301  	inner, err := asn1.Marshal(saed.sed)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  	outer := contentInfo{
   306  		ContentType: saed.contentTypeOid,
   307  		Content:     asn1.RawValue{Class: asn1.ClassContextSpecific, Tag: 0, Bytes: inner, IsCompound: true},
   308  	}
   309  	return asn1.Marshal(outer)
   310  }