-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathcertificates.go
More file actions
149 lines (122 loc) · 3.99 KB
/
certificates.go
File metadata and controls
149 lines (122 loc) · 3.99 KB
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
package libp2ptls
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
ic "github.com/libp2p/go-libp2p/core/crypto"
)
// DefaultCertManager is the default certificate manager that creates self-signed certificates
type DefaultCertManager struct{}
// CreateCertificate generates a new ECDSA private key and corresponding x509 certificate.
// The certificate includes an extension that cryptographically ties it to the provided libp2p
// private key to authenticate TLS connections.
func (m *DefaultCertManager) CreateCertificate(privKey ic.PrivKey, template *x509.Certificate) (*tls.Certificate, error) {
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
// Generate the signed extension that binds the certificate to the libp2p key
extension, err := GenerateSignedExtension(privKey, certKey.Public())
if err != nil {
return nil, err
}
template.ExtraExtensions = append(template.ExtraExtensions, extension)
// Self-signed certificate
certDER, err := x509.CreateCertificate(rand.Reader, template, template, certKey.Public(), certKey)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certDER},
PrivateKey: certKey,
}, nil
}
// CACertManager is a certificate manager that uses a CA to sign certificates
type CACertManager struct {
CACert *x509.Certificate
CAPrivKey crypto.PrivateKey
CAPool *x509.CertPool
defaultCertManager *DefaultCertManager
}
// NewCACertManager creates a new CA certificate manager from a file
func NewCACertManager(caCertPath string) (*CACertManager, error) {
caCertPEM, err := os.ReadFile(caCertPath)
if err != nil {
return nil, err
}
block, rest := pem.Decode(caCertPEM)
if block == nil || block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("no valid CA cert found in %s", caCertPath)
}
caCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
blockKey, _ := pem.Decode(rest)
if blockKey == nil {
return nil, fmt.Errorf("no CA private key found in %s", caCertPath)
}
caKey, err := x509.ParsePKCS8PrivateKey(blockKey.Bytes)
if err != nil {
return nil, err
}
caPool := x509.NewCertPool()
caPool.AddCert(caCert)
return &CACertManager{
CACert: caCert,
CAPrivKey: caKey,
CAPool: caPool,
}, nil
}
// CreateCertificate generates a CA-signed certificate
func (m *CACertManager) CreateCertificate(privKey ic.PrivKey, template *x509.Certificate) (*tls.Certificate, error) {
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
extension, err := GenerateSignedExtension(privKey, certKey.Public())
if err != nil {
return nil, err
}
template.ExtraExtensions = append(template.ExtraExtensions, extension)
// CA-signed certificate (not self-signed)
certDER, err := x509.CreateCertificate(rand.Reader, template, m.CACert, certKey.Public(), m.CAPrivKey)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certDER, m.CACert.Raw},
PrivateKey: certKey,
}, nil
}
// VerifyCertChain first adds CA verification on top of the default certificate verification
func (m *CACertManager) VerifyCertChain(chain []*x509.Certificate) (ic.PubKey, error) {
// Ensure there's at least one certificate beyond the CA certificate.
if len(chain) < 2 {
return nil, fmt.Errorf("insufficient certificate chain length")
}
opts := x509.VerifyOptions{
Roots: m.CAPool, // trusted root certificate
Intermediates: x509.NewCertPool(),
}
for _, cert := range chain[1:] {
opts.Intermediates.AddCert(cert)
}
// Verify the leaf certificate
_, err := chain[0].Verify(opts)
if err != nil {
return nil, fmt.Errorf("full chain verification failed: %v", err)
}
// Verify first cert against the default cert manager
pubKey, err := m.defaultCertManager.VerifyCertChain(chain[0:1])
if err != nil {
return nil, fmt.Errorf("cert verification failed: %v", err)
}
return pubKey, nil
}