@@ -16,7 +16,7 @@ import (
16
16
"strings"
17
17
"time"
18
18
19
- multierror "github.com/hashicorp/go-multierror"
19
+ "github.com/hashicorp/go-multierror"
20
20
"github.com/mitchellh/cli"
21
21
)
22
22
@@ -25,8 +25,10 @@ type CreateCA struct {
25
25
}
26
26
27
27
type CreateCAArguments struct {
28
- Days int
29
- OutputDir string
28
+ Days int
29
+ OutputDir string
30
+ CACertificatePath string
31
+ CAKeyPath string
30
32
}
31
33
32
34
func (c * CreateCA ) Run (args []string ) int {
@@ -36,6 +38,8 @@ func (c *CreateCA) Run(args []string) int {
36
38
flags .Usage = func () { c .Ui .Info (c .Help ()) }
37
39
flags .IntVar (& config .Days , "days" , 0 , "the validity period of the certificate in days" )
38
40
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" )
39
43
40
44
if err := flags .Parse (args ); err != nil {
41
45
return 1
@@ -46,6 +50,12 @@ func (c *CreateCA) Run(args []string) int {
46
50
multierror .Append (validationErrors , errors .New ("days must be positive" ))
47
51
}
48
52
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
+
49
59
if validationErrors .ErrorOrNil () != nil {
50
60
c .Ui .Error (validationErrors .Error ())
51
61
return 1
@@ -59,9 +69,27 @@ func (c *CreateCA) Run(args []string) int {
59
69
days = config .Days
60
70
years = 0
61
71
}
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
+ }
62
90
63
91
outputDir := config .OutputDir
64
- err : = generateCACertificate (defaultKeySize , years , days , outputDir )
92
+ err = generateCACertificate (years , days , outputDir , caCert , caKey )
65
93
if err != nil {
66
94
c .Ui .Error (err .Error ())
67
95
} else {
@@ -70,7 +98,7 @@ func (c *CreateCA) Run(args []string) int {
70
98
return 0
71
99
}
72
100
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 {
74
102
serialNumber , err := generateSerialNumber (128 )
75
103
if err != nil {
76
104
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
81
109
return fmt .Errorf ("could not generate RSA private key: %s" , err .Error ())
82
110
}
83
111
84
- keyID := generateKeyIDFromRSAPublicKey (privateKey .N , privateKey .E )
85
-
86
112
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
+ }
87
122
88
123
cert := & x509.Certificate {
89
124
SerialNumber : serialNumber ,
@@ -92,14 +127,22 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
92
127
Organization : []string {"Event Store Ltd" },
93
128
Country : []string {"UK" },
94
129
},
130
+ IsCA : true ,
131
+ BasicConstraintsValid : true ,
132
+ MaxPathLen : maxPathLen ,
95
133
NotBefore : time .Now (),
96
134
NotAfter : time .Now ().AddDate (years , 0 , days ),
97
- IsCA : true ,
98
- MaxPathLen : 1 ,
99
135
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
103
146
}
104
147
105
148
privateKeyPem := new (bytes.Buffer )
@@ -111,7 +154,7 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
111
154
return fmt .Errorf ("could not encode private key to PEM format: %s" , err .Error ())
112
155
}
113
156
114
- certBytes , err := x509 .CreateCertificate (rand .Reader , cert , cert , & privateKey .PublicKey , privateKey )
157
+ certBytes , err := x509 .CreateCertificate (rand .Reader , cert , parentCert , & privateKey .PublicKey , certPrivateKey )
115
158
if err != nil {
116
159
return fmt .Errorf ("could not generate certificate: %s" , err .Error ())
117
160
}
@@ -153,14 +196,16 @@ func generateCACertificate(keysize int, years int, days int, outputDir string) e
153
196
func (c * CreateCA ) Help () string {
154
197
helpText := `
155
198
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
157
200
Options:
158
201
-days The validity period of the certificate in days (default: 5 years)
159
202
-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
160
205
`
161
206
return strings .TrimSpace (helpText )
162
207
}
163
208
164
209
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"
166
211
}
0 commit comments