| /* |
| Copyright 2018 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| // Package keyutil contains utilities for managing public/private key pairs. |
| package keyutil |
| |
| import ( |
| "crypto" |
| "crypto/ecdsa" |
| "crypto/elliptic" |
| cryptorand "crypto/rand" |
| "crypto/rsa" |
| "crypto/x509" |
| "encoding/pem" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| ) |
| |
| const ( |
| // ECPrivateKeyBlockType is a possible value for pem.Block.Type. |
| ECPrivateKeyBlockType = "EC PRIVATE KEY" |
| // RSAPrivateKeyBlockType is a possible value for pem.Block.Type. |
| RSAPrivateKeyBlockType = "RSA PRIVATE KEY" |
| // PrivateKeyBlockType is a possible value for pem.Block.Type. |
| PrivateKeyBlockType = "PRIVATE KEY" |
| // PublicKeyBlockType is a possible value for pem.Block.Type. |
| PublicKeyBlockType = "PUBLIC KEY" |
| ) |
| |
| // MakeEllipticPrivateKeyPEM creates an ECDSA private key |
| func MakeEllipticPrivateKeyPEM() ([]byte, error) { |
| privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader) |
| if err != nil { |
| return nil, err |
| } |
| |
| derBytes, err := x509.MarshalECPrivateKey(privateKey) |
| if err != nil { |
| return nil, err |
| } |
| |
| privateKeyPemBlock := &pem.Block{ |
| Type: ECPrivateKeyBlockType, |
| Bytes: derBytes, |
| } |
| return pem.EncodeToMemory(privateKeyPemBlock), nil |
| } |
| |
| // WriteKey writes the pem-encoded key data to keyPath. |
| // The key file will be created with file mode 0600. |
| // If the key file already exists, it will be overwritten. |
| // The parent directory of the keyPath will be created as needed with file mode 0755. |
| func WriteKey(keyPath string, data []byte) error { |
| if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil { |
| return err |
| } |
| return ioutil.WriteFile(keyPath, data, os.FileMode(0600)) |
| } |
| |
| // LoadOrGenerateKeyFile looks for a key in the file at the given path. If it |
| // can't find one, it will generate a new key and store it there. |
| func LoadOrGenerateKeyFile(keyPath string) (data []byte, wasGenerated bool, err error) { |
| loadedData, err := ioutil.ReadFile(keyPath) |
| // Call verifyKeyData to ensure the file wasn't empty/corrupt. |
| if err == nil && verifyKeyData(loadedData) { |
| return loadedData, false, err |
| } |
| if !os.IsNotExist(err) { |
| return nil, false, fmt.Errorf("error loading key from %s: %v", keyPath, err) |
| } |
| |
| generatedData, err := MakeEllipticPrivateKeyPEM() |
| if err != nil { |
| return nil, false, fmt.Errorf("error generating key: %v", err) |
| } |
| if err := WriteKey(keyPath, generatedData); err != nil { |
| return nil, false, fmt.Errorf("error writing key to %s: %v", keyPath, err) |
| } |
| return generatedData, true, nil |
| } |
| |
| // MarshalPrivateKeyToPEM converts a known private key type of RSA or ECDSA to |
| // a PEM encoded block or returns an error. |
| func MarshalPrivateKeyToPEM(privateKey crypto.PrivateKey) ([]byte, error) { |
| switch t := privateKey.(type) { |
| case *ecdsa.PrivateKey: |
| derBytes, err := x509.MarshalECPrivateKey(t) |
| if err != nil { |
| return nil, err |
| } |
| block := &pem.Block{ |
| Type: ECPrivateKeyBlockType, |
| Bytes: derBytes, |
| } |
| return pem.EncodeToMemory(block), nil |
| case *rsa.PrivateKey: |
| block := &pem.Block{ |
| Type: RSAPrivateKeyBlockType, |
| Bytes: x509.MarshalPKCS1PrivateKey(t), |
| } |
| return pem.EncodeToMemory(block), nil |
| default: |
| return nil, fmt.Errorf("private key is not a recognized type: %T", privateKey) |
| } |
| } |
| |
| // PrivateKeyFromFile returns the private key in rsa.PrivateKey or ecdsa.PrivateKey format from a given PEM-encoded file. |
| // Returns an error if the file could not be read or if the private key could not be parsed. |
| func PrivateKeyFromFile(file string) (interface{}, error) { |
| data, err := ioutil.ReadFile(file) |
| if err != nil { |
| return nil, err |
| } |
| key, err := ParsePrivateKeyPEM(data) |
| if err != nil { |
| return nil, fmt.Errorf("error reading private key file %s: %v", file, err) |
| } |
| return key, nil |
| } |
| |
| // PublicKeysFromFile returns the public keys in rsa.PublicKey or ecdsa.PublicKey format from a given PEM-encoded file. |
| // Reads public keys from both public and private key files. |
| func PublicKeysFromFile(file string) ([]interface{}, error) { |
| data, err := ioutil.ReadFile(file) |
| if err != nil { |
| return nil, err |
| } |
| keys, err := ParsePublicKeysPEM(data) |
| if err != nil { |
| return nil, fmt.Errorf("error reading public key file %s: %v", file, err) |
| } |
| return keys, nil |
| } |
| |
| // verifyKeyData returns true if the provided data appears to be a valid private key. |
| func verifyKeyData(data []byte) bool { |
| if len(data) == 0 { |
| return false |
| } |
| _, err := ParsePrivateKeyPEM(data) |
| return err == nil |
| } |
| |
| // ParsePrivateKeyPEM returns a private key parsed from a PEM block in the supplied data. |
| // Recognizes PEM blocks for "EC PRIVATE KEY", "RSA PRIVATE KEY", or "PRIVATE KEY" |
| func ParsePrivateKeyPEM(keyData []byte) (interface{}, error) { |
| var privateKeyPemBlock *pem.Block |
| for { |
| privateKeyPemBlock, keyData = pem.Decode(keyData) |
| if privateKeyPemBlock == nil { |
| break |
| } |
| |
| switch privateKeyPemBlock.Type { |
| case ECPrivateKeyBlockType: |
| // ECDSA Private Key in ASN.1 format |
| if key, err := x509.ParseECPrivateKey(privateKeyPemBlock.Bytes); err == nil { |
| return key, nil |
| } |
| case RSAPrivateKeyBlockType: |
| // RSA Private Key in PKCS#1 format |
| if key, err := x509.ParsePKCS1PrivateKey(privateKeyPemBlock.Bytes); err == nil { |
| return key, nil |
| } |
| case PrivateKeyBlockType: |
| // RSA or ECDSA Private Key in unencrypted PKCS#8 format |
| if key, err := x509.ParsePKCS8PrivateKey(privateKeyPemBlock.Bytes); err == nil { |
| return key, nil |
| } |
| } |
| |
| // tolerate non-key PEM blocks for compatibility with things like "EC PARAMETERS" blocks |
| // originally, only the first PEM block was parsed and expected to be a key block |
| } |
| |
| // we read all the PEM blocks and didn't recognize one |
| return nil, fmt.Errorf("data does not contain a valid RSA or ECDSA private key") |
| } |
| |
| // ParsePublicKeysPEM is a helper function for reading an array of rsa.PublicKey or ecdsa.PublicKey from a PEM-encoded byte array. |
| // Reads public keys from both public and private key files. |
| func ParsePublicKeysPEM(keyData []byte) ([]interface{}, error) { |
| var block *pem.Block |
| keys := []interface{}{} |
| for { |
| // read the next block |
| block, keyData = pem.Decode(keyData) |
| if block == nil { |
| break |
| } |
| |
| // test block against parsing functions |
| if privateKey, err := parseRSAPrivateKey(block.Bytes); err == nil { |
| keys = append(keys, &privateKey.PublicKey) |
| continue |
| } |
| if publicKey, err := parseRSAPublicKey(block.Bytes); err == nil { |
| keys = append(keys, publicKey) |
| continue |
| } |
| if privateKey, err := parseECPrivateKey(block.Bytes); err == nil { |
| keys = append(keys, &privateKey.PublicKey) |
| continue |
| } |
| if publicKey, err := parseECPublicKey(block.Bytes); err == nil { |
| keys = append(keys, publicKey) |
| continue |
| } |
| |
| // tolerate non-key PEM blocks for backwards compatibility |
| // originally, only the first PEM block was parsed and expected to be a key block |
| } |
| |
| if len(keys) == 0 { |
| return nil, fmt.Errorf("data does not contain any valid RSA or ECDSA public keys") |
| } |
| return keys, nil |
| } |
| |
| // parseRSAPublicKey parses a single RSA public key from the provided data |
| func parseRSAPublicKey(data []byte) (*rsa.PublicKey, error) { |
| var err error |
| |
| // Parse the key |
| var parsedKey interface{} |
| if parsedKey, err = x509.ParsePKIXPublicKey(data); err != nil { |
| if cert, err := x509.ParseCertificate(data); err == nil { |
| parsedKey = cert.PublicKey |
| } else { |
| return nil, err |
| } |
| } |
| |
| // Test if parsed key is an RSA Public Key |
| var pubKey *rsa.PublicKey |
| var ok bool |
| if pubKey, ok = parsedKey.(*rsa.PublicKey); !ok { |
| return nil, fmt.Errorf("data doesn't contain valid RSA Public Key") |
| } |
| |
| return pubKey, nil |
| } |
| |
| // parseRSAPrivateKey parses a single RSA private key from the provided data |
| func parseRSAPrivateKey(data []byte) (*rsa.PrivateKey, error) { |
| var err error |
| |
| // Parse the key |
| var parsedKey interface{} |
| if parsedKey, err = x509.ParsePKCS1PrivateKey(data); err != nil { |
| if parsedKey, err = x509.ParsePKCS8PrivateKey(data); err != nil { |
| return nil, err |
| } |
| } |
| |
| // Test if parsed key is an RSA Private Key |
| var privKey *rsa.PrivateKey |
| var ok bool |
| if privKey, ok = parsedKey.(*rsa.PrivateKey); !ok { |
| return nil, fmt.Errorf("data doesn't contain valid RSA Private Key") |
| } |
| |
| return privKey, nil |
| } |
| |
| // parseECPublicKey parses a single ECDSA public key from the provided data |
| func parseECPublicKey(data []byte) (*ecdsa.PublicKey, error) { |
| var err error |
| |
| // Parse the key |
| var parsedKey interface{} |
| if parsedKey, err = x509.ParsePKIXPublicKey(data); err != nil { |
| if cert, err := x509.ParseCertificate(data); err == nil { |
| parsedKey = cert.PublicKey |
| } else { |
| return nil, err |
| } |
| } |
| |
| // Test if parsed key is an ECDSA Public Key |
| var pubKey *ecdsa.PublicKey |
| var ok bool |
| if pubKey, ok = parsedKey.(*ecdsa.PublicKey); !ok { |
| return nil, fmt.Errorf("data doesn't contain valid ECDSA Public Key") |
| } |
| |
| return pubKey, nil |
| } |
| |
| // parseECPrivateKey parses a single ECDSA private key from the provided data |
| func parseECPrivateKey(data []byte) (*ecdsa.PrivateKey, error) { |
| var err error |
| |
| // Parse the key |
| var parsedKey interface{} |
| if parsedKey, err = x509.ParseECPrivateKey(data); err != nil { |
| return nil, err |
| } |
| |
| // Test if parsed key is an ECDSA Private Key |
| var privKey *ecdsa.PrivateKey |
| var ok bool |
| if privKey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { |
| return nil, fmt.Errorf("data doesn't contain valid ECDSA Private Key") |
| } |
| |
| return privKey, nil |
| } |