Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding IP Address support #11

Merged
merged 5 commits into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions gencerts.go
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
}
147 changes: 147 additions & 0 deletions gencerts_test.go
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")
}
})
}
56 changes: 56 additions & 0 deletions kpconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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 is a configuration for generating an X.509 key pair.
type KeyPairConfig struct {
// Domains is a list of domains to include in the certificate as Subject
// Alternative Names.
Domains []string

// IPAddresses is a list of IP addresses to include in the certificate
// as Subject Alternative Names.
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
}

// IPAddresses returns a list of IP addresses in Net.IP format.
func (c *KeyPairConfig) IPNetAddresses() ([]net.IP, error) {
var ips []net.IP
for _, ip := range c.IPAddresses {
parsed := net.ParseIP(ip)
if parsed == nil {
return nil, ErrInvalidIP
}
ips = append(ips, parsed)
}
return ips, nil
}
Loading
Loading