-
Notifications
You must be signed in to change notification settings - Fork 1
/
keygenerator.go
314 lines (278 loc) · 8.32 KB
/
keygenerator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
Package keysupport is used to generate self-signed keys for testing purposes.
This code was pulled and modified from the following resources:
- https://gist.github.com/shaneutt/5e1995295cff6721c89a71d13a71c251
- https://shaneutt.com/blog/golang-ca-and-signed-cert-go/.
USAGE:
Use the hexaKeyTool command to call this routine.
go run cmd/hexaKeyTool
This will generate a CA cert/key pair and use that to sign Server cert/key pair
and Client cert/key pair.
Use these certs for tests such as websupport_test and orchestrator_test.
*/
package keysupport
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"log"
"math/big"
"net"
"os"
"path/filepath"
"strings"
"time"
)
const (
EnvCertOrg string = "HEXA_CERT_ORG"
EnvCertCountry string = "HEXA_CERT_COUNTRY"
EnvCertProv string = "HEXA_CERT_PROV"
EnvCertLocality string = "HEXA_CERT_LOCALITY"
EnvCertCaKey string = "HEXA_CA_KEYFILE" // The location of a private key used to generate server keys
EnvCertDirectory string = "HEXA_CERT_DIRECTORY" // The location where keys are stored.
EnvServerCert string = "SERVER_CERT"
EnvServerKey string = "SERVER_KEY_PATH"
EnvAutoCreate string = "HEXA_AUTO_SELFSIGN"
)
type KeyConfig struct {
KeyFile string // The file containing a PEM encoded PKCS1 private key
CertDir string // This is the directory where generated keys are output
PkixName pkix.Name
ServerCertPath string
ServerKeyPath string
}
/*
GetKeyConfig reads environment variables and sets up configuration parameters in KeyConfig struct. Note that if
no environment variables are set, the default directory is the current directory plus "./.certs". When running in
docker-compose as a minimum, HEXA_CERT_DIRECTORY should be set.
*/
func GetKeyConfig() KeyConfig {
org := os.Getenv(EnvCertOrg)
if org == "" {
org = "Hexa Organization"
}
country := os.Getenv(EnvCertCountry)
if country == "" {
country = "US"
}
prov := os.Getenv(EnvCertProv)
if prov == "" {
prov = "CO"
}
locality := os.Getenv(EnvCertLocality)
if locality == "" {
locality = "Boulder"
}
certDir := os.Getenv(EnvCertDirectory)
if certDir == "" {
file := os.Getenv("HOME")
certDir = filepath.Join(file, "./.certs")
fmt.Println("Defaulting certificate directory to: " + certDir)
}
// Create the directory if it does not exist
err := os.Mkdir(certDir, 0755)
if !os.IsExist(err) {
panic(fmt.Sprintf("Was unable to open or create certificate directory(%s):%s", certDir, err))
}
caKey := os.Getenv(EnvCertCaKey)
if caKey == "" {
caKey = filepath.Join(certDir, "ca-key.pem")
}
serverKeyPath := os.Getenv(EnvServerKey)
if serverKeyPath == "" {
serverKeyPath = filepath.Join(certDir, "server-key.pem")
}
serverCertPath := os.Getenv(EnvServerCert)
if serverCertPath == "" {
serverCertPath = filepath.Join(certDir, "server-cert.pem")
}
return KeyConfig{
KeyFile: caKey,
CertDir: certDir,
ServerKeyPath: serverKeyPath,
ServerCertPath: serverCertPath,
PkixName: pkix.Name{
Organization: []string{org},
Country: []string{country},
Province: []string{prov},
Locality: []string{locality},
},
}
}
func (config KeyConfig) CertDirExists() bool {
dirStat, err := os.Stat(config.CertDir)
if err != nil {
return false
}
return dirStat.IsDir()
}
func (config KeyConfig) RootKeyExists() bool {
_, err := os.Stat(config.KeyFile)
return err == nil
}
func (config KeyConfig) ServerKeyExists() bool {
_, err := os.Stat(config.ServerKeyPath)
return err == nil
}
/*
CreateSelfSignedKeys creates a set of self signed keys and writes them out to the directory in KeyConfig.CertDir
This includes: Certificate Authority Certificate and Key (ca-cert/ca-key), Server certificate (server-cert.pem) and key
(server-key.pem), and a client certificate (client-cert.pem) and key (client-key.pem).
*/
func (config KeyConfig) CreateSelfSignedKeys() (err error) {
auto := os.Getenv(EnvAutoCreate)
if auto != "" && strings.ToLower(auto[0:1]) == "f" {
log.Println("Auto self-sign create is disabled (HEXA_AUTO_SELFSIGN). Will not generate.")
return nil
}
// set up our CA certificate
ca := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: config.PkixName,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
var caPrivKey *rsa.PrivateKey
keyFile := config.KeyFile
if keyFile != "" {
_, err := os.Stat(keyFile) // first check that it exists, otherwise fall through to create
if err == nil {
log.Println(fmt.Sprintf("Attempting to load existing CA Key from %s ...", keyFile))
keyBytes, err := os.ReadFile(keyFile)
if err != nil {
return err
}
if len(keyBytes) > 10 {
pemBlock, _ := pem.Decode(keyBytes)
if pemBlock == nil {
return errors.New("expecting file to contain a PEM key")
}
caBytes := pemBlock.Bytes
caPrivKey, err = x509.ParsePKCS1PrivateKey(caBytes)
if err != nil {
return err
}
}
}
}
if caPrivKey == nil {
log.Println("Generating new CA key...")
// create our private and public key
caPrivKey, err = rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return err
}
caPrivKeyPEM := new(bytes.Buffer)
_ = pem.Encode(caPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
})
log.Println("Writing out CA Key")
err = os.WriteFile(config.KeyFile, caPrivKeyPEM.Bytes(), 0644)
if err != nil {
return err
}
}
// create the CA Public key file (in case it does not exist)
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
if err != nil {
return err
}
// pem encode
caPEM := new(bytes.Buffer)
_ = pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})
log.Println("Writing out CA Cert")
err = os.WriteFile(filepath.Join(config.CertDir, "ca-cert.pem"), caPEM.Bytes(), 0644)
if err != nil {
return err
}
// set up our server certificate
certPEM, certPrivKeyPEM, err := config.generateCert(
ca,
caPrivKey,
[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
[]string{"hexa-bundle-server"},
)
if err != nil {
return err
}
log.Println("Writing out Server Cert")
err = os.WriteFile(config.ServerCertPath, certPEM, 0644)
if err != nil {
return err
}
log.Println("Writing out Server Key")
err = os.WriteFile(config.ServerKeyPath, certPrivKeyPEM, 0644)
if err != nil {
return err
}
// set up our client certificate
clientCertPEM, clientCertPrivKeyPEM, err := config.generateCert(
ca,
caPrivKey,
[]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
[]string{},
)
if err != nil {
return err
}
log.Println("Writing out Client Cert")
err = os.WriteFile(filepath.Join(config.CertDir, "client-cert.pem"), clientCertPEM, 0644)
if err != nil {
return err
}
log.Println("Writing out Client Key")
err = os.WriteFile(filepath.Join(config.CertDir, "client-key.pem"), clientCertPrivKeyPEM, 0644)
if err != nil {
return err
}
return
}
func (config KeyConfig) generateCert(
ca *x509.Certificate,
caPrivKey *rsa.PrivateKey,
keyUsage []x509.ExtKeyUsage,
dnsNames []string) ([]byte, []byte, error) {
cert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: config.PkixName,
DNSNames: dnsNames,
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: keyUsage,
KeyUsage: x509.KeyUsageDigitalSignature,
}
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
if err != nil {
return nil, nil, err
}
certPEM := new(bytes.Buffer)
_ = pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})
certPrivKeyPEM := new(bytes.Buffer)
_ = pem.Encode(certPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})
return certPEM.Bytes(), certPrivKeyPEM.Bytes(), nil
}