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 }