-
Notifications
You must be signed in to change notification settings - Fork 165
/
pki.go
138 lines (110 loc) · 3.18 KB
/
pki.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
package pki
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"sync"
)
var caMap map[string]x509.CertPool = make(map[string]x509.CertPool)
var mu sync.RWMutex
type RootCAs struct {
RootsInfos []RootInfo `json:"RootsInfos"`
}
type RootInfo struct {
RootName string `json:"rootName"`
CaName string `json:"CaName"`
Cdp string `json:"Cdp"`
AppType string `json:"AppType"`
StoreLocation string `json:"StoreLocation"`
StoreName string `json:"StoreName"`
Body string `json:"Body"`
PEM string `json:"PEM"`
Intermediates []IntermediateInfo `json:"Intermediates"`
}
type IntermediateInfo struct {
IntermediateName string `json:"IntermediateName"`
AppType string `json:"AppType"`
Cdp string `json:"Cdp"`
StoreLocation string `json:"StoreLocation"`
StoreName string `json:"StoreName"`
Body string `json:"Body"`
PEM string `json:"PEM"`
}
// https://aka.ms/getissuers
// The v3 endpoint can be used to get ca certs
// For example https://issuer.pki.azure.com/dsms/issuercertificates?getissuersv3&caName=ame
// returns the ame certs
func FetchDataFromGetIssuerPki(url string) (*RootCAs, error) {
response, err := http.Get(url)
if err != nil {
return nil, err
}
defer response.Body.Close()
if err != nil {
return nil, err
}
// Read in certs from endpoint
body, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}
var rootCAs RootCAs
json.Unmarshal(body, &rootCAs)
return &rootCAs, nil
}
func GetTlsCertPool(urlTemplate, caName string) (*x509.CertPool, error) {
url := fmt.Sprintf(urlTemplate, caName)
caCertPool, ok := getCaCertPoolFromMap(url)
if ok {
return &caCertPool, nil
} else {
caCertPool, err := buildCertPoolForCaName(url)
if err != nil || caCertPool == nil {
return nil, err
}
setCaCertPoolInMap(url, *caCertPool)
return caCertPool, nil
}
}
func getCaCertPoolFromMap(key string) (x509.CertPool, bool) {
mu.RLock()
defer mu.RUnlock()
caCertPool, ok := caMap[key]
return caCertPool, ok
}
func setCaCertPoolInMap(key string, caCertPool x509.CertPool) {
mu.Lock()
defer mu.Unlock()
caMap[key] = caCertPool
}
func buildCertPoolForCaName(url string) (*x509.CertPool, error) {
data, err := FetchDataFromGetIssuerPki(url)
if err != nil {
return nil, err
}
// Create a CertPool
caCertPool, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
for _, rootInfo := range data.RootsInfos {
caCert := rootInfo.PEM
// Append the custom CA certificate to the CertPool
if ok := caCertPool.AppendCertsFromPEM([]byte(caCert)); !ok {
return nil, errors.New("failed to load cert into cert pool")
}
for _, intermediate := range rootInfo.Intermediates {
intermediateCert := intermediate.PEM
// Append the custom CA certificate to the CertPool
if ok := caCertPool.AppendCertsFromPEM([]byte(intermediateCert)); !ok {
return nil, errors.New("failed to load cert into cert pool")
}
}
}
return caCertPool, nil
}