mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-22 02:06:18 +08:00
229 lines
6.4 KiB
Go
229 lines
6.4 KiB
Go
![]() |
package pkcs7
|
||
|
|
||
|
import (
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/rand"
|
||
|
"crypto/rsa"
|
||
|
"crypto/x509/pkix"
|
||
|
"encoding/asn1"
|
||
|
"errors"
|
||
|
|
||
|
"github.com/emmansun/gmsm/pkcs"
|
||
|
"github.com/emmansun/gmsm/sm2"
|
||
|
"github.com/emmansun/gmsm/smx509"
|
||
|
)
|
||
|
|
||
|
type envelopedData struct {
|
||
|
Version int
|
||
|
RecipientInfos []recipientInfo `asn1:"set"`
|
||
|
EncryptedContentInfo encryptedContentInfo
|
||
|
}
|
||
|
|
||
|
type encryptedData struct {
|
||
|
Version int
|
||
|
EncryptedContentInfo encryptedContentInfo
|
||
|
}
|
||
|
|
||
|
type recipientInfo struct {
|
||
|
Version int
|
||
|
IssuerAndSerialNumber issuerAndSerial
|
||
|
KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||
|
EncryptedKey []byte
|
||
|
}
|
||
|
|
||
|
type encryptedContentInfo struct {
|
||
|
ContentType asn1.ObjectIdentifier
|
||
|
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||
|
EncryptedContent asn1.RawValue `asn1:"tag:0,optional"`
|
||
|
}
|
||
|
|
||
|
func (data envelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo {
|
||
|
for _, recp := range data.RecipientInfos {
|
||
|
if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
|
||
|
return &recp
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (data envelopedData) GetEncryptedContentInfo() *encryptedContentInfo {
|
||
|
return &data.EncryptedContentInfo
|
||
|
}
|
||
|
|
||
|
// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
|
||
|
// content with an unsupported algorithm.
|
||
|
var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, AES-GCM, SM4-CBC and SM4-GCM supported")
|
||
|
|
||
|
// ErrPSKNotProvided is returned when attempting to encrypt
|
||
|
// using a PSK without actually providing the PSK.
|
||
|
var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided")
|
||
|
|
||
|
// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
|
||
|
// recipient keys for each recipient public key.
|
||
|
//
|
||
|
// The algorithm used to perform encryption is determined by the argument cipher
|
||
|
//
|
||
|
// TODO(fullsailor): Add support for encrypting content with other algorithms
|
||
|
func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
|
||
|
return encrypt(cipher, content, recipients, false)
|
||
|
}
|
||
|
|
||
|
// EncryptSM creates and returns an envelope data PKCS7 structure with encrypted
|
||
|
// recipient keys for each recipient public key.
|
||
|
// The OIDs use GM/T 0010 - 2012 set
|
||
|
//
|
||
|
// The algorithm used to perform encryption is determined by the argument cipher
|
||
|
//
|
||
|
func EncryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
|
||
|
return encrypt(cipher, content, recipients, true)
|
||
|
}
|
||
|
|
||
|
func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate, isSM bool) ([]byte, error) {
|
||
|
var key []byte
|
||
|
var err error
|
||
|
|
||
|
// Create key
|
||
|
key = make([]byte, cipher.KeySize())
|
||
|
_, err = rand.Read(key)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
id, ciphertext, err := cipher.Encrypt(key, content)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
envelope := envelopedData{
|
||
|
Version: 0,
|
||
|
EncryptedContentInfo: encryptedContentInfo{
|
||
|
ContentType: OIDData,
|
||
|
ContentEncryptionAlgorithm: *id,
|
||
|
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
if isSM {
|
||
|
envelope.EncryptedContentInfo.ContentType = SM2OIDData
|
||
|
}
|
||
|
|
||
|
// Prepare each recipient's encrypted cipher key
|
||
|
recipientInfos := make([]recipientInfo, len(recipients))
|
||
|
for i, recipient := range recipients {
|
||
|
encrypted, err := encryptKey(key, recipient)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
ias, err := cert2issuerAndSerial(recipient)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var keyEncryptionAlgorithm asn1.ObjectIdentifier = OIDEncryptionAlgorithmRSA
|
||
|
if recipient.SignatureAlgorithm == smx509.SM2WithSM3 {
|
||
|
keyEncryptionAlgorithm = OIDKeyEncryptionAlgorithmSM2
|
||
|
} else if isSM {
|
||
|
return nil, errors.New("pkcs7: Shangmi does not support RSA")
|
||
|
}
|
||
|
|
||
|
info := recipientInfo{
|
||
|
Version: 0,
|
||
|
IssuerAndSerialNumber: ias,
|
||
|
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||
|
Algorithm: keyEncryptionAlgorithm,
|
||
|
},
|
||
|
EncryptedKey: encrypted,
|
||
|
}
|
||
|
recipientInfos[i] = info
|
||
|
}
|
||
|
|
||
|
envelope.RecipientInfos = recipientInfos
|
||
|
|
||
|
innerContent, err := asn1.Marshal(envelope)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Prepare outer payload structure
|
||
|
wrapper := contentInfo{
|
||
|
ContentType: OIDEnvelopedData,
|
||
|
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
|
||
|
}
|
||
|
|
||
|
if isSM {
|
||
|
wrapper.ContentType = SM2OIDEnvelopedData
|
||
|
}
|
||
|
|
||
|
return asn1.Marshal(wrapper)
|
||
|
}
|
||
|
|
||
|
// EncryptUsingPSK creates and returns an encrypted data PKCS7 structure,
|
||
|
// encrypted using caller provided pre-shared secret.
|
||
|
func EncryptUsingPSK(cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||
|
return encryptUsingPSK(false, cipher, content, key)
|
||
|
}
|
||
|
|
||
|
// EncryptSMUsingPSK creates and returns an encrypted data PKCS7 structure,
|
||
|
// encrypted using caller provided pre-shared secret.
|
||
|
// This method uses China Standard OID
|
||
|
func EncryptSMUsingPSK(cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||
|
return encryptUsingPSK(true, cipher, content, key)
|
||
|
}
|
||
|
|
||
|
func encryptUsingPSK(isSM bool, cipher pkcs.Cipher, content []byte, key []byte) ([]byte, error) {
|
||
|
var err error
|
||
|
|
||
|
if key == nil {
|
||
|
return nil, ErrPSKNotProvided
|
||
|
}
|
||
|
|
||
|
id, ciphertext, err := cipher.Encrypt(key, content)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Prepare encrypted-data content
|
||
|
ed := encryptedData{
|
||
|
Version: 0,
|
||
|
EncryptedContentInfo: encryptedContentInfo{
|
||
|
ContentType: OIDData,
|
||
|
ContentEncryptionAlgorithm: *id,
|
||
|
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||
|
},
|
||
|
}
|
||
|
if isSM {
|
||
|
ed.EncryptedContentInfo.ContentType = SM2OIDData
|
||
|
}
|
||
|
|
||
|
innerContent, err := asn1.Marshal(ed)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var contentType asn1.ObjectIdentifier = OIDEncryptedData
|
||
|
if isSM {
|
||
|
contentType = SM2OIDEncryptedData
|
||
|
}
|
||
|
// Prepare outer payload structure
|
||
|
wrapper := contentInfo{
|
||
|
ContentType: contentType,
|
||
|
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
|
||
|
}
|
||
|
|
||
|
return asn1.Marshal(wrapper)
|
||
|
}
|
||
|
|
||
|
func marshalEncryptedContent(content []byte) asn1.RawValue {
|
||
|
asn1Content, _ := asn1.Marshal(content)
|
||
|
return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
|
||
|
}
|
||
|
|
||
|
func encryptKey(key []byte, recipient *smx509.Certificate) ([]byte, error) {
|
||
|
if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok {
|
||
|
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||
|
}
|
||
|
if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() {
|
||
|
return sm2.EncryptASN1(rand.Reader, pub, key)
|
||
|
}
|
||
|
return nil, ErrUnsupportedAlgorithm
|
||
|
}
|