diff options
Diffstat (limited to 'pkg/easyca/easyca.go')
| -rw-r--r-- | pkg/easyca/easyca.go | 155 |
1 files changed, 152 insertions, 3 deletions
diff --git a/pkg/easyca/easyca.go b/pkg/easyca/easyca.go index 2bd43fe..b7521da 100644 --- a/pkg/easyca/easyca.go +++ b/pkg/easyca/easyca.go @@ -1,6 +1,7 @@ package easyca import ( + "bufio" "crypto/rand" "crypto/rsa" "crypto/sha1" @@ -12,9 +13,22 @@ import ( "math/big" "os" "path/filepath" + "regexp" "time" ) +var ( + // Index format + // 0 full string + // 1 Valid/Revoked/Expired + // 2 Expiration date + // 3 Revocation date + // 4 Serial + // 5 Filename + // 6 Subject + indexRegexp = regexp.MustCompile("^(V|R|E)\t([0-9]{12}Z)\t([0-9]{12}Z)?\t([0-9a-fA-F]{2,})\t([^\t]+)\t(.+)") +) + func GeneratePrivateKey(path string) (*rsa.PrivateKey, error) { keyFile, err := os.Create(path) if err != nil { @@ -39,8 +53,13 @@ func GeneratePrivateKey(path string) (*rsa.PrivateKey, error) { func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error { // TODO(jclerc): check that pki has been init + var crtPath string privateKeyPath := filepath.Join(pkiroot, "private", name+".key") - crtPath := filepath.Join(pkiroot, name+".crt") + if name == "ca" { + crtPath = filepath.Join(pkiroot, name+".crt") + } else { + crtPath = filepath.Join(pkiroot, "issued", name+".crt") + } var caCrt *x509.Certificate var caKey *rsa.PrivateKey @@ -150,8 +169,72 @@ func GetCA(pkiroot string) (*x509.Certificate, *rsa.PrivateKey, error) { return caCrt, caKey, nil } +func RevokeSerial(pkiroot string, serial *big.Int) error { + f, err := os.OpenFile(filepath.Join(pkiroot, "index.txt"), os.O_RDWR, 0644) + if err != nil { + return err + } + defer f.Close() + + var lines []string + scanner := bufio.NewScanner(f) + for scanner.Scan() { + matches := indexRegexp.FindStringSubmatch(scanner.Text()) + if len(matches) != 7 { + return fmt.Errorf("wrong line format") + } + matchedSerial := big.NewInt(0) + fmt.Sscanf(matches[4], "%X", matchedSerial) + if matchedSerial.Cmp(serial) == 0 { + if matches[1] == "R" { + return fmt.Errorf("certificate already revoked") + } + + lines = append(lines, fmt.Sprintf("R\t%v\t%vZ\t%v\t%v\t%v", + matches[2], + time.Now().UTC().Format("060102150405"), + matches[4], + matches[5], + matches[6])) + } else { + lines = append(lines, matches[0]) + } + } + + f.Truncate(0) + f.Seek(0, 0) + + for _, line := range lines { + n, err := fmt.Fprintln(f, line) + if err != nil { + return fmt.Errorf("write line: %v", err) + } + if n == 0 { + return fmt.Errorf("supposed to write [%v], written 0 bytes", line) + } + } + return nil +} + +func GetCertificate(path string) (*x509.Certificate, error) { + crtBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read crt: %v", err) + } + p, _ := pem.Decode(crtBytes) + if p == nil { + return nil, fmt.Errorf("pem decode did not found pem encoded cert") + } + crt, err := x509.ParseCertificate(p.Bytes) + if err != nil { + return nil, fmt.Errorf("parse crt: %v", err) + } + + return crt, 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) + f, err := os.OpenFile(filepath.Join(pkiroot, "index.txt"), os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } @@ -162,7 +245,7 @@ func WriteIndex(pkiroot, filename string, crt *x509.Certificate) error { if len(serialOutput)%2 == 1 { serialOutput = "0" + serialOutput } - + // subject: /C=FR/ST=IDF/O=Umbrella Corporation/CN=test.clerc.io // 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", @@ -178,3 +261,69 @@ func WriteIndex(pkiroot, filename string, crt *x509.Certificate) error { } return nil } + +// |-ca.crt +// |-crlnumber +// |-index.txt +// |-index.txt.attr +// |-serial +// |-issued/ +// |- name.crt +// |-private +// |- ca.key +// |- name.key +func GeneratePKIStructure(pkiroot string) error { + + for _, dir := range []string{"private", "issued"} { + err := os.Mkdir(filepath.Join(pkiroot, dir), 0755) + if err != nil { + return fmt.Errorf("creating dir %v: %v", dir, err) + } + } + + serial, err := os.Create(filepath.Join(pkiroot, "serial")) + if err != nil { + return fmt.Errorf("create serial: %v", err) + } + defer serial.Close() + n, err := fmt.Fprintln(serial, "01") + if err != nil { + return fmt.Errorf("write serial: %v", err) + } + if n == 0 { + return fmt.Errorf("write serial, written 0 bytes") + } + + crlnumber, err := os.Create(filepath.Join(pkiroot, "crlnumber")) + if err != nil { + return fmt.Errorf("create crlnumber: %v", err) + } + defer crlnumber.Close() + n, err = fmt.Fprintln(crlnumber, "01") + if err != nil { + return fmt.Errorf("write crlnumber: %v", err) + } + if n == 0 { + return fmt.Errorf("write crlnumber, written 0 bytes") + } + + index, err := os.Create(filepath.Join(pkiroot, "index.txt")) + if err != nil { + return fmt.Errorf("create index: %v", err) + } + defer index.Close() + + indexattr, err := os.Create(filepath.Join(pkiroot, "index.txt.attr")) + if err != nil { + return fmt.Errorf("create index.txt.attr: %v", err) + } + defer indexattr.Close() + n, err = fmt.Fprintln(indexattr, "unique_subject = no") + if err != nil { + return fmt.Errorf("write index.txt.attr: %v", err) + } + if n == 0 { + return fmt.Errorf("write index.txt.attr, written 0 bytes") + } + return nil +} |
