forked from clburlison/goproxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsigner.go
130 lines (110 loc) · 3.01 KB
/
signer.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
package goproxy
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"math/rand"
"net"
"runtime"
"sort"
"strings"
"sync"
"time"
)
var tlsCache sync.Map
func hashSorted(lst []string) []byte {
c := make([]string, len(lst))
copy(c, lst)
sort.Strings(c)
h := sha1.New()
for _, s := range c {
h.Write([]byte(s + ","))
}
return h.Sum(nil)
}
func hashSortedBigInt(lst []string) *big.Int {
rv := new(big.Int)
rv.SetBytes(hashSorted(lst))
return rv
}
var goproxySignerVersion = ":goroxy1"
func signHost(ca tls.Certificate, hosts []string) (cert *tls.Certificate, err error) {
var x509ca *x509.Certificate
// Use the provided ca and not the global GoproxyCa for certificate generation.
if x509ca, err = x509.ParseCertificate(ca.Certificate[0]); err != nil {
return
}
hostsKey := strings.Join(hosts, ",")
if val, ok := tlsCache.Load(hostsKey); ok {
if cert, ok := val.(*tls.Certificate); ok {
if leaf, err := x509.ParseCertificate(cert.Certificate[0]); err == nil {
if !time.Now().After(leaf.NotAfter) {
return cert, nil
}
}
}
}
start := time.Unix(time.Now().Unix()-2592000, 0) // 2592000 = 30 day
end := time.Unix(time.Now().Unix()+7776000, 0) // 31536000 = 90 days
serial := big.NewInt(rand.Int63())
template := x509.Certificate{
// TODO(elazar): instead of this ugly hack, just encode the certificate and hash the binary form.
SerialNumber: serial,
Issuer: x509ca.Subject,
Subject: pkix.Name{
Organization: []string{"GoProxy untrusted MITM proxy Inc"},
},
NotBefore: start,
NotAfter: end,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
template.Subject.CommonName = h
}
}
hash := hashSorted(append(hosts, goproxySignerVersion, ":"+runtime.Version()))
var csprng CounterEncryptorRand
if csprng, err = NewCounterEncryptorRandFromKey(ca.PrivateKey, hash); err != nil {
return
}
var certpriv crypto.Signer
switch ca.PrivateKey.(type) {
case *rsa.PrivateKey:
if certpriv, err = rsa.GenerateKey(&csprng, 2048); err != nil {
return
}
case *ecdsa.PrivateKey:
if certpriv, err = ecdsa.GenerateKey(elliptic.P256(), &csprng); err != nil {
return
}
default:
err = fmt.Errorf("unsupported key type %T", ca.PrivateKey)
}
var derBytes []byte
if derBytes, err = x509.CreateCertificate(&csprng, &template, x509ca, certpriv.Public(), ca.PrivateKey); err != nil {
return
}
newcert := &tls.Certificate{
Certificate: [][]byte{derBytes, ca.Certificate[0]},
PrivateKey: certpriv,
}
tlsCache.Store(hostsKey, newcert)
return newcert, nil
}
func init() {
// Avoid deterministic random numbers
rand.Seed(time.Now().UnixNano())
}