Skip to content

Commit c17de65

Browse files
authored
Merge pull request #11 from madflojo/ip-support
Adding IP Address support
2 parents ba61e88 + ccd9603 commit c17de65

File tree

6 files changed

+693
-273
lines changed

6 files changed

+693
-273
lines changed

gencerts.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package testcerts
2+
3+
// GenerateCerts generates a x509 certificate and key.
4+
// It returns the certificate and key as byte slices, and any error that occurred.
5+
//
6+
// cert, key, err := GenerateCerts()
7+
// if err != nil {
8+
// // handle error
9+
// }
10+
func GenerateCerts(domains ...string) ([]byte, []byte, error) {
11+
ca := NewCA()
12+
13+
// Returning CA for backwards compatibility
14+
if len(domains) == 0 {
15+
return ca.PublicKey(), ca.PrivateKey(), nil
16+
}
17+
18+
// If domains exist return a regular cert
19+
kp, err := ca.NewKeyPair(domains...)
20+
if err != nil {
21+
return nil, nil, err
22+
}
23+
return kp.PublicKey(), kp.PrivateKey(), nil
24+
}
25+
26+
// GenerateCertsToFile creates a x509 certificate and key and writes it to the specified file paths.
27+
//
28+
// err := GenerateCertsToFile("/path/to/cert", "/path/to/key")
29+
// if err != nil {
30+
// // handle error
31+
// }
32+
//
33+
// If the specified file paths already exist, it will overwrite the existing files.
34+
func GenerateCertsToFile(certFile, keyFile string) error {
35+
// Create Certs using CA for backwards compatibility
36+
return NewCA().ToFile(certFile, keyFile)
37+
}
38+
39+
// GenerateCertsToTempFile will create a temporary x509 certificate and key in a randomly generated file using the
40+
// directory path provided. If no directory is specified, the default directory for temporary files as returned by
41+
// os.TempDir will be used.
42+
//
43+
// cert, key, err := GenerateCertsToTempFile("/tmp/")
44+
// if err != nil {
45+
// // handle error
46+
// }
47+
func GenerateCertsToTempFile(dir string) (string, string, error) {
48+
// Create Certs using CA for backwards compatibility
49+
cert, key, err := NewCA().ToTempFile(dir)
50+
if err != nil {
51+
return "", "", err
52+
}
53+
54+
return cert.Name(), key.Name(), nil
55+
}

