diff --git a/cfca/pkcs7_envelope_test.go b/cfca/pkcs7_envelope_test.go index e2261c4..4937ca6 100644 --- a/cfca/pkcs7_envelope_test.go +++ b/cfca/pkcs7_envelope_test.go @@ -22,7 +22,7 @@ import ( type certKeyPair struct { Certificate *smx509.Certificate - PrivateKey *crypto.PrivateKey + PrivateKey crypto.PrivateKey } func createTestSM2Certificate(allCA bool) (certKeyPair, error) { @@ -64,7 +64,7 @@ func createTestSM2CertificateByIssuer(name string, issuer *certKeyPair, sigAlg x } if issuer != nil { issuerCert = issuer.Certificate - issuerKey = *issuer.PrivateKey + issuerKey = issuer.PrivateKey } switch sigAlg { @@ -106,7 +106,7 @@ func createTestSM2CertificateByIssuer(name string, issuer *certKeyPair, sigAlg x // pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) return &certKeyPair{ Certificate: cert, - PrivateKey: &priv, + PrivateKey: priv, }, nil } @@ -125,12 +125,12 @@ func TestEnvelopeMessage(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, *cert.PrivateKey) + _, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey) if err == nil { t.Fatalf("expected error when decrypting with wrong key, got nil") } // pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted}) - result, err := OpenEnvelopedMessage(encrypted, cert.Certificate, *cert.PrivateKey) + result, err := OpenEnvelopedMessage(encrypted, cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %v", err) } @@ -155,12 +155,12 @@ func TestEnvelopeMessageLegacy(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, *cert.PrivateKey) + _, err = OpenEnvelopedMessage(encrypted[:len(encrypted)-1], cert.Certificate, cert.PrivateKey) if err == nil { t.Fatalf("expected error when decrypting with wrong key, got nil") } // pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: encrypted}) - result, err := OpenEnvelopedMessageLegacy(encrypted, cert.Certificate, *cert.PrivateKey) + result, err := OpenEnvelopedMessageLegacy(encrypted, cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %v", err) } diff --git a/cfca/pkcs7_sign.go b/cfca/pkcs7_sign.go new file mode 100644 index 0000000..ed4085b --- /dev/null +++ b/cfca/pkcs7_sign.go @@ -0,0 +1,58 @@ +// Copyright 2024 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cfca + +import ( + "crypto" + + "github.com/emmansun/gmsm/pkcs7" + "github.com/emmansun/gmsm/smx509" +) + +func signMessage(data []byte, cert *smx509.Certificate, key crypto.PrivateKey, detached bool) ([]byte, error) { + signData, _ := pkcs7.NewSMSignedData(data) + if err := signData.SignWithoutAttr(cert, key, pkcs7.SignerInfoConfig{}); err != nil { + return nil, err + } + if detached { + signData.Detach() + } + return signData.Finish() +} + +// SignMessageAttach signs the data with the certificate and private key, returns the signed data in PKCS7 (DER) format. +// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignMessageAttach. +func SignMessageAttach(data []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) { + return signMessage(data, cert, key, false) +} + +// VerifyMessageAttach verifies the signed data in PKCS7 (DER) format. +// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyMessageAttach. +// If verification fails, an error is returned. otherwise, nil is returned. +func VerifyMessageAttach(p7Der []byte) error { + p7, err := pkcs7.Parse(p7Der) + if err != nil { + return err + } + return p7.Verify() +} + +// SignMessageDetach signs the data with the certificate and private key, returns the signed data in PKCS7 (DER) format. +// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignMessageDetach. +func SignMessageDetach(data []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) { + return signMessage(data, cert, key, true) +} + +// VerifyMessageDetach verifies the signed data in PKCS7 (DER) format with the given source data. +// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyMessageDetach. +// If verification fails, an error is returned. otherwise, nil is returned. +func VerifyMessageDetach(p7Der, sourceData []byte) error { + p7, err := pkcs7.Parse(p7Der) + if err != nil { + return err + } + p7.Content = sourceData + return p7.Verify() +} diff --git a/cfca/pkcs7_sign_test.go b/cfca/pkcs7_sign_test.go new file mode 100644 index 0000000..728e840 --- /dev/null +++ b/cfca/pkcs7_sign_test.go @@ -0,0 +1,93 @@ +// Copyright 2024 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cfca + +import ( + "encoding/base64" + "testing" +) + +func TestSignMessageAttach(t *testing.T) { + _, err := SignMessageAttach(nil, nil, nil) + if err == nil { + t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true) + } + pair, err := createTestSM2Certificate(false) + if err != nil { + t.Fatal(err) + } + _, err = SignMessageAttach([]byte("test"), pair.Certificate, nil) + if err == nil { + t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true) + } + p7, err := SignMessageAttach([]byte("test"), pair.Certificate, pair.PrivateKey) + if err != nil { + t.Fatal(err) + } + lastByte := p7[len(p7)-1] + p7[len(p7)-1] = 0 + err = VerifyMessageAttach(p7) + if err == nil { + t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) + } + p7[len(p7)-1] = lastByte + err = VerifyMessageAttach(p7) + if err != nil { + t.Fatal(err) + } + + p7, _ = base64.StdEncoding.DecodeString(sadkSignedData) + err = VerifyMessageAttach(p7) + if err != nil { + t.Fatal(err) + } +} + +func TestSignMessageDetach(t *testing.T) { + _, err := SignMessageDetach(nil, nil, nil) + if err == nil { + t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true) + } + pair, err := createTestSM2Certificate(false) + if err != nil { + t.Fatal(err) + } + _, err = SignMessageDetach([]byte("test"), pair.Certificate, nil) + if err == nil { + t.Fatalf("SignMessageAttach() error = %v, wantErr %v", err, true) + } + p7, err := SignMessageDetach([]byte("test"), pair.Certificate, pair.PrivateKey) + if err != nil { + t.Fatal(err) + } + lastByte := p7[len(p7)-1] + p7[len(p7)-1] = 0 + err = VerifyMessageDetach(p7, []byte("test")) + if err == nil { + t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) + } + p7[len(p7)-1] = lastByte + err = VerifyMessageDetach(p7, []byte("test")) + if err != nil { + t.Fatal(err) + } + err = VerifyMessageDetach(p7, []byte("test 1")) + if err == nil || err.Error() != "x509: SM2 verification failure" { + t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) + } + err = VerifyMessageDetach(p7, nil) + if err == nil || err.Error() != "x509: SM2 verification failure" { + t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) + } + + p7, _ = base64.StdEncoding.DecodeString(sadkSignedDataDetach) + err = VerifyMessageDetach(p7, []byte("Hello Secret World!")) + if err != nil { + t.Fatal(err) + } +} + +var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0=" +var sadkSignedDataDetach = "MIICaQYKKoEcz1UGAQQCAqCCAlkwggJVAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIIBjTCCAYkwggEvoAMCAQICBQCp3BqQMAoGCCqBHM9VAYN1MCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0yNDExMTkwMDEyMjVaFw0yNTExMTkwMDEyMjZaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBTbm93MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE2HIKx1yZhTtf7f/VkOhpAlug9Mg8ndsmucEV41QFNPCPTqOpPfYwdjD2rgp+uFvNQD0Qjxy5cFjrbiLJkpkIY6NIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB8GA1UdIwQYMBaAFMgX2GnhicWn/Uk/WgkYKLQvjML8MAoGCCqBHM9VAYN1A0gAMEUCIEWhdDwO+HAmCnrvKWeFQ0LYvqxNvNx6v7Rl811MzlilAiEA5d89W6GcbUbHG5zhjl4NTWPsqGNpc+W+t5Bjk5J/0scxgaAwgZ0CAQEwMjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQCp3BqQMAwGCCqBHM9VAYMRBQAwDQYJKoEcz1UBgi0BBQAERzBFAiEA4ylCl8qQDfNDfBw7VkxVN0bUs4N56TZDqZhAdEv01N8CIDtOG5VbmWNZeagC8VRfzEhu+ratFCo3fTu2liV8kH5h" diff --git a/pkcs7/envelope_test.go b/pkcs7/envelope_test.go index efa6ea4..c453e11 100644 --- a/pkcs7/envelope_test.go +++ b/pkcs7/envelope_test.go @@ -98,7 +98,7 @@ func TestEncrypt(t *testing.T) { if err != nil { t.Fatalf("cannot Parse encrypted result: %s", err) } - result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) + result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %s", err) } @@ -133,7 +133,7 @@ func TestEncryptSM(t *testing.T) { if err != nil { t.Fatalf("cannot Parse encrypted result: %s", err) } - result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) + result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %s", err) } @@ -169,7 +169,7 @@ func TestEncryptCFCA(t *testing.T) { if err != nil { t.Fatalf("cannot Parse encrypted result: %s", err) } - result, err := p7.DecryptCFCA(cert.Certificate, *cert.PrivateKey) + result, err := p7.DecryptCFCA(cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %s", err) } @@ -289,7 +289,7 @@ func TestEnvelopeMessageCFCA(t *testing.T) { if err != nil { t.Fatalf("cannot Parse encrypted result: %s", err) } - result, err := p7.Decrypt(cert.Certificate, *cert.PrivateKey) + result, err := p7.Decrypt(cert.Certificate, cert.PrivateKey) if err != nil { t.Fatalf("cannot Decrypt encrypted result: %s", err) } diff --git a/pkcs7/pkcs7_test.go b/pkcs7/pkcs7_test.go index ec707d6..9ee4c30 100644 --- a/pkcs7/pkcs7_test.go +++ b/pkcs7/pkcs7_test.go @@ -83,7 +83,7 @@ func fromBase10(base10 string) *big.Int { type certKeyPair struct { Certificate *smx509.Certificate - PrivateKey *crypto.PrivateKey + PrivateKey crypto.PrivateKey } func createTestCertificate(sigAlg x509.SignatureAlgorithm, allCA bool) (certKeyPair, error) { @@ -125,7 +125,7 @@ func createTestCertificateByIssuer(name string, issuer *certKeyPair, sigAlg x509 } if issuer != nil { issuerCert = issuer.Certificate - issuerKey = *issuer.PrivateKey + issuerKey = issuer.PrivateKey } switch sigAlg { diff --git a/pkcs7/sign.go b/pkcs7/sign.go index 0887da7..a1d3b9d 100644 --- a/pkcs7/sign.go +++ b/pkcs7/sign.go @@ -143,6 +143,10 @@ func (sd *SignedData) AddSigner(ee *smx509.Certificate, pkey crypto.PrivateKey, // The signature algorithm used to hash the data is the one of the end-entity // certificate. func (sd *SignedData) AddSignerChain(ee *smx509.Certificate, pkey crypto.PrivateKey, parents []*smx509.Certificate, config SignerInfoConfig) error { + if ee == nil { + return errors.New("pkcs7: certificate is nil") + } + // Following RFC 2315, 9.2 SignerInfo type, the distinguished name of // the issuer of the end-entity signer is stored in the issuerAndSerialNumber // section of the SignedData.SignerInfo, alongside the serial number of diff --git a/pkcs7/sign_enveloped_test.go b/pkcs7/sign_enveloped_test.go index 47c4734..56c3035 100644 --- a/pkcs7/sign_enveloped_test.go +++ b/pkcs7/sign_enveloped_test.go @@ -171,7 +171,7 @@ func TestCreateSignedEvnvelopedDataSM(t *testing.T) { t.Fatal(err) } privKey := make([]byte, 32) - sm2Key, ok := (*encryptKey.PrivateKey).(*sm2.PrivateKey) + sm2Key, ok := (encryptKey.PrivateKey).(*sm2.PrivateKey) if !ok { t.Fatal("should be sm2 private key") } @@ -183,7 +183,7 @@ func TestCreateSignedEvnvelopedDataSM(t *testing.T) { if err != nil { t.Fatal(err) } - err = saed.AddSigner(rootCert.Certificate, *rootCert.PrivateKey) + err = saed.AddSigner(rootCert.Certificate, rootCert.PrivateKey) if err != nil { t.Fatal(err) } @@ -203,7 +203,7 @@ func TestCreateSignedEvnvelopedDataSM(t *testing.T) { if err != nil { t.Fatal(err) } - encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, *recipient.PrivateKey, func() error { + encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, recipient.PrivateKey, func() error { return p7Data.Verify() }) if err != nil { @@ -234,7 +234,7 @@ func TestCreateSignedEvnvelopedData(t *testing.T) { t.Fatal(err) } privKey := make([]byte, 32) - ecdsaKey, ok := (*encryptKey.PrivateKey).(*ecdsa.PrivateKey) + ecdsaKey, ok := (encryptKey.PrivateKey).(*ecdsa.PrivateKey) if !ok { t.Fatal("should be ecdsa private key") } @@ -247,7 +247,7 @@ func TestCreateSignedEvnvelopedData(t *testing.T) { t.Fatal(err) } saed.SetDigestAlgorithm(OIDDigestAlgorithmSHA256) - err = saed.AddSigner(rootCert.Certificate, *rootCert.PrivateKey) + err = saed.AddSigner(rootCert.Certificate, rootCert.PrivateKey) if err != nil { t.Fatal(err) } @@ -288,7 +288,7 @@ func TestCreateSignedEvnvelopedData(t *testing.T) { 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 { + encKeyBytes, err := p7Data.DecryptAndVerify(recipient.Certificate, recipient.PrivateKey, func() error { return p7Data.Verify() }) if err != nil { diff --git a/pkcs7/sign_test.go b/pkcs7/sign_test.go index 4f37079..8cc132c 100644 --- a/pkcs7/sign_test.go +++ b/pkcs7/sign_test.go @@ -51,7 +51,7 @@ func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureA signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner) toBeSigned.SetDigestAlgorithm(signerDigest) - if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { + if err := toBeSigned.AddSignerChain(signerCert.Certificate, signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil { t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err) } if testDetach { @@ -152,7 +152,7 @@ func TestUnmarshalSignedAttribute(t *testing.T) { } oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7} testValue := "TestValue" - if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{ + if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{ ExtraSignedAttributes: []Attribute{{Type: oidTest, Value: testValue}}, }); err != nil { t.Fatalf("Cannot add signer: %s", err) @@ -190,7 +190,7 @@ func TestSkipCertificates(t *testing.T) { t.Fatalf("Cannot initialize signed data: %s", err) } - if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{}); err != nil { + if err := toBeSigned.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil { t.Fatalf("Cannot add signer: %s", err) } signed, err := toBeSigned.Finish() @@ -209,7 +209,7 @@ func TestSkipCertificates(t *testing.T) { if err != nil { t.Fatalf("Cannot initialize signed data: %s", err) } - if err := toBeSigned2.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: true}); err != nil { + if err := toBeSigned2.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{SkipCertificates: true}); err != nil { t.Fatalf("Cannot add signer: %s", err) } signed, err = toBeSigned2.Finish() @@ -313,7 +313,7 @@ func TestSignWithoutAttr(t *testing.T) { if err != nil { t.Fatalf("Cannot initialize signed data: %s", err) } - if err := toBeSigned.SignWithoutAttr(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil { + if err := toBeSigned.SignWithoutAttr(cert.Certificate, cert.PrivateKey, SignerInfoConfig{SkipCertificates: sigalg.skipCert}); err != nil { t.Fatalf("Cannot add signer: %s", err) } signed, err := toBeSigned.Finish() diff --git a/pkcs7/verify_test.go b/pkcs7/verify_test.go index 44a3bad..20f5c92 100644 --- a/pkcs7/verify_test.go +++ b/pkcs7/verify_test.go @@ -527,7 +527,7 @@ but that's not what ships are built for. t.Fatal(err) } var derKey []byte - priv := *signerCert.PrivateKey + priv := signerCert.PrivateKey switch priv := priv.(type) { case *rsa.PrivateKey: derKey = x509.MarshalPKCS1PrivateKey(priv)