-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding KeyPair Config for more options
- Loading branch information
Showing
6 changed files
with
343 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package testcerts | ||
|
||
// GenerateCerts generates a 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 a 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package testcerts | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestGeneratingCerts(t *testing.T) { | ||
_, _, err := GenerateCerts() | ||
if err != nil { | ||
t.Errorf("Error while generating certificates - %s", err) | ||
} | ||
} | ||
|
||
func TestGeneratingCertsToFile(t *testing.T) { | ||
t.Run("Test the happy path", func(t *testing.T) { | ||
tempDir, err := os.MkdirTemp("", "") | ||
if err != nil { | ||
t.Errorf("Error creating temporary directory: %s", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
|
||
certPath := filepath.Join(tempDir, "cert") | ||
keyPath := filepath.Join(tempDir, "key") | ||
|
||
err = GenerateCertsToFile(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("Testing the unhappy path for cert files", func(t *testing.T) { | ||
tempDir, err := os.MkdirTemp("", "") | ||
if err != nil { | ||
t.Errorf("Error creating temporary directory: %s", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
|
||
certPath := filepath.Join(tempDir, "doesntexist", "cert") | ||
keyPath := filepath.Join(tempDir, "key") | ||
|
||
err = GenerateCertsToFile(certPath, keyPath) | ||
if err == nil { | ||
t.Errorf("Expected error when generating a certificate with a bad path got nil") | ||
} | ||
}) | ||
|
||
t.Run("Testing the unhappy path for key files", func(t *testing.T) { | ||
tempDir, err := os.MkdirTemp("", "") | ||
if err != nil { | ||
t.Errorf("Error creating temporary directory: %s", err) | ||
} | ||
defer os.RemoveAll(tempDir) | ||
|
||
certPath := filepath.Join(tempDir, "cert") | ||
keyPath := filepath.Join(tempDir, "doesntexist", "key") | ||
|
||
err = GenerateCertsToFile(certPath, keyPath) | ||
if err == nil { | ||
t.Errorf("Expected error when generating a key with a bad path got nil") | ||
} | ||
}) | ||
|
||
t.Run("Testing the unhappy path for insufficient permissions", func(t *testing.T) { | ||
dir, err := os.MkdirTemp("", "permission-test") | ||
if err != nil { | ||
t.Errorf("Error creating temp directory - %s", err) | ||
} | ||
defer os.RemoveAll(dir) | ||
|
||
// Change permissions of the temp directory so that it can't be written to | ||
err = os.Chmod(dir, 0444) | ||
if err != nil { | ||
t.Errorf("Error changing permissions of temp directory - %s", err) | ||
} | ||
|
||
certPath := filepath.Join(dir, "cert") | ||
keyPath := filepath.Join(dir, "key") | ||
|
||
err = GenerateCertsToFile(certPath, keyPath) | ||
if err == nil { | ||
t.Errorf("Expected error when generating certificate with insufficient permissions, got nil") | ||
} | ||
}) | ||
} | ||
|
||
func TestGenerateCertsToTempFile(t *testing.T) { | ||
t.Run("Test the happy path", func(t *testing.T) { | ||
certFile, keyFile, err := GenerateCertsToTempFile("/tmp") | ||
if err != nil { | ||
t.Errorf("Error while generating certificates to temp files - %s", err) | ||
} | ||
|
||
// Check if Cert file exists | ||
_, err = os.Stat(certFile) | ||
if err != nil { | ||
t.Errorf("Error while generating certificates to temp files file error - %s", err) | ||
} | ||
_ = os.Remove(certFile) | ||
|
||
// Check if Key file exists | ||
_, err = os.Stat(keyFile) | ||
if err != nil { | ||
t.Errorf("Error while generating certificates to temp files file error - %s", err) | ||
} | ||
_ = os.Remove(keyFile) | ||
}) | ||
|
||
t.Run("Testing the unhappy path when creating cert temp file", func(t *testing.T) { | ||
_, _, err := GenerateCertsToTempFile("/doesnotexist") | ||
if err == nil { | ||
t.Errorf("Expected error when generating a certificate with a bad directory path got nil") | ||
} | ||
}) | ||
|
||
t.Run("Testing the unhappy path for insufficient permissions when creating temp file", func(t *testing.T) { | ||
dir, err := os.MkdirTemp("", "permission-test") | ||
if err != nil { | ||
t.Errorf("Error creating temp directory - %s", err) | ||
} | ||
defer os.RemoveAll(dir) | ||
|
||
// Change permissions of the temp directory so that it can't be written to | ||
err = os.Chmod(dir, 0444) | ||
if err != nil { | ||
t.Errorf("Error changing permissions of temp directory - %s", err) | ||
} | ||
|
||
_, _, err = GenerateCertsToTempFile(dir) | ||
if err == nil { | ||
t.Errorf("Expected error when generating a key with a bad directory path got nil") | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package testcerts | ||
|
||
import ( | ||
"errors" | ||
"net" | ||
) | ||
|
||
var ( | ||
// ErrEmptyConfig is returned when a KeyPairConfig is empty. | ||
ErrEmptyConfig = errors.New("empty KeyPairConfig") | ||
|
||
// ErrInvalidIP is returned when an IP address is invalid. | ||
ErrInvalidIP = errors.New("invalid IP address") | ||
) | ||
|
||
// KeyPairConfig represents a configuration for generating an X509 KeyPair. | ||
type KeyPairConfig struct { | ||
// Domains is a list of domains to include in the certificate. | ||
Domains []string | ||
|
||
// IPAddresses is a list of IP addresses to include in the certificate. | ||
IPAddresses []string | ||
} | ||
|
||
// Validate validates the KeyPairConfig ensuring that it is not empty and that | ||
// provided values are valid. | ||
func (c *KeyPairConfig) Validate() error { | ||
// Check if the config is empty. | ||
if len(c.Domains) == 0 && len(c.IPAddresses) == 0 { | ||
return ErrEmptyConfig | ||
} | ||
|
||
// Validate IP addresses. | ||
for _, ip := range c.IPAddresses { | ||
if net.ParseIP(ip) == nil { | ||
return ErrInvalidIP | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package testcerts | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
type KPConfigTestCase struct { | ||
name string | ||
cfg KeyPairConfig | ||
err error | ||
} | ||
|
||
func TestKPConfigs(t *testing.T) { | ||
tc := []KPConfigTestCase{ | ||
{ | ||
name: "Happy Path - Simple Domain", | ||
cfg: KeyPairConfig{ | ||
Domains: []string{"example.com"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - Multiple Domains", | ||
cfg: KeyPairConfig{ | ||
Domains: []string{"example.com", "example.org"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - Multiple Domains with Wildcard", | ||
cfg: KeyPairConfig{ | ||
Domains: []string{"example.com", "*.example.com"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Empty Config", | ||
cfg: KeyPairConfig{}, | ||
err: ErrEmptyConfig, | ||
}, | ||
{ | ||
name: "Happy Path - Valid IP", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"127.0.0.1"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - Multiple Valid IPs", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"127.0.0.1", "10.0.0.0"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - IPv6 Localhost", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"::1"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - Multiple IPv6 Addresses", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"::1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Happy Path - Valid IP and Domain", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"127.0.0.1", "10.0.0.0"}, | ||
Domains: []string{"example.com", "localhost"}, | ||
}, | ||
err: nil, | ||
}, | ||
{ | ||
name: "Invalid IP", | ||
cfg: KeyPairConfig{ | ||
IPAddresses: []string{"127.0.0.1", "not an IP"}, | ||
}, | ||
err: ErrInvalidIP, | ||
}, | ||
} | ||
|
||
for _, c := range tc { | ||
t.Run(c.name, func(t *testing.T) { | ||
err := c.cfg.Validate() | ||
if err != c.err { | ||
t.Errorf("Validation failed, expected error return of %v, got %v", c.err, err) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.