17170: Merge branch 'master'
[arvados.git] / lib / selfsigned / cert.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package selfsigned
6
7 import (
8         "crypto/rand"
9         "crypto/rsa"
10         "crypto/tls"
11         "crypto/x509"
12         "crypto/x509/pkix"
13         "fmt"
14         "math/big"
15         "net"
16         "time"
17 )
18
19 type CertGenerator struct {
20         Bits  int
21         Hosts []string
22         IsCA  bool
23 }
24
25 func (gen CertGenerator) Generate() (cert tls.Certificate, err error) {
26         keyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
27         if gen.IsCA {
28                 keyUsage |= x509.KeyUsageCertSign
29         }
30         notBefore := time.Now()
31         notAfter := time.Now().Add(time.Hour * 24 * 365)
32         snMax := new(big.Int).Lsh(big.NewInt(1), 128)
33         sn, err := rand.Int(rand.Reader, snMax)
34         if err != nil {
35                 err = fmt.Errorf("Failed to generate serial number: %w", err)
36                 return
37         }
38         template := x509.Certificate{
39                 SerialNumber: sn,
40                 Subject: pkix.Name{
41                         Organization: []string{"N/A"},
42                 },
43                 NotBefore:             notBefore,
44                 NotAfter:              notAfter,
45                 KeyUsage:              keyUsage,
46                 ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
47                 BasicConstraintsValid: true,
48                 IsCA:                  gen.IsCA,
49         }
50         for _, h := range gen.Hosts {
51                 if ip := net.ParseIP(h); ip != nil {
52                         template.IPAddresses = append(template.IPAddresses, ip)
53                 } else {
54                         template.DNSNames = append(template.DNSNames, h)
55                 }
56         }
57         bits := gen.Bits
58         if bits == 0 {
59                 bits = 4096
60         }
61         priv, err := rsa.GenerateKey(rand.Reader, bits)
62         if err != nil {
63                 err = fmt.Errorf("error generating key: %w", err)
64                 return
65         }
66         certder, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
67         if err != nil {
68                 err = fmt.Errorf("error creating certificate: %w", err)
69                 return
70         }
71         cert = tls.Certificate{
72                 Certificate: [][]byte{certder},
73                 PrivateKey:  priv,
74         }
75         return
76 }