2017-10-13 03:06:35 +08:00
|
|
|
using k8s.Exceptions;
|
|
|
|
|
using Org.BouncyCastle.Crypto;
|
|
|
|
|
using Org.BouncyCastle.OpenSsl;
|
|
|
|
|
using Org.BouncyCastle.Pkcs;
|
|
|
|
|
using Org.BouncyCastle.Security;
|
|
|
|
|
using Org.BouncyCastle.X509;
|
2019-03-11 06:39:28 -07:00
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
2017-09-22 20:59:41 -07:00
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
namespace k8s
|
|
|
|
|
{
|
2017-11-08 14:22:10 +08:00
|
|
|
public static class CertUtils
|
2017-09-22 20:59:41 -07:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
2017-10-11 21:27:22 +08:00
|
|
|
/// Load pem encoded cert file
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="file">Path to pem encoded cert file</param>
|
2019-03-11 06:39:28 -07:00
|
|
|
/// <returns>List of x509 instances.</returns>
|
|
|
|
|
public static X509Certificate2Collection LoadPemFileCert(string file)
|
2017-10-11 21:27:22 +08:00
|
|
|
{
|
2019-03-11 06:39:28 -07:00
|
|
|
var certCollection = new X509Certificate2Collection();
|
2021-06-14 16:22:08 -07:00
|
|
|
using (var stream = FileUtils.FileSystem().File.OpenRead(file))
|
2019-02-15 11:57:24 -08:00
|
|
|
{
|
2021-06-14 16:22:08 -07:00
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
certCollection.Add(new X509Certificate2(cert.GetEncoded()));
|
|
|
|
|
}
|
2019-02-15 11:57:24 -08:00
|
|
|
}
|
2019-03-11 06:39:28 -07:00
|
|
|
|
|
|
|
|
return certCollection;
|
2017-10-11 21:27:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generates pfx from client configuration
|
2017-09-22 20:59:41 -07:00
|
|
|
/// </summary>
|
2018-03-12 17:55:21 -04:00
|
|
|
/// <param name="config">Kubernetes Client Configuration</param>
|
2017-09-22 20:59:41 -07:00
|
|
|
/// <returns>Generated Pfx Path</returns>
|
2017-09-27 21:51:00 -07:00
|
|
|
public static X509Certificate2 GeneratePfx(KubernetesClientConfiguration config)
|
2017-09-22 20:59:41 -07:00
|
|
|
{
|
2020-11-22 14:52:09 -08:00
|
|
|
if (config == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(config));
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
byte[] keyData = null;
|
|
|
|
|
byte[] certData = null;
|
2017-09-22 20:59:41 -07:00
|
|
|
|
2017-10-13 01:53:25 +08:00
|
|
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificateKeyData))
|
2017-09-22 20:59:41 -07:00
|
|
|
{
|
2017-10-13 01:53:25 +08:00
|
|
|
keyData = Convert.FromBase64String(config.ClientCertificateKeyData);
|
2017-09-22 20:59:41 -07:00
|
|
|
}
|
2020-04-23 11:40:06 -07:00
|
|
|
|
2017-10-13 01:53:25 +08:00
|
|
|
if (!string.IsNullOrWhiteSpace(config.ClientKeyFilePath))
|
2017-09-22 20:59:41 -07:00
|
|
|
{
|
2017-10-13 01:53:25 +08:00
|
|
|
keyData = File.ReadAllBytes(config.ClientKeyFilePath);
|
2017-09-22 20:59:41 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
if (keyData == null)
|
|
|
|
|
{
|
2018-06-12 00:09:51 +04:00
|
|
|
throw new KubeConfigException("keyData is empty");
|
2017-10-13 03:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
2017-09-22 20:59:41 -07:00
|
|
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificateData))
|
|
|
|
|
{
|
|
|
|
|
certData = Convert.FromBase64String(config.ClientCertificateData);
|
|
|
|
|
}
|
2020-04-23 11:40:06 -07:00
|
|
|
|
2017-10-13 01:53:25 +08:00
|
|
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificateFilePath))
|
2017-09-22 20:59:41 -07:00
|
|
|
{
|
2017-10-13 01:53:25 +08:00
|
|
|
certData = File.ReadAllBytes(config.ClientCertificateFilePath);
|
2017-09-22 20:59:41 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
if (certData == null)
|
|
|
|
|
{
|
|
|
|
|
throw new KubeConfigException("certData is empty");
|
|
|
|
|
}
|
2017-09-22 20:59:41 -07:00
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
var cert = new X509CertificateParser().ReadCertificate(new MemoryStream(certData));
|
2019-11-21 20:29:28 -08:00
|
|
|
// 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.
|
2020-04-22 12:15:45 -07:00
|
|
|
if (cert != null && cert.GetKeyUsage() != null && !cert.GetKeyUsage()[0])
|
|
|
|
|
{
|
2019-11-21 20:29:28 -08:00
|
|
|
throw new Exception(
|
|
|
|
|
"Client certificates must be marked for digital signing. " +
|
|
|
|
|
"See https://github.com/kubernetes-client/csharp/issues/319");
|
|
|
|
|
}
|
2020-04-23 11:40:06 -07:00
|
|
|
|
2017-10-13 03:06:35 +08:00
|
|
|
object obj;
|
2017-09-22 20:59:41 -07:00
|
|
|
using (var reader = new StreamReader(new MemoryStream(keyData)))
|
|
|
|
|
{
|
2017-10-13 03:06:35 +08:00
|
|
|
obj = new PemReader(reader).ReadObject();
|
|
|
|
|
var key = obj as AsymmetricCipherKeyPair;
|
|
|
|
|
if (key != null)
|
|
|
|
|
{
|
|
|
|
|
var cipherKey = key;
|
2017-09-27 21:51:00 -07:00
|
|
|
obj = cipherKey.Private;
|
|
|
|
|
}
|
2017-10-13 03:06:35 +08:00
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:15:45 -07:00
|
|
|
var keyParams = (AsymmetricKeyParameter)obj;
|
2017-10-13 03:06:35 +08:00
|
|
|
|
|
|
|
|
var store = new Pkcs12StoreBuilder().Build();
|
2020-04-22 12:15:45 -07:00
|
|
|
store.SetKeyEntry("K8SKEY", new AsymmetricKeyEntry(keyParams), new[] { new X509CertificateEntry(cert) });
|
2017-10-13 03:06:35 +08:00
|
|
|
|
|
|
|
|
using (var pkcs = new MemoryStream())
|
|
|
|
|
{
|
2017-10-13 03:34:24 +08:00
|
|
|
store.Save(pkcs, new char[0], new SecureRandom());
|
2019-02-07 07:58:08 +01:00
|
|
|
|
|
|
|
|
if (config.ClientCertificateKeyStoreFlags.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return new X509Certificate2(pkcs.ToArray(), "", config.ClientCertificateKeyStoreFlags.Value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return new X509Certificate2(pkcs.ToArray());
|
|
|
|
|
}
|
2017-09-22 20:59:41 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-20 15:51:58 +02:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Retrieves Client Certificate PFX from configuration
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="config">Kubernetes Client Configuration</param>
|
|
|
|
|
/// <returns>Client certificate PFX</returns>
|
|
|
|
|
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;
|
|
|
|
|
}
|
2017-09-22 20:59:41 -07:00
|
|
|
}
|
2017-10-11 21:27:22 +08:00
|
|
|
}
|