aboutsummaryrefslogblamecommitdiff
path: root/create_cert.go
blob: d47732a80ffb3305a361fd7c5c17fb623e71b9ae (plain) (tree)


































































































































































































                                                                                                  
package main

import (
	"crypto/x509"
	"flag"
	"fmt"
	"math/big"
	"time"

	"github.com/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 to generate the request")
	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", 0, "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")
	}

	out, err := openOutput(flagOutput)
	if err != nil {
		return err
	}
	// FIXME check all other out.Close for stdout exception
	if flagOutput != "stdout" {
		defer out.Close()
	}
	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
	}
	return writePem(cert, out)
}

func parseCSR(path string) (*pki.CertificateRequest, error) {
	pems_raw, err := openInput(path)
	if err != nil {
		return nil, err
	}
	defer pems_raw.Close()
	pems, err := parseFile(pems_raw)
	if err != nil {
		return nil, err
	}
	csr_raw, err := getSectionFromPem(pems, pki.PemLabelCertificateRequest)
	if err != nil {
		return nil, err
	}
	csr, err := pki.LoadCertificateSignRequest(csr_raw)
	if err != nil {
		return nil, err
	}
	return csr, nil
}

func parseCA(path string) (*pki.Certificate, error) {
	pems_raw, err := openInput(path)
	if err != nil {
		return nil, err
	}
	defer pems_raw.Close()
	pems, err := parseFile(pems_raw)
	if err != nil {
		return nil, err
	}
	ca_raw, err := getSectionFromPem(pems, pki.PemLabelCertificate)
	if err != nil {
		return nil, err
	}
	ca, err := pki.LoadCertificate(ca_raw)
	if err != nil {
		return nil, err
	}
	return ca, nil
}