1 package main
2
3 import (
4 "crypto"
5 "crypto/sha256"
6 "crypto/x509"
7 "crypto/x509/pkix"
8 "encoding/asn1"
9 "errors"
10 "fmt"
11 "io"
12 "math/big"
13 "strconv"
14 "strings"
15 "time"
16 )
17
18 type policyInfoConfig struct {
19 OID string
20
21
22 CPSURI string `yaml:"cps-uri"`
23 }
24
25
26 type certProfile struct {
27
28
29 SignatureAlgorithm string `yaml:"signature-algorithm"`
30
31
32 CommonName string `yaml:"common-name"`
33
34 Organization string `yaml:"organization"`
35
36 Country string `yaml:"country"`
37
38
39
40
41 NotBefore string `yaml:"not-before"`
42
43
44
45 NotAfter string `yaml:"not-after"`
46
47
48
49 OCSPURL string `yaml:"ocsp-url"`
50
51
52 CRLURL string `yaml:"crl-url"`
53
54
55
56 IssuerURL string `yaml:"issuer-url"`
57
58
59
60
61 Policies []policyInfoConfig `yaml:"policies"`
62
63
64 KeyUsages []string `yaml:"key-usages"`
65 }
66
67
68 var AllowedSigAlgs = map[string]x509.SignatureAlgorithm{
69 "SHA256WithRSA": x509.SHA256WithRSA,
70 "SHA384WithRSA": x509.SHA384WithRSA,
71 "SHA512WithRSA": x509.SHA512WithRSA,
72 "ECDSAWithSHA256": x509.ECDSAWithSHA256,
73 "ECDSAWithSHA384": x509.ECDSAWithSHA384,
74 "ECDSAWithSHA512": x509.ECDSAWithSHA512,
75 }
76
77 type certType int
78
79 const (
80 rootCert certType = iota
81 intermediateCert
82 ocspCert
83 crlCert
84 crossCert
85 requestCert
86 )
87
88
89 func (profile *certProfile) Subject() pkix.Name {
90 return pkix.Name{
91 CommonName: profile.CommonName,
92 Organization: []string{profile.Organization},
93 Country: []string{profile.Country},
94 }
95 }
96
97 func (profile *certProfile) verifyProfile(ct certType) error {
98 if ct == requestCert {
99 if profile.NotBefore != "" {
100 return errors.New("not-before cannot be set for a CSR")
101 }
102 if profile.NotAfter != "" {
103 return errors.New("not-after cannot be set for a CSR")
104 }
105 if profile.SignatureAlgorithm != "" {
106 return errors.New("signature-algorithm cannot be set for a CSR")
107 }
108 if profile.OCSPURL != "" {
109 return errors.New("ocsp-url cannot be set for a CSR")
110 }
111 if profile.CRLURL != "" {
112 return errors.New("crl-url cannot be set for a CSR")
113 }
114 if profile.IssuerURL != "" {
115 return errors.New("issuer-url cannot be set for a CSR")
116 }
117 if profile.Policies != nil {
118 return errors.New("policies cannot be set for a CSR")
119 }
120 if profile.KeyUsages != nil {
121 return errors.New("key-usages cannot be set for a CSR")
122 }
123 } else {
124 if profile.NotBefore == "" {
125 return errors.New("not-before is required")
126 }
127 if profile.NotAfter == "" {
128 return errors.New("not-after is required")
129 }
130 if profile.SignatureAlgorithm == "" {
131 return errors.New("signature-algorithm is required")
132 }
133 }
134 if profile.CommonName == "" {
135 return errors.New("common-name is required")
136 }
137 if profile.Organization == "" {
138 return errors.New("organization is required")
139 }
140 if profile.Country == "" {
141 return errors.New("country is required")
142 }
143
144 if ct == rootCert {
145 if len(profile.Policies) != 0 {
146 return errors.New("policies should not be set on root certs")
147 }
148 }
149
150 if ct == intermediateCert || ct == crossCert {
151 if profile.CRLURL == "" {
152 return errors.New("crl-url is required for subordinate CAs")
153 }
154 if profile.IssuerURL == "" {
155 return errors.New("issuer-url is required for subordinate CAs")
156 }
157
158
159
160 if len(profile.Policies) != 1 || profile.Policies[0].OID != "2.23.140.1.2.1" {
161 return errors.New("policy should be exactly BRs domain-validated for subordinate CAs")
162 }
163 }
164
165 if ct == ocspCert || ct == crlCert {
166 if len(profile.KeyUsages) != 0 {
167 return errors.New("key-usages cannot be set for a delegated signer")
168 }
169 if profile.CRLURL != "" {
170 return errors.New("crl-url cannot be set for a delegated signer")
171 }
172 if profile.OCSPURL != "" {
173 return errors.New("ocsp-url cannot be set for a delegated signer")
174 }
175 }
176 return nil
177 }
178
179 func parseOID(oidStr string) (asn1.ObjectIdentifier, error) {
180 var oid asn1.ObjectIdentifier
181 for _, a := range strings.Split(oidStr, ".") {
182 i, err := strconv.Atoi(a)
183 if err != nil {
184 return nil, err
185 }
186 if i <= 0 {
187 return nil, errors.New("OID components must be >= 1")
188 }
189 oid = append(oid, i)
190 }
191 return oid, nil
192 }
193
194 var stringToKeyUsage = map[string]x509.KeyUsage{
195 "Digital Signature": x509.KeyUsageDigitalSignature,
196 "CRL Sign": x509.KeyUsageCRLSign,
197 "Cert Sign": x509.KeyUsageCertSign,
198 }
199
200 var oidOCSPNoCheck = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}
201
202 func generateSKID(pk []byte) ([]byte, error) {
203 var pkixPublicKey struct {
204 Algo pkix.AlgorithmIdentifier
205 BitString asn1.BitString
206 }
207 if _, err := asn1.Unmarshal(pk, &pkixPublicKey); err != nil {
208 return nil, err
209 }
210 skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
211 return skid[:], nil
212 }
213
214
215 func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte, tbcs *x509.Certificate, ct certType) (*x509.Certificate, error) {
216
217 if ct == crossCert && tbcs == nil {
218 return nil, fmt.Errorf("toBeCrossSigned cert field was nil, but was required to gather EKUs for the lint cert")
219 }
220
221 var ocspServer []string
222 if profile.OCSPURL != "" {
223 ocspServer = []string{profile.OCSPURL}
224 }
225 var crlDistributionPoints []string
226 if profile.CRLURL != "" {
227 crlDistributionPoints = []string{profile.CRLURL}
228 }
229 var issuingCertificateURL []string
230 if profile.IssuerURL != "" {
231 issuingCertificateURL = []string{profile.IssuerURL}
232 }
233
234 subjectKeyID, err := generateSKID(pubKey)
235 if err != nil {
236 return nil, err
237 }
238
239 serial := make([]byte, 16)
240 _, err = randReader.Read(serial)
241 if err != nil {
242 return nil, fmt.Errorf("failed to generate serial number: %s", err)
243 }
244
245 var ku x509.KeyUsage
246 for _, kuStr := range profile.KeyUsages {
247 kuBit, ok := stringToKeyUsage[kuStr]
248 if !ok {
249 return nil, fmt.Errorf("unknown key usage %q", kuStr)
250 }
251 ku |= kuBit
252 }
253 if ct == ocspCert {
254 ku = x509.KeyUsageDigitalSignature
255 } else if ct == crlCert {
256 ku = x509.KeyUsageCRLSign
257 }
258 if ku == 0 {
259 return nil, errors.New("at least one key usage must be set")
260 }
261
262 cert := &x509.Certificate{
263 SerialNumber: big.NewInt(0).SetBytes(serial),
264 BasicConstraintsValid: true,
265 IsCA: true,
266 Subject: profile.Subject(),
267 OCSPServer: ocspServer,
268 CRLDistributionPoints: crlDistributionPoints,
269 IssuingCertificateURL: issuingCertificateURL,
270 KeyUsage: ku,
271 SubjectKeyId: subjectKeyID,
272 }
273
274 if ct != requestCert {
275 sigAlg, ok := AllowedSigAlgs[profile.SignatureAlgorithm]
276 if !ok {
277 return nil, fmt.Errorf("unsupported signature algorithm %q", profile.SignatureAlgorithm)
278 }
279 cert.SignatureAlgorithm = sigAlg
280 notBefore, err := time.Parse(time.DateTime, profile.NotBefore)
281 if err != nil {
282 return nil, err
283 }
284 cert.NotBefore = notBefore
285 notAfter, err := time.Parse(time.DateTime, profile.NotAfter)
286 if err != nil {
287 return nil, err
288 }
289 cert.NotAfter = notAfter
290 }
291
292 switch ct {
293
294
295
296
297 case ocspCert:
298 cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}
299
300 ocspNoCheckExt := pkix.Extension{Id: oidOCSPNoCheck, Value: []byte{5, 0}}
301 cert.ExtraExtensions = append(cert.ExtraExtensions, ocspNoCheckExt)
302 cert.IsCA = false
303 case crlCert:
304 cert.IsCA = false
305 case requestCert, intermediateCert:
306
307
308
309
310
311 cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
312 cert.MaxPathLenZero = true
313 case crossCert:
314 cert.ExtKeyUsage = tbcs.ExtKeyUsage
315 cert.MaxPathLenZero = tbcs.MaxPathLenZero
316 }
317
318 for _, policyConfig := range profile.Policies {
319 oid, err := parseOID(policyConfig.OID)
320 if err != nil {
321 return nil, err
322 }
323 cert.PolicyIdentifiers = append(cert.PolicyIdentifiers, oid)
324 }
325
326 return cert, nil
327 }
328
329
330
331
332
333
334
335 type failReader struct{}
336
337 func (fr *failReader) Read([]byte) (int, error) {
338 return 0, errors.New("empty reader used by x509.CreateCertificate")
339 }
340
341 func generateCSR(profile *certProfile, signer crypto.Signer) ([]byte, error) {
342 csrDER, err := x509.CreateCertificateRequest(&failReader{}, &x509.CertificateRequest{
343 Subject: profile.Subject(),
344 }, signer)
345 if err != nil {
346 return nil, fmt.Errorf("failed to create and sign CSR: %s", err)
347 }
348 return csrDER, nil
349 }
350
View as plain text