From ff59b79d60a796dae89eb5c99108b64b41733270 Mon Sep 17 00:00:00 2001 From: Sun Yimin Date: Mon, 30 Sep 2024 08:26:42 +0800 Subject: [PATCH] pkcs7: support GetRecipients #252 --- pkcs7/decrypt.go | 32 ++++++++++++++++++++++++++++++++ pkcs7/decrypt_test.go | 13 +++++++++++++ pkcs7/encrypt_test.go | 4 ++++ pkcs7/envelope.go | 9 +++++++++ pkcs7/sign_enveloped.go | 9 +++++++++ pkcs7/sign_enveloped_test.go | 25 +++++++++++++++++++++++++ 6 files changed, 92 insertions(+) diff --git a/pkcs7/decrypt.go b/pkcs7/decrypt.go index db9b6a2..032fb97 100644 --- a/pkcs7/decrypt.go +++ b/pkcs7/decrypt.go @@ -6,23 +6,55 @@ import ( "crypto/rand" "encoding/asn1" "errors" + "math/big" "github.com/emmansun/gmsm/pkcs" "github.com/emmansun/gmsm/sm2" "github.com/emmansun/gmsm/smx509" ) +// IssuerAndSerial is a structure that holds the issuer name and serial number +type IssuerAndSerial struct { + RawIssuer []byte + SerialNumber *big.Int +} + +func newIssuerAndSerial(issuerAndSerial issuerAndSerial) IssuerAndSerial { + is := IssuerAndSerial{} + if len(issuerAndSerial.IssuerName.FullBytes) > 0 { + is.RawIssuer = make([]byte, len(issuerAndSerial.IssuerName.FullBytes)) + copy(is.RawIssuer, issuerAndSerial.IssuerName.FullBytes) + } + if issuerAndSerial.SerialNumber != nil { + is.SerialNumber = new(big.Int).Set(issuerAndSerial.SerialNumber) + } + return is +} + // ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, SM2, DES, DES-EDE3, AES and SM4 supported") // ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data var ErrNotEncryptedContent = errors.New("pkcs7: content data is NOT a decryptable data type") +// ErrNotEnvelopedData is returned when attempting to Decrypt data that is not enveloped data +var ErrNotEnvelopedData = errors.New("pkcs7: content data is NOT an enveloped data type") + type decryptable interface { GetRecipient(cert *smx509.Certificate) *recipientInfo + GetRecipients() ([]IssuerAndSerial, error) GetEncryptedContentInfo() *encryptedContentInfo } +// GetRecipients returns the list of recipients for the enveloped data +func (p7 *PKCS7) GetRecipients() ([]IssuerAndSerial, error) { + decryptableData, ok := p7.raw.(decryptable) + if !ok { + return nil, ErrNotEnvelopedData + } + return decryptableData.GetRecipients() +} + // 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) diff --git a/pkcs7/decrypt_test.go b/pkcs7/decrypt_test.go index f8d1592..35db3ae 100644 --- a/pkcs7/decrypt_test.go +++ b/pkcs7/decrypt_test.go @@ -11,6 +11,19 @@ func TestDecrypt(t *testing.T) { if err != nil { t.Fatal(err) } + recipents, err := p7.GetRecipients() + if err != nil { + t.Fatal(err) + } + if len(recipents) != 1 { + t.Errorf("Expected 1 recipient, got %d", len(recipents)) + } + if recipents[0].SerialNumber.Cmp(fixture.Certificate.SerialNumber) != 0 { + t.Errorf("Recipient serial number does not match.\n\tExpected:%s\n\tActual:%s", fixture.Certificate.SerialNumber, recipents[0].SerialNumber) + } + if !bytes.Equal(recipents[0].RawIssuer, fixture.Certificate.RawIssuer) { + t.Errorf("Recipient issuer name does not match.\n\tExpected:%x\n\tActual:%x", fixture.Certificate.RawIssuer, recipents[0].RawIssuer) + } content, err := p7.Decrypt(fixture.Certificate, fixture.PrivateKey) if err != nil { t.Errorf("Cannot Decrypt with error: %v", err) diff --git a/pkcs7/encrypt_test.go b/pkcs7/encrypt_test.go index bfe75d4..f96cac6 100644 --- a/pkcs7/encrypt_test.go +++ b/pkcs7/encrypt_test.go @@ -30,6 +30,10 @@ func TestEncryptUsingPSK(t *testing.T) { } p7, _ := Parse(ciphertext) + _, err = p7.GetRecipients() + if err != ErrNotEnvelopedData { + t.Errorf("expected ErrNotEnvelopedData, got %v", err) + } result, err := p7.DecryptUsingPSK(key) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %s", err) diff --git a/pkcs7/envelope.go b/pkcs7/envelope.go index f7d7dcf..887b281 100644 --- a/pkcs7/envelope.go +++ b/pkcs7/envelope.go @@ -48,6 +48,15 @@ func (data envelopedData) GetRecipient(cert *smx509.Certificate) *recipientInfo return nil } +// GetRecipients returns the list of recipients (READONLY) for the enveloped data +func (data envelopedData) GetRecipients() ([]IssuerAndSerial, error) { + var recipients []IssuerAndSerial + for _, recp := range data.RecipientInfos { + recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber)) + } + return recipients, nil +} + func (data envelopedData) GetEncryptedContentInfo() *encryptedContentInfo { return &data.EncryptedContentInfo } diff --git a/pkcs7/sign_enveloped.go b/pkcs7/sign_enveloped.go index c7e828d..64b2170 100644 --- a/pkcs7/sign_enveloped.go +++ b/pkcs7/sign_enveloped.go @@ -34,6 +34,15 @@ func (data signedEnvelopedData) GetRecipient(cert *smx509.Certificate) *recipien return nil } +// GetRecipients returns the list of recipients (READONLY) for the enveloped data +func (data signedEnvelopedData) GetRecipients() ([]IssuerAndSerial, error) { + var recipients []IssuerAndSerial + for _, recp := range data.RecipientInfos { + recipients = append(recipients, newIssuerAndSerial(recp.IssuerAndSerialNumber)) + } + return recipients, nil +} + func (data signedEnvelopedData) GetEncryptedContentInfo() *encryptedContentInfo { return &data.EncryptedContentInfo } diff --git a/pkcs7/sign_enveloped_test.go b/pkcs7/sign_enveloped_test.go index 9f1c74b..47c4734 100644 --- a/pkcs7/sign_enveloped_test.go +++ b/pkcs7/sign_enveloped_test.go @@ -103,6 +103,14 @@ func TestParseSignedEvnvelopedData(t *testing.T) { t.Fatal("should only one certificate") } + recipients, err := p7Data.GetRecipients() + if err != nil { + t.Fatal(err) + } + if len(recipients) != 1 { + t.Fatal("should only one recipient") + } + block, rest = pem.Decode([]byte(signKey)) if len(rest) != 0 { t.Fatal("unexpected remaining PEM block during decode") @@ -263,6 +271,23 @@ func TestCreateSignedEvnvelopedData(t *testing.T) { if err != nil { t.Fatal(err) } + + recipients, err := p7Data.GetRecipients() + if err != nil { + t.Fatal(err) + } + if len(recipients) != 1 { + t.Fatal("should only one recipient") + } + + if recipients[0].SerialNumber.Cmp(recipient.Certificate.SerialNumber) != 0 { + t.Errorf("Recipient serial number does not match.\n\tExpected:%s\n\tActual:%s", recipient.Certificate.SerialNumber, recipients[0].SerialNumber) + } + + if !bytes.Equal(recipients[0].RawIssuer, recipient.Certificate.RawIssuer) { + t.Errorf("Recipient issuer name does not match.\n\tExpected:%x\n\tActual:%x", recipient.Certificate.RawIssuer, recipients[0].RawIssuer) + } + encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, *recipient.PrivateKey, func() error { return p7Data.Verify() })