package main import ( "crypto" "crypto/tls" "crypto/x509" "fmt" "io" "os" "path" "path/filepath" "time" "github.com/dim13/acme" ) type Cert struct { tls.Certificate keyFile string crtFile string sigFile string crypto.PrivateKey } func (c Cert) String() string { return fmt.Sprint(c.Leaf.DNSNames, " valid until ", c.Leaf.NotAfter) } func (c Cert) IsValid(grace time.Duration) bool { return time.Now().Add(grace).Before(c.Leaf.NotAfter) } func loadFiles(crtFile, keyFile string) (Cert, error) { crt, err := tls.LoadX509KeyPair(crtFile, keyFile) if err != nil { return Cert{}, err } crt.Leaf, err = x509.ParseCertificate(crt.Certificate[0]) if err != nil { return Cert{}, err } return Cert{Certificate: crt, keyFile: keyFile, crtFile: crtFile}, nil } func newFile(fname string, mode os.FileMode) (io.WriteCloser, error) { os.Rename(fname, fname[:len(fname)-4]+".old") return os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) } func (c Cert) saveFiles() error { fd, err := newFile(c.keyFile, 0600) if err != nil { return err } defer fd.Close() if err := acme.SaveKey(fd, c.Certificate.PrivateKey); err != nil { return err } fd, err = newFile(c.crtFile, 0644) if err != nil { return err } defer fd.Close() for _, crt := range c.Certificate.Certificate { if err := acme.SaveCert(fd, crt); err != nil { return err } } return nil } func scanFiles(dir string) ([]Cert, error) { var certs []Cert keys, err := filepath.Glob(path.Join(dir, "private", "*.key")) if err != nil { return nil, err } for _, k := range keys { c := filepath.Join(dir, "certs", filepath.Base(k[:len(k)-4])+".pem") crt, err := loadFiles(c, k) if err != nil { continue } certs = append(certs, crt) } return certs, nil }