...

Source file src/github.com/digitorus/pkcs7/sign_test.go

Documentation: github.com/digitorus/pkcs7

     1  package pkcs7
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/dsa"
     7  	"crypto/x509"
     8  	"encoding/asn1"
     9  	"encoding/pem"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"math/big"
    13  	"os"
    14  	"os/exec"
    15  	"testing"
    16  )
    17  
    18  func TestSign(t *testing.T) {
    19  	content := []byte("Hello World")
    20  	sigalgs := []x509.SignatureAlgorithm{
    21  		x509.SHA1WithRSA,
    22  		x509.SHA256WithRSA,
    23  		x509.SHA512WithRSA,
    24  		x509.ECDSAWithSHA1,
    25  		x509.ECDSAWithSHA256,
    26  		x509.ECDSAWithSHA384,
    27  		x509.ECDSAWithSHA512,
    28  		x509.PureEd25519,
    29  	}
    30  	for _, sigalgroot := range sigalgs {
    31  		rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
    32  		if err != nil {
    33  			t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
    34  		}
    35  		truststore := x509.NewCertPool()
    36  		truststore.AddCert(rootCert.Certificate)
    37  		for _, sigalginter := range sigalgs {
    38  			interCert, err := createTestCertificateByIssuer("PKCS7 Test Intermediate Cert", rootCert, sigalginter, true)
    39  			if err != nil {
    40  				t.Fatalf("test %s/%s: cannot generate intermediate cert: %s", sigalgroot, sigalginter, err)
    41  			}
    42  			var parents []*x509.Certificate
    43  			parents = append(parents, interCert.Certificate)
    44  			for _, sigalgsigner := range sigalgs {
    45  				signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", interCert, sigalgsigner, false)
    46  				if err != nil {
    47  					t.Fatalf("test %s/%s/%s: cannot generate signer cert: %s", sigalgroot, sigalginter, sigalgsigner, err)
    48  				}
    49  				for _, testDetach := range []bool{false, true} {
    50  					toBeSigned, err := NewSignedData(content)
    51  					if err != nil {
    52  						t.Fatalf("test %s/%s/%s: cannot initialize signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    53  					}
    54  
    55  					// Set the digest to match the end entity cert
    56  					signerDigest, _ := GetDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm)
    57  					toBeSigned.SetDigestAlgorithm(signerDigest)
    58  
    59  					if err := toBeSigned.AddSignerChain(signerCert.Certificate, *signerCert.PrivateKey, parents, SignerInfoConfig{}); err != nil {
    60  						t.Fatalf("test %s/%s/%s: cannot add signer: %s", sigalgroot, sigalginter, sigalgsigner, err)
    61  					}
    62  					if testDetach {
    63  						toBeSigned.Detach()
    64  					}
    65  					signed, err := toBeSigned.Finish()
    66  					if err != nil {
    67  						t.Fatalf("test %s/%s/%s: cannot finish signing data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    68  					}
    69  					p7, err := Parse(signed)
    70  					if err != nil {
    71  						t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    72  					}
    73  					if testDetach {
    74  						p7.Content = content
    75  					}
    76  					if !bytes.Equal(content, p7.Content) {
    77  						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)
    78  					}
    79  					if err := p7.VerifyWithChain(truststore); err != nil {
    80  						t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    81  					}
    82  					if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
    83  						t.Errorf("test %s/%s/%s: expected digest algorithm %q but got %q",
    84  							sigalgroot, sigalginter, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
    85  					}
    86  				}
    87  			}
    88  		}
    89  	}
    90  }
    91  
    92  func TestDSASignAndVerifyWithOpenSSL(t *testing.T) {
    93  	content := []byte("Hello World")
    94  	// write the content to a temp file
    95  	tmpContentFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_content")
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
   100  
   101  	block, _ := pem.Decode([]byte(dsaPublicCert))
   102  	if block == nil {
   103  		t.Fatal("failed to parse certificate PEM")
   104  	}
   105  	signerCert, err := x509.ParseCertificate(block.Bytes)
   106  	if err != nil {
   107  		t.Fatal("failed to parse certificate: " + err.Error())
   108  	}
   109  
   110  	// write the signer cert to a temp file
   111  	tmpSignerCertFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signer")
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	ioutil.WriteFile(tmpSignerCertFile.Name(), dsaPublicCert, 0755)
   116  
   117  	priv := dsa.PrivateKey{
   118  		PublicKey: dsa.PublicKey{Parameters: dsa.Parameters{P: fromHex("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7"),
   119  			Q: fromHex("9760508F15230BCCB292B982A2EB840BF0581CF5"),
   120  			G: fromHex("F7E1A085D69B3DDECBBCAB5C36B857B97994AFBBFA3AEA82F9574C0B3D0782675159578EBAD4594FE67107108180B449167123E84C281613B7CF09328CC8A6E13C167A8B547C8D28E0A3AE1E2BB3A675916EA37F0BFA213562F1FB627A01243BCCA4F1BEA8519089A883DFE15AE59F06928B665E807B552564014C3BFECF492A"),
   121  		},
   122  		},
   123  		X: fromHex("7D6E1A3DD4019FD809669D8AB8DA73807CEF7EC1"),
   124  	}
   125  	toBeSigned, err := NewSignedData(content)
   126  	if err != nil {
   127  		t.Fatalf("test case: cannot initialize signed data: %s", err)
   128  	}
   129  	if err := toBeSigned.SignWithoutAttr(signerCert, &priv, SignerInfoConfig{}); err != nil {
   130  		t.Fatalf("Cannot add signer: %s", err)
   131  	}
   132  	toBeSigned.Detach()
   133  	signed, err := toBeSigned.Finish()
   134  	if err != nil {
   135  		t.Fatalf("test case: cannot finish signing data: %s", err)
   136  	}
   137  
   138  	// write the signature to a temp file
   139  	tmpSignatureFile, err := ioutil.TempFile("", "TestDSASignAndVerifyWithOpenSSL_signature")
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	ioutil.WriteFile(tmpSignatureFile.Name(), pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: signed}), 0755)
   144  
   145  	// call openssl to verify the signature on the content using the root
   146  	opensslCMD := exec.Command("openssl", "smime", "-verify", "-noverify",
   147  		"-in", tmpSignatureFile.Name(), "-inform", "PEM",
   148  		"-content", tmpContentFile.Name())
   149  	out, err := opensslCMD.CombinedOutput()
   150  	if err != nil {
   151  		t.Fatalf("test case: openssl command failed with %s: %s", err, out)
   152  	}
   153  	os.Remove(tmpSignatureFile.Name())  // clean up
   154  	os.Remove(tmpContentFile.Name())    // clean up
   155  	os.Remove(tmpSignerCertFile.Name()) // clean up
   156  }
   157  
   158  func TestSignWithoutAttributes(t *testing.T) {
   159  	content := []byte("Hello World")
   160  	sigalgs := []x509.SignatureAlgorithm{
   161  		x509.SHA1WithRSA,
   162  		x509.SHA256WithRSA,
   163  		x509.SHA512WithRSA,
   164  		x509.ECDSAWithSHA1,
   165  		x509.ECDSAWithSHA256,
   166  		x509.ECDSAWithSHA384,
   167  		x509.ECDSAWithSHA512,
   168  		x509.PureEd25519,
   169  	}
   170  	for _, sigalgroot := range sigalgs {
   171  		rootCert, err := createTestCertificateByIssuer("PKCS7 Test Root CA", nil, sigalgroot, true)
   172  		if err != nil {
   173  			t.Fatalf("test %s: cannot generate root cert: %s", sigalgroot, err)
   174  		}
   175  		truststore := x509.NewCertPool()
   176  		truststore.AddCert(rootCert.Certificate)
   177  		for _, sigalgsigner := range sigalgs {
   178  			signerCert, err := createTestCertificateByIssuer("PKCS7 Test Signer Cert", rootCert, sigalgsigner, false)
   179  			if err != nil {
   180  				t.Fatalf("test %s/%s: cannot generate signer cert: %s", sigalgroot, sigalgsigner, err)
   181  			}
   182  			for _, testDetach := range []bool{false, true} {
   183  				toBeSigned, err := NewSignedData(content)
   184  				if err != nil {
   185  					t.Fatalf("test %s/%s: cannot initialize signed data: %s", sigalgroot, sigalgsigner, err)
   186  				}
   187  
   188  				// Set the digest to match the end entity cert
   189  				signerDigest, _ := GetDigestOIDForSignatureAlgorithm(signerCert.Certificate.SignatureAlgorithm)
   190  				toBeSigned.SetDigestAlgorithm(signerDigest)
   191  
   192  				if err := toBeSigned.SignWithoutAttr(signerCert.Certificate, (*signerCert.PrivateKey).(crypto.Signer), SignerInfoConfig{}); err != nil {
   193  					t.Fatalf("test %s/%s: cannot add signer: %s", sigalgroot, sigalgsigner, err)
   194  				}
   195  				if testDetach {
   196  					toBeSigned.Detach()
   197  				}
   198  				signed, err := toBeSigned.Finish()
   199  				if err != nil {
   200  					t.Fatalf("test %s/%s: cannot finish signing data: %s", sigalgroot, sigalgsigner, err)
   201  				}
   202  				p7, err := Parse(signed)
   203  				if err != nil {
   204  					t.Fatalf("test %s/%s: cannot parse signed data: %s", sigalgroot, sigalgsigner, err)
   205  				}
   206  				if testDetach {
   207  					p7.Content = content
   208  				}
   209  				if !bytes.Equal(content, p7.Content) {
   210  					t.Errorf("test %s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalgsigner, content, p7.Content)
   211  				}
   212  				if err := p7.VerifyWithChain(truststore); err != nil {
   213  					t.Errorf("test %s/%s: cannot verify signed data: %s", sigalgroot, sigalgsigner, err)
   214  				}
   215  				if !signerDigest.Equal(p7.Signers[0].DigestAlgorithm.Algorithm) {
   216  					t.Errorf("test %s/%s: expected digest algorithm %q but got %q",
   217  						sigalgroot, sigalgsigner, signerDigest, p7.Signers[0].DigestAlgorithm.Algorithm)
   218  				}
   219  			}
   220  		}
   221  	}
   222  }
   223  
   224  func ExampleSignedData() {
   225  	// generate a signing cert or load a key pair
   226  	cert, err := createTestCertificate(x509.SHA256WithRSA)
   227  	if err != nil {
   228  		fmt.Printf("Cannot create test certificates: %s", err)
   229  	}
   230  
   231  	// Initialize a SignedData struct with content to be signed
   232  	signedData, err := NewSignedData([]byte("Example data to be signed"))
   233  	if err != nil {
   234  		fmt.Printf("Cannot initialize signed data: %s", err)
   235  	}
   236  
   237  	// Add the signing cert and private key
   238  	if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
   239  		fmt.Printf("Cannot add signer: %s", err)
   240  	}
   241  
   242  	// Call Detach() is you want to remove content from the signature
   243  	// and generate an S/MIME detached signature
   244  	signedData.Detach()
   245  
   246  	// Finish() to obtain the signature bytes
   247  	_, err = signedData.Finish()
   248  	if err != nil {
   249  		fmt.Printf("Cannot finish signing data: %s", err)
   250  	}
   251  }
   252  
   253  func TestSetContentType(t *testing.T) {
   254  	cert, err := createTestCertificate(x509.SHA256WithRSA)
   255  	if err != nil {
   256  		t.Fatalf("Cannot create test certificates: %s", err)
   257  	}
   258  
   259  	signedData, err := NewSignedData([]byte("Example data to be signed"))
   260  	if err != nil {
   261  		t.Fatalf("Cannot initialize signed data: %s", err)
   262  	}
   263  
   264  	idctTSTInfo := asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4}
   265  	signedData.SetContentType(idctTSTInfo)
   266  
   267  	if err := signedData.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{}); err != nil {
   268  		t.Fatalf("Cannot add signer: %s", err)
   269  	}
   270  
   271  	_, err = signedData.Finish()
   272  	if err != nil {
   273  		t.Fatalf("Cannot finish signing data: %s", err)
   274  	}
   275  
   276  	sd := signedData.GetSignedData()
   277  	if !sd.ContentInfo.ContentType.Equal(idctTSTInfo) {
   278  		t.Fatalf("Unexpected content type: %s", err)
   279  	}
   280  }
   281  
   282  func TestUnmarshalSignedAttribute(t *testing.T) {
   283  	cert, err := createTestCertificate(x509.SHA512WithRSA)
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  	content := []byte("Hello World")
   288  	toBeSigned, err := NewSignedData(content)
   289  	if err != nil {
   290  		t.Fatalf("Cannot initialize signed data: %s", err)
   291  	}
   292  	oidTest := asn1.ObjectIdentifier{2, 3, 4, 5, 6, 7}
   293  	testValue := "TestValue"
   294  	if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{
   295  		ExtraSignedAttributes: []Attribute{Attribute{Type: oidTest, Value: testValue}},
   296  	}); err != nil {
   297  		t.Fatalf("Cannot add signer: %s", err)
   298  	}
   299  	signed, err := toBeSigned.Finish()
   300  	if err != nil {
   301  		t.Fatalf("Cannot finish signing data: %s", err)
   302  	}
   303  	p7, err := Parse(signed)
   304  	if err != nil {
   305  		t.Fatalf("Cannot parse signed data: %v", err)
   306  	}
   307  	var actual string
   308  	err = p7.UnmarshalSignedAttribute(oidTest, &actual)
   309  	if err != nil {
   310  		t.Fatalf("Cannot unmarshal test value: %s", err)
   311  	}
   312  	if testValue != actual {
   313  		t.Errorf("Attribute does not match test value\n\tExpected: %s\n\tActual: %s", testValue, actual)
   314  	}
   315  }
   316  
   317  func TestDegenerateCertificate(t *testing.T) {
   318  	cert, err := createTestCertificate(x509.SHA1WithRSA)
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  	deg, err := DegenerateCertificate(cert.Certificate.Raw)
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  	testOpenSSLParse(t, deg)
   327  }
   328  
   329  func TestSkipCertificates(t *testing.T) {
   330  	cert, err := createTestCertificate(x509.SHA512WithRSA)
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	content := []byte("Hello World")
   335  	toBeSigned, err := NewSignedData(content)
   336  	if err != nil {
   337  		t.Fatalf("Cannot initialize signed data: %s", err)
   338  	}
   339  	if err := toBeSigned.AddSigner(cert.Certificate, *cert.PrivateKey, SignerInfoConfig{
   340  		SkipCertificates: true,
   341  	}); err != nil {
   342  		t.Fatalf("Cannot add signer: %s", err)
   343  	}
   344  	signed, err := toBeSigned.Finish()
   345  	if err != nil {
   346  		t.Fatalf("Cannot finish signing data: %s", err)
   347  	}
   348  	p7, err := Parse(signed)
   349  	if err != nil {
   350  		t.Fatalf("Cannot parse signed data: %v", err)
   351  	}
   352  	if len(p7.Certificates) > 0 {
   353  		t.Fatalf("Got %d certificate(s), exected none", len(p7.Certificates))
   354  	}
   355  }
   356  
   357  // writes the cert to a temporary file and tests that openssl can read it.
   358  func testOpenSSLParse(t *testing.T, certBytes []byte) {
   359  	tmpCertFile, err := ioutil.TempFile("", "testCertificate")
   360  	if err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	defer os.Remove(tmpCertFile.Name()) // clean up
   364  
   365  	if _, err := tmpCertFile.Write(certBytes); err != nil {
   366  		t.Fatal(err)
   367  	}
   368  
   369  	opensslCMD := exec.Command("openssl", "pkcs7", "-inform", "der", "-in", tmpCertFile.Name())
   370  	_, err = opensslCMD.Output()
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  
   375  	if err := tmpCertFile.Close(); err != nil {
   376  		t.Fatal(err)
   377  	}
   378  
   379  }
   380  func fromHex(s string) *big.Int {
   381  	result, ok := new(big.Int).SetString(s, 16)
   382  	if !ok {
   383  		panic(s)
   384  	}
   385  	return result
   386  }
   387  

View as plain text