consolidate (un)marshalling of keys to/from PEM format (#43260)

* consolidate (un)marshalling of key PEMs

This commit consolidates various functions we have for marshalling
private and public keys to and from the PEM format, mostly by replacing
functions from lib/utils and lib/tlsca with equivalents in api/utils/keys.

The new functions also support ECDSA and Ed25519 keys, which is
necessary for the implementation of RFD136.

* speed up TestMTLSClientCAs

* fix new use in local proxy middleware
This commit is contained in:
Nic Klaassen
2024-06-21 19:32:11 +00:00
committed by GitHub
parent 72bfc80f9b
commit bcb169912d
34 changed files with 216 additions and 268 deletions
+89
View File
@@ -0,0 +1,89 @@
// Copyright 2024 Gravitational, Inc.
//
// 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 keys
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"github.com/gravitational/trace"
)
const (
// PKCS1PublicKeyType is the PEM encoding type commonly used for PKCS#1, ASN.1 DER form public keys.
PKCS1PublicKeyType = "RSA PUBLIC KEY"
// PKIXPublicKeyType is the PEM encoding type commonly used for PKIX, ASN.1 DER form public keys.
PKIXPublicKeyType = "PUBLIC KEY"
)
// MarshalPublicKey returns a PEM encoding of the given public key. Encodes RSA keys in PKCS1 format for
// backward compatibility. Only supports *rsa.PublicKey, *ecdsa.PublicKey, and ed25519.PublicKey.
func MarshalPublicKey(pub crypto.PublicKey) ([]byte, error) {
switch pubKey := pub.(type) {
case *rsa.PublicKey:
pubPEM := pem.EncodeToMemory(&pem.Block{
Type: PKCS1PublicKeyType,
Bytes: x509.MarshalPKCS1PublicKey(pubKey),
})
return pubPEM, nil
case *ecdsa.PublicKey, ed25519.PublicKey:
der, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return nil, trace.Wrap(err)
}
pubPEM := pem.EncodeToMemory(&pem.Block{
Type: PKIXPublicKeyType,
Bytes: der,
})
return pubPEM, nil
default:
return nil, trace.BadParameter("unsupported public key type %T", pub)
}
}
// ParsePublicKey parses a PEM-encoded public key. Supports PEM encodings of PKCS#1 or PKIX ASN.1 DER form
// public keys.
func ParsePublicKey(keyPEM []byte) (crypto.PublicKey, error) {
block, _ := pem.Decode(keyPEM)
if block == nil {
return nil, trace.BadParameter("failed to decode public key PEM block")
}
switch block.Type {
case PKCS1PublicKeyType:
pub, pkcs1Err := x509.ParsePKCS1PublicKey(block.Bytes)
if pkcs1Err != nil {
// Failed to parse as PKCS#1. We have been known to stuff PKIX DER encoded RSA public keys into
// "RSA PUBLIC KEY" PEM blocks, so try to parse as PKIX.
pub, pkixErr := x509.ParsePKIXPublicKey(block.Bytes)
if pkixErr != nil {
// Parsing as both formats failed. We really should expect PKCS#1 in this PEM block, so return
// that error.
return nil, trace.Wrap(pkcs1Err)
}
return pub, nil
}
return pub, nil
case PKIXPublicKeyType:
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
return pub, trace.Wrap(err)
default:
return nil, trace.BadParameter("unsupported public key type %q", block.Type)
}
}
+2 -1
View File
@@ -27,6 +27,7 @@ import (
"k8s.io/client-go/rest"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/services"
@@ -91,7 +92,7 @@ func ProxyClient(cfg ProxyConfig) (*kubernetes.Clientset, *rest.Config, error) {
if err != nil {
return nil, nil, trace.Wrap(err)
}
priv, err := tlsca.ParsePrivateKeyPEM(privPEM)
priv, err := keys.ParsePrivateKey(privPEM)
if err != nil {
return nil, nil, trace.Wrap(err)
}
+1 -1
View File
@@ -4252,7 +4252,7 @@ func (a *Server) GenerateHostCerts(ctx context.Context, req *proto.HostCertsRequ
if _, _, _, _, err := ssh.ParseAuthorizedKey(req.PublicSSHKey); err != nil {
return nil, trace.BadParameter("failed to parse SSH public key")
}
cryptoPubKey, err := tlsca.ParsePublicKeyPEM(req.PublicTLSKey)
cryptoPubKey, err := keys.ParsePublicKey(req.PublicTLSKey)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -26,12 +26,12 @@ import (
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/integrations/awsoidc"
"github.com/gravitational/teleport/lib/jwt"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
func TestGenerateAWSOIDCToken(t *testing.T) {
@@ -114,7 +114,7 @@ func TestGenerateAWSOIDCToken(t *testing.T) {
require.NotEmpty(t, ca.GetActiveKeys().JWT)
jwtPubKey := ca.GetActiveKeys().JWT[0].PublicKey
publicKey, err := utils.ParsePublicKey(jwtPubKey)
publicKey, err := keys.ParsePublicKey(jwtPubKey)
require.NoError(t, err)
// Validate JWT against public key
+2 -2
View File
@@ -40,10 +40,10 @@ import (
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/cloud"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/utils"
)
// TestAWSKMS_deleteUnusedKeys tests the AWS KMS keystore's deleteUnusedKeys
@@ -298,7 +298,7 @@ func (f *fakeAWSKMSService) Sign(input *kms.SignInput) (*kms.SignOutput, error)
default:
return nil, trace.BadParameter("unsupported SigningAlgorithm %q", aws.StringValue(input.SigningAlgorithm))
}
signer, err := utils.ParsePrivateKeyPEM(testRawPrivateKey)
signer, err := keys.ParsePrivateKey(testRawPrivateKey)
if err != nil {
return nil, trace.Wrap(err)
}
+3 -3
View File
@@ -46,6 +46,7 @@ import (
"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/grpc/interceptors"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/keystore/internal/faketime"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/cloud"
@@ -53,7 +54,6 @@ import (
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
const (
@@ -160,7 +160,7 @@ func (f *fakeGCPKMSServer) GetPublicKey(ctx context.Context, req *kmspb.GetPubli
return nil, trace.BadParameter("cannot fetch public key, state has value %s", keyState.cryptoKeyVersion.State)
}
signer, err := utils.ParsePrivateKeyPEM([]byte(keyState.pem))
signer, err := keys.ParsePrivateKey([]byte(keyState.pem))
if err != nil {
return nil, trace.Wrap(err)
}
@@ -192,7 +192,7 @@ func (f *fakeGCPKMSServer) AsymmetricSign(ctx context.Context, req *kmspb.Asymme
return nil, trace.BadParameter("cannot fetch key, state has value %s", keyState.cryptoKeyVersion.State)
}
signer, err := utils.ParsePrivateKeyPEM([]byte(keyState.pem))
signer, err := keys.ParsePrivateKey([]byte(keyState.pem))
if err != nil {
return nil, trace.Wrap(err)
}
+3 -2
View File
@@ -35,6 +35,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/cloud"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
@@ -188,7 +189,7 @@ func TestBackends(t *testing.T) {
var err error
rawPrivateKeys[i], signer, err = backend.generateRSA(ctx)
require.NoError(t, err)
rawPublicKeys[i], err = utils.MarshalPublicKey(signer)
rawPublicKeys[i], err = keys.MarshalPublicKey(signer.Public())
require.NoError(t, err)
}
@@ -307,7 +308,7 @@ func TestManager(t *testing.T) {
jwtSigner, err := manager.GetJWTSigner(ctx, ca)
require.NoError(t, err, trace.DebugReport(err))
pubkeyPem, err := utils.MarshalPublicKey(jwtSigner)
pubkeyPem, err := keys.MarshalPublicKey(jwtSigner.Public())
require.NoError(t, err)
require.Equal(t, jwtKeyPair.PublicKey, pubkeyPem)
+3 -3
View File
@@ -35,11 +35,11 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/keystore/internal/faketime"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
// Manager provides an interface to interact with teleport CA private keys,
@@ -309,7 +309,7 @@ func (m *Manager) GetJWTSigner(ctx context.Context, ca types.CertAuthority) (cry
if !canSign {
continue
}
pub, err := utils.ParsePublicKey(keyPair.PublicKey)
pub, err := keys.ParsePublicKey(keyPair.PublicKey)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -368,7 +368,7 @@ func (m *Manager) NewJWTKeyPair(ctx context.Context) (*types.JWTKeyPair, error)
if err != nil {
return nil, trace.Wrap(err)
}
publicKey, err := utils.MarshalPublicKey(signer)
publicKey, err := keys.MarshalPublicKey(signer.Public())
if err != nil {
return nil, trace.Wrap(err)
}
+2 -2
View File
@@ -25,8 +25,8 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/utils"
)
type softwareKeyStore struct {
@@ -81,7 +81,7 @@ func (s *softwareKeyStore) getSigner(ctx context.Context, rawKey []byte, publicK
}
func (s *softwareKeyStore) getSignerWithoutPublicKey(ctx context.Context, rawKey []byte) (crypto.Signer, error) {
signer, err := utils.ParsePrivateKeyPEM(rawKey)
signer, err := keys.ParsePrivateKey(rawKey)
return signer, trace.Wrap(err)
}
+10 -7
View File
@@ -20,7 +20,6 @@ package auth
import (
"context"
"crypto/rsa"
"crypto/x509/pkix"
"fmt"
"time"
@@ -32,11 +31,11 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/keystore"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
// RotateRequest is a request to start rotation of the certificate authority.
@@ -373,17 +372,17 @@ func (a *Server) startNewRotation(ctx context.Context, req rotationReq, ca types
if len(req.privateKey) != 0 {
log.Infof("Generating CA, using pregenerated test private key.")
rsaKey, err := ssh.ParseRawPrivateKey(req.privateKey)
signer, err := keys.ParsePrivateKey(req.privateKey)
if err != nil {
return trace.Wrap(err)
}
if len(activeKeys.SSH) > 0 {
signer, err := ssh.NewSignerFromKey(rsaKey)
sshSigner, err := ssh.NewSignerFromKey(signer)
if err != nil {
return trace.Wrap(err)
}
sshPublicKey := ssh.MarshalAuthorizedKey(signer.PublicKey())
sshPublicKey := ssh.MarshalAuthorizedKey(sshSigner.PublicKey())
newKeys.SSH = append(newKeys.SSH, &types.SSHKeyPair{
PublicKey: sshPublicKey,
PrivateKey: req.privateKey,
@@ -393,7 +392,7 @@ func (a *Server) startNewRotation(ctx context.Context, req rotationReq, ca types
if len(activeKeys.TLS) > 0 {
tlsCert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{
Signer: rsaKey.(*rsa.PrivateKey),
Signer: signer,
Entity: pkix.Name{
CommonName: ca.GetClusterName(),
Organization: []string{ca.GetClusterName()},
@@ -412,7 +411,11 @@ func (a *Server) startNewRotation(ctx context.Context, req rotationReq, ca types
}
if len(activeKeys.JWT) > 0 {
jwtPublicKey, jwtPrivateKey, err := utils.MarshalPrivateKey(rsaKey.(*rsa.PrivateKey))
jwtPublicKey, err := keys.MarshalPublicKey(signer.Public())
if err != nil {
return trace.Wrap(err)
}
jwtPrivateKey, err := keys.MarshalPrivateKey(signer)
if err != nil {
return trace.Wrap(err)
}
+4 -3
View File
@@ -55,6 +55,7 @@ import (
eventtypes "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/types/wrappers"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/join"
@@ -2612,7 +2613,7 @@ func TestGenerateCerts(t *testing.T) {
require.Error(t, err)
require.True(t, trace.IsAccessDenied(err), "trace.IsAccessDenied failed: err=%v (%T)", err, trace.Unwrap(err))
_, privateKeyPEM, err := utils.MarshalPrivateKey(privateKey.(crypto.Signer))
privateKeyPEM, err := keys.MarshalPrivateKey(privateKey.(crypto.Signer))
require.NoError(t, err)
clientCert, err := tls.X509KeyPair(userCerts.TLS, privateKeyPEM)
@@ -4855,7 +4856,7 @@ func TestGRPCServer_DeleteToken(t *testing.T) {
func verifyJWT(clock clockwork.Clock, clusterName string, pairs []*types.JWTKeyPair, token string) (*jwt.Claims, error) {
errs := []error{}
for _, pair := range pairs {
publicKey, err := utils.ParsePublicKey(pair.PublicKey)
publicKey, err := keys.ParsePublicKey(pair.PublicKey)
if err != nil {
errs = append(errs, trace.Wrap(err))
continue
@@ -4889,7 +4890,7 @@ func verifyJWT(clock clockwork.Clock, clusterName string, pairs []*types.JWTKeyP
func verifyJWTAWSOIDC(clock clockwork.Clock, clusterName string, pairs []*types.JWTKeyPair, token, issuer string) (*jwt.Claims, error) {
errs := []error{}
for _, pair := range pairs {
publicKey, err := utils.ParsePublicKey(pair.PublicKey)
publicKey, err := keys.ParsePublicKey(pair.PublicKey)
if err != nil {
errs = append(errs, trace.Wrap(err))
continue
+3 -2
View File
@@ -32,6 +32,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
@@ -85,11 +86,11 @@ func createClientWallet(key *client.Key, certPem []byte, password string, wallet
}
func createJKSWallet(keyPEM, certPEM, caPEM []byte, password string) ([]byte, error) {
key, err := utils.ParsePrivateKey(keyPEM)
key, err := keys.ParsePrivateKey(keyPEM)
if err != nil {
return nil, trace.Wrap(err)
}
privateKey, err := x509.MarshalPKCS8PrivateKey(key)
privateKey, err := x509.MarshalPKCS8PrivateKey(key.Signer)
if err != nil {
return nil, trace.Wrap(err)
}
+2 -2
View File
@@ -517,7 +517,7 @@ func writeOracleFormat(cfg WriteConfig, writer ConfigWriter) ([]string, error) {
if err != nil {
return nil, trace.Wrap(err)
}
keyK, err := utils.ParsePrivateKeyPEM(cfg.Key.PrivateKeyPEM())
keyK, err := keys.ParsePrivateKey(cfg.Key.PrivateKeyPEM())
if err != nil {
return nil, trace.Wrap(err)
}
@@ -527,7 +527,7 @@ func writeOracleFormat(cfg WriteConfig, writer ConfigWriter) ([]string, error) {
// issuer for an oracle wallet user_cert, and the server cert we create
// is not signed by the DB Client CA, so don't pass trusted certs
// (DB Client CA) here.
pf, err := pkcs12.LegacyRC2.WithRand(rand.Reader).Encode(keyK, certBlock, nil, cfg.Password)
pf, err := pkcs12.LegacyRC2.WithRand(rand.Reader).Encode(keyK.Signer, certBlock, nil, cfg.Password)
if err != nil {
return nil, trace.Wrap(err)
}
+5 -1
View File
@@ -378,7 +378,11 @@ func (r *LocalCertGenerator) generateCert(host string) (*tls.Certificate, error)
return nil, trace.Wrap(err)
}
cert, err := tls.X509KeyPair(certPem, tlsca.MarshalPrivateKeyPEM(certKey))
keyPEM, err := keys.MarshalPrivateKey(certKey)
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := tls.X509KeyPair(certPem, keyPEM)
if err != nil {
return nil, trace.Wrap(err)
}
+6 -1
View File
@@ -33,6 +33,7 @@ import (
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/tlsca"
)
@@ -128,7 +129,11 @@ func (g *gcpSQLAdminClient) GenerateEphemeralCert(ctx context.Context, db types.
}
// Create TLS certificate from returned ephemeral certificate and private key.
cert, err := tls.X509KeyPair([]byte(resp.EphemeralCert.Cert), tlsca.MarshalPrivateKeyPEM(pkey))
keyPEM, err := keys.MarshalPrivateKey(pkey)
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := tls.X509KeyPair([]byte(resp.EphemeralCert.Cert), keyPEM)
if err != nil {
return nil, trace.Wrap(err)
}
+2 -2
View File
@@ -28,8 +28,8 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
)
// JWK is a JSON Web Key, described in detail in RFC 7517.
@@ -61,7 +61,7 @@ func KeyID(pub *rsa.PublicKey) string {
// MarshalJWK will marshal a supported public key into JWK format.
func MarshalJWK(bytes []byte) (JWK, error) {
// Parse the public key and validate type.
p, err := utils.ParsePublicKey(bytes)
p, err := keys.ParsePublicKey(bytes)
if err != nil {
return JWK{}, trace.Wrap(err)
}
+6 -2
View File
@@ -42,7 +42,7 @@ import (
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/api/utils/keys"
)
// Config defines the clock and PEM encoded bytes of a public and private
@@ -559,7 +559,11 @@ func GenerateKeyPair() ([]byte, []byte, error) {
return nil, nil, trace.Wrap(err)
}
public, private, err := utils.MarshalPrivateKey(privateKey)
public, err := keys.MarshalPublicKey(privateKey.Public())
if err != nil {
return nil, nil, trace.Wrap(err)
}
private, err := keys.MarshalPrivateKey(privateKey)
if err != nil {
return nil, nil, trace.Wrap(err)
}
+9 -9
View File
@@ -27,14 +27,14 @@ import (
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
)
func TestSignAndVerify(t *testing.T) {
_, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
clock := clockwork.NewFakeClockAt(time.Now())
@@ -73,9 +73,9 @@ func TestSignAndVerify(t *testing.T) {
func TestPublicOnlyVerifyAzure(t *testing.T) {
publicBytes, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
publicKey, err := utils.ParsePublicKey(publicBytes)
publicKey, err := keys.ParsePublicKey(publicBytes)
require.NoError(t, err)
// Create a new key that can sign and verify tokens.
@@ -118,9 +118,9 @@ func TestPublicOnlyVerifyAzure(t *testing.T) {
func TestPublicOnlyVerify(t *testing.T) {
publicBytes, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
publicKey, err := utils.ParsePublicKey(publicBytes)
publicKey, err := keys.ParsePublicKey(publicBytes)
require.NoError(t, err)
clock := clockwork.NewFakeClockAt(time.Now())
@@ -175,7 +175,7 @@ func TestPublicOnlyVerify(t *testing.T) {
func TestKey_SignAndVerifyPROXY(t *testing.T) {
_, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
clock := clockwork.NewFakeClockAt(time.Now())
@@ -250,7 +250,7 @@ func TestKey_SignAndVerifyPROXY(t *testing.T) {
func TestKey_SignAndVerifyAWSOIDC(t *testing.T) {
_, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
clock := clockwork.NewFakeClockAt(time.Now())
@@ -314,7 +314,7 @@ func TestKey_SignAndVerifyAWSOIDC(t *testing.T) {
func TestExpiry(t *testing.T) {
_, privateBytes, err := GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
clock := clockwork.NewFakeClockAt(time.Now())
+4 -7
View File
@@ -23,11 +23,9 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"net"
@@ -42,11 +40,11 @@ import (
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/authclient"
testingkubemock "github.com/gravitational/teleport/lib/kube/proxy/testing/kube_server"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
func TestServeConfigureError(t *testing.T) {
@@ -66,13 +64,13 @@ func TestMTLSClientCAs(t *testing.T) {
cas: make(map[string]types.CertAuthority),
}
// Reuse the same CA private key for performance.
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
addCA := func(t *testing.T, name string) (key, cert []byte) {
cert, err := tlsca.GenerateSelfSignedCAWithSigner(caKey, pkix.Name{CommonName: name}, nil, time.Minute)
require.NoError(t, err)
_, key, err = utils.MarshalPrivateKey(caKey)
key, err = keys.MarshalPrivateKey(caKey)
require.NoError(t, err)
ca, err := types.NewCertAuthority(types.CertAuthoritySpecV2{
Type: types.HostCA,
@@ -105,9 +103,8 @@ func TestMTLSClientCAs(t *testing.T) {
DNSNames: sans,
})
require.NoError(t, err)
keyRaw, err := x509.MarshalECPrivateKey(userHostKey)
keyPEM, err := keys.MarshalPrivateKey(userHostKey)
require.NoError(t, err)
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyRaw})
cert, err := tls.X509KeyPair(certRaw, keyPEM)
require.NoError(t, err)
return cert
+2 -1
View File
@@ -46,6 +46,7 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/keygen"
@@ -507,7 +508,7 @@ func (c *TestContext) GenTestKubeClientTLSCert(t *testing.T, userName, kubeClust
privPEM, _, err := testauthority.New().GenerateKeyPair()
require.NoError(t, err)
priv, err := tlsca.ParsePrivateKeyPEM(privPEM)
priv, err := keys.ParsePrivateKey(privPEM)
require.NoError(t, err)
id := tlsca.Identity{
+7 -2
View File
@@ -47,6 +47,7 @@ import (
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/fixtures"
@@ -745,7 +746,9 @@ func TestMux(t *testing.T) {
DNSNames: []string{"127.0.0.1"},
})
require.NoError(t, err)
serverCert, err := tls.X509KeyPair(serverPEM, tlsca.MarshalPrivateKeyPEM(serverRSAKey))
serverKeyPEM, err := keys.MarshalPrivateKey(serverRSAKey)
require.NoError(t, err)
serverCert, err := tls.X509KeyPair(serverPEM, serverKeyPEM)
require.NoError(t, err)
// Sign client certificate with database access identity.
@@ -765,7 +768,9 @@ func TestMux(t *testing.T) {
NotAfter: time.Now().Add(time.Hour),
})
require.NoError(t, err)
clientCert, err := tls.X509KeyPair(clientPEM, tlsca.MarshalPrivateKeyPEM(clientRSAKey))
clientKeyPEM, err := keys.MarshalPrivateKey(clientRSAKey)
require.NoError(t, err)
clientCert, err := tls.X509KeyPair(clientPEM, clientKeyPEM)
require.NoError(t, err)
webLis, err := NewWebListener(WebListenerConfig{
+2 -1
View File
@@ -79,6 +79,7 @@ import (
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/aws"
"github.com/gravitational/teleport/api/utils/grpc/interceptors"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/agentless"
"github.com/gravitational/teleport/lib/auditd"
@@ -5106,7 +5107,7 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
}
func (process *TeleportProcess) getPROXYSigner(ident *state.Identity) (multiplexer.PROXYHeaderSigner, error) {
signer, err := utils.ParsePrivateKeyPEM(ident.KeyBytes)
signer, err := keys.ParsePrivateKey(ident.KeyBytes)
if err != nil {
return nil, trace.Wrap(err, "could not parse identity's private key")
}
+4 -4
View File
@@ -143,7 +143,7 @@ func checkDatabaseCA(cai types.CertAuthority) error {
if len(pair.Cert) > 0 {
_, err = tls.X509KeyPair(pair.Cert, pair.Key)
} else {
_, err = utils.ParsePrivateKey(pair.Key)
_, err = keys.ParsePrivateKey(pair.Key)
}
if err != nil {
return trace.Wrap(err)
@@ -199,12 +199,12 @@ func checkJWTKeys(cai types.CertAuthority) error {
for _, pair := range ca.GetTrustedJWTKeyPairs() {
// TODO(nic): validate PKCS11 private keys
if len(pair.PrivateKey) > 0 && pair.PrivateKeyType == types.PrivateKeyType_RAW {
privateKey, err = utils.ParsePrivateKey(pair.PrivateKey)
privateKey, err = keys.ParsePrivateKey(pair.PrivateKey)
if err != nil {
return trace.Wrap(err)
}
}
publicKey, err := utils.ParsePublicKey(pair.PublicKey)
publicKey, err := keys.ParsePublicKey(pair.PublicKey)
if err != nil {
return trace.Wrap(err)
}
@@ -240,7 +240,7 @@ func checkSAMLIDPCA(cai types.CertAuthority) error {
if len(pair.Cert) > 0 {
_, err = tls.X509KeyPair(pair.Cert, pair.Key)
} else {
_, err = utils.ParsePrivateKey(pair.Key)
_, err = keys.ParsePrivateKey(pair.Key)
}
if err != nil {
return trace.Wrap(err)
@@ -34,16 +34,16 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/jwt"
"github.com/gravitational/teleport/lib/utils"
)
func TestAzureMSIMiddlewareHandleRequest(t *testing.T) {
newPrivateKey := func() crypto.Signer {
_, privateBytes, err := jwt.GenerateKeyPair()
require.NoError(t, err)
privateKey, err := utils.ParsePrivateKey(privateBytes)
privateKey, err := keys.ParsePrivateKey(privateBytes)
require.NoError(t, err)
return privateKey
}
+6 -1
View File
@@ -29,6 +29,7 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
)
@@ -245,7 +246,11 @@ func (r *CertGenListener) generateCertFor(host string) (*tls.Certificate, error)
return nil, trace.Wrap(err)
}
cert, err := tls.X509KeyPair(certPem, tlsca.MarshalPrivateKeyPEM(certKey))
keyPem, err := keys.MarshalPrivateKey(certKey)
if err != nil {
return nil, trace.Wrap(err)
}
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return nil, trace.Wrap(err)
}
+2 -1
View File
@@ -36,6 +36,7 @@ import (
machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1"
trustpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/trust/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/teleport/lib/services"
@@ -207,7 +208,7 @@ func getTestIdent(t *testing.T, username string, reqs ...identRequest) *identity
tlsPublicKeyPEM, err := tlsca.MarshalPublicKeyFromPrivateKeyPEM(sshPrivateKey)
require.NoError(t, err)
tlsPublicKey, err := tlsca.ParsePublicKeyPEM(tlsPublicKeyPEM)
tlsPublicKey, err := keys.ParsePublicKey(tlsPublicKeyPEM)
require.NoError(t, err)
// Note: it'd be nice to make this more universally useful in our tests at
+1 -1
View File
@@ -71,7 +71,7 @@ func FromKeys(certPEM, keyPEM []byte) (*CertAuthority, error) {
return nil, trace.Wrap(err)
}
if len(keyPEM) != 0 {
ca.Signer, err = ParsePrivateKeyPEM(keyPEM)
ca.Signer, err = keys.ParsePrivateKey(keyPEM)
if err != nil {
return nil, trace.Wrap(err)
}
+2 -1
View File
@@ -38,6 +38,7 @@ import (
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
apievents "github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/fixtures"
)
@@ -58,7 +59,7 @@ func TestPrincipals(t *testing.T) {
{
name: "FromCertAndSigner",
createFunc: func() (*CertAuthority, error) {
signer, err := ParsePrivateKeyPEM([]byte(fixtures.TLSCAKeyPEM))
signer, err := keys.ParsePrivateKey([]byte(fixtures.TLSCAKeyPEM))
if err != nil {
return nil, trace.Wrap(err)
}
+17 -67
View File
@@ -21,7 +21,6 @@ package tlsca
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -35,6 +34,7 @@ import (
"github.com/jonboulle/clockwork"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/utils"
)
@@ -194,77 +194,27 @@ func ParseCertificatePEMs(bytes []byte) ([]*x509.Certificate, error) {
return certs, nil
}
// ParsePrivateKeyPEM parses PEM-encoded private key
func ParsePrivateKeyPEM(bytes []byte) (crypto.Signer, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, trace.BadParameter("expected PEM-encoded block")
}
return ParsePrivateKeyDER(block.Bytes)
}
// ParsePrivateKeyDER parses unencrypted DER-encoded private key
func ParsePrivateKeyDER(der []byte) (crypto.Signer, error) {
generalKey, err := x509.ParsePKCS8PrivateKey(der)
if err != nil {
generalKey, err = x509.ParsePKCS1PrivateKey(der)
if err != nil {
generalKey, err = x509.ParseECPrivateKey(der)
if err != nil {
return nil, trace.BadParameter("failed parsing private key")
}
}
}
switch k := generalKey.(type) {
case *rsa.PrivateKey:
return k, nil
case *ecdsa.PrivateKey:
return k, nil
}
return nil, trace.BadParameter("unsupported private key type")
}
// ParsePublicKeyPEM parses public key PEM
func ParsePublicKeyPEM(bytes []byte) (interface{}, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, trace.BadParameter("expected PEM-encoded block")
}
return ParsePublicKeyDER(block.Bytes)
}
// ParsePublicKeyDER parses unencrypted DER-encoded publice key
func ParsePublicKeyDER(der []byte) (crypto.PublicKey, error) {
generalKey, err := x509.ParsePKIXPublicKey(der)
if err != nil {
return nil, trace.Wrap(err)
}
return generalKey, nil
}
// MarshalPublicKeyFromPrivateKeyPEM extracts public key from private key
// and returns PEM marshaled key
func MarshalPublicKeyFromPrivateKeyPEM(privateKey crypto.PrivateKey) ([]byte, error) {
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
if !ok {
return nil, trace.BadParameter("expected RSA key")
// TODO(nklaassen): DELETE IN 18.0.0 when this quirk is no longer necessary because all parsers can handle
// either format.
if rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey); ok {
// This is weird and historical: we're marshaling an RSA public key into PKIX DER format and then
// putting it into an "RSA PUBLIC KEY" PEM block. Normally RSA keys should either be:
// - PKCS#1 DER format in an "RSA PUBLIC KEY" PEM block
// - PKIX DER format in a "PUBLIC KEY" PEM block
derBytes, err := x509.MarshalPKIXPublicKey(rsaPrivateKey.Public())
if err != nil {
return nil, trace.Wrap(err)
}
return pem.EncodeToMemory(&pem.Block{Type: keys.PKCS1PublicKeyType, Bytes: derBytes}), nil
}
rsaPublicKey := rsaPrivateKey.Public()
derBytes, err := x509.MarshalPKIXPublicKey(rsaPublicKey)
if err != nil {
return nil, trace.Wrap(err)
// All private keys in the standard library implement crypto.Signer, which gives access to the public key.
if signer, ok := privateKey.(crypto.Signer); ok {
return keys.MarshalPublicKey(signer.Public())
}
return pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: derBytes}), nil
}
// MarshalPrivateKeyPEM marshals provided rsa.PrivateKey into PEM format.
func MarshalPrivateKeyPEM(privateKey *rsa.PrivateKey) []byte {
return pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
return nil, trace.BadParameter("unsupported key type %T", privateKey)
}
// MarshalCertificatePEM takes a *x509.Certificate and returns the PEM
+6 -34
View File
@@ -16,7 +16,6 @@ package utils
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
@@ -30,9 +29,9 @@ import (
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
"github.com/sirupsen/logrus"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/tlsutils"
)
@@ -42,11 +41,11 @@ func ParseKeyStorePEM(keyPEM, certPEM string) (*KeyStore, error) {
if err != nil {
return nil, trace.Wrap(err)
}
key, err := ParsePrivateKeyPEM([]byte(keyPEM))
key, err := keys.ParsePrivateKey([]byte(keyPEM))
if err != nil {
return nil, trace.Wrap(err)
}
rsaKey, ok := key.(*rsa.PrivateKey)
rsaKey, ok := key.Signer.(*rsa.PrivateKey)
if !ok {
return nil, trace.BadParameter("key of type %T is not supported, only RSA keys are supported", key)
}
@@ -105,37 +104,10 @@ func GenerateSelfSignedSigningCert(entity pkix.Name, dnsNames []string, ttl time
return keyPEM, certPEM, nil
}
// ParsePrivateKeyPEM parses PEM-encoded private key
// ParsePrivateKeyPEM parses PEM-encoded private key.
// Prefer [keys.ParsePrivateKey], this will be deleted after references are removed from teleport.e.
func ParsePrivateKeyPEM(bytes []byte) (crypto.Signer, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, trace.BadParameter("expected PEM-encoded block")
}
return ParsePrivateKeyDER(block.Bytes)
}
// ParsePrivateKeyDER parses unencrypted DER-encoded private key
func ParsePrivateKeyDER(der []byte) (crypto.Signer, error) {
generalKey, err := x509.ParsePKCS8PrivateKey(der)
if err != nil {
generalKey, err = x509.ParsePKCS1PrivateKey(der)
if err != nil {
generalKey, err = x509.ParseECPrivateKey(der)
if err != nil {
logrus.Errorf("Failed to parse key: %v.", err)
return nil, trace.BadParameter("failed parsing private key")
}
}
}
switch k := generalKey.(type) {
case *rsa.PrivateKey:
return k, nil
case *ecdsa.PrivateKey:
return k, nil
}
return nil, trace.BadParameter("unsupported private key type")
return keys.ParsePrivateKey(bytes)
}
// VerifyCertificateExpiry checks the certificate's expiration status.
+2 -1
View File
@@ -31,6 +31,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/api/utils/sshutils"
)
@@ -94,7 +95,7 @@ func startSSHServer(t *testing.T, listener net.Listener, sshConnCh chan<- sshCon
privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize)
require.NoError(t, err)
_, private, err := MarshalPrivateKey(privateKey)
private, err := keys.MarshalPrivateKey(privateKey)
require.NoError(t, err)
signer, err := ssh.ParsePrivateKey(private)
-98
View File
@@ -1,98 +0,0 @@
/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"github.com/gravitational/trace"
)
// MarshalPrivateKey will return a PEM encoded crypto.Signer. Only supports
// RSA private keys.
func MarshalPrivateKey(key crypto.Signer) ([]byte, []byte, error) {
switch privateKey := key.(type) {
case *rsa.PrivateKey:
privateBytes := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
publicKey, ok := key.Public().(*rsa.PublicKey)
if !ok {
return nil, nil, trace.BadParameter("invalid key type: %T", publicKey)
}
publicBytes := pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(publicKey),
})
return publicBytes, privateBytes, nil
default:
return nil, nil, trace.BadParameter("unsupported private key type %T", key)
}
}
// MarshalPublicKey returns a PEM encoded public key for a given crypto.Signer
func MarshalPublicKey(signer crypto.Signer) ([]byte, error) {
switch publicKey := signer.Public().(type) {
case *rsa.PublicKey:
return pem.EncodeToMemory(&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: x509.MarshalPKCS1PublicKey(publicKey),
}), nil
default:
return nil, trace.BadParameter("unsupported public key type %T", publicKey)
}
}
// ParsePrivateKey parses a PEM encoded private key and returns a
// crypto.Signer. Only supports RSA private keys.
func ParsePrivateKey(bytes []byte) (crypto.Signer, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, trace.BadParameter("failed to decode private key PEM block")
}
switch block.Type {
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(block.Bytes)
default:
return nil, trace.BadParameter("unsupported private key type %q", block.Type)
}
}
// ParsePublicKey parses a PEM encoded public key and returns a
// crypto.PublicKey. Only support RSA public keys.
func ParsePublicKey(bytes []byte) (crypto.PublicKey, error) {
block, _ := pem.Decode(bytes)
if block == nil {
return nil, trace.BadParameter("failed to decode public key PEM block")
}
switch block.Type {
case "RSA PUBLIC KEY":
return x509.ParsePKCS1PublicKey(block.Bytes)
default:
return nil, trace.BadParameter("unsupported public key type %q", block.Type)
}
}
+2 -1
View File
@@ -35,6 +35,7 @@ import (
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/defaults"
@@ -296,7 +297,7 @@ func (t *transport) resignAzureJWTCookie(r *http.Request) error {
}
// Create a new jwt key using the web session private key to sign a new token.
wsPrivateKey, err := utils.ParsePrivateKey(t.c.ws.GetPriv())
wsPrivateKey, err := keys.ParsePrivateKey(t.c.ws.GetPriv())
if err != nil {
return trace.Wrap(err)
}
+3 -1
View File
@@ -32,6 +32,7 @@ import (
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/identityfile"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/fixtures"
"github.com/gravitational/teleport/lib/tlsca"
)
@@ -54,7 +55,8 @@ func TestGetKubeCredentialData(t *testing.T) {
})
require.NoError(t, err)
privateKeyBytes := tlsca.MarshalPrivateKeyPEM(privateKey)
privateKeyBytes, err := keys.MarshalPrivateKey(privateKey)
require.NoError(t, err)
idFile := &identityfile.IdentityFile{
PrivateKey: privateKeyBytes,
Certs: identityfile.Certs{