-
-
Notifications
You must be signed in to change notification settings - Fork 68
/
certificate.go
143 lines (120 loc) · 3.42 KB
/
certificate.go
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
package server
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
"log"
"math/big"
"os"
"sync/atomic"
"time"
)
// Based on: https://github.com/ulixee/hero/blob/main/mitm-socket/go/generate_cert.go
var (
caFile string = "ca.der"
caKeyFile string = "caKey.der"
)
// While generating a new certificate, in order to get a unique serial
// number every time we increment this value.
var currentSerialNumber int64 = time.Now().Unix()
func readCertFromDisk(file string) (*x509.Certificate, error) {
bytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(bytes)
if err != nil {
return nil, err
}
return cert, nil
}
func readPrivateKeyFromDisk(file string) (*rsa.PrivateKey, error) {
bytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
key, err := x509.ParsePKCS8PrivateKey(bytes)
if err != nil {
return nil, err
}
privatePkcs8RsaKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("Pkcs8 contained non-RSA key. Expected RSA key.")
}
return privatePkcs8RsaKey, nil
}
// NewCertificateAuthority creates a new CA certificate and associated private key, unless it already exists on disk.
func NewCertificateAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
certFromDisk, err := readCertFromDisk(caFile)
if err != nil && !errors.Is(err, os.ErrNotExist) {
log.Printf("Error reading cert from disk %s %e", caFile, err)
} else if err == nil {
keyFromDisk, err := readPrivateKeyFromDisk(caKeyFile)
if err != nil {
log.Printf("Error reading private key from disk %s %e", caKeyFile, err)
} else {
return certFromDisk, keyFromDisk, nil
}
}
// Generating the private key that will be used for domain certificates
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
pub := priv.Public()
// Subject Key Identifier support for end entity certificate.
// https://tools.ietf.org/html/rfc3280#section-4.2.1.2
pkixpub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, nil, err
}
h := sha1.New()
_, err = h.Write(pkixpub)
if err != nil {
return nil, nil, err
}
keyID := h.Sum(nil)
// Increment the serial number
serial := atomic.AddInt64(¤tSerialNumber, 1)
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: pkix.Name{
CommonName: "Awesome TLS",
Organization: []string{"Sleeyax"},
},
SubjectKeyId: keyID,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
NotBefore: time.Now().AddDate(-1, 0, 0),
NotAfter: time.Now().AddDate(1, 0, 0),
DNSNames: []string{"awesometls", "localhost"},
IsCA: true,
}
raw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, pub, priv)
if err != nil {
return nil, nil, err
}
err = os.WriteFile(caFile, raw, 0o600)
if err != nil {
return nil, nil, err
}
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, nil, err
}
err = os.WriteFile(caKeyFile, privBytes, 0o600)
if err != nil {
return nil, nil, err
}
// Parse certificate bytes so that we have a leaf certificate.
x509c, err := x509.ParseCertificate(raw)
if err != nil {
return nil, nil, err
}
return x509c, priv, nil
}