From a71ab2e67ac1195804bb52bfe01495fad825bb87 Mon Sep 17 00:00:00 2001 From: Benjamin Cane Date: Fri, 7 Apr 2023 05:02:03 -0700 Subject: [PATCH 1/4] Updating and extending --- README.md | 89 ++++++------ testcerts.go | 334 +++++++++++++++++++++++++++++++++++----------- testcerts_test.go | 195 +++++++++++++++++++++++++++ 3 files changed, 499 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index 94ddf34..f12e4b5 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,74 @@ # testcerts ![Actions Status](https://github.com/madflojo/testcerts/actions/workflows/go.yaml/badge.svg?branch=main) - [![Coverage Status](https://coveralls.io/repos/github/madflojo/testcerts/badge.svg?branch=master)](https://coveralls.io/github/madflojo/testcerts?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/madflojo/testcerts)](https://goreportcard.com/report/github.com/madflojo/testcerts) [![Documentation](https://godoc.org/github.com/madflojo/testcerts?status.svg)](http://godoc.org/github.com/madflojo/testcerts) +[![codecov](https://codecov.io/gh/madflojo/testcerts/branch/main/graph/badge.svg?token=H9C9B6I0AS)](https://codecov.io/gh/madflojo/testcerts) +[![Go Report Card](https://goreportcard.com/badge/github.com/madflojo/testcerts)](https://goreportcard.com/report/github.com/madflojo/testcerts) +[![Go Reference](https://pkg.go.dev/badge/github.com/madflojo/testcerts.svg)](https://pkg.go.dev/github.com/madflojo/testcerts) [![license](https://img.shields.io/github/license/madflojo/testcerts.svg?maxAge=2592000)](https://github.com/madflojo/testcerts/LICENSE) -testcerts is a Go package that makes it easy for developers to generate x509 certificates for testing and development purposes. The package provides an easy-to-use API for generating self-signed certificates and keys for testing. - -What makes testcerts unique is its ability to generate certificates and keys with a single line of code, and also its ability to handle saving them to temp and non-temp files, which eliminates the need for developers to handle file operations while testing their code. - -Overall, testcerts simplifies the process of generating and managing test certificates, making it a valuable tool for any developer working with x509 certificates. - -## Usage - -### Generating Certificates to File - -The `GenerateCertsToFile` function generates an X.509 certificate and key and writes them to the file paths provided. +Stop saving test certificates in your code repos. Start generating them in your tests. ```go -func TestSomething(t *testing.T) { - err := testcerts.GenerateCertsToFile("/tmp/cert", "/tmp/key") +func TestFunc(t *testing.T) { + // Create and write self-signed Certificate and Key to temporary files + cert, key, err := testcerts.GenerateToTempFile("/tmp/") if err != nil { - // do stuff + // do something } + defer os.Remove(key) + defer os.Remove(cert) - _ = something.Run("/tmp/cert", "/tmp/key") - // do more testing -} -``` - -### Generating Certificates to Temporary File - -The `GenerateCertsToTempFile` function generates an X.509 certificate and key and writes them to randomly generated files in the directory provided or the system's temporary directory if none is provided. The function returns the file paths of the generated files. - -```go -func TestSomething(t *testing.T) { - certFile, keyFile, err := testcerts.GenerateCertsToTempFile("/tmp/") + // Start HTTP Listener with test certificates + err = http.ListenAndServeTLS("127.0.0.1:443", cert, key, someHandler) if err != nil { - // do stuff + // do something } - - _ = something.Run(certFile, keyFile) - // do more testing } ``` -### Generating Certificates - -The `GenerateCerts` function generates an X.509 certificate and key and returns them as byte slices. +For more complex tests, you can also use this package to create a Certificate Authority and a key pair signed by that Certificate Authority for any test domain you want. ```go -func TestSomething(t *testing.T) { - cert, key, err := testcerts.GenerateCerts() - if err != nil { - // do stuff +func TestFunc(t *testing.T) { + // Generate Certificate Authority + ca := testcerts.NewCA() + + go func() { + // Create a signed Certificate and Key for "localhost" + certs, err := ca.NewKeyPair("localhost") + if err != nil { + // do something + } + + // Write certificates to a file + err = certs.ToFile("/tmp/cert", "/tmp/key") + if err { + // do something + } + + // Start HTTP Listener + err = http.ListenAndServeTLS("localhost:443", cert, key, someHandler) + if err != nil { + // do something + } + }() + + // Create a client with the self-signed CA + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: ca.CertPool(), + }, + }, } - _ = something.Run(cert, key) - // do more testing + // Make an HTTPS request + r, _ := client.Get("https://myserver.internal.net:9443") } ``` +Simplify your testing, and don't hassle with certificates anymore. + ## Contributing If you find a bug or have an idea for a feature, please open an issue or a pull request. diff --git a/testcerts.go b/testcerts.go index 57ca702..0a390fc 100644 --- a/testcerts.go +++ b/testcerts.go @@ -1,23 +1,64 @@ /* -Package testcerts provides a set of functions for generating and saving x509 test certificates to file. +Package testcerts provides an easy-to-use suite of functions for generating x509 test certificates. -This package can be used in testing and development environments where a set of trusted certificates are needed. -The main function, GenerateCertsToTempFile, generates an x509 certificate and key and writes them to a randomly -named file in a specified or temporary directory. +Stop saving test certificates in your code repos. Start generating them in your tests. -For example, to generate and save a certificate and key to a temporary directory: + func TestFunc(t *testing.T) { + // Create and write self-signed Certificate and Key to temporary files + cert, key, err := testcerts.GenerateToTempFile("/tmp/") + if err != nil { + // do something + } + defer os.Remove(key) + defer os.Remove(cert) - func TestSomething(t *testing.T) { - certFile, keyFile, err := testcerts.GenerateCertsToTempFile("/tmp/") + // Start HTTP Listener with test certificates + err = http.ListenAndServeTLS("127.0.0.1:443", cert, key, someHandler) if err != nil { - // do stuff + // do something + } + } + +For more complex tests, you can also use this package to create a Certificate Authority and a key pair signed by that Certificate Authority for any test domain you want. + + func TestFunc(t *testing.T) { + // Generate Certificate Authority + ca := testcerts.NewCA() + + go func() { + // Create a signed Certificate and Key for "localhost" + certs, err := ca.NewKeyPair("localhost") + if err != nil { + // do something + } + + // Write certificates to a file + err = certs.ToFile("/tmp/cert", "/tmp/key") + if err { + // do something + } + + // Start HTTP Listener + err = http.ListenAndServeTLS("localhost:443", cert, key, someHandler) + if err != nil { + // do something + } + }() + + // Create a client with the self-signed CA + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: ca.CertPool(), + }, + }, } - _ = something.Run(certFile, keyFile) - // do more testing + // Make an HTTPS request + r, _ := client.Get("https://localhost:443") } -This will create a temporary certificate and key and print the paths to where the files were written. +Simplify your testing, and don't hassle with certificates anymore. */ package testcerts @@ -33,116 +74,252 @@ import ( "time" ) -// GenerateCerts generates an x509 certificate and key. -// It returns the certificate and key as byte slices, and any error that occurred. -// -// cert, key, err := GenerateCerts() -// if err != nil { -// // handle error -// } -func GenerateCerts() ([]byte, []byte, error) { - // Create certs and return as []byte - c, k, err := genCerts() +// CertificateAuthority represents an x509 certificate authority. +type CertificateAuthority struct { + cert *x509.Certificate + certPool *x509.CertPool + publicKey *pem.Block + privateKey *pem.Block +} + +// KeyPair represents a pair of x509 certificate and private key. +type KeyPair struct { + cert *x509.Certificate + publicKey *pem.Block + privateKey *pem.Block +} + +// NewCA creates a new CertificateAuthority. +func NewCA() *CertificateAuthority { + // Create a Certificate Authority Cert + ca := &CertificateAuthority{cert: &x509.Certificate{ + Subject: pkix.Name{ + Organization: []string{"Never Use this Certificate in Production Inc."}, + }, + SerialNumber: big.NewInt(42), + NotAfter: time.Now().Add(2 * time.Hour), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + }} + + var err error + + // Generate KeyPair + ca.publicKey, ca.privateKey, err = genKeyPair(ca.cert, ca.cert) if err != nil { - return nil, nil, err + // Should never error, but just incase + return ca } - return pem.EncodeToMemory(c), pem.EncodeToMemory(k), nil + + // Crete CertPool + ca.certPool = x509.NewCertPool() + result := ca.certPool.AppendCertsFromPEM(ca.PublicKey()) + if !result { + // if error set an empty pool + ca.certPool = x509.NewCertPool() + } + + return ca } -// GenerateCertsToFile creates an x509 certificate and key and writes it to the specified file paths. -// -// err := GenerateCertsToFile("/path/to/cert", "/path/to/key") -// if err != nil { -// // handle error -// } -// -// If the specified file paths already exist, it will overwrite the existing files. -func GenerateCertsToFile(certFile, keyFile string) error { - // Create Certs - c, k, err := GenerateCerts() +// NewKeyPair generates a new KeyPair signed by the CertificateAuthority for the given domains. +// The domains are used to populate the Subject Alternative Name field of the certificate. +// Returns an error if any of the given domains are invalid. +func (ca *CertificateAuthority) NewKeyPair(domains ...string) (*KeyPair, error) { + // Create a Certificate + kp := &KeyPair{cert: &x509.Certificate{ + Subject: pkix.Name{ + Organization: []string{"Never Use this Certificate in Production Inc."}, + }, + DNSNames: domains, + SerialNumber: big.NewInt(42), + NotAfter: time.Now().Add(2 * time.Hour), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + }} + + var err error + kp.publicKey, kp.privateKey, err = genKeyPair(ca.cert, kp.cert) if err != nil { - return err + return kp, fmt.Errorf("could not generate keypair: %s", err) } + return kp, nil +} + +// CertPool returns a Certificate Pool of the CertificateAuthority Certificate +func (ca *CertificateAuthority) CertPool() *x509.CertPool { + return ca.certPool +} +// PrivateKey returns the private key of the CertificateAuthority. +func (ca *CertificateAuthority) PrivateKey() []byte { + return pem.EncodeToMemory(ca.privateKey) +} + +// PublicKey returns the public key of the CertificateAuthority. +func (ca *CertificateAuthority) PublicKey() []byte { + return pem.EncodeToMemory(ca.publicKey) +} + +// ToFile saves the CertificateAuthority certificate and private key to the specified files. +// Returns an error if any file operation fails. +func (ca *CertificateAuthority) ToFile(certFile, keyFile string) error { // Write Certificate - cfh, err := os.Create(certFile) + err := os.WriteFile(certFile, ca.PublicKey(), 0640) if err != nil { return fmt.Errorf("unable to create certificate file - %s", err) } - defer cfh.Close() - _, err = cfh.Write(c) + + // Write Key + err = os.WriteFile(keyFile, ca.PrivateKey(), 0640) if err != nil { return fmt.Errorf("unable to create certificate file - %s", err) } - // Write Key - kfh, err := os.Create(keyFile) + return nil +} + +// ToTempFile saves the CertificateAuthority certificate and private key to temporary files. +// The temporary files are created in the specified directory and have random names. +func (ca *CertificateAuthority) ToTempFile(dir string) (*os.File, *os.File, error) { + cfh, err := os.CreateTemp(dir, "*.cert") if err != nil { - return fmt.Errorf("unable to create certificate file - %s", err) + return &os.File{}, &os.File{}, fmt.Errorf("could not create temporary file - %s", err) + } + defer cfh.Close() + _, err = cfh.Write(ca.PublicKey()) + if err != nil { + return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) + } + + // Write to Key File + kfh, err := os.CreateTemp(dir, "*.key") + if err != nil { + return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) } defer kfh.Close() - _, err = kfh.Write(k) + _, err = kfh.Write(ca.PrivateKey()) if err != nil { - return fmt.Errorf("unable to create certificate file - %s", err) + return cfh, kfh, fmt.Errorf("unable to create certificate file - %s", err) } - return nil + return cfh, kfh, nil } -// GenerateCertsToTempFile will create a temporary x509 certificate and key in a randomly generated file using the -// directory path provided. If no directory is specified, the default directory for temporary files as returned by -// os.TempDir will be used. -// -// cert, key, err := GenerateCertsToTempFile("/tmp/") -// if err != nil { -// // handle error -// } -func GenerateCertsToTempFile(dir string) (string, string, error) { - // Create Certs - c, k, err := GenerateCerts() +// PrivateKey returns the private key of the KeyPair. +func (kp *KeyPair) PrivateKey() []byte { + return pem.EncodeToMemory(kp.privateKey) +} + +// PublicKey returns the public key of the KeyPair. +func (kp *KeyPair) PublicKey() []byte { + return pem.EncodeToMemory(kp.publicKey) +} + +// ToFile saves the KeyPair certificate and private key to the specified files. +// Returns an error if any file operation fails. +func (kp *KeyPair) ToFile(certFile, keyFile string) error { + // Write Certificate + err := os.WriteFile(certFile, kp.PublicKey(), 0640) if err != nil { - return "", "", err + return fmt.Errorf("unable to create certificate file - %s", err) + } + + // Write Key + err = os.WriteFile(keyFile, kp.PrivateKey(), 0640) + if err != nil { + return fmt.Errorf("unable to create key file - %s", err) } + return nil +} + +// ToTempFile saves the KeyPair certificate and private key to temporary files. +// The temporary files are created in the specified directory and have random names. +func (kp *KeyPair) ToTempFile(dir string) (*os.File, *os.File, error) { cfh, err := os.CreateTemp(dir, "*.cert") if err != nil { - return "", "", fmt.Errorf("could not create temporary file - %s", err) + return &os.File{}, &os.File{}, fmt.Errorf("could not create temporary file - %s", err) } defer cfh.Close() - _, err = cfh.Write(c) + _, err = cfh.Write(kp.PublicKey()) if err != nil { - return cfh.Name(), "", fmt.Errorf("unable to create certificate file - %s", err) + return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) } // Write to Key File kfh, err := os.CreateTemp(dir, "*.key") if err != nil { - return cfh.Name(), "", fmt.Errorf("unable to create certificate file - %s", err) + return cfh, &os.File{}, fmt.Errorf("unable to create key file - %s", err) } defer kfh.Close() - _, err = kfh.Write(k) + _, err = kfh.Write(kp.PrivateKey()) if err != nil { - return cfh.Name(), kfh.Name(), fmt.Errorf("unable to create certificate file - %s", err) + return cfh, kfh, fmt.Errorf("unable to create key file - %s", err) } - return cfh.Name(), kfh.Name(), nil + return cfh, kfh, nil } -// genCerts will perform the task of creating a temporary Certificate and Key. -func genCerts() (*pem.Block, *pem.Block, error) { - // Create a Certificate Authority Cert - ca := &x509.Certificate{ - Subject: pkix.Name{ - Organization: []string{"Never Use this Certificate in Production Inc."}, - }, - SerialNumber: big.NewInt(42), - NotAfter: time.Now().Add(2 * time.Hour), - IsCA: true, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - BasicConstraintsValid: true, +// GenerateCerts generates an x509 certificate and key. +// It returns the certificate and key as byte slices, and any error that occurred. +// +// cert, key, err := GenerateCerts() +// if err != nil { +// // handle error +// } +func GenerateCerts(domains ...string) ([]byte, []byte, error) { + ca := NewCA() + + // Returning CA for backwards compatibility + if len(domains) == 0 { + return ca.PublicKey(), ca.PrivateKey(), nil + } + + // If domains exist return a regular cert + kp, err := ca.NewKeyPair(domains...) + if err != nil { + return nil, nil, err + } + return kp.PublicKey(), kp.PrivateKey(), nil +} + +// GenerateCertsToFile creates an x509 certificate and key and writes it to the specified file paths. +// +// err := GenerateCertsToFile("/path/to/cert", "/path/to/key") +// if err != nil { +// // handle error +// } +// +// If the specified file paths already exist, it will overwrite the existing files. +func GenerateCertsToFile(certFile, keyFile string) error { + // Create Certs using CA for backwards compatibility + return NewCA().ToFile(certFile, keyFile) +} + +// GenerateCertsToTempFile will create a temporary x509 certificate and key in a randomly generated file using the +// directory path provided. If no directory is specified, the default directory for temporary files as returned by +// os.TempDir will be used. +// +// cert, key, err := GenerateCertsToTempFile("/tmp/") +// if err != nil { +// // handle error +// } +func GenerateCertsToTempFile(dir string) (string, string, error) { + // Create Certs using CA for backwards compatibility + cert, key, err := NewCA().ToTempFile(dir) + if err != nil { + return "", "", err } + return cert.Name(), key.Name(), nil +} + +// genKeyPair will generate a key and certificate from the provided Certificate and CA. +func genKeyPair(ca *x509.Certificate, cert *x509.Certificate) (*pem.Block, *pem.Block, error) { // Create a Private Key key, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { @@ -150,14 +327,13 @@ func genCerts() (*pem.Block, *pem.Block, error) { } // Use CA Cert to sign a CSR and create a Public Cert - csr := &key.PublicKey - cert, err := x509.CreateCertificate(rand.Reader, ca, ca, csr, key) + certificate, err := x509.CreateCertificate(rand.Reader, cert, ca, &key.PublicKey, key) if err != nil { return nil, nil, fmt.Errorf("could not generate certificate - %s", err) } // Convert keys into pem.Block - c := &pem.Block{Type: "CERTIFICATE", Bytes: cert} + c := &pem.Block{Type: "CERTIFICATE", Bytes: certificate} k := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} return c, k, nil } diff --git a/testcerts_test.go b/testcerts_test.go index 0dc187f..4af4000 100644 --- a/testcerts_test.go +++ b/testcerts_test.go @@ -1,11 +1,206 @@ package testcerts import ( + "crypto/x509" + "fmt" "os" "path/filepath" "testing" ) +func TestCertsUsage(t *testing.T) { + // Generate Happy Path + + // Generate No Domains + + // Generate CA + ca := NewCA() + if len(ca.PrivateKey()) == 0 || len(ca.PublicKey()) == 0 { + t.Errorf("Unexpected key length from public/private key") + } + + t.Run("Verify CertPool", func(t *testing.T) { + cp := x509.NewCertPool() + if cp.AppendCertsFromPEM(ca.PublicKey()) { + if cp.Equal(ca.CertPool()) { + return + } + } + t.Errorf("certpool is not valid") + }) + + t.Run("Write to File", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "") + if err != nil { + t.Errorf("Error creating temporary directory: %s", err) + return + } + defer os.RemoveAll(tempDir) + + certPath := filepath.Join(tempDir, "cert") + keyPath := filepath.Join(tempDir, "key") + + err = ca.ToFile(certPath, keyPath) + if err != nil { + t.Errorf("Error while generating certificates to files - %s", err) + } + + // Check if Cert file exists + _, err = os.Stat(certPath) + if err != nil { + t.Errorf("Error while generating certificates to files file error - %s", err) + } + + // Check if Key file exists + _, err = os.Stat(keyPath) + if err != nil { + t.Errorf("Error while generating certificates to files file error - %s", err) + } + }) + + t.Run("Write to Invalid File", func(t *testing.T) { + certPath := "/notValid/path/cert" + keyPath := "/notValid/path/key" + + err := ca.ToFile(certPath, keyPath) + if err == nil { + t.Errorf("Unexpected success generating certificates to files") + } + + // Check if Cert file exists + _, err = os.Stat(certPath) + if !os.IsNotExist(err) { + t.Errorf("Unexpected success while generating certificates to files") + } + + // Check if Key file exists + _, err = os.Stat(keyPath) + if !os.IsNotExist(err) { + t.Errorf("Unexpected success while generating certificates to files") + } + }) + + t.Run("Write to TempFile", func(t *testing.T) { + cert, key, err := ca.ToTempFile("") + if err != nil { + t.Errorf("Error generating tempfile - %s", err) + } + + _, err = os.Stat(cert.Name()) + if err != nil { + t.Errorf("File does not exist - %s", cert.Name()) + } + defer os.Remove(cert.Name()) + + _, err = os.Stat(key.Name()) + if err != nil { + t.Errorf("File does not exist - %s", key.Name()) + } + defer os.Remove(key.Name()) + }) + + t.Run("Write to Invalid TempFile", func(t *testing.T) { + _, _, err := ca.ToTempFile("/notValidPath/") + if err == nil { + t.Errorf("Unexpected success with invalid tempfile directory") + } + }) + + for _, domains := range [][]string{{"localhost", "127.0.0.1", "example.com"}, {}} { + t.Run(fmt.Sprintf("Generate KeyPair with %d Domains", len(domains)), func(t *testing.T) { + kp, err := ca.NewKeyPair(domains...) + if err != nil { + t.Errorf("NewKeyPair() returned error when generating with domains: %s", err) + } + + t.Run("Validate Key Length", func(t *testing.T) { + if len(kp.PrivateKey()) == 0 || len(kp.PublicKey()) == 0 { + t.Errorf("Unexpected key length from public/private key") + } + }) + + t.Run("Write to File", func(t *testing.T) { + tempDir, err := os.MkdirTemp("", "") + if err != nil { + t.Errorf("Error creating temporary directory: %s", err) + return + } + defer os.RemoveAll(tempDir) + + certPath := filepath.Join(tempDir, "cert") + keyPath := filepath.Join(tempDir, "key") + + err = kp.ToFile(certPath, keyPath) + if err != nil { + t.Errorf("Error while generating certificates to files - %s", err) + } + + // Check if Cert file exists + _, err = os.Stat(certPath) + if err != nil { + t.Errorf("Error while generating certificates to files file error - %s", err) + } + + // Check if Key file exists + _, err = os.Stat(keyPath) + if err != nil { + t.Errorf("Error while generating certificates to files file error - %s", err) + } + }) + + t.Run("Write to Invalid File", func(t *testing.T) { + certPath := "/notValid/path/cert" + keyPath := "/notValid/path/key" + + err := kp.ToFile(certPath, keyPath) + if err == nil { + t.Errorf("Unexpected success generating certificates to files") + } + + // Check if Cert file exists + _, err = os.Stat(certPath) + if !os.IsNotExist(err) { + t.Errorf("Unexpected success while generating certificates to files") + } + + // Check if Key file exists + _, err = os.Stat(keyPath) + if !os.IsNotExist(err) { + t.Errorf("Unexpected success while generating certificates to files") + } + }) + + t.Run("Write to TempFile", func(t *testing.T) { + cert, key, err := kp.ToTempFile("") + if err != nil { + t.Errorf("Error generating tempfile - %s", err) + } + + _, err = os.Stat(cert.Name()) + if err != nil { + t.Errorf("File does not exist - %s", cert.Name()) + } + defer os.Remove(cert.Name()) + + _, err = os.Stat(key.Name()) + if err != nil { + t.Errorf("File does not exist - %s", key.Name()) + } + defer os.Remove(key.Name()) + }) + + t.Run("Write to Invalid TempFile", func(t *testing.T) { + _, _, err := kp.ToTempFile("/notValidPath/") + if err == nil { + t.Errorf("Unexpected success with invalid tempfile directory") + } + }) + + }) + } + +} + func TestGeneratingCerts(t *testing.T) { _, _, err := GenerateCerts() if err != nil { From 016bd097c40a94ad8de9ea71e9deb298225c76b4 Mon Sep 17 00:00:00 2001 From: Benjamin Cane Date: Fri, 7 Apr 2023 05:06:45 -0700 Subject: [PATCH 2/4] Dont need tickerizer for this... --- .github/workflows/lint.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8315f53..b641583 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,18 +36,3 @@ jobs: # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. # skip-build-cache: true - tickeryzer: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.16 - - - name: Install Tickeryzer - run: go get -u github.com/orijtech/tickeryzer/cmd/tickeryzer - - - name: Run Tickeryzer - run: tickeryzer ./... From d2c8488d8de53c581a370ae163353441ff05f2c8 Mon Sep 17 00:00:00 2001 From: Benjamin Cane Date: Fri, 7 Apr 2023 05:12:13 -0700 Subject: [PATCH 3/4] Fixing code examples --- README.md | 2 +- testcerts.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f12e4b5..fa0d06b 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ func TestFunc(t *testing.T) { } // Make an HTTPS request - r, _ := client.Get("https://myserver.internal.net:9443") + r, _ := client.Get("https://localhost") } ``` diff --git a/testcerts.go b/testcerts.go index 0a390fc..961f16f 100644 --- a/testcerts.go +++ b/testcerts.go @@ -55,7 +55,7 @@ For more complex tests, you can also use this package to create a Certificate Au } // Make an HTTPS request - r, _ := client.Get("https://localhost:443") + r, _ := client.Get("https://localhost") } Simplify your testing, and don't hassle with certificates anymore. From d3c9f81141aa97c24d2377fd1214cca4fe058a8b Mon Sep 17 00:00:00 2001 From: Benjamin Cane Date: Fri, 7 Apr 2023 08:07:41 -0700 Subject: [PATCH 4/4] Fixing comments --- README.md | 2 +- testcerts.go | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fa0d06b..c6469a6 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ func TestFunc(t *testing.T) { } // Start HTTP Listener - err = http.ListenAndServeTLS("localhost:443", cert, key, someHandler) + err = http.ListenAndServeTLS("localhost:443", "/tmp/cert", "/tmp/key", someHandler) if err != nil { // do something } diff --git a/testcerts.go b/testcerts.go index 961f16f..8920735 100644 --- a/testcerts.go +++ b/testcerts.go @@ -39,7 +39,7 @@ For more complex tests, you can also use this package to create a Certificate Au } // Start HTTP Listener - err = http.ListenAndServeTLS("localhost:443", cert, key, someHandler) + err = http.ListenAndServeTLS("localhost:443", "/tmp/cert", "/tmp/key", someHandler) if err != nil { // do something } @@ -74,7 +74,7 @@ import ( "time" ) -// CertificateAuthority represents an x509 certificate authority. +// CertificateAuthority represents a self-signed x509 certificate authority. type CertificateAuthority struct { cert *x509.Certificate certPool *x509.CertPool @@ -82,7 +82,7 @@ type CertificateAuthority struct { privateKey *pem.Block } -// KeyPair represents a pair of x509 certificate and private key. +// KeyPair represents a pair of self-signed x509 certificate and private key. type KeyPair struct { cert *x509.Certificate publicKey *pem.Block @@ -113,7 +113,7 @@ func NewCA() *CertificateAuthority { return ca } - // Crete CertPool + // Create CertPool ca.certPool = x509.NewCertPool() result := ca.certPool.AppendCertsFromPEM(ca.PublicKey()) if !result { @@ -126,7 +126,6 @@ func NewCA() *CertificateAuthority { // NewKeyPair generates a new KeyPair signed by the CertificateAuthority for the given domains. // The domains are used to populate the Subject Alternative Name field of the certificate. -// Returns an error if any of the given domains are invalid. func (ca *CertificateAuthority) NewKeyPair(domains ...string) (*KeyPair, error) { // Create a Certificate kp := &KeyPair{cert: &x509.Certificate{ @@ -142,6 +141,8 @@ func (ca *CertificateAuthority) NewKeyPair(domains ...string) (*KeyPair, error) }} var err error + + // Generate KeyPair kp.publicKey, kp.privateKey, err = genKeyPair(ca.cert, kp.cert) if err != nil { return kp, fmt.Errorf("could not generate keypair: %s", err) @@ -185,6 +186,7 @@ func (ca *CertificateAuthority) ToFile(certFile, keyFile string) error { // ToTempFile saves the CertificateAuthority certificate and private key to temporary files. // The temporary files are created in the specified directory and have random names. func (ca *CertificateAuthority) ToTempFile(dir string) (*os.File, *os.File, error) { + // Write Certificate cfh, err := os.CreateTemp(dir, "*.cert") if err != nil { return &os.File{}, &os.File{}, fmt.Errorf("could not create temporary file - %s", err) @@ -195,7 +197,7 @@ func (ca *CertificateAuthority) ToTempFile(dir string) (*os.File, *os.File, erro return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) } - // Write to Key File + // Write Key kfh, err := os.CreateTemp(dir, "*.key") if err != nil { return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) @@ -240,6 +242,7 @@ func (kp *KeyPair) ToFile(certFile, keyFile string) error { // ToTempFile saves the KeyPair certificate and private key to temporary files. // The temporary files are created in the specified directory and have random names. func (kp *KeyPair) ToTempFile(dir string) (*os.File, *os.File, error) { + // Write Certificate cfh, err := os.CreateTemp(dir, "*.cert") if err != nil { return &os.File{}, &os.File{}, fmt.Errorf("could not create temporary file - %s", err) @@ -250,7 +253,7 @@ func (kp *KeyPair) ToTempFile(dir string) (*os.File, *os.File, error) { return cfh, &os.File{}, fmt.Errorf("unable to create certificate file - %s", err) } - // Write to Key File + // Write Key kfh, err := os.CreateTemp(dir, "*.key") if err != nil { return cfh, &os.File{}, fmt.Errorf("unable to create key file - %s", err) @@ -326,7 +329,7 @@ func genKeyPair(ca *x509.Certificate, cert *x509.Certificate) (*pem.Block, *pem. return nil, nil, fmt.Errorf("could not generate rsa key - %s", err) } - // Use CA Cert to sign a CSR and create a Public Cert + // Use CA Cert to sign and create a Public Cert certificate, err := x509.CreateCertificate(rand.Reader, cert, ca, &key.PublicKey, key) if err != nil { return nil, nil, fmt.Errorf("could not generate certificate - %s", err)