forked from pwaller/httpcache
/
certs.go
150 lines (134 loc) · 3.78 KB
/
certs.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
144
145
146
147
148
149
150
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"sort"
"strings"
"time"
//systls "crypto/tls"
tls "github.com/pwaller/httpcache/mitmhttps/tls"
)
// TODO(pwaller): rw-locking for goroutine safety? Persistence?
var certCache = map[string]tls.Certificate{}
// Generate a signed tls.Certificate which is valid for the given list of
// hostnames using proxy_ca
func MakeCert(hostnames []string) *tls.Certificate {
key := strings.Join(hostnames, " ")
cert, ok := certCache[key]
if ok {
return &cert
}
proxy_ca_ready.Wait()
ca, err := x509.ParseCertificate(proxy_ca.Certificate[0])
certPem, keyPem, err := SignHost(ca, proxy_ca.PrivateKey, hostnames)
if err != nil {
panic(err)
}
cert, err = tls.X509KeyPair(certPem, keyPem)
if err != nil {
panic(err)
}
certCache[key] = cert
return &cert
}
type GenerateMITM struct {
ca tls.Certificate
}
// Determine ssl server names from a net.Addr
func GetTargetServernames(addr net.Addr) (hosts []string, err error) {
c, err := tls.Dial("tcp4", addr.String(), &tls.Config{RootCAs: trust_db})
if err != nil {
if err, ok := err.(x509.HostnameError); ok {
// This is a tls error condition our side because we asked for an
// IP connection (we don't know the hostname of the target, only
// the IP).
// It's okay because we're just interested in finding out what hosts
// we should tell the client we are. If it is invalid, the client
// will bail out.
hosts := err.Certificate.DNSNames
if len(hosts) == 0 {
hosts = []string{err.Certificate.Subject.CommonName} //err.Host}
}
return hosts, nil
}
return
}
err = c.Handshake()
if err != nil {
return
}
hosts = c.ConnectionState().PeerCertificates[0].DNSNames
return
}
func (gm GenerateMITM) GetCertificate(name string, conn net.Conn) *tls.Certificate {
var names []string
if name == "" {
// ClientHello didn't contain a hostname we can just impersonate directly.
// We'll have to go
target := GetOriginalAddr(conn)
// TODO(pwaller): cache ip:port -> certname mapping
var err error
names, err = GetTargetServernames(target)
if err != nil {
panic(err)
}
} else {
names = []string{name}
}
return MakeCert(names)
}
func hashSorted(lst []string) *big.Int {
c := make([]string, len(lst))
copy(c, lst)
sort.Strings(c)
h := sha1.New()
for _, s := range c {
h.Write([]byte(s + ","))
}
rv := new(big.Int)
rv.SetBytes(h.Sum(nil))
return rv
}
func SignHost(ca *x509.Certificate, capriv interface{}, hosts []string) (pemCert []byte, pemKey []byte, err error) {
now := time.Now()
template := x509.Certificate{
SerialNumber: hashSorted(hosts),
Issuer: ca.Subject,
Subject: pkix.Name{
Organization: []string{"Proxycache MITM proxy certificate"},
},
NotBefore: time.Now(),
NotAfter: now.Add(365 * 24 * time.Hour),
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)
//panic("Unimplemented")
} else {
template.DNSNames = append(template.DNSNames, h)
}
}
certpriv, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, nil, err
}
pemKeyBuf := new(bytes.Buffer)
pem.Encode(pemKeyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certpriv)})
derBytes, err := x509.CreateCertificate(rand.Reader, &template, ca, &certpriv.PublicKey, capriv)
if err != nil {
return nil, nil, err
}
pemCertBuf := new(bytes.Buffer)
pem.Encode(pemCertBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
return pemCertBuf.Bytes(), pemKeyBuf.Bytes(), nil
}