diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 56b94aacd4a..7fed1789165 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -464,7 +464,7 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController glog.Warningf("%q is disabled because there is no private key", saTokenControllerName) return false, nil } - privateKey, err := serviceaccount.ReadPrivateKey(ctx.Options.ServiceAccountKeyFile) + privateKey, err := certutil.PrivateKeyFromFile(ctx.Options.ServiceAccountKeyFile) if err != nil { return true, fmt.Errorf("error reading key for service account token controller: %v", err) } diff --git a/pkg/kubeapiserver/authenticator/config.go b/pkg/kubeapiserver/authenticator/config.go index b5cadfde243..418b616855c 100644 --- a/pkg/kubeapiserver/authenticator/config.go +++ b/pkg/kubeapiserver/authenticator/config.go @@ -212,7 +212,7 @@ func (config AuthenticatorConfig) New() (authenticator.Request, *spec.SecurityDe // IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file func IsValidServiceAccountKeyFile(file string) bool { - _, err := serviceaccount.ReadPublicKeys(file) + _, err := certutil.PublicKeysFromFile(file) return err == nil } @@ -256,7 +256,7 @@ func newAuthenticatorFromOIDCIssuerURL(issuerURL, clientID, caFile, usernameClai func newServiceAccountAuthenticator(keyfiles []string, lookup bool, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Token, error) { allPublicKeys := []interface{}{} for _, keyfile := range keyfiles { - publicKeys, err := serviceaccount.ReadPublicKeys(keyfile) + publicKeys, err := certutil.PublicKeysFromFile(keyfile) if err != nil { return nil, err } diff --git a/pkg/serviceaccount/BUILD b/pkg/serviceaccount/BUILD index 0bd8920d08d..c0babddda4c 100644 --- a/pkg/serviceaccount/BUILD +++ b/pkg/serviceaccount/BUILD @@ -23,7 +23,6 @@ go_library( "//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library", "//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library", "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//vendor/k8s.io/client-go/util/cert:go_default_library", ], ) diff --git a/pkg/serviceaccount/jwt.go b/pkg/serviceaccount/jwt.go index 83efe5be0aa..a7051be3077 100644 --- a/pkg/serviceaccount/jwt.go +++ b/pkg/serviceaccount/jwt.go @@ -21,16 +21,13 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" - "encoding/pem" "errors" "fmt" - "io/ioutil" "k8s.io/api/core/v1" "k8s.io/apiserver/pkg/authentication/authenticator" apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount" "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/client-go/util/cert" jwt "github.com/dgrijalva/jwt-go" "github.com/golang/glog" @@ -59,75 +56,6 @@ type TokenGenerator interface { GenerateToken(serviceAccount v1.ServiceAccount, secret v1.Secret) (string, error) } -// ReadPrivateKey is a helper function for reading a private key from a PEM-encoded file -func ReadPrivateKey(file string) (interface{}, error) { - data, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - key, err := cert.ParsePrivateKeyPEM(data) - if err != nil { - return nil, fmt.Errorf("error reading private key file %s: %v", file, err) - } - return key, nil -} - -// ReadPublicKeys is a helper function for reading an array of rsa.PublicKey or ecdsa.PublicKey from a PEM-encoded file. -// Reads public keys from both public and private key files. -func ReadPublicKeys(file string) ([]interface{}, error) { - data, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - keys, err := ReadPublicKeysFromPEM(data) - if err != nil { - return nil, fmt.Errorf("error reading public key file %s: %v", file, err) - } - return keys, nil -} - -// ReadPublicKeysFromPEM 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 ReadPublicKeysFromPEM(data []byte) ([]interface{}, error) { - var block *pem.Block - keys := []interface{}{} - for { - // read the next block - block, data = pem.Decode(data) - if block == nil { - break - } - - // get PEM bytes for just this block - blockData := pem.EncodeToMemory(block) - if privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(blockData); err == nil { - keys = append(keys, &privateKey.PublicKey) - continue - } - if publicKey, err := jwt.ParseRSAPublicKeyFromPEM(blockData); err == nil { - keys = append(keys, publicKey) - continue - } - - if privateKey, err := jwt.ParseECPrivateKeyFromPEM(blockData); err == nil { - keys = append(keys, &privateKey.PublicKey) - continue - } - if publicKey, err := jwt.ParseECPublicKeyFromPEM(blockData); 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 a valid RSA or ECDSA key") - } - return keys, nil -} - // JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey. // privateKey is a PEM-encoded byte array of a private RSA key. // JWTTokenAuthenticator() diff --git a/pkg/serviceaccount/jwt_test.go b/pkg/serviceaccount/jwt_test.go index a69f989b7b5..076f4ade98a 100644 --- a/pkg/serviceaccount/jwt_test.go +++ b/pkg/serviceaccount/jwt_test.go @@ -17,8 +17,6 @@ limitations under the License. package serviceaccount_test import ( - "io/ioutil" - "os" "reflect" "testing" @@ -27,7 +25,7 @@ import ( apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/util/cert" + certutil "k8s.io/client-go/util/cert" serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount" ) @@ -107,87 +105,14 @@ X2i8uIp/C/ASqiIGUeeKQtX0/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg== -----END PUBLIC KEY-----` func getPrivateKey(data string) interface{} { - key, _ := cert.ParsePrivateKeyPEM([]byte(data)) + key, _ := certutil.ParsePrivateKeyPEM([]byte(data)) return key } func getPublicKey(data string) interface{} { - keys, _ := serviceaccount.ReadPublicKeysFromPEM([]byte(data)) + keys, _ := certutil.ParsePublicKeysPEM([]byte(data)) return keys[0] } -func TestReadPrivateKey(t *testing.T) { - f, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("error creating tmpfile: %v", err) - } - defer os.Remove(f.Name()) - - if _, err := serviceaccount.ReadPrivateKey(f.Name()); err == nil { - t.Fatalf("Expected error reading key from empty file, got none") - } - - if err := ioutil.WriteFile(f.Name(), []byte(rsaPrivateKey), os.FileMode(0600)); err != nil { - t.Fatalf("error writing private key to tmpfile: %v", err) - } - if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil { - t.Fatalf("error reading private RSA key: %v", err) - } - - if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKey), os.FileMode(0600)); err != nil { - t.Fatalf("error writing private key to tmpfile: %v", err) - } - if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil { - t.Fatalf("error reading private ECDSA key: %v", err) - } - - if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKeyWithParams), os.FileMode(0600)); err != nil { - t.Fatalf("error writing private key to tmpfile: %v", err) - } - if _, err := serviceaccount.ReadPrivateKey(f.Name()); err != nil { - t.Fatalf("error reading private ECDSA key with params: %v", err) - } -} - -func TestReadPublicKeys(t *testing.T) { - f, err := ioutil.TempFile("", "") - if err != nil { - t.Fatalf("error creating tmpfile: %v", err) - } - defer os.Remove(f.Name()) - - if _, err := serviceaccount.ReadPublicKeys(f.Name()); err == nil { - t.Fatalf("Expected error reading keys from empty file, got none") - } - - if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil { - t.Fatalf("error writing public key to tmpfile: %v", err) - } - if keys, err := serviceaccount.ReadPublicKeys(f.Name()); err != nil { - t.Fatalf("error reading RSA public key: %v", err) - } else if len(keys) != 1 { - t.Fatalf("expected 1 key, got %d", len(keys)) - } - - if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil { - t.Fatalf("error writing public key to tmpfile: %v", err) - } - if keys, err := serviceaccount.ReadPublicKeys(f.Name()); err != nil { - t.Fatalf("error reading ECDSA public key: %v", err) - } else if len(keys) != 1 { - t.Fatalf("expected 1 key, got %d", len(keys)) - } - - if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey+"\n"+ecdsaPublicKey), os.FileMode(0600)); err != nil { - t.Fatalf("error writing public key to tmpfile: %v", err) - } - if keys, err := serviceaccount.ReadPublicKeys(f.Name()); err != nil { - t.Fatalf("error reading combined RSA/ECDSA public key file: %v", err) - } else if len(keys) != 2 { - t.Fatalf("expected 2 keys, got %d", len(keys)) - } - -} - func TestTokenGenerateAndValidate(t *testing.T) { expectedUserName := "system:serviceaccount:test:my-service-account" expectedUserUID := "12345" diff --git a/staging/src/k8s.io/client-go/util/cert/BUILD b/staging/src/k8s.io/client-go/util/cert/BUILD index 2ae5eed7dfe..3b4b1ef6f7d 100644 --- a/staging/src/k8s.io/client-go/util/cert/BUILD +++ b/staging/src/k8s.io/client-go/util/cert/BUILD @@ -10,7 +10,10 @@ load( go_test( name = "go_default_test", - srcs = ["csr_test.go"], + srcs = [ + "csr_test.go", + "pem_test.go", + ], library = ":go_default_library", tags = ["automanaged"], ) diff --git a/staging/src/k8s.io/client-go/util/cert/io.go b/staging/src/k8s.io/client-go/util/cert/io.go index b6b69038358..487456b69c0 100644 --- a/staging/src/k8s.io/client-go/util/cert/io.go +++ b/staging/src/k8s.io/client-go/util/cert/io.go @@ -138,13 +138,27 @@ func CertsFromFile(file string) ([]*x509.Certificate, error) { // 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) { - pemBlock, err := ioutil.ReadFile(file) + data, err := ioutil.ReadFile(file) if err != nil { return nil, err } - key, err := ParsePrivateKeyPEM(pemBlock) + key, err := ParsePrivateKeyPEM(data) if err != nil { - return nil, fmt.Errorf("error reading %s: %v", file, err) + 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 +} diff --git a/staging/src/k8s.io/client-go/util/cert/pem.go b/staging/src/k8s.io/client-go/util/cert/pem.go index 899845857c5..b99e366519c 100644 --- a/staging/src/k8s.io/client-go/util/cert/pem.go +++ b/staging/src/k8s.io/client-go/util/cert/pem.go @@ -17,6 +17,7 @@ limitations under the License. package cert import ( + "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/pem" @@ -29,17 +30,17 @@ const ( ECPrivateKeyBlockType = "EC PRIVATE KEY" // RSAPrivateKeyBlockType is a possible value for pem.Block.Type. RSAPrivateKeyBlockType = "RSA PRIVATE KEY" - // CertificateBlockType is a possible value for pem.Block.Type. - CertificateBlockType = "CERTIFICATE" - // CertificateRequestBlockType is a possible value for pem.Block.Type. - CertificateRequestBlockType = "CERTIFICATE REQUEST" // PrivateKeyBlockType is a possible value for pem.Block.Type. PrivateKeyBlockType = "PRIVATE KEY" // PublicKeyBlockType is a possible value for pem.Block.Type. PublicKeyBlockType = "PUBLIC KEY" + // CertificateBlockType is a possible value for pem.Block.Type. + CertificateBlockType = "CERTIFICATE" + // CertificateRequestBlockType is a possible value for pem.Block.Type. + CertificateRequestBlockType = "CERTIFICATE REQUEST" ) -// EncodePublicKeyPEM returns PEM-endcode public data +// EncodePublicKeyPEM returns PEM-encoded public data func EncodePublicKeyPEM(key *rsa.PublicKey) ([]byte, error) { der, err := x509.MarshalPKIXPublicKey(key) if err != nil { @@ -106,6 +107,46 @@ func ParsePrivateKeyPEM(keyData []byte) (interface{}, error) { 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 +} + // ParseCertsPEM returns the x509.Certificates contained in the given PEM-encoded byte array // Returns an error if a certificate could not be parsed, or if the data does not contain any certificates func ParseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) { @@ -132,7 +173,97 @@ func ParseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) { } if !ok { - return certs, errors.New("could not read any certificates") + return certs, errors.New("data does not contain any valid RSA or ECDSA certificates") } return certs, 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 +} diff --git a/staging/src/k8s.io/client-go/util/cert/pem_test.go b/staging/src/k8s.io/client-go/util/cert/pem_test.go new file mode 100644 index 00000000000..de3ce52537f --- /dev/null +++ b/staging/src/k8s.io/client-go/util/cert/pem_test.go @@ -0,0 +1,197 @@ +/* +Copyright 2014 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 cert + +import ( + "io/ioutil" + "os" + "testing" +) + +const ( + // rsaPrivateKey is a RSA Private Key in PKCS#1 format + // openssl genrsa -out rsa2048.pem 2048 + rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA92mVjhBKOFsdxFzb/Pjq+7b5TJlODAdY5hK+WxLZTIrfhDPq +FWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy1224RgkyNdMJsXhJKuCC24ZKY8SXtW +xuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv1VqY3amZIWFQMlZ9CNpxDSPa5yi4 +3gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4rZ30bcfC2ag6RLOFI2E/c4n8c38R8 +9MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVUuIrBQN+Y7tkN2T60Qq/TkKXUrhDe +fwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831rmwIDAQABAoIBAQCC9c6GDjVbM0/E +WurPMusfJjE7zII1d8YkspM0HfwLug6qKdikUYpnKC/NG4rEzfl/bbFwco/lgc6O +7W/hh2U8uQttlvCDA/Uk5YddKOZL0Hpk4vaB/SxxYK3luSKXpjY2knutGg2KdVCN +qdsFkkH4iyYTXuyBcMNEgedZQldI/kEujIH/L7FE+DF5TMzT4lHhozDoG+fy564q +qVGUZXJn0ubc3GaPn2QOLNNM44sfYA4UJCpKBXPu85bvNObjxVQO4WqwwxU1vRnL +UUsaGaelhSVJCo0dVPRvrfPPKZ09HTwpy40EkgQo6VriFc1EBoQDjENLbAJv9OfQ +aCc9wiZhAoGBAP/8oEy48Zbb0P8Vdy4djf5tfBW8yXFLWzXewJ4l3itKS1r42nbX +9q3cJsgRTQm8uRcMIpWxsc3n6zG+lREvTkoTB3ViI7+uQPiqA+BtWyNy7jzufFke +ONKZfg7QxxmYRWZBRnoNGNbMpNeERuLmhvQuom9D1WbhzAYJbfs/O4WTAoGBAPds +2FNDU0gaesFDdkIUGq1nIJqRQDW485LXZm4pFqBFxdOpbdWRuYT2XZjd3fD0XY98 +Nhkpb7NTMCuK3BdKcqIptt+cK+quQgYid0hhhgZbpCQ5AL6c6KgyjgpYlh2enzU9 +Zo3yg8ej1zbbA11sBlhX+5iO2P1u5DG+JHLwUUbZAoGAUwaU102EzfEtsA4+QW7E +hyjrfgFlNKHES4yb3K9bh57pIfBkqvcQwwMMcQdrfSUAw0DkVrjzel0mI1Q09QXq +1ould6UFAz55RC2gZEITtUOpkYmoOx9aPrQZ9qQwb1S77ZZuTVfCHqjxLhVxCFbM +npYhiQTvShciHTMhwMOZgpECgYAVV5EtVXBYltgh1YTc3EkUzgF087R7LdHsx6Gx +POATwRD4WfP8aQ58lpeqOPEM+LcdSlSMRRO6fyF3kAm+BJDwxfJdRWZQXumZB94M +I0VhRQRaj4Qt7PDwmTPBVrTUJzuKZxpyggm17b8Bn1Ch/VBqzGQKW8AB1E/grosM +UwhfuQKBgQC2JO/iqTQScHClf0qlItCJsBuVukFmSAVCkpOD8YdbdlPdOOwSk1wQ +C0eAlsC3BCMvkpidKQmra6IqIrvTGI6EFgkrb3aknWdup2w8j2udYCNqyE3W+fVe +p8FdYQ1FkACQ+daO5VlClL/9l0sGjKXlNKbpmJ2H4ngZmXj5uGmxuQ== +-----END RSA PRIVATE KEY-----` + + // rsaPublicKey is a RSA Public Key in PEM encoded format + // openssl rsa -in rsa2048.pem -pubout -out rsa2048pub.pem + rsaPublicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA92mVjhBKOFsdxFzb/Pjq ++7b5TJlODAdY5hK+WxLZTIrfhDPqFWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy12 +24RgkyNdMJsXhJKuCC24ZKY8SXtWxuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv +1VqY3amZIWFQMlZ9CNpxDSPa5yi43gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4r +Z30bcfC2ag6RLOFI2E/c4n8c38R89MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVU +uIrBQN+Y7tkN2T60Qq/TkKXUrhDefwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831r +mwIDAQAB +-----END PUBLIC KEY-----` + + // certificate is an x509 certificate in PEM encoded format + // openssl req -new -key rsa2048.pem -sha256 -nodes -x509 -days 1826 -out x509certificate.pem -subj "/C=US/CN=not-valid" + certificate = `-----BEGIN CERTIFICATE----- +MIIDFTCCAf2gAwIBAgIJAN8B8NOwtiUCMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV +BAYTAlVTMRIwEAYDVQQDDAlub3QtdmFsaWQwHhcNMTcwMzIyMDI1NjM2WhcNMjIw +MzIyMDI1NjM2WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJbm90LXZhbGlkMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA92mVjhBKOFsdxFzb/Pjq+7b5 +TJlODAdY5hK+WxLZTIrfhDPqFWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy1224Rg +kyNdMJsXhJKuCC24ZKY8SXtWxuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv1VqY +3amZIWFQMlZ9CNpxDSPa5yi43gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4rZ30b +cfC2ag6RLOFI2E/c4n8c38R89MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVUuIrB +QN+Y7tkN2T60Qq/TkKXUrhDefwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831rmwID +AQABo1AwTjAdBgNVHQ4EFgQU1I5GfinLF7ta+dBJ6UWcrYaexLswHwYDVR0jBBgw +FoAU1I5GfinLF7ta+dBJ6UWcrYaexLswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B +AQsFAAOCAQEAUl0wUD4y41juHFOVMYiziPYr1ShSpQXdwp8FfaHrzI5hsr8UMe8D +dzb9QzZ4bx3yZhiG3ahrSBh956thMTHrKTEwAfJIEXI4cuSVWQAaOJ4Em5SDFxQe +d0E6Ui2nGh1SFGF7oyuEXyzqgRMWFNDFw9HLUNgXaO18Zfouw8+K0BgbfEWEcSi1 +JLQbyhCjz088gltrliQGPWDFAg9cHBKtJhuTzZkvuqK1CLEmBhtzP1zFiGBfOJc8 +v+aKjAwrPUNX11cXOCPxBv2qXMetxaovBem6AI2hvypCInXaVQfP+yOLubzlTDjS +Y708SlY38hmS1uTwDpyLOn8AKkZ8jtx75g== +-----END CERTIFICATE-----` + + // ecdsaPrivateKeyWithParams is a ECDSA Private Key with included EC Parameters block + // openssl ecparam -name prime256v1 -genkey -out ecdsa256params.pem + ecdsaPrivateKeyWithParams = `-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAwSOWQqlMTZNqNF7tgua812Jxib1DVOgb2pHHyIEyNNoAoGCCqGSM49 +AwEHoUQDQgAEyxYNrs6a6tsNCFNYn+l+JDUZ0PnUZbcsDgJn2O62D1se8M5iQ5rY +iIv6RpxE3VHvlHEIvYgCZkG0jHszTUopBg== +-----END EC PRIVATE KEY-----` + + // ecdsaPrivateKey is a ECDSA Private Key in ASN.1 format + // openssl ecparam -name prime256v1 -genkey -noout -out ecdsa256.pem + ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIP6Qw6dHDiLsSnLXUhQVTPE0fTQQrj3XSbiQAZPXnk5+oAoGCCqGSM49 +AwEHoUQDQgAEZZzi1u5f2/AEGFI/HYUhU+u6cTK1q2bbtE7r1JMK+/sQA5sNAp+7 +Vdc3psr1OaNzyTyuhTECyRdFKXm63cMnGg== +-----END EC PRIVATE KEY-----` + + // ecdsaPublicKey is a ECDSA Public Key in PEM encoded format + // openssl ec -in ecdsa256.pem -pubout -out ecdsa256pub.pem + ecdsaPublicKey = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZZzi1u5f2/AEGFI/HYUhU+u6cTK1 +q2bbtE7r1JMK+/sQA5sNAp+7Vdc3psr1OaNzyTyuhTECyRdFKXm63cMnGg== +-----END PUBLIC KEY-----` +) + +func TestReadPrivateKey(t *testing.T) { + f, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("error creating tmpfile: %v", err) + } + defer os.Remove(f.Name()) + + if _, err := PrivateKeyFromFile(f.Name()); err == nil { + t.Fatalf("Expected error reading key from empty file, got none") + } + + if err := ioutil.WriteFile(f.Name(), []byte(rsaPrivateKey), os.FileMode(0600)); err != nil { + t.Fatalf("error writing private key to tmpfile: %v", err) + } + if _, err := PrivateKeyFromFile(f.Name()); err != nil { + t.Fatalf("error reading private RSA key: %v", err) + } + + if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKey), os.FileMode(0600)); err != nil { + t.Fatalf("error writing private key to tmpfile: %v", err) + } + if _, err := PrivateKeyFromFile(f.Name()); err != nil { + t.Fatalf("error reading private ECDSA key: %v", err) + } + + if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKeyWithParams), os.FileMode(0600)); err != nil { + t.Fatalf("error writing private key to tmpfile: %v", err) + } + if _, err := PrivateKeyFromFile(f.Name()); err != nil { + t.Fatalf("error reading private ECDSA key with params: %v", err) + } +} + +func TestReadPublicKeys(t *testing.T) { + f, err := ioutil.TempFile("", "") + if err != nil { + t.Fatalf("error creating tmpfile: %v", err) + } + defer os.Remove(f.Name()) + + if _, err := PublicKeysFromFile(f.Name()); err == nil { + t.Fatalf("Expected error reading keys from empty file, got none") + } + + if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil { + t.Fatalf("error writing public key to tmpfile: %v", err) + } + if keys, err := PublicKeysFromFile(f.Name()); err != nil { + t.Fatalf("error reading RSA public key: %v", err) + } else if len(keys) != 1 { + t.Fatalf("expected 1 key, got %d", len(keys)) + } + + if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil { + t.Fatalf("error writing public key to tmpfile: %v", err) + } + if keys, err := PublicKeysFromFile(f.Name()); err != nil { + t.Fatalf("error reading ECDSA public key: %v", err) + } else if len(keys) != 1 { + t.Fatalf("expected 1 key, got %d", len(keys)) + } + + if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey+"\n"+ecdsaPublicKey), os.FileMode(0600)); err != nil { + t.Fatalf("error writing public key to tmpfile: %v", err) + } + if keys, err := PublicKeysFromFile(f.Name()); err != nil { + t.Fatalf("error reading combined RSA/ECDSA public key file: %v", err) + } else if len(keys) != 2 { + t.Fatalf("expected 2 keys, got %d", len(keys)) + } + + if err := ioutil.WriteFile(f.Name(), []byte(certificate), os.FileMode(0600)); err != nil { + t.Fatalf("error writing certificate to tmpfile: %v", err) + } + if keys, err := PublicKeysFromFile(f.Name()); err != nil { + t.Fatalf("error reading public key from certificate file: %v", err) + } else if len(keys) != 1 { + t.Fatalf("expected 1 keys, got %d", len(keys)) + } + +}