forked from fcjr/aia-transport-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransport.go
133 lines (117 loc) · 3.25 KB
/
transport.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
package aia
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"errors"
"log"
"net"
"runtime"
"time"
)
// NewTransport returns a http.Transport that supports AIA (Authority Information Access) resolution
// for incomplete certificate chains.
func NewTransport() (*http.Transport, error) {
// Support windows.
if runtime.GOOS == "windows" {
return &http.Transport{}, nil
}
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
return NewTransportWithCustomTLSClientConfig(&tls.Config{RootCAs: rootCAs})
}
// Allow custom TLSClientConfig parameter which could have custom RootCAs
func NewTransportWithCustomTLSClientConfig(TLSClientConfig *tls.Config) (*http.Transport, error) {
// Support windows.
if runtime.GOOS == "windows" {
return &http.Transport{TLSClientConfig: TLSClientConfig}, nil
}
return &http.Transport{
TLSClientConfig: TLSClientConfig,
DialTLS: func(network, addr string) (net.Conn, error) {
conn, err := tls.Dial(network, addr, &tls.Config{
InsecureSkipVerify: true,
RootCAs: TLSClientConfig.RootCAs,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
serverName, _, err := net.SplitHostPort(addr)
if err != nil {
return err
}
return VerifyPeerCerts(TLSClientConfig.RootCAs, serverName, rawCerts)
},
})
if err != nil {
return conn, err
}
return conn, nil
},
}, nil
}
func VerifyPeerCerts(rootCAs *x509.CertPool, serverName string, rawCerts [][]byte) error {
certs := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return errors.New("failed to parse certificate from server: " + err.Error())
}
certs[i] = cert
}
opts := &x509.VerifyOptions{
Roots: rootCAs,
CurrentTime: time.Now(),
DNSName: serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(*opts)
if err != nil {
if _, ok := err.(x509.UnknownAuthorityError); ok {
if len(certs[0].IssuingCertificateURL) >= 1 && certs[0].IssuingCertificateURL[0] != "" {
err1 := VerifyIncompleteChain(certs[0].IssuingCertificateURL[0], certs[0], opts)
if err1 != nil {
log.Println(err1)
return err // return original x509.UnknownAuthorityError
}
return nil
}
}
return err
}
return nil
}
func VerifyIncompleteChain(issuingCertificateURL string, baseCert *x509.Certificate, opts *x509.VerifyOptions) error {
issuer, err := GetCert(issuingCertificateURL)
if err != nil {
return err
}
opts.Intermediates.AddCert(issuer)
_, err = baseCert.Verify(*opts)
if err != nil {
if _, ok := err.(x509.UnknownAuthorityError); ok {
if len(issuer.IssuingCertificateURL) >= 1 && issuer.IssuingCertificateURL[0] != "" {
return VerifyIncompleteChain(issuer.IssuingCertificateURL[0], baseCert, opts)
}
}
return err
}
return nil
}
func GetCert(url string) (*x509.Certificate, error) {
resp, err := http.Get(url)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return x509.ParseCertificate(data)
}