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 }