mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-22 02:06:18 +08:00
351 lines
10 KiB
Go
351 lines
10 KiB
Go
package pkcs7
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"testing"
|
|
|
|
"github.com/emmansun/gmsm/smx509"
|
|
)
|
|
|
|
func testSign(t *testing.T, isSM bool, content []byte, sigalgs []x509.SignatureAlgorithm) {
|
|
for _, sigalgroot := range sigalgs {
|
|
rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
|
|
if err != nil {
|
|
t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
|
|
}
|
|
truststore := smx509.NewCertPool()
|
|
truststore.AddCert(rootCert.Certificate)
|
|
for _, sigalginter := range sigalgs {
|
|
interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
|
|
if err != nil {
|
|
t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
|
|
}
|
|
var parents []*smx509.Certificate
|
|
parents = append(parents, interCert.Certificate)
|
|
for _, sigalgsigner := range sigalgs {
|
|
signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
|
|
if err != nil {
|
|
t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
|
}
|
|
for _, testDetach := range []bool{false, true} {
|
|
log.Printf("test %s/%s/%s detached %t\n", sigalgroot, sigalginter, sigalgsigner, testDetach)
|
|
var toBeSigned *SignedData
|
|
if isSM {
|
|
toBeSigned, err = NewSMSignedData(content)
|
|
} else {
|
|
toBeSigned, err = NewSignedData(content)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
|
}
|
|
|
|
// Set the digest to match the end entity cert
|
|
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalgsigner)
|
|
toBeSigned.SetDigestAlgorithm(signerDigest)
|
|
|
|
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 {
|
|
toBeSigned.Detach()
|
|
}
|
|
signed, err := toBeSigned.Finish()
|
|
if err != nil {
|
|
t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
|
}
|
|
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
|
|
p7, err := Parse(signed)
|
|
if err != nil {
|
|
t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
|
}
|
|
if testDetach {
|
|
// Detached signature should not contain the content
|
|
// So we should not be able to find the content in the parsed data
|
|
// We should suppliment the content to the parsed data before verifying
|
|
p7.Content = content
|
|
}
|
|
if !bytes.Equal(content, p7.Content) {
|
|
t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
|
|
}
|
|
if err := p7.VerifyWithChain(truststore); err != nil {
|
|
t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
|
|
}
|
|
if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
|
|
t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
|
|
sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
func TestSign(t *testing.T) {
|
|
content := []byte("Hello World")
|
|
sigalgs := []x509.SignatureAlgorithm{
|
|
x509.SHA1WithRSA,
|
|
x509.SHA256WithRSA,
|
|
x509.SHA512WithRSA,
|
|
x509.ECDSAWithSHA1,
|
|
x509.ECDSAWithSHA256,
|
|
x509.ECDSAWithSHA384,
|
|
x509.ECDSAWithSHA512,
|
|
smx509.SM2WithSM3,
|
|
}
|
|
testSign(t, false, content, sigalgs)
|
|
}
|
|
|
|
func TestSignSM(t *testing.T) {
|
|
content := []byte("Hello World")
|
|
sigalgs := []x509.SignatureAlgorithm{
|
|
smx509.SM2WithSM3,
|
|
}
|
|
testSign(t, true, content, sigalgs)
|
|
}
|
|
|
|
func ExampleSignedData() {
|
|
// generate a signing cert or load a key pair
|
|
cert, err := createTestCertificate(x509.SHA256WithRSA)
|
|
if err != nil {
|
|
fmt.Printf("Cannot create test certificates: %s", err)
|
|
}
|
|
|
|
// Initialize a SignedData struct with content to be signed
|
|
signedData, err := NewSignedData([]byte("Example data to be signed"))
|
|
if err != nil {
|
|
fmt.Printf("Cannot initialize signed data: %s", err)
|
|
}
|
|
|
|
// Add the signing cert and private key
|
|
if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
|
|
fmt.Printf("Cannot add signer: %s", err)
|
|
}
|
|
|
|
// Call Detach() is you want to remove content from the signature
|
|
// and generate an S/MIME detached signature
|
|
signedData.Detach()
|
|
|
|
// Finish() to obtain the signature bytes
|
|
detachedSignature, err := signedData.Finish()
|
|
if err != nil {
|
|
fmt.Printf("Cannot finish signing data: %s", err)
|
|
}
|
|
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature})
|
|
}
|
|
|
|
func TestUnmarshalSignedAttribute(t *testing.T) {
|
|
cert, err := createTestCertificate(x509.SHA512WithRSA)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content := []byte("Hello World")
|
|
toBeSigned, err := NewSignedData(content)
|
|
if err != nil {
|
|
t.Fatalf("Cannot initialize signed data: %s", err)
|
|
}
|
|
oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
|
|
testValue := "TestValue"
|
|
if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{
|
|
ExtraSignedAttributes: []Attribute{{Type: oidTest, Value: testValue}},
|
|
}); err != nil {
|
|
t.Fatalf("Cannot add signer: %s", err)
|
|
}
|
|
signed, err := toBeSigned.Finish()
|
|
if err != nil {
|
|
t.Fatalf("Cannot finish signing data: %s", err)
|
|
}
|
|
p7, err := Parse(signed)
|
|
if err != nil {
|
|
t.Fatalf("Cannot parse signed data: %v", err)
|
|
}
|
|
var actual string
|
|
err = p7.UnmarshalSignedAttribute(oidTest, &actual)
|
|
if err != nil {
|
|
t.Fatalf("Cannot unmarshal test value: %s", err)
|
|
}
|
|
if testValue != actual {
|
|
t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual)
|
|
}
|
|
err = p7.Verify()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSkipCertificates(t *testing.T) {
|
|
cert, err := createTestCertificate(x509.SHA512WithRSA)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
content := []byte("Hello World")
|
|
toBeSigned, err := NewSignedData(content)
|
|
if err != nil {
|
|
t.Fatalf("Cannot initialize signed data: %s", err)
|
|
}
|
|
|
|
if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, 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)
|
|
}
|
|
p7, err := Parse(signed)
|
|
if err != nil {
|
|
t.Fatalf("Cannot parse signed data: %v", err)
|
|
}
|
|
if len(p7.Certificates) == 0 {
|
|
t.Errorf("No certificates")
|
|
}
|
|
|
|
toBeSigned2, err := NewSignedData(content)
|
|
if err != nil {
|
|
t.Fatalf("Cannot initialize signed data: %s", err)
|
|
}
|
|
if err := toBeSigned2.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{SkipCertificates: true}); err != nil {
|
|
t.Fatalf("Cannot add signer: %s", err)
|
|
}
|
|
signed, err = toBeSigned2.Finish()
|
|
if err != nil {
|
|
t.Fatalf("Cannot finish signing data: %s", err)
|
|
}
|
|
p7, err = Parse(signed)
|
|
if err != nil {
|
|
t.Fatalf("Cannot parse signed data: %v", err)
|
|
}
|
|
if len(p7.Certificates) > 0 {
|
|
t.Errorf("Have certificates: %v", p7.Certificates)
|
|
}
|
|
// For skip certificates, we should not be able to verify the signature
|
|
// because the signer certificate is not in the chain
|
|
// we should suppliment the signer certificate to the parsed data before verifying
|
|
p7.Certificates = append(p7.Certificates, cert.Certificate)
|
|
err = p7.Verify()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestDegenerateCertificate(t *testing.T) {
|
|
cert, err := createTestCertificate(x509.SHA1WithRSA)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deg, err := DegenerateCertificate(cert.Certificate.Raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testOpenSSLParse(t, deg)
|
|
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: deg})
|
|
}
|
|
|
|
// writes the cert to a temporary file and tests that openssl can read it.
|
|
func testOpenSSLParse(t *testing.T, certBytes []byte) {
|
|
tmpCertFile, err := ioutil.TempFile("", "testCertificate")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(tmpCertFile.Name()) // clean up
|
|
|
|
if _, err := tmpCertFile.Write(certBytes); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name())
|
|
_, err = opensslCMD.Output()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := tmpCertFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestSignWithoutAttr(t *testing.T) {
|
|
content := []byte("Hello World")
|
|
sigalgs := []struct {
|
|
isSM bool
|
|
sigAlg x509.SignatureAlgorithm
|
|
skipCert bool
|
|
}{
|
|
{
|
|
false,
|
|
x509.SHA256WithRSA,
|
|
false,
|
|
},
|
|
{
|
|
true,
|
|
smx509.SM2WithSM3,
|
|
false,
|
|
},
|
|
{
|
|
false,
|
|
x509.SHA256WithRSA,
|
|
true,
|
|
},
|
|
{
|
|
true,
|
|
smx509.SM2WithSM3,
|
|
true,
|
|
},
|
|
}
|
|
for _, sigalg := range sigalgs {
|
|
cert, err := createTestCertificate(sigalg.sigAlg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var toBeSigned *SignedData
|
|
if sigalg.isSM {
|
|
toBeSigned, err = NewSMSignedData(content)
|
|
} else {
|
|
toBeSigned, err = NewSignedData(content)
|
|
signerDigest, _ := getDigestOIDForSignatureAlgorithm(sigalg.sigAlg)
|
|
toBeSigned.SetDigestAlgorithm(signerDigest)
|
|
}
|
|
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 {
|
|
t.Fatalf("Cannot add signer: %s", err)
|
|
}
|
|
signed, err := toBeSigned.Finish()
|
|
if err != nil {
|
|
t.Fatalf("Cannot finish signing data: %s", err)
|
|
}
|
|
p7, err := Parse(signed)
|
|
if err != nil {
|
|
t.Fatalf("Cannot parse signed data: %v", err)
|
|
}
|
|
if !sigalg.skipCert {
|
|
if len(p7.Certificates) == 0 {
|
|
t.Errorf("No certificates")
|
|
}
|
|
err = p7.Verify()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
} else {
|
|
if len(p7.Certificates) > 0 {
|
|
t.Errorf("No certificates expected")
|
|
}
|
|
err = p7.Verify()
|
|
if sigalg.skipCert && err.Error() != "pkcs7: No certificate for signer" {
|
|
t.Fatalf("Expected pkcs7: No certificate for signer")
|
|
}
|
|
p7.Certificates = append(p7.Certificates, cert.Certificate)
|
|
err = p7.Verify()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
}
|