/
ca.go
170 lines (142 loc) · 5.16 KB
/
ca.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package ca
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"time"
)
// Issuing certificates concurrently is not supported.
type CA struct {
// validity is the duration for which issued certificates are valid. This
// is approximately cert.NotAfter - cert.NotBefore with some additional
// allowance for clock skew.
//
// Currently this is used for the CA's validity too, but nothing should
// assume that the CA's validity period is the same as issued certificates'
// validity.
validity time.Duration
// clockSkewAllocance is the maximum supported clock skew. Everything that
// processes the certificates must have a system clock that is off by no
// more than this allowance in either direction.
clockSkewAllocance time.Duration
// The CA's private key.
privateKey *ecdsa.PrivateKey
// The CA's certificate.
root *x509.Certificate
// The PEM X.509 encoding of `root`
rootPEM string
// nextSerialNumber is the serial number of the next certificate to issue.
// Serial numbers must not be reused.
//
// It is assumed there is only one instance of CA and it is assumed that a
// given CA object isn't requested to issue certificates concurrently.
//
// For now we do not attempt to meet CABForum requirements (e.g. regarding
// randomness).
nextSerialNumber uint64
}
type CertificateAndPrivateKey struct {
// The ASN.1 DER-encoded (binary, not PEM) certificate.
Certificate []byte
// The PKCS#8 DER-encoded (binary, not PEM) private key.
PrivateKey []byte
}
// NewCA is the only way to create a CA.
func NewCA() (*CA, error) {
// Initially all certificates will be valid for one year. TODO: Shorten the
// validity duration of CA and end-entity certificates downward.
validity := (24 * 365) * time.Hour
// Allow half a day of clock skew. TODO: decrease the default value of this
// and make it tunable. TODO: Reconsider how this interacts with the
// similar logic in the webpki verifier; since both are trying to account
// for clock skew, there is somewhat of an over-correction.
clockSkewAllocance := 12 * time.Hour
privateKey, err := generateKeyPair()
if err != nil {
return nil, err
}
ca := CA{
validity: validity,
clockSkewAllocance: clockSkewAllocance,
privateKey: privateKey,
nextSerialNumber: 1,
}
template := ca.createTemplate(&ca.privateKey.PublicKey)
template.Subject = pkix.Name{CommonName: "Cluster-local Managed Pod CA"}
// basicConstraints.cA = true
template.IsCA = true
template.MaxPathLen = -1
template.BasicConstraintsValid = true
// `parent == template` means "self-signed".
rootDer, err := x509.CreateCertificate(rand.Reader, &template, &template, privateKey.Public(), privateKey)
if err != nil {
return nil, err
}
ca.root, err = x509.ParseCertificate(rootDer)
if err != nil {
return nil, err
}
ca.rootPEM = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: ca.root.Raw}))
return &ca, nil
}
// TrustAnchorDER returns the PEM-encoded X.509 certificate of the trust anchor
// (root CA).
func (ca *CA) TrustAnchorPEM() string {
return ca.rootPEM
}
// IssueEndEntityCertificate creates a new certificate that is valid for the
// given DNS name, generating a new keypair for it.
func (ca *CA) IssueEndEntityCertificate(dnsName string) (*CertificateAndPrivateKey, error) {
privateKey, err := generateKeyPair()
if err != nil {
return nil, err
}
p8, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, err
}
template := ca.createTemplate(&privateKey.PublicKey)
template.DNSNames = []string{dnsName}
crt, err := x509.CreateCertificate(rand.Reader, &template, ca.root, &privateKey.PublicKey, ca.privateKey)
if err != nil {
return nil, err
}
return &CertificateAndPrivateKey{
Certificate: crt,
PrivateKey: p8,
}, nil
}
// createTemplate returns a certificate template for a non-CA certificate with
// no subject name, no subjectAltNames. The template can then be modified into
// a (root) CA template or an end-entity template by the caller.
func (ca *CA) createTemplate(publicKey *ecdsa.PublicKey) x509.Certificate {
// ECDSA is used instead of RSA because ECDSA key generation is
// straightforward and fast whereas RSA key generation is extremely slow
// and error-prone.
//
// CA certificates are signed with the same algorithm as end-entity
// certificates because they are relatively short-lived, because using one
// algorithm minimizes exposure to implementation flaws, and to speed up
// signature verification time.
//
// SHA-256 is used because any larger digest would be truncated to 256 bits
// anyway since a P-256 scalar is only 256 bits long.
const SignatureAlgorithm = x509.ECDSAWithSHA256
serialNumber := big.NewInt(int64(ca.nextSerialNumber))
ca.nextSerialNumber += 1
notBefore := time.Now()
return x509.Certificate{
SerialNumber: serialNumber,
SignatureAlgorithm: SignatureAlgorithm,
NotBefore: notBefore.Add(-ca.clockSkewAllocance),
NotAfter: notBefore.Add(ca.validity).Add(ca.clockSkewAllocance),
PublicKey: publicKey,
}
}
func generateKeyPair() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}