diff --git a/pkcs7/decrypt.go b/pkcs7/decrypt.go index 4478c9f..db9b6a2 100644 --- a/pkcs7/decrypt.go +++ b/pkcs7/decrypt.go @@ -8,6 +8,7 @@ import ( "errors" "github.com/emmansun/gmsm/pkcs" + "github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/smx509" ) @@ -24,6 +25,16 @@ type decryptable interface { // Decrypt decrypts encrypted content info for recipient cert and private key 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) if !ok { return nil, ErrNotEncryptedContent @@ -36,7 +47,16 @@ func (p7 *PKCS7) Decrypt(cert *smx509.Certificate, pkey crypto.PrivateKey) ([]by switch pkey := pkey.(type) { case crypto.Decrypter: // 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 { return nil, err } diff --git a/pkcs7/encrypt.go b/pkcs7/encrypt.go index 5e4227c..96da3a7 100644 --- a/pkcs7/encrypt.go +++ b/pkcs7/encrypt.go @@ -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 // 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 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 @@ -73,12 +73,20 @@ func Encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificat // 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) + 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 err error @@ -110,7 +118,7 @@ func encrypt(cipher pkcs.Cipher, content []byte, recipients []*smx509.Certificat // Prepare each recipient's encrypted cipher key recipientInfos := make([]recipientInfo, len(recipients)) for i, recipient := range recipients { - encrypted, err := encryptKey(key, recipient) + encrypted, err := encryptKey(key, recipient, isCFCA) if err != nil { return nil, err } @@ -217,12 +225,20 @@ func marshalEncryptedContent(content []byte) asn1.RawValue { 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 { 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) + 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 nil, errors.New("pkcs7: only supports RSA/SM2 key") } diff --git a/pkcs7/encrypt_test.go b/pkcs7/encrypt_test.go index 77b1143..b288f3c 100644 --- a/pkcs7/encrypt_test.go +++ b/pkcs7/encrypt_test.go @@ -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) { ciphers := []pkcs.Cipher{ pkcs.DESCBC, diff --git a/pkcs7/sign_enveloped.go b/pkcs7/sign_enveloped.go index 1dec2e7..dca6edc 100644 --- a/pkcs7/sign_enveloped.go +++ b/pkcs7/sign_enveloped.go @@ -254,7 +254,7 @@ func (saed *SignedAndEnvelopedData) AddCertificate(cert *smx509.Certificate) { } 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 { return err }