Skip to content

Commit

Permalink
Merge pull request #11 from madflojo/ip-support
Browse files Browse the repository at this point in the history
Adding IP Address support
  • Loading branch information
madflojo committed May 19, 2024
2 parents ba61e88 + ccd9603 commit c17de65
Show file tree
Hide file tree
Showing 6 changed files with 693 additions and 273 deletions.
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

0 comments on commit c17de65

Please sign in to comment.