@@ -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
2727type CreateCAArguments struct {
28- Days int
29- OutputDir string
28+ Days int
29+ OutputDir string
30+ CACertificatePath string
31+ CAKeyPath string
3032}
3133
3234func (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
153196func (c * CreateCA ) Help () string {
154197 helpText := `
155198Usage: 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
157200Options:
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
164209func (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}
0 commit comments