pkcs7: support CFCA legacy SM2 envelope message #225

This commit is contained in:
Sun Yimin 2024-06-10 19:46:51 +08:00 committed by GitHub
parent fbe4073a46
commit 23081971e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 10 deletions

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"github.com/emmansun/gmsm/pkcs" "github.com/emmansun/gmsm/pkcs"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509" "github.com/emmansun/gmsm/smx509"
) )
@ -24,6 +25,16 @@ type decryptable interface {
// Decrypt decrypts encrypted content info for recipient cert and private key // Decrypt decrypts encrypted content info for recipient cert and private key
func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]byte, error) { func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]byte, error) {
return p7.decrypt(cert, pkey, false)
}
// DecryptCFCA decrypts encrypted content info for recipient cert and private key whose SM2 encrypted key is C1C2C3 format
// and without 0x4 prefix.
func (p7 *PKCS7) DecryptCFCA(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]byte, error) {
return p7.decrypt(cert, pkey, true)
}
func (p7 *PKCS7) decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey, isCFCA bool) ([]byte, error) {
decryptableData, ok := p7.raw.(decryptable) decryptableData, ok := p7.raw.(decryptable)
if !ok { if !ok {
return nil, ErrNotEncryptedContent return nil, ErrNotEncryptedContent
@ -36,7 +47,16 @@ func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]by
switch pkey := pkey.(type) { switch pkey := pkey.(type) {
case crypto.Decrypter: case crypto.Decrypter:
// Generic case to handle anything that provides the crypto.Decrypter interface. // Generic case to handle anything that provides the crypto.Decrypter interface.
contentKey, err := pkey.Decrypt(rand.Reader, recipient.EncryptedKey, nil) encryptedKey := recipient.EncryptedKey
var decrypterOpts crypto.DecrypterOpts
if _, ok := pkey.(*sm2.PrivateKey); ok && isCFCA {
encryptedKey = make([]byte, len(recipient.EncryptedKey)+1)
encryptedKey[0] = 0x04
copy(encryptedKey[1:], recipient.EncryptedKey)
decrypterOpts = sm2.NewPlainDecrypterOpts(sm2.C1C2C3)
}
contentKey, err := pkey.Decrypt(rand.Reader, encryptedKey, decrypterOpts)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -61,11 +61,11 @@ var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provi
// Encrypt creates and returns an envelope data PKCS7 structure with encrypted // Encrypt creates and returns an envelope data PKCS7 structure with encrypted
// recipient keys for each recipient public key. // recipient keys for each recipient public key.
// //
// The algorithm used to perform encryption is determined by the argument cipher // # The algorithm used to perform encryption is determined by the argument cipher
// //
// TODO(fullsailor): Add support for encrypting content with other algorithms // TODO(fullsailor): Add support for encrypting content with other algorithms
func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) { func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
return encrypt(cipher, content, recipients, false) return encrypt(cipher, content, recipients, false, false)
} }
// EncryptSM creates and returns an envelope data PKCS7 structure with encrypted // EncryptSM creates and returns an envelope data PKCS7 structure with encrypted
@ -73,12 +73,20 @@ func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificat
// The OIDs use GM/T 0010 - 2012 set // The OIDs use GM/T 0010 - 2012 set
// //
// The algorithm used to perform encryption is determined by the argument cipher // The algorithm used to perform encryption is determined by the argument cipher
//
func EncryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) { func EncryptSM(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
return encrypt(cipher, content, recipients, true) return encrypt(cipher, content, recipients, true, false)
} }
func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate, isSM bool) ([]byte, error) { // EncryptCFCA 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 and the encrypted key use C1C2C3 format and without 0x4 prefix.
//
// The algorithm used to perform encryption is determined by the argument cipher
func EncryptCFCA(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate) ([]byte, error) {
return encrypt(cipher, content, recipients, true, true)
}
func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificate, isSM, isCFCA bool) ([]byte, error) {
var key []byte var key []byte
var err error var err error
@ -110,7 +118,7 @@ func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificat
// Prepare each recipient's encrypted cipher key // Prepare each recipient's encrypted cipher key
recipientInfos := make([]recipientInfo, len(recipients)) recipientInfos := make([]recipientInfo, len(recipients))
for i, recipient := range recipients { for i, recipient := range recipients {
encrypted, err := encryptKey(key, recipient) encrypted, err := encryptKey(key, recipient, isCFCA)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -217,12 +225,20 @@ func marshalEncryptedContent(content []byte) asn1.RawValue {
return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true} return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
} }
func encryptKey(key []byte, recipient *smx509.Certificate) ([]byte, error) { func encryptKey(key []byte, recipient *smx509.Certificate, isCFCA bool) ([]byte, error) {
if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok { if pub, ok := recipient.PublicKey.(*rsa.PublicKey); ok {
return rsa.EncryptPKCS1v15(rand.Reader, pub, key) return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
} }
if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() { if pub, ok := recipient.PublicKey.(*ecdsa.PublicKey); ok && pub.Curve == sm2.P256() {
if isCFCA {
encryptedKey, err := sm2.Encrypt(rand.Reader, pub, key, sm2.NewPlainEncrypterOpts(sm2.MarshalUncompressed, sm2.C1C2C3))
if err != nil {
return nil, err
}
return encryptedKey[1:], nil
} else {
return sm2.EncryptASN1(rand.Reader, pub, key) return sm2.EncryptASN1(rand.Reader, pub, key)
} }
}
return nil, errors.New("pkcs7: only supports RSA/SM2 key") return nil, errors.New("pkcs7: only supports RSA/SM2 key")
} }

View File

@ -91,6 +91,41 @@ func TestEncryptSM(t *testing.T) {
} }
} }
func TestEncryptCFCA(t *testing.T) {
ciphers := []pkcs.Cipher{
pkcs.SM4CBC,
pkcs.SM4GCM,
}
sigalgs := []x509.SignatureAlgorithm{
smx509.SM2WithSM3,
}
for _, cipher := range ciphers {
for _, sigalg := range sigalgs {
plaintext := []byte("Hello Secret World!")
cert, err := createTestCertificate(sigalg)
if err != nil {
t.Fatal(err)
}
encrypted, err := EncryptCFCA(cipher, plaintext, []*smx509.Certificate{cert.Certificate})
if err != nil {
t.Fatal(err)
}
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted})
p7, err := Parse(encrypted)
if err != nil {
t.Fatalf("cannot Parse encrypted result: %s", err)
}
result, err := p7.DecryptCFCA(cert.Certificate, *cert.PrivateKey)
if err != nil {
t.Fatalf("cannot Decrypt encrypted result: %s", err)
}
if !bytes.Equal(plaintext, result) {
t.Errorf("encrypted data does not match plaintext:\n\tExpected: %s\n\tActual: %s", plaintext, result)
}
}
}
}
func TestEncryptUsingPSK(t *testing.T) { func TestEncryptUsingPSK(t *testing.T) {
ciphers := []pkcs.Cipher{ ciphers := []pkcs.Cipher{
pkcs.DESCBC, pkcs.DESCBC,

View File

@ -254,7 +254,7 @@ func (saed *SignedAndEnvelopedData) AddCertificate(cert *smx509.Certificate) {
} }
func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error { func (saed *SignedAndEnvelopedData) AddRecipient(recipient *smx509.Certificate) error {
encryptedKey, err := encryptKey(saed.cek, recipient) encryptedKey, err := encryptKey(saed.cek, recipient, false) //TODO: check if CFCA has such function
if err != nil { if err != nil {
return err return err
} }