diff --git a/controllers/remote/cluster_cache_tracker.go b/controllers/remote/cluster_cache_tracker.go
index 1931dabeb424..deb2c9eeae38 100644
--- a/controllers/remote/cluster_cache_tracker.go
+++ b/controllers/remote/cluster_cache_tracker.go
@@ -18,6 +18,7 @@ package remote
 
 import (
 	"context"
+	"crypto/rsa"
 	"fmt"
 	"os"
 	"sync"
@@ -48,6 +49,7 @@ import (
 
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/feature"
+	"sigs.k8s.io/cluster-api/util/certs"
 	"sigs.k8s.io/cluster-api/util/conditions"
 )
 
@@ -166,12 +168,23 @@ func (t *ClusterCacheTracker) GetRESTConfig(ctc context.Context, cluster client.
 	return accessor.config, nil
 }
 
+// GetEtcdClientCertificateKey returns a cached certificate key to be used for generating certificates for accessing etcd in the given cluster.
+func (t *ClusterCacheTracker) GetEtcdClientCertificateKey(ctx context.Context, cluster client.ObjectKey) (*rsa.PrivateKey, error) {
+	accessor, err := t.getClusterAccessor(ctx, cluster, t.indexes...)
+	if err != nil {
+		return nil, err
+	}
+
+	return accessor.etcdClientCertificateKey, nil
+}
+
 // clusterAccessor represents the combination of a delegating client, cache, and watches for a remote cluster.
 type clusterAccessor struct {
-	cache   *stoppableCache
-	client  client.Client
-	watches sets.Set[string]
-	config  *rest.Config
+	cache                    *stoppableCache
+	client                   client.Client
+	watches                  sets.Set[string]
+	config                   *rest.Config
+	etcdClientCertificateKey *rsa.PrivateKey
 }
 
 // clusterAccessorExists returns true if a clusterAccessor exists for cluster.
@@ -335,11 +348,20 @@ func (t *ClusterCacheTracker) newClusterAccessor(ctx context.Context, cluster cl
 		return nil, err
 	}
 
+	// Generating a new private key to be used for generating temporary certificates to connect to
+	// etcd on the target cluster.
+	// NOTE: Generating a private key is an expensive operation, so we store it in the cluster accessor.
+	etcdKey, err := certs.NewPrivateKey()
+	if err != nil {
+		return nil, errors.Wrapf(err, "error creating etcd client key for remote cluster %q", cluster.String())
+	}
+
 	return &clusterAccessor{
-		cache:   cache,
-		config:  config,
-		client:  delegatingClient,
-		watches: sets.Set[string]{},
+		cache:                    cache,
+		config:                   config,
+		client:                   delegatingClient,
+		watches:                  sets.Set[string]{},
+		etcdClientCertificateKey: etcdKey,
 	}, nil
 }
 
diff --git a/controlplane/kubeadm/internal/cluster.go b/controlplane/kubeadm/internal/cluster.go
index 47c4e419b946..60b71604cff1 100644
--- a/controlplane/kubeadm/internal/cluster.go
+++ b/controlplane/kubeadm/internal/cluster.go
@@ -141,7 +141,12 @@ func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.O
 	// TODO: consider if we can detect if we are using external etcd in a more explicit way (e.g. looking at the config instead of deriving from the existing certificates)
 	var clientCert tls.Certificate
 	if keyData != nil {
-		clientCert, err = generateClientCert(crtData, keyData)
+		clientKey, err := m.Tracker.GetEtcdClientCertificateKey(ctx, clusterKey)
+		if err != nil {
+			return nil, err
+		}
+
+		clientCert, err = generateClientCert(crtData, keyData, clientKey)
 		if err != nil {
 			return nil, err
 		}
diff --git a/controlplane/kubeadm/internal/workload_cluster.go b/controlplane/kubeadm/internal/workload_cluster.go
index 7ffacda66193..4112106f570e 100644
--- a/controlplane/kubeadm/internal/workload_cluster.go
+++ b/controlplane/kubeadm/internal/workload_cluster.go
@@ -489,11 +489,7 @@ func calculateAPIServerPort(config *bootstrapv1.KubeadmConfig) int32 {
 	return 6443
 }
 
-func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, error) {
-	privKey, err := certs.NewPrivateKey()
-	if err != nil {
-		return tls.Certificate{}, err
-	}
+func generateClientCert(caCertEncoded, caKeyEncoded []byte, clientKey *rsa.PrivateKey) (tls.Certificate, error) {
 	caCert, err := certs.DecodeCertPEM(caCertEncoded)
 	if err != nil {
 		return tls.Certificate{}, err
@@ -502,11 +498,11 @@ func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, er
 	if err != nil {
 		return tls.Certificate{}, err
 	}
-	x509Cert, err := newClientCert(caCert, privKey, caKey)
+	x509Cert, err := newClientCert(caCert, clientKey, caKey)
 	if err != nil {
 		return tls.Certificate{}, err
 	}
-	return tls.X509KeyPair(certs.EncodeCertPEM(x509Cert), certs.EncodePrivateKeyPEM(privKey))
+	return tls.X509KeyPair(certs.EncodeCertPEM(x509Cert), certs.EncodePrivateKeyPEM(clientKey))
 }
 
 func newClientCert(caCert *x509.Certificate, key *rsa.PrivateKey, caKey crypto.Signer) (*x509.Certificate, error) {