From c772d8e8c4528a6aa5b28b6a666122c2f3bf0b65 Mon Sep 17 00:00:00 2001 From: Boshi Lian Date: Mon, 11 Sep 2023 07:42:11 -0700 Subject: [PATCH] force using AES for cert algo (#1345) * force using 3des for cert algo * happy build * use aes * happy build --- src/KubernetesClient.Classic/CertUtils.cs | 157 ++++++++++++++++++ .../KubernetesClient.Classic.csproj | 5 +- src/KubernetesClient/CertUtils.cs | 102 ------------ 3 files changed, 159 insertions(+), 105 deletions(-) create mode 100644 src/KubernetesClient.Classic/CertUtils.cs diff --git a/src/KubernetesClient.Classic/CertUtils.cs b/src/KubernetesClient.Classic/CertUtils.cs new file mode 100644 index 000000000..60805cd93 --- /dev/null +++ b/src/KubernetesClient.Classic/CertUtils.cs @@ -0,0 +1,157 @@ +using k8s.Exceptions; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; +using System.IO; +using System.Security.Cryptography.X509Certificates; + +namespace k8s +{ + internal static class CertUtils + { + /// + /// Load pem encoded cert file + /// + /// Path to pem encoded cert file + /// List of x509 instances. + public static X509Certificate2Collection LoadPemFileCert(string file) + { + var certCollection = new X509Certificate2Collection(); + using (var stream = FileSystem.Current.OpenRead(file)) + { + 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 + /// + /// Kubernetes Client Configuration + /// Generated Pfx Path + public static X509Certificate2 GeneratePfx(KubernetesClientConfiguration config) + { + if (config == null) + { + throw new ArgumentNullException(nameof(config)); + } + + byte[] keyData = null; + byte[] certData = null; + + if (!string.IsNullOrWhiteSpace(config.ClientCertificateKeyData)) + { + keyData = Convert.FromBase64String(config.ClientCertificateKeyData); + } + + if (!string.IsNullOrWhiteSpace(config.ClientKeyFilePath)) + { + keyData = File.ReadAllBytes(config.ClientKeyFilePath); + } + + if (keyData == null) + { + throw new KubeConfigException("keyData is empty"); + } + + if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) + { + certData = Convert.FromBase64String(config.ClientCertificateData); + } + + if (!string.IsNullOrWhiteSpace(config.ClientCertificateFilePath)) + { + certData = File.ReadAllBytes(config.ClientCertificateFilePath); + } + + if (certData == null) + { + throw new KubeConfigException("certData is empty"); + } + + var cert = new X509CertificateParser().ReadCertificate(new MemoryStream(certData)); + // key usage is a bit string, zero-th bit is 'digitalSignature' + // See https://www.alvestrand.no/objectid/2.5.29.15.html for more details. + if (cert != null && cert.GetKeyUsage() != null && !cert.GetKeyUsage()[0]) + { + throw new Exception( + "Client certificates must be marked for digital signing. " + + "See https://github.com/kubernetes-client/csharp/issues/319"); + } + + object obj; + using (var reader = new StreamReader(new MemoryStream(keyData))) + { + obj = new PemReader(reader).ReadObject(); + if (obj is AsymmetricCipherKeyPair key) + { + var cipherKey = key; + obj = cipherKey.Private; + } + } + + var keyParams = (AsymmetricKeyParameter)obj; + + var store = new Pkcs12StoreBuilder() + .SetKeyAlgorithm(NistObjectIdentifiers.IdAes128Cbc, PkcsObjectIdentifiers.IdHmacWithSha1) + .Build(); + store.SetKeyEntry("K8SKEY", new AsymmetricKeyEntry(keyParams), new[] { new X509CertificateEntry(cert) }); + + using var pkcs = new MemoryStream(); + + store.Save(pkcs, new char[0], new SecureRandom()); + + // 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; + + if (config.ClientCertificateKeyStoreFlags.HasValue) + { + return new X509Certificate2(pkcs.ToArray(), nullPassword, config.ClientCertificateKeyStoreFlags.Value); + } + else + { + return new X509Certificate2(pkcs.ToArray(), nullPassword); + } + } + + /// + /// Retrieves Client Certificate PFX from configuration + /// + /// Kubernetes Client Configuration + /// Client certificate PFX + public static X509Certificate2 GetClientCert(KubernetesClientConfiguration config) + { + if (config == null) + { + throw new ArgumentNullException(nameof(config)); + } + + if ((!string.IsNullOrWhiteSpace(config.ClientCertificateData) || + !string.IsNullOrWhiteSpace(config.ClientCertificateFilePath)) && + (!string.IsNullOrWhiteSpace(config.ClientCertificateKeyData) || + !string.IsNullOrWhiteSpace(config.ClientKeyFilePath))) + { + return GeneratePfx(config); + } + + return null; + } + } +} diff --git a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj index 7ea498c10..d6c21edfa 100644 --- a/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj +++ b/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj @@ -6,8 +6,8 @@ - - + + @@ -18,7 +18,6 @@ - diff --git a/src/KubernetesClient/CertUtils.cs b/src/KubernetesClient/CertUtils.cs index 347771417..04a8245e4 100644 --- a/src/KubernetesClient/CertUtils.cs +++ b/src/KubernetesClient/CertUtils.cs @@ -1,14 +1,6 @@ using k8s.Exceptions; -#if !NET5_0_OR_GREATER -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.X509; -#else using System.Runtime.InteropServices; using System.Text; -#endif using System.IO; using System.Security.Cryptography.X509Certificates; @@ -26,22 +18,7 @@ public static X509Certificate2Collection LoadPemFileCert(string file) var certCollection = new X509Certificate2Collection(); using (var stream = FileSystem.Current.OpenRead(file)) { -#if NET5_0_OR_GREATER certCollection.ImportFromPem(new StreamReader(stream).ReadToEnd()); -#else - 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)); - } -#endif } return certCollection; @@ -59,7 +36,6 @@ public static X509Certificate2 GeneratePfx(KubernetesClientConfiguration config) throw new ArgumentNullException(nameof(config)); } -#if NET5_0_OR_GREATER string keyData = null; string certData = null; @@ -114,84 +90,6 @@ public static X509Certificate2 GeneratePfx(KubernetesClientConfiguration config) } return cert; -#else - - byte[] keyData = null; - byte[] certData = null; - - if (!string.IsNullOrWhiteSpace(config.ClientCertificateKeyData)) - { - keyData = Convert.FromBase64String(config.ClientCertificateKeyData); - } - - if (!string.IsNullOrWhiteSpace(config.ClientKeyFilePath)) - { - keyData = File.ReadAllBytes(config.ClientKeyFilePath); - } - - if (keyData == null) - { - throw new KubeConfigException("keyData is empty"); - } - - if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) - { - certData = Convert.FromBase64String(config.ClientCertificateData); - } - - if (!string.IsNullOrWhiteSpace(config.ClientCertificateFilePath)) - { - certData = File.ReadAllBytes(config.ClientCertificateFilePath); - } - - if (certData == null) - { - throw new KubeConfigException("certData is empty"); - } - - var cert = new X509CertificateParser().ReadCertificate(new MemoryStream(certData)); - // key usage is a bit string, zero-th bit is 'digitalSignature' - // See https://www.alvestrand.no/objectid/2.5.29.15.html for more details. - if (cert != null && cert.GetKeyUsage() != null && !cert.GetKeyUsage()[0]) - { - throw new Exception( - "Client certificates must be marked for digital signing. " + - "See https://github.com/kubernetes-client/csharp/issues/319"); - } - - object obj; - using (var reader = new StreamReader(new MemoryStream(keyData))) - { - obj = new PemReader(reader).ReadObject(); - if (obj is AsymmetricCipherKeyPair key) - { - var cipherKey = key; - obj = cipherKey.Private; - } - } - - var keyParams = (AsymmetricKeyParameter)obj; - - var store = new Pkcs12StoreBuilder().Build(); - store.SetKeyEntry("K8SKEY", new AsymmetricKeyEntry(keyParams), new[] { new X509CertificateEntry(cert) }); - - using var pkcs = new MemoryStream(); - - store.Save(pkcs, new char[0], new SecureRandom()); - - // 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; - - if (config.ClientCertificateKeyStoreFlags.HasValue) - { - return new X509Certificate2(pkcs.ToArray(), nullPassword, config.ClientCertificateKeyStoreFlags.Value); - } - else - { - return new X509Certificate2(pkcs.ToArray(), nullPassword); - } -#endif } ///