aboutsummaryrefslogtreecommitdiff
path: root/create_cert.go
blob: c49e953de13a6c4e6aca0c0e3364062fe45b2ffe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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 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")
	}

	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
}