-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
csr.go
211 lines (183 loc) · 6.18 KB
/
csr.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"time"
"github.com/dapr/dapr/pkg/sentry/certs"
"github.com/dapr/dapr/pkg/sentry/identity"
)
const (
blockTypeECPrivateKey = "EC PRIVATE KEY" // EC private key
blockTypePrivateKey = "PRIVATE KEY" // PKCS#8 private key
encodeMsgCSR = "CERTIFICATE REQUEST"
encodeMsgCert = "CERTIFICATE"
)
// The OID for the SAN extension (http://www.alvestrand.no/objectid/2.5.29.17.html)
var oidSubjectAlternativeName = asn1.ObjectIdentifier{2, 5, 29, 17}
// GenerateCSR creates a X.509 certificate sign request and private key.
func GenerateCSR(org string, pkcs8 bool) ([]byte, []byte, error) {
key, err := certs.GenerateECPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("unable to generate private keys: %w", err)
}
templ, err := genCSRTemplate(org)
if err != nil {
return nil, nil, fmt.Errorf("error generating csr template: %w", err)
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, templ, key)
if err != nil {
return nil, nil, fmt.Errorf("failed to create CSR: %w", err)
}
crtPem, keyPem, err := encode(true, csrBytes, key, pkcs8)
return crtPem, keyPem, err
}
func genCSRTemplate(org string) (*x509.CertificateRequest, error) {
return &x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{org},
},
}, nil
}
// generateBaseCert returns a base non-CA cert that can be made a workload or CA cert
// By adding subjects, key usage and additional proerties.
func generateBaseCert(ttl, skew time.Duration, publicKey interface{}) (*x509.Certificate, error) {
serNum, err := newSerialNumber()
if err != nil {
return nil, err
}
now := time.Now().UTC()
// Allow for clock skew with the NotBefore validity bound.
notBefore := now.Add(-1 * skew)
notAfter := now.Add(ttl)
return &x509.Certificate{
SerialNumber: serNum,
NotBefore: notBefore,
NotAfter: notAfter,
PublicKey: publicKey,
}, nil
}
func GenerateIssuerCertCSR(cn string, publicKey interface{}, ttl, skew time.Duration) (*x509.Certificate, error) {
cert, err := generateBaseCert(ttl, skew, publicKey)
if err != nil {
return nil, err
}
cert.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
cert.Subject = pkix.Name{
CommonName: cn,
}
cert.DNSNames = []string{cn}
cert.IsCA = true
cert.BasicConstraintsValid = true
cert.SignatureAlgorithm = x509.ECDSAWithSHA256
return cert, nil
}
// GenerateRootCertCSR returns a CA root cert x509 Certificate.
func GenerateRootCertCSR(org, cn string, publicKey interface{}, ttl, skew time.Duration) (*x509.Certificate, error) {
cert, err := generateBaseCert(ttl, skew, publicKey)
if err != nil {
return nil, err
}
cert.KeyUsage = x509.KeyUsageCertSign
cert.ExtKeyUsage = append(cert.ExtKeyUsage, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth)
cert.Subject = pkix.Name{
CommonName: cn,
Organization: []string{org},
}
cert.DNSNames = []string{cn}
cert.IsCA = true
cert.BasicConstraintsValid = true
cert.SignatureAlgorithm = x509.ECDSAWithSHA256
return cert, nil
}
// GenerateCSRCertificate returns an x509 Certificate from a CSR, signing cert, public key, signing private key and duration.
func GenerateCSRCertificate(csr *x509.CertificateRequest, subject string, identityBundle *identity.Bundle, signingCert *x509.Certificate, publicKey interface{}, signingKey crypto.PrivateKey,
ttl, skew time.Duration, isCA bool,
) ([]byte, error) {
cert, err := generateBaseCert(ttl, skew, publicKey)
if err != nil {
return nil, fmt.Errorf("error generating csr certificate: %w", err)
}
if isCA {
cert.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign
} else {
cert.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
cert.ExtKeyUsage = append(cert.ExtKeyUsage, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth)
}
if subject == "cluster.local" {
cert.Subject = pkix.Name{
CommonName: subject,
}
cert.DNSNames = []string{subject}
}
cert.Issuer = signingCert.Issuer
cert.IsCA = isCA
cert.IPAddresses = csr.IPAddresses
cert.Extensions = csr.Extensions
cert.BasicConstraintsValid = true
cert.SignatureAlgorithm = csr.SignatureAlgorithm
if identityBundle != nil {
spiffeID, err := identity.CreateSPIFFEID(identityBundle.TrustDomain, identityBundle.Namespace, identityBundle.ID)
if err != nil {
return nil, fmt.Errorf("error generating spiffe id: %w", err)
}
rv := []asn1.RawValue{
{
Bytes: []byte(spiffeID),
Class: asn1.ClassContextSpecific,
Tag: asn1.TagOID,
},
{
Bytes: []byte(fmt.Sprintf("%s.%s.svc.cluster.local", subject, identityBundle.Namespace)),
Class: asn1.ClassContextSpecific,
Tag: 2,
},
}
b, err := asn1.Marshal(rv)
if err != nil {
return nil, fmt.Errorf("failed to marshal asn1 raw value for spiffe id: %w", err)
}
cert.ExtraExtensions = append(cert.ExtraExtensions, pkix.Extension{
Id: oidSubjectAlternativeName,
Value: b,
Critical: true, // According to x509 and SPIFFE specs, a SubjAltName extension must be critical if subject name and DNS are not present.
})
}
return x509.CreateCertificate(rand.Reader, cert, signingCert, publicKey, signingKey)
}
func encode(csr bool, csrOrCert []byte, privKey *ecdsa.PrivateKey, pkcs8 bool) ([]byte, []byte, error) {
encodeMsg := encodeMsgCert
if csr {
encodeMsg = encodeMsgCSR
}
csrOrCertPem := pem.EncodeToMemory(&pem.Block{Type: encodeMsg, Bytes: csrOrCert})
var encodedKey, privPem []byte
var err error
if pkcs8 {
if encodedKey, err = x509.MarshalPKCS8PrivateKey(privKey); err != nil {
return nil, nil, err
}
privPem = pem.EncodeToMemory(&pem.Block{Type: blockTypePrivateKey, Bytes: encodedKey})
} else {
encodedKey, err = x509.MarshalECPrivateKey(privKey)
if err != nil {
return nil, nil, err
}
privPem = pem.EncodeToMemory(&pem.Block{Type: blockTypeECPrivateKey, Bytes: encodedKey})
}
return csrOrCertPem, privPem, nil
}
func newSerialNumber() (*big.Int, error) {
serialNumLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNum, err := rand.Int(rand.Reader, serialNumLimit)
if err != nil {
return nil, fmt.Errorf("error generating serial number: %w", err)
}
return serialNum, nil
}