package pkiws import ( "crypto/x509" "encoding/pem" "fmt" "log" "regexp" "time" "git.paulbsd.com/paulbsd/pki/src/cert" "git.paulbsd.com/paulbsd/pki/src/config" "git.paulbsd.com/paulbsd/pki/src/pki" ) const timeformatstring string = "2006-01-02 15:04:05" var domainRegex, err = regexp.Compile(`^[a-z0-9\*]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$`) // GetCertificate get certificate from database if exists, of request it from ACME func GetCertificate(cfg *config.Config, user *pki.User, domains *[]string) (result map[string]EntryResponse, err error) { err = CheckDomains(domains) if err != nil { return result, err } result = make(map[string]EntryResponse) firstdomain := (*domains)[0] entry, err := user.GetEntry(cfg, &firstdomain) if err != nil { certs, err := user.RequestNewCert(cfg, domains) if err != nil { log.Printf("Error fetching new certificate %s\n", err) return result, err } NotBefore, NotAfter, err := GetDates(certs.Certificate) if err != nil { log.Println("Error where parsing dates") return result, err } entry := cert.Entry{Domain: certs.Domain, Certificate: string(certs.Certificate), PrivateKey: string(certs.PrivateKey), ValidityBegin: NotBefore, ValidityEnd: NotAfter, AuthURL: cfg.ACME.AuthURL} cfg.Db.Insert(&entry) result[firstdomain] = convertEntryToResponse(entry) return result, err } result[firstdomain] = convertEntryToResponse(entry) return } // CheckDomains check if requested domains are valid func CheckDomains(domains *[]string) (err error) { for _, domain := range *domains { err = CheckDomain(&domain) if err != nil { return } } return } // CheckDomain check if requested domain are valid func CheckDomain(domain *string) (err error) { res := domainRegex.Match([]byte(*domain)) if !res { return fmt.Errorf("Domain %s has not a valid syntax %s, please verify", *domain, err) } return } // GetDates decodes NotBefore and NotAfter date of cert func GetDates(cert []byte) (NotBefore time.Time, NotAfter time.Time, err error) { block, _ := pem.Decode(cert) if block.Type == "CERTIFICATE" { ce, err := x509.ParseCertificate(block.Bytes) if err != nil { log.Println("Error when parsing certificate") } NotBefore = ce.NotBefore NotAfter = ce.NotAfter } return } // convertEntryToResponse converts database ACME entry to JSON ACME entry func convertEntryToResponse(in cert.Entry) (out EntryResponse) { out.Domains = append(out.Domains, in.Domain) out.Certificate = in.Certificate out.PrivateKey = in.PrivateKey out.ValidityBegin = in.ValidityBegin.Format(timeformatstring) out.ValidityEnd = in.ValidityEnd.Format(timeformatstring) return } // EntryRequest type EntryRequest struct { Domains []string `json:"domains"` } // EntryResponse is the struct defining JSON response from webservice type EntryResponse struct { Domains []string `json:"domains"` Certificate string `json:"certificate"` PrivateKey string `json:"privatekey"` ValidityBegin string `json:"validitybegin"` ValidityEnd string `json:"validityend"` }