diff --git a/marshal.go b/marshal.go new file mode 100644 index 0000000..e94339b --- /dev/null +++ b/marshal.go @@ -0,0 +1,25 @@ +package pkcs7 + +import "encoding/asn1" + +func (p7 *PKCS7) Marshal() ([]byte, error) { + var contentType asn1.ObjectIdentifier + switch p7.raw.(type) { + case signedData: + contentType = OIDSignedData + case envelopedData: + contentType = OIDEnvelopedData + case encryptedData: + contentType = OIDEncryptedData + default: + return nil, ErrUnsupportedContentType + } + inner, err := asn1.Marshal(p7.raw) + if err != nil { + return nil, err + } + return asn1.Marshal(contentInfo{ + ContentType: contentType, + Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, + }) +} diff --git a/marshal_test.go b/marshal_test.go new file mode 100644 index 0000000..204b450 --- /dev/null +++ b/marshal_test.go @@ -0,0 +1,60 @@ +package pkcs7 + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "os" + "testing" +) + +func TestPKCS7_Marshal(t *testing.T) { + content := []byte("Hello World") + rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, x509.SHA256WithRSA, true) + if err != nil { + t.Fatalf("cannot generate root cert: %s", err) + } + truststore := x509.NewCertPool() + truststore.AddCert(rootCert.Certificate) + signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", rootCert, x509.SHA256WithRSA, false) + if err != nil { + t.Fatalf("cannot generate signer cert: %s", err) + } + toBeSigned, err := NewSignedData(content) + if err != nil { + t.Fatalf("cannot initialize signed data: %s", err) + } + + // Set the digest to match the end entity cert + signerDigest, _ := getDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm) + toBeSigned.SetDigestAlgorithm(signerDigest) + + if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, nil, SignerInfoConfig{}); err != nil { + t.Fatalf("cannot add signer: %s", err) + } + signed, err := toBeSigned.Finish() + if err != nil { + t.Fatalf("cannot finish signing data: %s", err) + } + pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed}) + + p7, err := Parse(signed) + if err != nil { + t.Fatalf("cannot parse signed data: %s", err) + } + + marshaled, err := p7.Marshal() + if err != nil { + t.Fatalf("cannot marshal signed data: %s", err) + } + p7Reparsed, err := Parse(marshaled) + if err != nil { + t.Fatalf("cannot reparse signed data: %s", err) + } + if !bytes.Equal(p7.Content, p7Reparsed.Content) { + t.Errorf("content was not found in the reparsed data:\n\tExpected: %s\n\tActual: %s", p7.Content, p7Reparsed.Content) + } + if err := p7Reparsed.VerifyWithChain(truststore); err != nil { + t.Errorf("cannot verify reparsed data: %s", err) + } +} diff --git a/sign.go b/sign.go index eb07f30..8ecc18d 100644 --- a/sign.go +++ b/sign.go @@ -23,9 +23,9 @@ type SignedData struct { encryptionOid asn1.ObjectIdentifier } -// Copy creates a new SignedData struct based on existing PKCS7 signed data. -func Copy(data *PKCS7, unauthenticatedAttrs ...Attribute) ([]byte, error) { - rawData, isSignedData := data.raw.(signedData) +// CopyWithUnsignedAttributes creates a copy of the PKCS7 struct with different unsigned attributes. +func (p7 *PKCS7) CopyWithUnsignedAttributes(unauthenticatedAttrs ...Attribute) (*PKCS7, error) { + rawData, isSignedData := p7.raw.(signedData) if !isSignedData { return nil, ErrUnsupportedContentType } @@ -38,15 +38,17 @@ func Copy(data *PKCS7, unauthenticatedAttrs ...Attribute) ([]byte, error) { return nil, err } } - inner, err := asn1.Marshal(rawData) + certs, err := rawData.Certificates.Parse() if err != nil { return nil, err } - outer := contentInfo{ - ContentType: OIDSignedData, - Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true}, - } - return asn1.Marshal(outer) + return &PKCS7{ + Content: p7.Content, + Certificates: certs, + CRLs: rawData.CRLs, + Signers: rawData.SignerInfos, + raw: rawData, + }, nil } // NewSignedData takes data and initializes a PKCS7 SignedData struct that is diff --git a/sign_test.go b/sign_test.go index 65e544d..ee1a2c4 100644 --- a/sign_test.go +++ b/sign_test.go @@ -265,7 +265,7 @@ func fromHex(s string) *big.Int { return result } -func TestCopy(t *testing.T) { +func TestCopyWithUnsignedAttributes(t *testing.T) { content := []byte("Hello World") rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, x509.SHA256WithRSA, true) if err != nil { @@ -301,17 +301,13 @@ func TestCopy(t *testing.T) { } testOid := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} testValue := "TestValue" - copied, err := Copy(p7, Attribute{ + p7, err = p7.CopyWithUnsignedAttributes(Attribute{ Type: testOid, Value: testValue, }) if err != nil { t.Fatalf("cannot copy signed data: %s", err) } - p7, err = Parse(copied) - if err != nil { - t.Fatalf("cannot parse copied data: %s", err) - } if !bytes.Equal(content, p7.Content) { t.Errorf("content was not found in the copied data:\n\tExpected: %s\n\tActual: %s", content, p7.Content)