diff options
| -rw-r--r-- | cmd/easyca/main.go | 76 | ||||
| -rw-r--r-- | pkg/easyca/easyca.go | 40 | ||||
| -rw-r--r-- | pkg/easyca/serial.go | 47 |
3 files changed, 121 insertions, 42 deletions
diff --git a/cmd/easyca/main.go b/cmd/easyca/main.go index 4a16172..e4a5ff7 100644 --- a/cmd/easyca/main.go +++ b/cmd/easyca/main.go @@ -3,6 +3,7 @@ package main import ( "crypto/x509" "crypto/x509/pkix" + "fmt" "log" "net" "os" @@ -19,10 +20,43 @@ import ( func initPki(c *cli.Context) { log.Print("generating new pki structure") - err := os.MkdirAll(filepath.Join(c.GlobalString("root"), "private"), 0755) + pkiroot := filepath.Join(c.GlobalString("root")) + + for _, dir := range []string{"private", "issued"} { + err := os.Mkdir(filepath.Join(pkiroot, dir), 0755) + if err != nil { + log.Fatalf("creating dir %v: %v", dir, err) + } + log.Printf("created %v directory", dir) + } + + serial, err := os.Create(filepath.Join(pkiroot, "serial")) + if err != nil { + log.Fatalf("create serial: %v", err) + } + defer serial.Close() + n, err := fmt.Fprintln(serial, "01") + if err != nil { + log.Fatalf("write serial: %v", err) + } + if n == 0 { + log.Fatal("write serial, written 0 bytes") + } + log.Print("created serial") + + crlnumber, err := os.Create(filepath.Join(pkiroot, "crlnumber")) + if err != nil { + log.Fatalf("create crlnumber: %v", err) + } + defer crlnumber.Close() + n, err = fmt.Fprintln(crlnumber, "01") if err != nil { - log.Fatalf("creating pki structure %v", err) + log.Fatalf("write crlnumber: %v", err) } + if n == 0 { + log.Fatal("write crlnumber, written 0 bytes") + } + log.Print("created crlnumber") } func createBundle(c *cli.Context) { @@ -32,24 +66,29 @@ func createBundle(c *cli.Context) { "different name if you need multiple certs for same cn)", c.Command.FullName()) } - var filename string commonName := strings.Join(c.Args()[:], " ") - - if len(c.String("filename")) > 0 { - filename = c.String("filename") - } else { + var filename string + if filename = c.String("filename"); len(filename) == 0 { filename = strings.Replace(commonName, " ", "_", -1) filename = strings.Replace(filename, "*", "wildcard", -1) } + subject := pkix.Name{CommonName: commonName} + if str := c.String("organization"); len(str) > 0 { + subject.Organization = []string{str} + } + if str := c.String("locality"); len(str) > 0 { + subject.Locality = []string{str} + } + if str := c.String("country"); len(str) > 0 { + subject.Country = []string{str} + } + if str := c.String("province"); len(str) > 0 { + subject.Province = []string{str} + } + template := &x509.Certificate{ - Subject: pkix.Name{ - CommonName: commonName, - Organization: c.StringSlice("organization"), - Locality: c.StringSlice("locality"), - Country: c.StringSlice("country"), - Province: c.StringSlice("province"), - }, + Subject: subject, NotAfter: time.Now().AddDate(0, 0, c.Int("expire")), } @@ -76,6 +115,7 @@ func createBundle(c *cli.Context) { if err != nil { log.Fatal(err) } + } func parseArgs() { @@ -123,20 +163,20 @@ func parseArgs() { Name: "filename", Usage: "filename for bundle, use when you generate multiple certs for same cn", }, - cli.StringSliceFlag{ + cli.StringFlag{ Name: "organization", EnvVar: "PKI_ORGANIZATION", }, - cli.StringSliceFlag{ + cli.StringFlag{ Name: "locality", EnvVar: "PKI_LOCALITY", }, - cli.StringSliceFlag{ + cli.StringFlag{ Name: "country", EnvVar: "PKI_COUNTRY", Usage: "Country name, 2 letter code", }, - cli.StringSliceFlag{ + cli.StringFlag{ Name: "province", Usage: "province/state", EnvVar: "PKI_PROVINCE", diff --git a/pkg/easyca/easyca.go b/pkg/easyca/easyca.go index be77ae6..2bd43fe 100644 --- a/pkg/easyca/easyca.go +++ b/pkg/easyca/easyca.go @@ -83,7 +83,7 @@ func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error if err != nil { return fmt.Errorf("get next serial: %v", err) } - template.SerialNumber = big.NewInt(serialNumber) + template.SerialNumber = serialNumber caCrt, caKey, err = GetCA(pkiroot) if err != nil { @@ -110,6 +110,13 @@ func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error return fmt.Errorf("pem encode crt: %v", err) } + // I do not think we have to write the ca.crt in the index + if !template.IsCA { + WriteIndex(pkiroot, name, template) + if err != nil { + return fmt.Errorf("write index: %v", err) + } + } return nil } @@ -126,6 +133,7 @@ func GetCA(pkiroot string) (*x509.Certificate, *rsa.PrivateKey, error) { if err != nil { return nil, nil, fmt.Errorf("parse ca private key: %v", err) } + caCrtBytes, err := ioutil.ReadFile(filepath.Join(pkiroot, "ca.crt")) if err != nil { return nil, nil, fmt.Errorf("read ca crt: %v", err) @@ -138,5 +146,35 @@ func GetCA(pkiroot string) (*x509.Certificate, *rsa.PrivateKey, error) { if err != nil { return nil, nil, fmt.Errorf("parse ca crt: %v", err) } + return caCrt, caKey, nil } + +func WriteIndex(pkiroot, filename string, crt *x509.Certificate) error { + f, err := os.OpenFile(filepath.Join(pkiroot, "index.txt"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return err + } + defer f.Close() + + serialOutput := fmt.Sprintf("%X", crt.SerialNumber) + // For compatibility with openssl we need an even length + if len(serialOutput)%2 == 1 { + serialOutput = "0" + serialOutput + } + + // Date format: yymmddHHMMSSZ + // E|R|V<tab>Expiry<tab>[RevocationDate]<tab>Serial<tab>filename<tab>SubjectDN + n, err := fmt.Fprintf(f, "V\t%vZ\t\t%v\t%v.crt\t%v\n", + crt.NotAfter.UTC().Format("060102150405"), + serialOutput, + filename, + "/CN="+crt.Subject.CommonName) + if err != nil { + return err + } + if n == 0 { + return fmt.Errorf("written 0 bytes in index file") + } + return nil +} diff --git a/pkg/easyca/serial.go b/pkg/easyca/serial.go index aded542..7fadb84 100644 --- a/pkg/easyca/serial.go +++ b/pkg/easyca/serial.go @@ -2,43 +2,44 @@ package easyca import ( "fmt" - "io/ioutil" + "math/big" "os" "path/filepath" - "strconv" - "strings" ) -func NextSerial(pkiroot string) (int64, error) { - var serial int64 - f, err := os.OpenFile(filepath.Join(pkiroot, "serial"), os.O_RDWR|os.O_CREATE, 0644) +func NextSerial(pkiroot string) (*big.Int, error) { + serial := big.NewInt(0) + + f, err := os.OpenFile(filepath.Join(pkiroot, "serial"), os.O_RDWR, 0644) if err != nil { - return 0, err + return nil, err } defer f.Close() - out, err := ioutil.ReadAll(f) + + n, err := fmt.Fscanf(f, "%X\n", serial) if err != nil { - return 0, err + return nil, err } - if len(out) == 0 { - serial = 1 - } else { - // If serial file is edited manually, it will probably get \n or \r\n - // We make sure to clean the unwanted characters - serial, err = strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64) - if err != nil { - return 0, err - } - serial += 1 + if n != 1 { + return nil, fmt.Errorf("supposed to read 1 element, read: %v", n) } + next := big.NewInt(1) + next.Add(serial, next) + output := fmt.Sprintf("%X", next) + // For compatibility with openssl we need an even length + if len(output)%2 == 1 { + output = "0" + output + } + f.Truncate(0) f.Seek(0, 0) - written, err := fmt.Fprint(f, serial) + + n, err = fmt.Fprintln(f, output) if err != nil { - return 0, err + return nil, err } - if written == 0 { - return 0, fmt.Errorf("wanted to write %s to serial file, no byte written", written) + if n == 0 { + return nil, fmt.Errorf("supposed to write 1 element, written: %v", n) } return serial, nil |
