@@ -4,14 +4,21 @@ import (
4
4
"crypto/ecdsa"
5
5
"crypto/elliptic"
6
6
"crypto/rand"
7
+ "crypto/sha256"
7
8
"crypto/x509"
8
9
"crypto/x509/pkix"
10
+ "encoding/hex"
9
11
"encoding/pem"
10
12
"fmt"
13
+ "math"
11
14
"math/big"
12
15
"time"
13
16
)
14
17
18
+ const (
19
+ Organization = "Red Hat, Inc."
20
+ )
21
+
15
22
// KeyPair stores an x509 certificate and its ECDSA private key
16
23
type KeyPair struct {
17
24
Cert * x509.Certificate
@@ -42,19 +49,21 @@ func (kp *KeyPair) ToPEM() (certPEM []byte, privPEM []byte, err error) {
42
49
}
43
50
44
51
// GenerateCA generates a self-signed CA cert/key pair that expires in expiresIn days
45
- func GenerateCA (expiresIn int ) (* KeyPair , time.Time , error ) {
46
- if expiresIn > 730 || expiresIn <= 0 {
47
- return nil , time.Time {}, fmt .Errorf ("invalid cert expiration" )
52
+ func GenerateCA (notAfter time.Time ) (* KeyPair , error ) {
53
+ notBefore := time .Now ()
54
+ if notAfter .Before (notBefore ) {
55
+ return nil , fmt .Errorf ("invalid notAfter: %s before %s" , notAfter .String (), notBefore .String ())
48
56
}
49
57
50
- notBefore := time .Now ()
51
- notAfter := notBefore .AddDate (0 , 0 , expiresIn )
58
+ serial , err := rand .Int (rand .Reader , new (big.Int ).SetInt64 (math .MaxInt64 ))
59
+ if err != nil {
60
+ return nil , err
61
+ }
52
62
53
63
caDetails := & x509.Certificate {
54
- //TODO(Nick): figure out what to use for a SerialNumber
55
- SerialNumber : big .NewInt (1653 ),
64
+ SerialNumber : serial ,
56
65
Subject : pkix.Name {
57
- Organization : []string {"Red Hat, Inc." },
66
+ Organization : []string {Organization },
58
67
},
59
68
NotBefore : notBefore ,
60
69
NotAfter : notAfter ,
@@ -66,43 +75,47 @@ func GenerateCA(expiresIn int) (*KeyPair, time.Time, error) {
66
75
67
76
privateKey , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
68
77
if err != nil {
69
- return nil , time. Time {}, err
78
+ return nil , err
70
79
}
71
80
72
81
publicKey := & privateKey .PublicKey
73
82
certRaw , err := x509 .CreateCertificate (rand .Reader , caDetails , caDetails , publicKey , privateKey )
74
83
if err != nil {
75
- return nil , time. Time {}, err
84
+ return nil , err
76
85
}
77
86
78
87
cert , err := x509 .ParseCertificate (certRaw )
79
88
if err != nil {
80
- return nil , time. Time {}, err
89
+ return nil , err
81
90
}
82
91
83
92
ca := & KeyPair {
84
93
Cert : cert ,
85
94
Priv : privateKey ,
86
95
}
87
96
88
- return ca , notAfter , nil
97
+ return ca , nil
89
98
}
90
99
91
100
// CreateSignedServingPair creates a serving cert/key pair signed by the given ca
92
- func CreateSignedServingPair (expiresIn int , ca * KeyPair , hosts []string ) (* KeyPair , error ) {
93
- if expiresIn > 730 || expiresIn <= 0 {
94
- return nil , fmt .Errorf ("invalid cert expiration" )
101
+ func CreateSignedServingPair (notAfter time.Time , ca * KeyPair , hosts []string ) (* KeyPair , error ) {
102
+ notBefore := time .Now ()
103
+ if notAfter .Before (notBefore ) {
104
+ return nil , fmt .Errorf ("invalid notAfter: %s before %s" , notAfter .String (), notBefore .String ())
105
+ }
106
+
107
+ serial , err := rand .Int (rand .Reader , new (big.Int ).SetInt64 (math .MaxInt64 ))
108
+ if err != nil {
109
+ return nil , err
95
110
}
96
111
97
112
certDetails := & x509.Certificate {
98
- //TODO(Nick): figure out what to use for a SerialNumber
99
- SerialNumber : big .NewInt (1653 ),
113
+ SerialNumber : serial ,
100
114
Subject : pkix.Name {
101
- Organization : []string {"Red Hat, Inc." },
115
+ Organization : []string {Organization },
102
116
},
103
- NotBefore : time .Now (),
104
- // Valid for 2 years
105
- NotAfter : time .Now ().AddDate (0 , 0 , expiresIn ),
117
+ NotBefore : notBefore ,
118
+ NotAfter : notAfter ,
106
119
ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth , x509 .ExtKeyUsageServerAuth },
107
120
KeyUsage : x509 .KeyUsageDigitalSignature | x509 .KeyUsageCertSign ,
108
121
BasicConstraintsValid : true ,
@@ -132,3 +145,51 @@ func CreateSignedServingPair(expiresIn int, ca *KeyPair, hosts []string) (*KeyPa
132
145
133
146
return servingCert , nil
134
147
}
148
+
149
+ // PEMToCert converts the PEM block of the given byte array to an x509 certificate
150
+ func PEMToCert (certPEM []byte ) (* x509.Certificate , error ) {
151
+ block , _ := pem .Decode (certPEM )
152
+ if block == nil {
153
+ return nil , fmt .Errorf ("cert PEM empty" )
154
+ }
155
+
156
+ cert , err := x509 .ParseCertificate (block .Bytes )
157
+ if err != nil {
158
+ return nil , err
159
+ }
160
+
161
+ return cert , nil
162
+ }
163
+
164
+ // VerifyCert checks that the given cert is signed and trusted by the given CA
165
+ func VerifyCert (ca , cert * x509.Certificate , host string ) error {
166
+ roots := x509 .NewCertPool ()
167
+ roots .AddCert (ca )
168
+
169
+ opts := x509.VerifyOptions {
170
+ DNSName : host ,
171
+ Roots : roots ,
172
+ }
173
+
174
+ if _ , err := cert .Verify (opts ); err != nil {
175
+ return err
176
+ }
177
+
178
+ return nil
179
+ }
180
+
181
+ // Active checks if the given cert is within its valid time window
182
+ func Active (cert * x509.Certificate ) bool {
183
+ now := time .Now ()
184
+ active := now .After (cert .NotBefore ) && now .Before (cert .NotAfter )
185
+ return active
186
+ }
187
+
188
+ type PEMHash func (certPEM []byte ) (hash string )
189
+
190
+ func PEMSHA256 (certPEM []byte ) (hash string ) {
191
+ hasher := sha256 .New ()
192
+ hasher .Write (certPEM )
193
+ hash = hex .EncodeToString (hasher .Sum (nil ))
194
+ return
195
+ }
0 commit comments