github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/keys/privatekey.go (about)

     1  /*
     2  Copyright 2022 Gravitational, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package keys defines common interfaces for Teleport client keys.
    18  package keys
    19  
    20  import (
    21  	"bytes"
    22  	"crypto"
    23  	"crypto/ecdsa"
    24  	"crypto/ed25519"
    25  	"crypto/rsa"
    26  	"crypto/tls"
    27  	"crypto/x509"
    28  	"encoding/pem"
    29  	"os"
    30  
    31  	"github.com/gravitational/trace"
    32  	"golang.org/x/crypto/ssh"
    33  
    34  	"github.com/gravitational/teleport/api/utils/sshutils/ppk"
    35  )
    36  
    37  const (
    38  	PKCS1PrivateKeyType      = "RSA PRIVATE KEY"
    39  	PKCS8PrivateKeyType      = "PRIVATE KEY"
    40  	ECPrivateKeyType         = "EC PRIVATE KEY"
    41  	pivYubiKeyPrivateKeyType = "PIV YUBIKEY PRIVATE KEY"
    42  )
    43  
    44  type cryptoPublicKeyI interface {
    45  	Equal(x crypto.PublicKey) bool
    46  }
    47  
    48  // PrivateKey implements crypto.Signer with additional helper methods. The underlying
    49  // private key may be a standard crypto.Signer implemented in the standard library
    50  // (aka *rsa.PrivateKey, *ecdsa.PrivateKey, or ed25519.PrivateKey), or it may be a
    51  // custom implementation for a non-standard private key, such as a hardware key.
    52  type PrivateKey struct {
    53  	crypto.Signer
    54  	// sshPub is the public key in ssh.PublicKey form.
    55  	sshPub ssh.PublicKey
    56  	// keyPEM is PEM-encoded private key data which can be parsed with ParsePrivateKey.
    57  	keyPEM []byte
    58  }
    59  
    60  // NewPrivateKey returns a new PrivateKey for the given crypto.Signer.
    61  func NewPrivateKey(signer crypto.Signer, keyPEM []byte) (*PrivateKey, error) {
    62  	sshPub, err := ssh.NewPublicKey(signer.Public())
    63  	if err != nil {
    64  		return nil, trace.Wrap(err)
    65  	}
    66  
    67  	return &PrivateKey{
    68  		Signer: signer,
    69  		sshPub: sshPub,
    70  		keyPEM: keyPEM,
    71  	}, nil
    72  }
    73  
    74  // SSHPublicKey returns the ssh.PublicKey representiation of the public key.
    75  func (k *PrivateKey) SSHPublicKey() ssh.PublicKey {
    76  	return k.sshPub
    77  }
    78  
    79  // SSHPublicKey returns the ssh.PublicKey representiation of the public key.
    80  func (k *PrivateKey) MarshalSSHPublicKey() []byte {
    81  	return ssh.MarshalAuthorizedKey(k.sshPub)
    82  }
    83  
    84  // PrivateKeyPEM returns PEM encoded private key data. This may be data necessary
    85  // to retrieve the key, such as a YubiKey serial number and slot, or it can be a
    86  // PKCS marshaled private key.
    87  //
    88  // The resulting PEM encoded data should only be decoded with ParsePrivateKey to
    89  // prevent errors from parsing non PKCS marshaled keys, such as a PIV key.
    90  func (k *PrivateKey) PrivateKeyPEM() []byte {
    91  	return k.keyPEM
    92  }
    93  
    94  // TLSCertificate parses the given TLS certificate(s) paired with the private key
    95  // to rerturn a tls.Certificate, ready to be used in a TLS handshake.
    96  func (k *PrivateKey) TLSCertificate(certPEMBlock []byte) (tls.Certificate, error) {
    97  	cert := tls.Certificate{
    98  		PrivateKey: k.Signer,
    99  	}
   100  
   101  	var skippedBlockTypes []string
   102  	for {
   103  		var certDERBlock *pem.Block
   104  		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
   105  		if certDERBlock == nil {
   106  			break
   107  		}
   108  		if certDERBlock.Type == "CERTIFICATE" {
   109  			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
   110  		} else {
   111  			skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
   112  		}
   113  	}
   114  
   115  	if len(cert.Certificate) == 0 {
   116  		if len(skippedBlockTypes) == 0 {
   117  			return tls.Certificate{}, trace.BadParameter("tls: failed to find any PEM data in certificate input")
   118  		}
   119  		return tls.Certificate{}, trace.BadParameter("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes)
   120  	}
   121  
   122  	// Check that the certificate's public key matches this private key.
   123  	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
   124  	if err != nil {
   125  		return tls.Certificate{}, trace.Wrap(err)
   126  	}
   127  
   128  	if keyPub, ok := k.Public().(cryptoPublicKeyI); !ok {
   129  		return tls.Certificate{}, trace.BadParameter("private key does not contain a valid public key")
   130  	} else if !keyPub.Equal(x509Cert.PublicKey) {
   131  		return tls.Certificate{}, trace.BadParameter("private key does not match certificate's public key")
   132  	}
   133  
   134  	return cert, nil
   135  }
   136  
   137  // PPKFile returns a PuTTY PPK-formatted keypair
   138  func (k *PrivateKey) PPKFile() ([]byte, error) {
   139  	rsaKey, ok := k.Signer.(*rsa.PrivateKey)
   140  	if !ok {
   141  		return nil, trace.BadParameter("cannot use private key of type %T as rsa.PrivateKey", k)
   142  	}
   143  	ppkFile, err := ppk.ConvertToPPK(rsaKey, k.MarshalSSHPublicKey())
   144  	if err != nil {
   145  		return nil, trace.Wrap(err)
   146  	}
   147  	return ppkFile, nil
   148  }
   149  
   150  // RSAPrivateKeyPEM returns a PEM encoded RSA private key for the given key.
   151  // If the given key is not an RSA key, then an error will be returned.
   152  //
   153  // This is used by some integrations which currently only support raw RSA private keys,
   154  // like Kubernetes, MongoDB, and PPK files for windows.
   155  func (k *PrivateKey) RSAPrivateKeyPEM() ([]byte, error) {
   156  	if _, ok := k.Signer.(*rsa.PrivateKey); !ok {
   157  		return nil, trace.BadParameter("cannot get rsa key PEM for private key of type %T", k.Signer)
   158  	}
   159  	return k.keyPEM, nil
   160  }
   161  
   162  // LoadPrivateKey returns the PrivateKey for the given key file.
   163  func LoadPrivateKey(keyFile string) (*PrivateKey, error) {
   164  	keyPEM, err := os.ReadFile(keyFile)
   165  	if err != nil {
   166  		return nil, trace.ConvertSystemError(err)
   167  	}
   168  
   169  	priv, err := ParsePrivateKey(keyPEM)
   170  	if err != nil {
   171  		return nil, trace.Wrap(err)
   172  	}
   173  	return priv, nil
   174  }
   175  
   176  // ParsePrivateKey returns the PrivateKey for the given key PEM block.
   177  func ParsePrivateKey(keyPEM []byte) (*PrivateKey, error) {
   178  	block, _ := pem.Decode(keyPEM)
   179  	if block == nil {
   180  		return nil, trace.BadParameter("expected PEM encoded private key")
   181  	}
   182  
   183  	switch block.Type {
   184  	case PKCS1PrivateKeyType:
   185  		cryptoSigner, err := x509.ParsePKCS1PrivateKey(block.Bytes)
   186  		if err != nil {
   187  			return nil, trace.Wrap(err)
   188  		}
   189  		return NewPrivateKey(cryptoSigner, keyPEM)
   190  	case ECPrivateKeyType:
   191  		cryptoSigner, err := x509.ParseECPrivateKey(block.Bytes)
   192  		if err != nil {
   193  			return nil, trace.Wrap(err)
   194  		}
   195  		return NewPrivateKey(cryptoSigner, keyPEM)
   196  	case PKCS8PrivateKeyType:
   197  		priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
   198  		if err != nil {
   199  			return nil, trace.Wrap(err)
   200  		}
   201  		cryptoSigner, ok := priv.(crypto.Signer)
   202  		if !ok {
   203  			return nil, trace.BadParameter("x509.ParsePKCS8PrivateKey returned an invalid private key of type %T", priv)
   204  		}
   205  		return NewPrivateKey(cryptoSigner, keyPEM)
   206  	case pivYubiKeyPrivateKeyType:
   207  		priv, err := parseYubiKeyPrivateKeyData(block.Bytes)
   208  		if err != nil {
   209  			return nil, trace.Wrap(err, "failed to parse YubiKey private key")
   210  		}
   211  		return priv, nil
   212  	default:
   213  		return nil, trace.BadParameter("unexpected private key PEM type %q", block.Type)
   214  	}
   215  }
   216  
   217  // MarshalPrivateKey will return a PEM encoded crypto.Signer.
   218  // Only supports rsa, ecdsa, and ed25519 keys.
   219  func MarshalPrivateKey(key crypto.Signer) ([]byte, error) {
   220  	switch privateKey := key.(type) {
   221  	case *rsa.PrivateKey:
   222  		privPEM := pem.EncodeToMemory(&pem.Block{
   223  			Type:  PKCS1PrivateKeyType,
   224  			Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
   225  		})
   226  		return privPEM, nil
   227  	case *ecdsa.PrivateKey, ed25519.PrivateKey:
   228  		der, err := x509.MarshalPKCS8PrivateKey(privateKey)
   229  		if err != nil {
   230  			return nil, trace.Wrap(err)
   231  		}
   232  		privPEM := pem.EncodeToMemory(&pem.Block{
   233  			Type:  PKCS8PrivateKeyType,
   234  			Bytes: der,
   235  		})
   236  		return privPEM, nil
   237  	default:
   238  		return nil, trace.BadParameter("unsupported private key type %T", key)
   239  	}
   240  }
   241  
   242  // LoadKeyPair returns the PrivateKey for the given private and public key files.
   243  func LoadKeyPair(privFile, sshPubFile string) (*PrivateKey, error) {
   244  	privPEM, err := os.ReadFile(privFile)
   245  	if err != nil {
   246  		return nil, trace.ConvertSystemError(err)
   247  	}
   248  
   249  	marshalledSSHPub, err := os.ReadFile(sshPubFile)
   250  	if err != nil {
   251  		return nil, trace.ConvertSystemError(err)
   252  	}
   253  
   254  	priv, err := ParseKeyPair(privPEM, marshalledSSHPub)
   255  	if err != nil {
   256  		return nil, trace.Wrap(err)
   257  	}
   258  	return priv, nil
   259  }
   260  
   261  // ParseKeyPair returns the PrivateKey for the given private and public key PEM blocks.
   262  func ParseKeyPair(privPEM, marshalledSSHPub []byte) (*PrivateKey, error) {
   263  	priv, err := ParsePrivateKey(privPEM)
   264  	if err != nil {
   265  		return nil, trace.Wrap(err)
   266  	}
   267  
   268  	// Verify that the private key's public key matches the expected public key.
   269  	if !bytes.Equal(ssh.MarshalAuthorizedKey(priv.SSHPublicKey()), marshalledSSHPub) {
   270  		return nil, trace.CompareFailed("the given private and public keys do not form a valid keypair")
   271  	}
   272  
   273  	return priv, nil
   274  }
   275  
   276  // LoadX509KeyPair parse a tls.Certificate from a private key file and certificate file.
   277  // This should be used instead of tls.LoadX509KeyPair to support non-raw private keys, like PIV keys.
   278  func LoadX509KeyPair(certFile, keyFile string) (tls.Certificate, error) {
   279  	keyPEMBlock, err := os.ReadFile(keyFile)
   280  	if err != nil {
   281  		return tls.Certificate{}, trace.ConvertSystemError(err)
   282  	}
   283  
   284  	certPEMBlock, err := os.ReadFile(certFile)
   285  	if err != nil {
   286  		return tls.Certificate{}, trace.ConvertSystemError(err)
   287  	}
   288  
   289  	tlsCert, err := X509KeyPair(certPEMBlock, keyPEMBlock)
   290  	if err != nil {
   291  		return tls.Certificate{}, trace.Wrap(err)
   292  	}
   293  
   294  	return tlsCert, nil
   295  }
   296  
   297  // X509KeyPair parse a tls.Certificate from a private key PEM and certificate PEM.
   298  // This should be used instead of tls.X509KeyPair to support non-raw private keys, like PIV keys.
   299  func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (tls.Certificate, error) {
   300  	priv, err := ParsePrivateKey(keyPEMBlock)
   301  	if err != nil {
   302  		return tls.Certificate{}, trace.Wrap(err)
   303  	}
   304  
   305  	tlsCert, err := priv.TLSCertificate(certPEMBlock)
   306  	if err != nil {
   307  		return tls.Certificate{}, trace.Wrap(err)
   308  	}
   309  
   310  	return tlsCert, nil
   311  }
   312  
   313  // IsRSAPrivateKey returns true if the given private key is an RSA private key.
   314  // This function does a similar check to ParsePrivateKey, followed by key.RSAPrivateKeyPEM()
   315  // without parsing the private fully into a crypto.Signer.
   316  // This reduces the time it takes to check if a private key is an RSA private key
   317  // and improves the performance compared to ParsePrivateKey by a factor of 20.
   318  func IsRSAPrivateKey(privKey []byte) bool {
   319  	block, _ := pem.Decode(privKey)
   320  	if block == nil {
   321  		return false
   322  	}
   323  	switch block.Type {
   324  	case PKCS1PrivateKeyType:
   325  		return true
   326  	case PKCS8PrivateKeyType:
   327  		priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
   328  		if err != nil {
   329  			return false
   330  		}
   331  		_, ok := priv.(*rsa.PrivateKey)
   332  		return ok
   333  	default:
   334  		return false
   335  	}
   336  }