package main import ( "crypto/x509" "flag" "fmt" "math/big" "time" "git.zero-knowledge.org/gibheer/pki" ) var ( // the possible valid key usages to check against the commandline ValidKeyUsages = map[string]x509.KeyUsage{ "digitalsignature": x509.KeyUsageDigitalSignature, "contentcommitment": x509.KeyUsageContentCommitment, "keyencipherment": x509.KeyUsageKeyEncipherment, "dataencipherment": x509.KeyUsageDataEncipherment, "keyagreement": x509.KeyUsageKeyAgreement, "certsign": x509.KeyUsageCertSign, "crlsign": x509.KeyUsageCRLSign, "encipheronly": x509.KeyUsageEncipherOnly, "decipheronly": x509.KeyUsageDecipherOnly, } // the valid extended key usages, to check against the commandline ValidExtKeyUsages = map[string]x509.ExtKeyUsage{ "any": x509.ExtKeyUsageAny, "serverauth": x509.ExtKeyUsageServerAuth, "clientauth": x509.ExtKeyUsageClientAuth, "codesigning": x509.ExtKeyUsageCodeSigning, "emailprotection": x509.ExtKeyUsageEmailProtection, "ipsecendsystem": x509.ExtKeyUsageIPSECEndSystem, "ipsectunnel": x509.ExtKeyUsageIPSECTunnel, "ipsecuser": x509.ExtKeyUsageIPSECUser, "timestamping": x509.ExtKeyUsageTimeStamping, "ocspsigning": x509.ExtKeyUsageOCSPSigning, "microsoftservergatedcrypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto, "netscapeservergatedcrypto": x509.ExtKeyUsageNetscapeServerGatedCrypto, } ) func CreateCert(args []string) error { var ( flagUsageTemplate string flagKeyUsage string flagKeyExtUsage stringList flagNotBefore string flagNotAfter string flagSerial int64 flagLength int flagIsCA bool flagCA string flagPrivate string flagCSR string flagOutput string ) fs := flag.NewFlagSet("pkictl create-cert", flag.ExitOnError) fs.StringVar(&flagPrivate, "private-key", "", "the private key of the CA signing the certificate") fs.StringVar(&flagCSR, "sign-request", "", "the certificate sign request") fs.StringVar(&flagOutput, "output", "stdout", "path to the output file (default stdout)") fs.BoolVar(&flagIsCA, "is-ca", false, "is the result a CA - when true ca is ignored") fs.StringVar(&flagUsageTemplate, "usage", "", "templates for usage (all, server, client)") fs.StringVar(&flagKeyUsage, "key-usage", "", "comma separated list of key usages") fs.Var(&flagKeyExtUsage, "key-ext-usage", "comma separated list of further usages") fs.Int64Var(&flagSerial, "serial", 0, "the serial for the issued certificate") fs.IntVar(&flagLength, "length", -1, "the number of sub CAs allowed (-1 equals no limit)") fs.StringVar(&flagCA, "ca", "", "path to the CA certificate") fs.StringVar( &flagNotBefore, "not-before", time.Now().Format(time.RFC3339), "time before the certificate is not valid in RFC3339 format (default now)", ) fs.StringVar( &flagNotAfter, "not-after", time.Now().Format(time.RFC3339), "time after the certificate is not valid in RFC3339 format (default now)", ) fs.Parse(args) if flagPrivate == "" { return fmt.Errorf("missing private key") } if flagCSR == "" { return fmt.Errorf("missing certificate sign request") } pk, err := loadPrivateKey(flagPrivate) if err != nil { return err } csr, err := parseCSR(flagCSR) if err != nil { return err } var ca *pki.Certificate if !flagIsCA { ca, err = parseCA(flagCA) if err != nil { return err } } notBefore, err := time.Parse(time.RFC3339, flagNotBefore) if err != nil { return err } notAfter, err := time.Parse(time.RFC3339, flagNotAfter) if err != nil { return err } if notBefore.After(notAfter) { return fmt.Errorf("before and after range is wrong") } cert_opts := pki.CertificateOptions{ SerialNumber: big.NewInt(flagSerial), NotBefore: notBefore, NotAfter: notAfter, IsCA: flagIsCA, CALength: flagLength, } if flagKeyUsage != "" { keyUsage, found := ValidKeyUsages[flagKeyUsage] if !found { return fmt.Errorf("unknown key usage") } cert_opts.KeyUsage = keyUsage } for pos, name := range flagKeyExtUsage { if val, found := ValidExtKeyUsages[name]; !found { return fmt.Errorf("%d ext key usage '%s' unknown", pos, name) } else { cert_opts.KeyExtendedUsage = append(cert_opts.KeyExtendedUsage, val) } } cert, err := csr.ToCertificate(pk, cert_opts, ca) if err != nil { return err } out, err := openOutput(flagOutput) if err != nil { return err } // FIXME check all other out.Close for stdout exception if flagOutput != "stdout" { defer out.Close() } return writePem(cert, out) } func parseCSR(path string) (*pki.CertificateRequest, error) { pems_raw, err := openInput(path) if err != nil { return nil, fmt.Errorf("could not open file '%s': %s", path, err) } defer pems_raw.Close() pems, err := parseFile(pems_raw) if err != nil { return nil, fmt.Errorf("could not parse file '%s': %s", path, err) } csr_raw, err := getSectionFromPem(pems, pki.PemLabelCertificateRequest) if err != nil { return nil, fmt.Errorf("could not find sign request in '%s': %s", path, err) } csr, err := pki.LoadCertificateSignRequest(csr_raw) if err != nil { return nil, fmt.Errorf("could not load sign request from '%s': %s", path, err) } return csr, nil } func parseCA(path string) (*pki.Certificate, error) { pems_raw, err := openInput(path) if err != nil { return nil, fmt.Errorf("could not open file '%s': %s", path, err) } defer pems_raw.Close() pems, err := parseFile(pems_raw) if err != nil { return nil, fmt.Errorf("could not parse file '%s': %s", path, err) } ca_raw, err := getSectionFromPem(pems, pki.PemLabelCertificate) if err != nil { return nil, fmt.Errorf("could not find CA in '%s': %s", path, err) } ca, err := pki.LoadCertificate(ca_raw) if err != nil { return nil, fmt.Errorf("could not load certificate from '%s': %s", path, err) } return ca, nil }