blob: 83c2c6254e089c371dd474f7bf2f6331787c815e [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17// Package keyutil contains utilities for managing public/private key pairs.
18package keyutil
19
20import (
21 "crypto"
22 "crypto/ecdsa"
23 "crypto/elliptic"
24 cryptorand "crypto/rand"
25 "crypto/rsa"
26 "crypto/x509"
27 "encoding/pem"
28 "fmt"
29 "io/ioutil"
30 "os"
31 "path/filepath"
32)
33
34const (
35 // ECPrivateKeyBlockType is a possible value for pem.Block.Type.
36 ECPrivateKeyBlockType = "EC PRIVATE KEY"
37 // RSAPrivateKeyBlockType is a possible value for pem.Block.Type.
38 RSAPrivateKeyBlockType = "RSA PRIVATE KEY"
39 // PrivateKeyBlockType is a possible value for pem.Block.Type.
40 PrivateKeyBlockType = "PRIVATE KEY"
41 // PublicKeyBlockType is a possible value for pem.Block.Type.
42 PublicKeyBlockType = "PUBLIC KEY"
43)
44
45// MakeEllipticPrivateKeyPEM creates an ECDSA private key
46func MakeEllipticPrivateKeyPEM() ([]byte, error) {
47 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
48 if err != nil {
49 return nil, err
50 }
51
52 derBytes, err := x509.MarshalECPrivateKey(privateKey)
53 if err != nil {
54 return nil, err
55 }
56
57 privateKeyPemBlock := &pem.Block{
58 Type: ECPrivateKeyBlockType,
59 Bytes: derBytes,
60 }
61 return pem.EncodeToMemory(privateKeyPemBlock), nil
62}
63
64// WriteKey writes the pem-encoded key data to keyPath.
65// The key file will be created with file mode 0600.
66// If the key file already exists, it will be overwritten.
67// The parent directory of the keyPath will be created as needed with file mode 0755.
68func WriteKey(keyPath string, data []byte) error {
69 if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil {
70 return err
71 }
72 return ioutil.WriteFile(keyPath, data, os.FileMode(0600))
73}
74
75// LoadOrGenerateKeyFile looks for a key in the file at the given path. If it
76// can't find one, it will generate a new key and store it there.
77func LoadOrGenerateKeyFile(keyPath string) (data []byte, wasGenerated bool, err error) {
78 loadedData, err := ioutil.ReadFile(keyPath)
79 // Call verifyKeyData to ensure the file wasn't empty/corrupt.
80 if err == nil && verifyKeyData(loadedData) {
81 return loadedData, false, err
82 }
83 if !os.IsNotExist(err) {
84 return nil, false, fmt.Errorf("error loading key from %s: %v", keyPath, err)
85 }
86
87 generatedData, err := MakeEllipticPrivateKeyPEM()
88 if err != nil {
89 return nil, false, fmt.Errorf("error generating key: %v", err)
90 }
91 if err := WriteKey(keyPath, generatedData); err != nil {
92 return nil, false, fmt.Errorf("error writing key to %s: %v", keyPath, err)
93 }
94 return generatedData, true, nil
95}
96
97// MarshalPrivateKeyToPEM converts a known private key type of RSA or ECDSA to
98// a PEM encoded block or returns an error.
99func MarshalPrivateKeyToPEM(privateKey crypto.PrivateKey) ([]byte, error) {
100 switch t := privateKey.(type) {
101 case *ecdsa.PrivateKey:
102 derBytes, err := x509.MarshalECPrivateKey(t)
103 if err != nil {
104 return nil, err
105 }
106 block := &pem.Block{
107 Type: ECPrivateKeyBlockType,
108 Bytes: derBytes,
109 }
110 return pem.EncodeToMemory(block), nil
111 case *rsa.PrivateKey:
112 block := &pem.Block{
113 Type: RSAPrivateKeyBlockType,
114 Bytes: x509.MarshalPKCS1PrivateKey(t),
115 }
116 return pem.EncodeToMemory(block), nil
117 default:
118 return nil, fmt.Errorf("private key is not a recognized type: %T", privateKey)
119 }
120}
121
122// PrivateKeyFromFile returns the private key in rsa.PrivateKey or ecdsa.PrivateKey format from a given PEM-encoded file.
123// Returns an error if the file could not be read or if the private key could not be parsed.
124func PrivateKeyFromFile(file string) (interface{}, error) {
125 data, err := ioutil.ReadFile(file)
126 if err != nil {
127 return nil, err
128 }
129 key, err := ParsePrivateKeyPEM(data)
130 if err != nil {
131 return nil, fmt.Errorf("error reading private key file %s: %v", file, err)
132 }
133 return key, nil
134}
135
136// PublicKeysFromFile returns the public keys in rsa.PublicKey or ecdsa.PublicKey format from a given PEM-encoded file.
137// Reads public keys from both public and private key files.
138func PublicKeysFromFile(file string) ([]interface{}, error) {
139 data, err := ioutil.ReadFile(file)
140 if err != nil {
141 return nil, err
142 }
143 keys, err := ParsePublicKeysPEM(data)
144 if err != nil {
145 return nil, fmt.Errorf("error reading public key file %s: %v", file, err)
146 }
147 return keys, nil
148}
149
150// verifyKeyData returns true if the provided data appears to be a valid private key.
151func verifyKeyData(data []byte) bool {
152 if len(data) == 0 {
153 return false
154 }
155 _, err := ParsePrivateKeyPEM(data)
156 return err == nil
157}
158
159// ParsePrivateKeyPEM returns a private key parsed from a PEM block in the supplied data.
160// Recognizes PEM blocks for "EC PRIVATE KEY", "RSA PRIVATE KEY", or "PRIVATE KEY"
161func ParsePrivateKeyPEM(keyData []byte) (interface{}, error) {
162 var privateKeyPemBlock *pem.Block
163 for {
164 privateKeyPemBlock, keyData = pem.Decode(keyData)
165 if privateKeyPemBlock == nil {
166 break
167 }
168
169 switch privateKeyPemBlock.Type {
170 case ECPrivateKeyBlockType:
171 // ECDSA Private Key in ASN.1 format
172 if key, err := x509.ParseECPrivateKey(privateKeyPemBlock.Bytes); err == nil {
173 return key, nil
174 }
175 case RSAPrivateKeyBlockType:
176 // RSA Private Key in PKCS#1 format
177 if key, err := x509.ParsePKCS1PrivateKey(privateKeyPemBlock.Bytes); err == nil {
178 return key, nil
179 }
180 case PrivateKeyBlockType:
181 // RSA or ECDSA Private Key in unencrypted PKCS#8 format
182 if key, err := x509.ParsePKCS8PrivateKey(privateKeyPemBlock.Bytes); err == nil {
183 return key, nil
184 }
185 }
186
187 // tolerate non-key PEM blocks for compatibility with things like "EC PARAMETERS" blocks
188 // originally, only the first PEM block was parsed and expected to be a key block
189 }
190
191 // we read all the PEM blocks and didn't recognize one
192 return nil, fmt.Errorf("data does not contain a valid RSA or ECDSA private key")
193}
194
195// ParsePublicKeysPEM is a helper function for reading an array of rsa.PublicKey or ecdsa.PublicKey from a PEM-encoded byte array.
196// Reads public keys from both public and private key files.
197func ParsePublicKeysPEM(keyData []byte) ([]interface{}, error) {
198 var block *pem.Block
199 keys := []interface{}{}
200 for {
201 // read the next block
202 block, keyData = pem.Decode(keyData)
203 if block == nil {
204 break
205 }
206
207 // test block against parsing functions
208 if privateKey, err := parseRSAPrivateKey(block.Bytes); err == nil {
209 keys = append(keys, &privateKey.PublicKey)
210 continue
211 }
212 if publicKey, err := parseRSAPublicKey(block.Bytes); err == nil {
213 keys = append(keys, publicKey)
214 continue
215 }
216 if privateKey, err := parseECPrivateKey(block.Bytes); err == nil {
217 keys = append(keys, &privateKey.PublicKey)
218 continue
219 }
220 if publicKey, err := parseECPublicKey(block.Bytes); err == nil {
221 keys = append(keys, publicKey)
222 continue
223 }
224
225 // tolerate non-key PEM blocks for backwards compatibility
226 // originally, only the first PEM block was parsed and expected to be a key block
227 }
228
229 if len(keys) == 0 {
230 return nil, fmt.Errorf("data does not contain any valid RSA or ECDSA public keys")
231 }
232 return keys, nil
233}
234
235// parseRSAPublicKey parses a single RSA public key from the provided data
236func parseRSAPublicKey(data []byte) (*rsa.PublicKey, error) {
237 var err error
238
239 // Parse the key
240 var parsedKey interface{}
241 if parsedKey, err = x509.ParsePKIXPublicKey(data); err != nil {
242 if cert, err := x509.ParseCertificate(data); err == nil {
243 parsedKey = cert.PublicKey
244 } else {
245 return nil, err
246 }
247 }
248
249 // Test if parsed key is an RSA Public Key
250 var pubKey *rsa.PublicKey
251 var ok bool
252 if pubKey, ok = parsedKey.(*rsa.PublicKey); !ok {
253 return nil, fmt.Errorf("data doesn't contain valid RSA Public Key")
254 }
255
256 return pubKey, nil
257}
258
259// parseRSAPrivateKey parses a single RSA private key from the provided data
260func parseRSAPrivateKey(data []byte) (*rsa.PrivateKey, error) {
261 var err error
262
263 // Parse the key
264 var parsedKey interface{}
265 if parsedKey, err = x509.ParsePKCS1PrivateKey(data); err != nil {
266 if parsedKey, err = x509.ParsePKCS8PrivateKey(data); err != nil {
267 return nil, err
268 }
269 }
270
271 // Test if parsed key is an RSA Private Key
272 var privKey *rsa.PrivateKey
273 var ok bool
274 if privKey, ok = parsedKey.(*rsa.PrivateKey); !ok {
275 return nil, fmt.Errorf("data doesn't contain valid RSA Private Key")
276 }
277
278 return privKey, nil
279}
280
281// parseECPublicKey parses a single ECDSA public key from the provided data
282func parseECPublicKey(data []byte) (*ecdsa.PublicKey, error) {
283 var err error
284
285 // Parse the key
286 var parsedKey interface{}
287 if parsedKey, err = x509.ParsePKIXPublicKey(data); err != nil {
288 if cert, err := x509.ParseCertificate(data); err == nil {
289 parsedKey = cert.PublicKey
290 } else {
291 return nil, err
292 }
293 }
294
295 // Test if parsed key is an ECDSA Public Key
296 var pubKey *ecdsa.PublicKey
297 var ok bool
298 if pubKey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
299 return nil, fmt.Errorf("data doesn't contain valid ECDSA Public Key")
300 }
301
302 return pubKey, nil
303}
304
305// parseECPrivateKey parses a single ECDSA private key from the provided data
306func parseECPrivateKey(data []byte) (*ecdsa.PrivateKey, error) {
307 var err error
308
309 // Parse the key
310 var parsedKey interface{}
311 if parsedKey, err = x509.ParseECPrivateKey(data); err != nil {
312 return nil, err
313 }
314
315 // Test if parsed key is an ECDSA Private Key
316 var privKey *ecdsa.PrivateKey
317 var ok bool
318 if privKey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
319 return nil, fmt.Errorf("data doesn't contain valid ECDSA Private Key")
320 }
321
322 return privKey, nil
323}