-
Notifications
You must be signed in to change notification settings - Fork 0
/
cert.go
179 lines (150 loc) · 5.04 KB
/
cert.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
package pcert
import (
"crypto"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"reflect"
"time"
)
const (
// DefaultValidityPeriod is the validity period used for certificates which have not set NotAfter explicitly
DefaultValidityPeriod = time.Hour * 24 * 365
)
// Create creates a x509.Certificate and a key with the default key options. See CreateWithKeyOptions for more details.
func Create(cert *x509.Certificate, signCert *x509.Certificate, signKey crypto.PrivateKey) (certPEM, keyPEM []byte, err error) {
return CreateWithKeyOptions(cert, KeyOptions{}, signCert, signKey)
}
// CreateWithKeyOptions creates a key and certificate. The certificate is signed
// used signCert and signKey. If signCert or signKey are nil, a self-signed
// certificate will be created. The certificate and the key are returned PEM encoded.
func CreateWithKeyOptions(cert *x509.Certificate, keyOptions KeyOptions, signCert *x509.Certificate, signKey crypto.PrivateKey) (certPEM, keyPEM []byte, err error) {
priv, pub, err := GenerateKey(keyOptions)
if err != nil {
return
}
keyPEM, err = EncodeKey(priv)
if err != nil {
return
}
// If signCert and signKey are missing we self sign the certificate
if signCert == nil && signKey == nil {
certPEM, err = Sign(cert, pub, cert, priv)
} else if signCert != nil && signKey != nil {
certPEM, err = Sign(cert, pub, signCert, signKey)
} else {
if signCert == nil {
return nil, nil, fmt.Errorf("certificate for signing missing")
}
return nil, nil, fmt.Errorf("private key for signing missing")
}
return certPEM, keyPEM, err
}
// Sign set some defaults on a certificate and signs it with the signCert and
// the signKey. The following defaults are set they are not set explicitly in the
// certificate:
//
// - SubjectKeyId is generated based on the publicKey
// - The AuthorityKeyId is set based on the SubjectKeyId of the signCert
// - NotBefore is set to time.Now()
// - NotAfter is set to NotBefore + DefaultValidityPeriod
// - SerialNumber is set to a randomly generated serial number
//
// The created certificate is returned PEM encoded.
func Sign(cert *x509.Certificate, publicKey interface{}, signCert *x509.Certificate, signKey interface{}) (certPEM []byte, err error) {
if cert.SubjectKeyId == nil {
subjectKeyID, err := getSubjectKeyID(publicKey)
if err != nil {
return nil, err
}
cert.SubjectKeyId = subjectKeyID
}
if cert.AuthorityKeyId == nil {
cert.AuthorityKeyId = signCert.SubjectKeyId
}
if cert.NotBefore.IsZero() {
cert.NotBefore = time.Now()
}
if cert.NotAfter.IsZero() {
cert.NotAfter = cert.NotBefore.Add(DefaultValidityPeriod)
}
if cert.SerialNumber == nil {
serialNumber, err := getRandomSerialNumber()
if err != nil {
return nil, err
}
cert.SerialNumber = serialNumber
}
der, err := x509.CreateCertificate(rand.Reader, cert, signCert, publicKey, signKey)
if err != nil {
return nil, err
}
return Encode(der), nil
}
// Request creates a CSR and a key. The key is created with the default key
// options. See RequestWithKeyOptions for more details.
func Request(csr *x509.CertificateRequest) (csrPEM []byte, keyPEM []byte, err error) {
return RequestWithKeyOptions(csr, KeyOptions{})
}
// RequestWithKeyOptions creates a CSR and a key based on key options. The key is
// created with the default key options.
func RequestWithKeyOptions(csr *x509.CertificateRequest, keyOptions KeyOptions) (csrPEM []byte, keyPEM []byte, err error) {
priv, _, err := GenerateKey(keyOptions)
if err != nil {
return
}
keyPEM, err = EncodeKey(priv)
if err != nil {
return
}
der, err := x509.CreateCertificateRequest(rand.Reader, csr, priv)
if err != nil {
return
}
csrPEM = EncodeCSR(der)
return
}
// SignCSR applies the settings from csr and return the signed certificate
func SignCSR(csr *x509.CertificateRequest, cert *x509.Certificate, signCert *x509.Certificate, signKey interface{}) (certPEM []byte, err error) {
// TODO: settings from cert should take precedence
applyCSR(csr, cert)
return Sign(cert, csr.PublicKey, signCert, signKey)
}
// apply values of CSR to certificate
func applyCSR(csr *x509.CertificateRequest, cert *x509.Certificate) {
cert.Signature = csr.Signature
cert.SignatureAlgorithm = csr.SignatureAlgorithm
cert.PublicKeyAlgorithm = csr.PublicKeyAlgorithm
cert.PublicKey = csr.PublicKey
emptySubject := pkix.Name{}
if reflect.DeepEqual(cert.Subject, emptySubject) {
cert.Subject = csr.Subject
}
if cert.DNSNames == nil {
cert.DNSNames = csr.DNSNames
}
if cert.EmailAddresses == nil {
cert.EmailAddresses = csr.EmailAddresses
}
if cert.IPAddresses == nil {
cert.IPAddresses = csr.IPAddresses
}
if cert.URIs == nil {
cert.URIs = csr.URIs
}
}
func getSubjectKeyID(pub crypto.PublicKey) ([]byte, error) {
encodedPub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
pubHash := sha1.Sum(encodedPub)
return pubHash[:], nil
}
func getRandomSerialNumber() (*big.Int, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
return rand.Int(rand.Reader, serialNumberLimit)
}