-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcertificates.go
133 lines (111 loc) · 3.35 KB
/
certificates.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 CADDY_PFX_CERTIFICATES
import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
)
func getCertificateChain(initialCerts []*x509.Certificate) ([]*x509.Certificate, error) {
certsState := &certState{}
var fullChain []*x509.Certificate
// Add initial certificates to the state
for _, cert := range initialCerts {
addOrUpdateState(certsState, cert)
fullChain = append(fullChain, cert)
}
// Resolve the full chain, including downloading missing certificates
resolvedCerts, err := getUnresolvedCertificates(certsState)
if err != nil {
return nil, err
}
// Append resolved certificates to the PEM data
for _, cert := range resolvedCerts {
fullChain = append(fullChain, cert)
}
return fullChain, nil
}
type CertificateState struct {
SubjectKeyId string
AuthorityKeyId string
Resolved bool
IssuingCertificateURLs []string
}
type certState []CertificateState
func addOrUpdateState(certsState *certState, cert *x509.Certificate) {
for i := range *certsState {
if (*certsState)[i].SubjectKeyId == string(cert.SubjectKeyId) {
return
}
}
*certsState = append(*certsState, CertificateState{
SubjectKeyId: string(cert.SubjectKeyId),
AuthorityKeyId: string(cert.AuthorityKeyId),
IssuingCertificateURLs: cert.IssuingCertificateURL,
})
var unresolvedCerts []CertificateState
for _, state := range *certsState {
if state.AuthorityKeyId != string(cert.SubjectKeyId) {
unresolvedCerts = append(unresolvedCerts, state)
}
}
*certsState = unresolvedCerts
}
func getUnresolvedCertificates(certsState *certState) ([]*x509.Certificate, error) {
var resolvedCerts []*x509.Certificate
if len(*certsState) == 0 {
return resolvedCerts, nil
}
state := (*certsState)[0]
*certsState = (*certsState)[1:] // Shift
for _, url := range state.IssuingCertificateURLs {
cert, err := fetchCertificateFromURL(url)
if err != nil {
continue // Skip on error but continue processing
}
resolvedCerts = append(resolvedCerts, cert)
addOrUpdateState(certsState, cert) // Recursively append remaining certificates
}
// Recur for the remaining unresolved certificates
moreResolved, err := getUnresolvedCertificates(certsState)
if err != nil {
return nil, err
}
// Combine resolved certificates
resolvedCerts = append(resolvedCerts, moreResolved...)
return resolvedCerts, nil
}
func fetchCertificateFromURL(url string) (cert *x509.Certificate, err error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("failed to fetch certificate from URL %s: %v", url, err)
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
err = errors.Join(err, closeErr)
}
}()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received non-OK HTTP status: %s", resp.Status)
}
certData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read certificate data: %v", err)
}
// Try to decode in PEM
block, _ := pem.Decode(certData)
if block != nil && block.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PEM certificate: %v", err)
}
return cert, nil
}
// Try to decode in DER
cert, err = x509.ParseCertificate(certData)
if err != nil {
return nil, fmt.Errorf("failed to parse DER certificate: %v", err)
}
return cert, nil
}