gencerts_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package testcerts
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestGeneratingCerts(t *testing.T) {
10+
_, _, err := GenerateCerts()
11+
if err != nil {
12+
t.Errorf("Error while generating certificates - %s", err)
13+
}
14+
}
15+
16+
func TestGeneratingCertsToFile(t *testing.T) {
17+
t.Run("Test the happy path", func(t *testing.T) {
18+
tempDir, err := os.MkdirTemp("", "")
19+
if err != nil {
20+
t.Errorf("Error creating temporary directory: %s", err)
21+
}
22+
defer os.RemoveAll(tempDir)
23+
24+
certPath := filepath.Join(tempDir, "cert")
25+
keyPath := filepath.Join(tempDir, "key")
26+
27+
err = GenerateCertsToFile(certPath, keyPath)
28+
if err != nil {
29+
t.Errorf("Error while generating certificates to files - %s", err)
30+
}
31+
32+
// Check if Cert file exists
33+
_, err = os.Stat(certPath)
34+
if err != nil {
35+
t.Errorf("Error while generating certificates to files file error - %s", err)
36+
}
37+
38+
// Check if Key file exists
39+
_, err = os.Stat(keyPath)
40+
if err != nil {
41+
t.Errorf("Error while generating certificates to files file error - %s", err)
42+
}
43+
})
44+
45+
t.Run("Testing the unhappy path for cert files", func(t *testing.T) {
46+
tempDir, err := os.MkdirTemp("", "")
47+
if err != nil {
48+
t.Errorf("Error creating temporary directory: %s", err)
49+
}
50+
defer os.RemoveAll(tempDir)
51+
52+
certPath := filepath.Join(tempDir, "doesntexist", "cert")
53+
keyPath := filepath.Join(tempDir, "key")
54+
55+
err = GenerateCertsToFile(certPath, keyPath)
56+
if err == nil {
57+
t.Errorf("Expected error when generating a certificate with a bad path got nil")
58+
}
59+
})
60+
61+
t.Run("Testing the unhappy path for key files", func(t *testing.T) {
62+
tempDir, err := os.MkdirTemp("", "")
63+
if err != nil {
64+
t.Errorf("Error creating temporary directory: %s", err)
65+
}
66+
defer os.RemoveAll(tempDir)
67+
68+
certPath := filepath.Join(tempDir, "cert")
69+
keyPath := filepath.Join(tempDir, "doesntexist", "key")
70+
71+
err = GenerateCertsToFile(certPath, keyPath)
72+
if err == nil {
73+
t.Errorf("Expected error when generating a key with a bad path got nil")
74+
}
75+
})
76+
77+
t.Run("Testing the unhappy path for insufficient permissions", func(t *testing.T) {
78+
dir, err := os.MkdirTemp("", "permission-test")
79+
if err != nil {
80+
t.Errorf("Error creating temp directory - %s", err)
81+
}
82+
defer os.RemoveAll(dir)
83+
84+
// Change permissions of the temp directory so that it can't be written to
85+
err = os.Chmod(dir, 0444)
86+
if err != nil {
87+
t.Errorf("Error changing permissions of temp directory - %s", err)
88+
}
89+
90+
certPath := filepath.Join(dir, "cert")
91+
keyPath := filepath.Join(dir, "key")
92+
93+
err = GenerateCertsToFile(certPath, keyPath)
94+
if err == nil {
95+
t.Errorf("Expected error when generating certificate with insufficient permissions, got nil")
96+
}
97+
})
98+
}
99+
100+
func TestGenerateCertsToTempFile(t *testing.T) {
101+
t.Run("Test the happy path", func(t *testing.T) {
102+
certFile, keyFile, err := GenerateCertsToTempFile("/tmp")
103+
if err != nil {
104+
t.Errorf("Error while generating certificates to temp files - %s", err)
105+
}
106+
107+
// Check if Cert file exists
108+
_, err = os.Stat(certFile)
109+
if err != nil {
110+
t.Errorf("Error while generating certificates to temp files file error - %s", err)
111+
}
112+
_ = os.Remove(certFile)
113+
114+
// Check if Key file exists
115+
_, err = os.Stat(keyFile)
116+
if err != nil {
117+
t.Errorf("Error while generating certificates to temp files file error - %s", err)
118+
}
119+
_ = os.Remove(keyFile)
120+
})
121+
122+
t.Run("Testing the unhappy path when creating cert temp file", func(t *testing.T) {
123+
_, _, err := GenerateCertsToTempFile("/doesnotexist")
124+
if err == nil {
125+
t.Errorf("Expected error when generating a certificate with a bad directory path got nil")
126+
}
127+
})
128+
129+
t.Run("Testing the unhappy path for insufficient permissions when creating temp file", func(t *testing.T) {
130+
dir, err := os.MkdirTemp("", "permission-test")
131+
if err != nil {
132+
t.Errorf("Error creating temp directory - %s", err)
133+
}
134+
defer os.RemoveAll(dir)
135+
136+
// Change permissions of the temp directory so that it can't be written to
137+
err = os.Chmod(dir, 0444)
138+
if err != nil {
139+
t.Errorf("Error changing permissions of temp directory - %s", err)
140+
}
141+
142+
_, _, err = GenerateCertsToTempFile(dir)
143+
if err == nil {
144+
t.Errorf("Expected error when generating a key with a bad directory path got nil")
145+
}
146+
})
147+
}

kpconfig.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package testcerts
2+
3+
import (
4+
"errors"
5+
"net"
6+
)
7+
8+
var (
9+
// ErrEmptyConfig is returned when a KeyPairConfig is empty.
10+
ErrEmptyConfig = errors.New("empty KeyPairConfig")
11+
12+
// ErrInvalidIP is returned when an IP address is invalid.
13+
ErrInvalidIP = errors.New("invalid IP address")
14+
)
15+
16+
// KeyPairConfig is a configuration for generating an X.509 key pair.
17+
type KeyPairConfig struct {
18+
// Domains is a list of domains to include in the certificate as Subject
19+
// Alternative Names.
20+
Domains []string
21+
22+
// IPAddresses is a list of IP addresses to include in the certificate
23+
// as Subject Alternative Names.
24+
IPAddresses []string
25+
}
26+
27+
// Validate validates the KeyPairConfig ensuring that it is not empty and that
28+
// provided values are valid.
29+
func (c *KeyPairConfig) Validate() error {
30+
// Check if the config is empty.
31+
if len(c.Domains) == 0 && len(c.IPAddresses) == 0 {
32+
return ErrEmptyConfig
33+
}
34+
35+
// Validate IP addresses.
36+
for _, ip := range c.IPAddresses {
37+
if net.ParseIP(ip) == nil {
38+
return ErrInvalidIP
39+
}
40+
}
41+
42+
return nil
43+
}
44+
45+
// IPAddresses returns a list of IP addresses in Net.IP format.
46+
func (c *KeyPairConfig) IPNetAddresses() ([]net.IP, error) {
47+
var ips []net.IP
48+
for _, ip := range c.IPAddresses {
49+
parsed := net.ParseIP(ip)
50+
if parsed == nil {
51+
return nil, ErrInvalidIP
52+
}
53+
ips = append(ips, parsed)
54+
}
55+
return ips, nil
56+
}

0 commit comments

Comments
 (0)