Skip to content

Commit 90bb673

Browse files
author
Pascal van Buijtene
committed
Support creating intermediate CA
1 parent 81dd255 commit 90bb673

File tree

4 files changed

+62
-24
lines changed

4 files changed

+62
-24
lines changed

Diff for: certificates/create_ca.go

+60-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"strings"
1717
"time"
1818

19-
multierror "github.com/hashicorp/go-multierror"
19+
"github.com/hashicorp/go-multierror"
2020
"github.com/mitchellh/cli"
2121
)
2222

@@ -25,8 +25,10 @@ type CreateCA struct {
2525
}
2626

2727
type CreateCAArguments struct {
28-
Days int
29-
OutputDir string
28+
Days int
29+
OutputDir string
30+
CACertificatePath string
31+
CAKeyPath string
3032
}
3133

3234
func (c *CreateCA) Run(args []string) int {
@@ -36,6 +38,8 @@ func (c *CreateCA) Run(args []string) int {
3638
flags.Usage = func() { c.Ui.Info(c.Help()) }
3739
flags.IntVar(&config.Days, "days", 0, "the validity period of the certificate in days")
3840
flags.StringVar(&config.OutputDir, "out", "./ca", "The output directory")
41+
flags.StringVar(&config.CACertificatePath, "ca-certificate", "", "the path to a CA certificate file")
42+
flags.StringVar(&config.CAKeyPath, "ca-key", "", "the path to a CA key file")
3943

4044
if err := flags.Parse(args); err != nil {
4145
return 1
@@ -46,6 +50,12 @@ func (c *CreateCA) Run(args []string) int {
4650
multierror.Append(validationErrors, errors.New("days must be positive"))
4751
}
4852

53+
caCertPathLen := len(config.CACertificatePath)
54+
caKeyPathLen := len(config.CAKeyPath)
55+
if (caCertPathLen > 0 && caKeyPathLen == 0) || (caKeyPathLen > 0 && caCertPathLen == 0){
56+
multierror.Append(validationErrors, errors.New("both -ca-certificate and -ca-key options are required"))
57+
}
58+
4959
if validationErrors.ErrorOrNil() != nil {
5060
c.Ui.Error(validationErrors.Error())
5161
return 1
@@ -59,9 +69,27 @@ func (c *CreateCA) Run(args []string) int {
5969
days = config.Days
6070
years = 0
6171
}
72+
73+
var caCert *x509.Certificate
74+
var caKey *rsa.PrivateKey
75+
var err error
76+
if caCertPathLen > 0 {
77+
caCert, err = readCertificateFromFile(config.CACertificatePath)
78+
if err != nil {
79+
c.Ui.Error(err.Error())
80+
return 1
81+
}
82+
83+
caKey, err = readRSAKeyFromFile(config.CAKeyPath)
84+
if err != nil {
85+
err := fmt.Errorf("error: %s. please note that only RSA keys are currently supported", err.Error())
86+
c.Ui.Error(err.Error())
87+
return 1
88+
}
89+
}
6290

6391
outputDir := config.OutputDir
64-
err := generateCACertificate(defaultKeySize, years, days, outputDir)
92+
err = generateCACertificate(years, days, outputDir, caCert, caKey)
6593
if err != nil {
6694
c.Ui.Error(err.Error())
6795
} else {
@@ -70,7 +98,7 @@ func (c *CreateCA) Run(args []string) int {
7098
return 0
7199
}
72100

73-
func generateCACertificate(keysize int, years int, days int, outputDir string) error {
101+
func generateCACertificate(years int, days int, outputDir string, caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey) error {
74102
serialNumber, err := generateSerialNumber(128)
75103
if err != nil {
76104
return fmt.Errorf("could not generate 128-bit serial number: %s", err.Error())
@@ -81,9 +109,16 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
81109
return fmt.Errorf("could not generate RSA private key: %s", err.Error())
82110
}
83111

84-
keyID := generateKeyIDFromRSAPublicKey(privateKey.N, privateKey.E)
85-
86112
cn := fmt.Sprintf("EventStoreDB CA %s", serialNumber.Text(16))
113+
subjectKeyID := generateKeyIDFromRSAPublicKey(privateKey.N, privateKey.E)
114+
authorityKeyID := subjectKeyID
115+
maxPathLen := 2
116+
117+
if caCert != nil && caPrivateKey != nil {
118+
maxPathLen = -1
119+
cn = fmt.Sprintf("EventStoreDB Intermediate CA %s", serialNumber.Text(16))
120+
authorityKeyID = generateKeyIDFromRSAPublicKey(caPrivateKey.N, caPrivateKey.E)
121+
}
87122

88123
cert := &x509.Certificate{
89124
SerialNumber: serialNumber,
@@ -92,14 +127,22 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
92127
Organization: []string{"Event Store Ltd"},
93128
Country: []string{"UK"},
94129
},
130+
IsCA: true,
131+
BasicConstraintsValid: true,
132+
MaxPathLen: maxPathLen,
95133
NotBefore: time.Now(),
96134
NotAfter: time.Now().AddDate(years, 0, days),
97-
IsCA: true,
98-
MaxPathLen: 1,
99135
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
100-
BasicConstraintsValid: true,
101-
SubjectKeyId: keyID,
102-
AuthorityKeyId: keyID,
136+
SubjectKeyId: subjectKeyID,
137+
AuthorityKeyId: authorityKeyID,
138+
}
139+
140+
parentCert := cert
141+
certPrivateKey := privateKey
142+
143+
if caCert != nil && caPrivateKey != nil {
144+
parentCert = caCert
145+
certPrivateKey = caPrivateKey
103146
}
104147

105148
privateKeyPem := new(bytes.Buffer)
@@ -111,7 +154,7 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
111154
return fmt.Errorf("could not encode private key to PEM format: %s", err.Error())
112155
}
113156

114-
certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey)
157+
certBytes, err := x509.CreateCertificate(rand.Reader, cert, parentCert, &privateKey.PublicKey, certPrivateKey)
115158
if err != nil {
116159
return fmt.Errorf("could not generate certificate: %s", err.Error())
117160
}
@@ -153,14 +196,16 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
153196
func (c *CreateCA) Help() string {
154197
helpText := `
155198
Usage: create_ca [options]
156-
Generate a root/CA TLS certificate to be used with EventStoreDB
199+
Generate a root/intermediate CA TLS certificate to be used with EventStoreDB
157200
Options:
158201
-days The validity period of the certificate in days (default: 5 years)
159202
-out The output directory (default: ./ca)
203+
-ca-certificate The path to a CA certificate file for creating an intermediate CA certificate
204+
-ca-key The path to a CA key file for creating an intermediate CA certificate
160205
`
161206
return strings.TrimSpace(helpText)
162207
}
163208

164209
func (c *CreateCA) Synopsis() string {
165-
return "Generate a root/CA TLS certificate to be used with EventStoreDB"
210+
return "Generate a root/intermediate CA TLS certificate to be used with EventStoreDB"
166211
}

Diff for: certificates/create_node.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func (c *CreateNode) Run(args []string) int {
190190
years = 0
191191
}
192192

193-
err = generateNodeCertificate(caCert, caKey, ips, dnsNames, defaultKeySize, years, days, outputDir, outputBaseFileName)
193+
err = generateNodeCertificate(caCert, caKey, ips, dnsNames, years, days, outputDir, outputBaseFileName)
194194
if err != nil {
195195
c.Ui.Error(err.Error())
196196
return 1
@@ -200,7 +200,7 @@ func (c *CreateNode) Run(args []string) int {
200200
return 0
201201
}
202202

203-
func generateNodeCertificate(caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey, ips []net.IP, dnsNames []string, keysize int, years int, days int, outputDir string, outputBaseFileName string) error {
203+
func generateNodeCertificate(caCert *x509.Certificate, caPrivateKey *rsa.PrivateKey, ips []net.IP, dnsNames []string, years int, days int, outputDir string, outputBaseFileName string) error {
204204
serialNumber, err := generateSerialNumber(128)
205205
if err != nil {
206206
return fmt.Errorf("could not generate 128-bit serial number: %s", err.Error())

Diff for: go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ go 1.14
44

55
require (
66
github.com/hashicorp/go-multierror v1.0.0
7-
github.com/hashicorp/hcl v1.0.0
87
github.com/mitchellh/cli v1.1.1
98
)

Diff for: go.sum

-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,12 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26
22
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
33
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
44
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
5-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7-
github.com/eventstore/es-gencert-cli v0.0.0-20200810090631-41ce27532e69 h1:e919SNsU6EnmpOsYBwvH9rn7kohweA5FUfFOvVQPEXc=
8-
github.com/eventstore/es-gencert-cli v0.0.0-20200810090631-41ce27532e69/go.mod h1:0ypTrkuHDS23qDZ5I4P84q4CvOYB8IABnCt1rH7e3/U=
95
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
106
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
117
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
128
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
139
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
1410
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
15-
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
16-
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
1711
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
1812
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
1913
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=

0 commit comments

Comments
 (0)