diff --git a/src/KubernetesClient.Classic/CertUtils.cs b/src/KubernetesClient.Classic/CertUtils.cs
index 112ef922e..21bebebba 100644
--- a/src/KubernetesClient.Classic/CertUtils.cs
+++ b/src/KubernetesClient.Classic/CertUtils.cs
@@ -7,6 +7,7 @@
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System.Security.Cryptography.X509Certificates;
+using System.Text;
namespace k8s
{
@@ -39,6 +40,33 @@ public static X509Certificate2Collection LoadPemFileCert(string file)
return certCollection;
}
+ ///
+ /// Load pem encoded certificates from text
+ ///
+ /// PEM encoded certificate text
+ /// List of x509 instances.
+ public static X509Certificate2Collection LoadFromPemText(string pemText)
+ {
+ var certCollection = new X509Certificate2Collection();
+ using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(pemText)))
+ {
+ var certs = new X509CertificateParser().ReadCertificates(stream);
+
+ // Convert BouncyCastle X509Certificates to the .NET cryptography implementation and add
+ // it to the certificate collection
+ //
+ foreach (Org.BouncyCastle.X509.X509Certificate cert in certs)
+ {
+ // This null password is to change the constructor to fix this KB:
+ // https://support.microsoft.com/en-us/topic/kb5025823-change-in-how-net-applications-import-x-509-certificates-bf81c936-af2b-446e-9f7a-016f4713b46b
+ string nullPassword = null;
+ certCollection.Add(new X509Certificate2(cert.GetEncoded(), nullPassword));
+ }
+ }
+
+ return certCollection;
+ }
+
///
/// Generates pfx from client configuration
///
diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs
index 7a398d7e8..1f1a2180c 100644
--- a/src/KubernetesClient/CertUtils.cs
+++ b/src/KubernetesClient/CertUtils.cs
@@ -23,6 +23,18 @@ public static X509Certificate2Collection LoadPemFileCert(string file)
return certCollection;
}
+ ///
+ /// Load pem encoded certificates from text
+ ///
+ /// PEM encoded certificate text
+ /// List of x509 instances.
+ public static X509Certificate2Collection LoadFromPemText(string pemText)
+ {
+ var certCollection = new X509Certificate2Collection();
+ certCollection.ImportFromPem(pemText);
+ return certCollection;
+ }
+
///
/// Generates pfx from client configuration
///
diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
index f25c55bbb..ca9e206bd 100644
--- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
+++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs
@@ -4,7 +4,6 @@
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
-using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace k8s
@@ -308,26 +307,14 @@ private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext
if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData))
{
var data = clusterDetails.ClusterEndpoint.CertificateAuthorityData;
-#if NET9_0_OR_GREATER
- SslCaCerts = new X509Certificate2Collection(X509CertificateLoader.LoadCertificate(Convert.FromBase64String(data)));
-#else
- string nullPassword = null;
- // This null password is to change the constructor to fix this KB:
- // https://support.microsoft.com/en-us/topic/kb5025823-change-in-how-net-applications-import-x-509-certificates-bf81c936-af2b-446e-9f7a-016f4713b46b
- SslCaCerts = new X509Certificate2Collection(new X509Certificate2(Convert.FromBase64String(data), nullPassword));
-#endif
+ var pemText = Encoding.UTF8.GetString(Convert.FromBase64String(data));
+ SslCaCerts = CertUtils.LoadFromPemText(pemText);
}
else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
-#if NET9_0_OR_GREATER
- SslCaCerts = new X509Certificate2Collection(X509CertificateLoader.LoadCertificateFromFile(GetFullPath(
+ SslCaCerts = CertUtils.LoadPemFileCert(GetFullPath(
k8SConfig,
- clusterDetails.ClusterEndpoint.CertificateAuthority)));
-#else
- SslCaCerts = new X509Certificate2Collection(new X509Certificate2(GetFullPath(
- k8SConfig,
- clusterDetails.ClusterEndpoint.CertificateAuthority)));
-#endif
+ clusterDetails.ClusterEndpoint.CertificateAuthority));
}
}
}
diff --git a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
index a1239c9e9..397c4f431 100644
--- a/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
+++ b/tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
@@ -822,5 +822,31 @@ public void LoadInClusterNamespace()
Assert.Equal("some namespace", config.Namespace);
}
}
+
+ ///
+ /// Checks that multiple certificates are loaded from certificate-authority-data
+ ///
+ [Fact]
+ public void LoadMultipleCertificatesFromCertificateAuthorityData()
+ {
+ var fi = new FileInfo("assets/kubeconfig.multi-ca.yml");
+ var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "multi-ca-context");
+
+ Assert.NotNull(cfg.SslCaCerts);
+ Assert.Equal(2, cfg.SslCaCerts.Count);
+ }
+
+ ///
+ /// Checks that multiple certificates are loaded from certificate-authority file
+ ///
+ [Fact]
+ public void LoadMultipleCertificatesFromCertificateAuthorityFile()
+ {
+ var fi = new FileInfo("assets/kubeconfig.multi-ca-file.yml");
+ var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "multi-ca-file-context", useRelativePaths: false);
+
+ Assert.NotNull(cfg.SslCaCerts);
+ Assert.Equal(2, cfg.SslCaCerts.Count);
+ }
}
}
diff --git a/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca-file.yml b/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca-file.yml
new file mode 100644
index 000000000..af1340adf
--- /dev/null
+++ b/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca-file.yml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Config
+clusters:
+- cluster:
+ certificate-authority: assets/ca-bundle.crt
+ server: https://multi-ca-file-test.example.com:6443
+ name: multi-ca-file-cluster
+contexts:
+- context:
+ cluster: multi-ca-file-cluster
+ user: test-user
+ name: multi-ca-file-context
+current-context: multi-ca-file-context
+users:
+- name: test-user
+ user:
+ token: test-token
diff --git a/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca.yml b/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca.yml
new file mode 100644
index 000000000..0d9f3321a
--- /dev/null
+++ b/tests/KubernetesClient.Tests/assets/kubeconfig.multi-ca.yml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Config
+clusters:
+- cluster:
+ certificate-authority-data: LS0tIENvbW1lbnRzIHRvIG1ha2Ugc3VyZSB3ZSBzdGlsbCBwYXJzZSB0aGUgY2VydCBjb3JyZWN0bHkKCkludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZQoKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lDRUFFd0RRWUpLb1pJaHZjTkFRRUxCUUF3RlRFVE1CRUdBMVVFQXd3S2EzVmkKWlhKdVpYUmxjekFlRncweE9UQXpNRE14TnpBNE1EbGFGdzB5T1RBeU1qZ3hOekE0TURsYU1CWXhGREFTQmdOVgpCQU1NQzJWMFkyaGhibWN0ZFdJMU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCngxVHA3RGEzTmJqZEhtWWRZWi9HTnBDUkd2RkZhcDdFRzFwb2toZklMS1NiUHVzcWlPOXduS0RFNEFmZG4vWkUKQ1FWMFdod3RveDNqY3pCT0lSeStQNkZ2bFB5aEFwVXB5blZUd2dDaXVoVE0rdGhnT0RncGU2R1htVmxWSkd2dgpBb0x3N0NNbmRCNXNNczVISCtxQTJVMXE0VkZJL2NzcjMveWVLeldCaWszZFpWb2gwNHNJOVdUVkwrYmwvMVg1CjBkbDVxcnFrWWlEeDh5Y0FIeU9ubDhkaEpXK1JHbDY3SGlsaXVVZVNxNnZ3c2Z2OXJoM1RQOXdIVkYxUFhGSnAKV2ZYeTRXYkxtdWxkNXd4WG5RVk8yZzUxanFmcU45ZkQ4RkhJa2FlMUlrTy9QVVR1Y2xvTmxMaUZzcmFnUU9URApSVlNQK1RWM2dzaEFUQnMyTU1WWE13SURBUUFCbzJZd1pEQWRCZ05WSFE0RUZnUVUvM3c5QVIyY25FZXBXSDRFCjhhMXhMWkFuanlrd0h3WURWUjBqQkJnd0ZvQVVMcy9semN0OENHdlZkSWlxNHQ5VDRpZHU1T3d3RWdZRFZSMFQKQVFIL0JBZ3dCZ0VCL3dJQkFEQU9CZ05WSFE4QkFmOEVCQU1DQVlZd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQgpBS3c3NDFWMXdzekl0aEhCVjhkdkN5UW95b3pCSnVBbzRJSGJpaUZtenVpUXV5c2hNY1grUXM5YStnNk9HNWQxClVid0ZmVWxxem1aUWNiY1IvSmM2d016M3dPNkhveTVwUzN3L0ZSMlVNR1IzOW85NS83WENrVElPd0NxYXU2UHcKZHBndmJuYWlxUEZQcUQzb2hkVXVWUmNYRzN2YTVBbUtUc1VuN20rbFIvOTMvcXB0dCtTVVZwNmp3bmJHY3dvQgpzM3UyWFh4NXMxTTd0cXFqM3RBRU9QQ0tsb2hTNm1RNFgzd3VsZ3BaMVhwSjBXVHZjdm9QWEV0QTU2azd2WDNhCjRFNng2NkxaQ0ZBMlpSLzVDT3Y1RDA1NUFocmloS0w4a2JBdXR4aGZBMjdTSi9NR293em1UVDdrVlFoYTNTdTMKYW9PWVpnY1V3dytTa1JTR1ZydGdNZ1E9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KClJvb3QgY2VydGlmaWNhdGUKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFRENDQWZpZ0F3SUJBZ0lKQUpwYjlpcktnMkpqTUEwR0NTcUdTSWIzRFFFQkN3VUFNQlV4RXpBUkJnTlYKQkFNTUNtdDFZbVZ5Ym1WMFpYTXdIaGNOTVRrd016QXpNREF5TVRJM1doY05Nemt3TWpJMk1EQXlNVEkzV2pBVgpNUk13RVFZRFZRUUREQXByZFdKbGNtNWxkR1Z6TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCCkNnS0NBUUVBdW9LK05hK0x5S0lpcG1mdEJ3NFkrWjE5Zzh5djZZK2RZdDZLbEJnMjRYSE5BMHYxY013dE9DREYKTWxtNHJzRDdKZDVVTzZ1Z2RrM2Z4RXZHR2hteldUWFNCUlVXY1RiU2NBTTQ5bUFMQkZrQ3ZOVFBLMnZWaGs3UAppbTJRUWw4YTV2all6OEhMS0pUYi9PKzBxK0trdHBkN1hUYVUyVTdaZWJpTFZzNWJ2TmJiM1pEdElqQUFSWTlTCmFsWjRoT3p1Vk5hU1g5TUJScVRXcTNIdUt3RGlWVFQzZGFuL0FCb1U4TmRlZFBmSWJ5WTQ4d2lRZ2pFWWI2NGcKM2dlWXBBckxRZWZmbzhmbWhVRVBSUi8xV3Jmdll2dm04c1Y4alQrcnF4SVRLSjVWbzVrcFpVcG9tRE90R1ZNUwpnR0FsZTZtY1RycWxyc0NGYzRnRlJSb0hpSDFPRFFJREFRQUJvMk13WVRBZEJnTlZIUTRFRmdRVUxzL2x6Y3Q4CkNHdlZkSWlxNHQ5VDRpZHU1T3d3SHdZRFZSMGpCQmd3Rm9BVUxzL2x6Y3Q4Q0d2VmRJaXE0dDlUNGlkdTVPd3cKRHdZRFZSMFRBUUgvQkFVd0F3RUIvekFPQmdOVkhROEJBZjhFQkFNQ0FZWXdEUVlKS29aSWh2Y05BUUVMQlFBRApnZ0VCQUZNcTV6NE9vYUlocXgvaS9idHBWTFJuUURjcER3ZFV1ckUwaU5QejNiZ09JNVFpSWU5NW9Vd1hTRlFMCmNpRUN2T2JmTW1pVnV6K3A3TkQ1ZU5keFlSNGhscTFXMVBZY2dSUWd1c1hDQzRYZC9YQUdhTFpYekgzU0JybXAKYnM1c2Zva2hYS05jY3NuUXU1WWEzSlJrQUx4eFVKK0RjT24rdmk5Z21FQXppK25YYnlxVWpJaFNENW55Z0NsWAowYVNLYnZoVW1YeWFKcFVIMGk3ZFN4V1AzTHFDRGp0cnUvNWVqTnRCMDk3ZE5jeUY4anMzWXVrM2h3cXllZ1F4CkVMbjRjL1RLUEw5TDh2RTd0SmcvTTc4RFBBdlJDaXV3bDBIUWNhc0JFMkFYMHdkcFkwVWVYc05EeXpVZi8yV0YKZkhZNERudUJkZVZkSHRsMXlQbFhtUWtNb1FNPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ server: https://multi-ca-test.example.com:6443
+ name: multi-ca-cluster
+contexts:
+- context:
+ cluster: multi-ca-cluster
+ user: test-user
+ name: multi-ca-context
+current-context: multi-ca-context
+users:
+- name: test-user
+ user:
+ token: test-token