Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/mitm-socket/go/generate_cert.go
1029 views
1
package main
2
3
import (
4
"bufio"
5
"crypto"
6
"crypto/rand"
7
"crypto/rsa"
8
"crypto/sha1"
9
"crypto/x509"
10
"crypto/x509/pkix"
11
"encoding/pem"
12
"fmt"
13
"math/big"
14
"net"
15
"os"
16
"sync/atomic"
17
"time"
18
)
19
20
/// Reference/Credit: https://github.com/AdguardTeam/gomitmproxy
21
22
// While generating a new certificate, in order to get a unique serial
23
// number every time we increment this value.
24
var currentSerialNumber int64 = time.Now().Unix()
25
26
// Config is a set of configuration values that are used to build TLS configs
27
// capable of MITM.
28
type CertConfig struct {
29
ca *x509.Certificate // Root certificate authority
30
caPrivateKey *rsa.PrivateKey // CA private key
31
32
// privateKey is the private key that will be used to generate leaf certificates
33
publicKey crypto.PublicKey
34
privateKeyPEM string
35
36
keyID []byte // SKI to use in generated certificates (https://tools.ietf.org/html/rfc3280#section-4.2.1.2)
37
organization string // Organization (will be used for generated certificates)
38
}
39
40
func readBytesFromDisk(filename string) ([]byte, error) {
41
// first check files
42
file, err := os.Open(filename)
43
if err != nil {
44
return nil, err
45
}
46
defer file.Close()
47
48
pemfileinfo, _ := file.Stat()
49
var size int64 = pemfileinfo.Size()
50
bytes := make([]byte, size)
51
52
buffer := bufio.NewReader(file)
53
_, err = buffer.Read(bytes)
54
if err != nil {
55
return nil, err
56
}
57
58
return bytes, nil
59
60
}
61
62
func readCertFromDisk(file string) (*x509.Certificate, error) {
63
64
bytes, err := readBytesFromDisk(file)
65
if err != nil {
66
return nil, err
67
}
68
69
cert, err := x509.ParseCertificate(bytes)
70
if err != nil {
71
return nil, err
72
}
73
return cert, nil
74
}
75
76
func readPrivateKeyFromDisk(file string) (*rsa.PrivateKey, error) {
77
78
bytes, err := readBytesFromDisk(file)
79
if err != nil {
80
return nil, err
81
}
82
83
key, err := x509.ParsePKCS8PrivateKey(bytes)
84
if err != nil {
85
return nil, err
86
}
87
88
privatePkcs8RsaKey, ok := key.(*rsa.PrivateKey)
89
if !ok {
90
return nil, fmt.Errorf("Pkcs8 contained non-RSA key. Expected RSA key.")
91
}
92
return privatePkcs8RsaKey, nil
93
}
94
95
func writeKeyToDisk(bytes []byte, file string) error {
96
out, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
97
98
if err != nil {
99
return err
100
}
101
defer out.Close()
102
103
out.Write(bytes)
104
105
return nil
106
}
107
108
// NewAuthority creates a new CA certificate and associated private key.
109
func NewAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
110
var caFile string = "ca.der"
111
var caKeyFile string = "caKey.der"
112
113
certFromDisk, err := readCertFromDisk(caFile)
114
if err == nil {
115
keyFromDisk, err := readPrivateKeyFromDisk(caKeyFile)
116
if err != nil {
117
fmt.Printf("Error reading private key from disk", caKeyFile, err)
118
} else {
119
return certFromDisk, keyFromDisk, nil
120
}
121
}
122
123
// Generating the private key that will be used for domain certificates
124
priv, err := rsa.GenerateKey(rand.Reader, 2048)
125
if err != nil {
126
return nil, nil, err
127
}
128
pub := priv.Public()
129
130
// Subject Key Identifier support for end entity certificate.
131
// https://tools.ietf.org/html/rfc3280#section-4.2.1.2
132
pkixpub, err := x509.MarshalPKIXPublicKey(pub)
133
if err != nil {
134
return nil, nil, err
135
}
136
h := sha1.New()
137
_, err = h.Write(pkixpub)
138
if err != nil {
139
return nil, nil, err
140
}
141
keyID := h.Sum(nil)
142
143
// Increment the serial number
144
serial := atomic.AddInt64(&currentSerialNumber, 1)
145
146
tmpl := &x509.Certificate{
147
SerialNumber: big.NewInt(serial),
148
Subject: pkix.Name{
149
CommonName: "SecretAgentCA",
150
Organization: []string{"Data Liberation Foundation"},
151
},
152
SubjectKeyId: keyID,
153
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
154
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
155
BasicConstraintsValid: true,
156
NotBefore: time.Now().AddDate(-1, 0, 0),
157
NotAfter: time.Now().AddDate(1, 0, 0),
158
DNSNames: []string{"SecretAgentCA"},
159
IsCA: true,
160
}
161
162
raw, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, pub, priv)
163
if err != nil {
164
return nil, nil, err
165
}
166
167
writeKeyToDisk(raw, caFile)
168
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
169
if err != nil {
170
return nil, nil, err
171
}
172
writeKeyToDisk(privBytes, caKeyFile)
173
174
// Parse certificate bytes so that we have a leaf certificate.
175
x509c, err := x509.ParseCertificate(raw)
176
if err != nil {
177
return nil, nil, err
178
}
179
180
return x509c, priv, nil
181
}
182
183
func NewCertConfig(ca *x509.Certificate, caPrivateKey *rsa.PrivateKey) (*CertConfig, error) {
184
185
var priv *rsa.PrivateKey
186
187
if ca == nil {
188
var err error
189
ca, caPrivateKey, err = NewAuthority()
190
191
if err != nil {
192
return nil, err
193
}
194
195
priv, _ = readPrivateKeyFromDisk("privKey.der")
196
}
197
198
var needsSave bool = false
199
if priv == nil {
200
var err error
201
// Generating the private key that will be used for domain certificates
202
priv, err = rsa.GenerateKey(rand.Reader, 2048)
203
if err != nil {
204
return nil, err
205
}
206
needsSave = true
207
}
208
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
209
if err != nil {
210
return nil, err
211
}
212
213
if needsSave {
214
writeKeyToDisk(privBytes, "privKey.der")
215
}
216
pub := priv.Public()
217
218
// Subject Key Identifier support for end entity certificate.
219
// https://tools.ietf.org/html/rfc3280#section-4.2.1.2
220
pkixpub, err := x509.MarshalPKIXPublicKey(pub)
221
if err != nil {
222
return nil, err
223
}
224
h := sha1.New()
225
_, err = h.Write(pkixpub)
226
if err != nil {
227
return nil, err
228
}
229
keyID := h.Sum(nil)
230
231
privateKeyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})
232
233
return &CertConfig{
234
ca: ca,
235
caPrivateKey: caPrivateKey,
236
publicKey: pub,
237
privateKeyPEM: string(privateKeyPEM),
238
keyID: keyID,
239
organization: "Data Liberation Foundation",
240
}, nil
241
}
242
243
func (c *CertConfig) CreateCert(hostname string) ([]byte, int64, error) {
244
245
// Increment the serial number
246
serial := atomic.AddInt64(&currentSerialNumber, 1)
247
expireDate := time.Now().AddDate(0, 1, 0)
248
249
tmpl := &x509.Certificate{
250
SerialNumber: big.NewInt(serial),
251
Subject: pkix.Name{
252
CommonName: hostname,
253
Organization: []string{c.organization},
254
},
255
SubjectKeyId: c.keyID,
256
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
257
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
258
BasicConstraintsValid: true,
259
NotBefore: time.Now().AddDate(0, 0, -1),
260
NotAfter: expireDate,
261
}
262
263
if ip := net.ParseIP(hostname); ip != nil {
264
tmpl.IPAddresses = []net.IP{ip}
265
} else {
266
tmpl.DNSNames = []string{hostname}
267
}
268
269
derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, c.ca, c.publicKey, c.caPrivateKey)
270
if err != nil {
271
return nil, 0, err
272
}
273
274
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
275
276
return certPEM, expireDate.Unix(), nil
277
}
278
279