Я работаю над реализацией взаимных TLS (MTLS) в приложении C#, где: < /p>
Сервер управления ключами (KMS) генерирует сертификат корневого CA.
Знаки KMS Сертификат клиента для NMS (система управления сетью).
Клиент NMS представит свой подписанный сертификат при подключении к серверу KMS через TLS.
Сервер KMS будет проверять сертификат клиента, чтобы убедиться, что он был выпущен Корнем КМС КНЕЙ, прежде чем разрешить общение.
Я хотел бы достичь этого, используя библиотеку Bouncy Castle в C#. < /p>
Мой прогресс:
Я могу генерировать корень Сертификат CA и подпишите сертификат клиента, используя Bouncy Castle.
Я хочу:
Использовать сгенерированные сертификаты для сервера и клиента.
Проверьте сертификат клиента на стороне сервера во время рукопожатия TLS.
установить безопасную связь после успешной проверки.
Что мне нужна с помощью:
Как я могу правильно настроить сервер (sslstream) для проверки сертификата клиента во время рукопожатия?
Как Настроить ли я клиент для представления своего сертификата при установлении соединения TLS?
Можете ли вы привести полный пример реализаций сервера и клиентов для этого варианта использования?
Примечания:
Я хочу Используйте Bouncy Castle для создания и проверки сертификата.
Сервер и клиент общаются через TCP.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.OpenSsl;
using System;
using System.IO;
using Org.BouncyCastle.Crypto.Operators;
using System.Configuration;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Net;
namespace CertificateGeneration
{
public class NmsCertificateGenerator
{
public static void GenerateCertificate(string kmsKeysPath, string nmsCertificatesPath)
{
// Generate and save KMS certificate and key
GenerateAndSaveKmsCertificate(kmsKeysPath);
// Generate and save NMS certificate using KMS keys
GenerateAndSaveNmsCertificate(kmsKeysPath, nmsCertificatesPath);
Console.WriteLine("Certificate and key generation completed.");
}
private static void GenerateAndSaveKmsCertificate(string kmsKeysPath)
{
var keyPair = GenerateRsaKeyPair(2048);
var cert = GenerateSelfSignedCertificate(keyPair, "CN=127.0.0.1");
// Save private key and certificate in PEM format
File.WriteAllText(Path.Combine(kmsKeysPath, "kms_private_key.pem"), ExportPrivateKeyToPem(keyPair));
File.WriteAllText(Path.Combine(kmsKeysPath, "kms_certificate.pem"), ExportCertificateToPem(cert));
// Export the public key from the KMS certificate
File.WriteAllText(Path.Combine(kmsKeysPath, "kms_public_key.pem"), ExportPublicKeyToPem(keyPair));
Console.WriteLine("KMS Certificate and Private Key saved successfully.");
}
private static void GenerateAndSaveNmsCertificate(string kmsKeysPath, string nmsCertificatesPath)
{
// Load KMS private key and certificate
var kmsKeyPair = LoadKeyPairFromPem(kmsKeysPath, "kms_private_key.pem");
var kmsCert = LoadCertificateFromPem(kmsKeysPath, "kms_certificate.pem");
// Generate NMS key pair
var nmsKeyPair = GenerateRsaKeyPair(2048);
// Generate NMS certificate signed by KMS
var nmsCert = GenerateCertificateSignedByKms(nmsKeyPair, kmsKeyPair, kmsCert, "CN=NMS Certificate");
// Save NMS private key and certificate in PEM format
File.WriteAllText(Path.Combine(nmsCertificatesPath, "nms_private_key.pem"), ExportPrivateKeyToPem(nmsKeyPair));
File.WriteAllText(Path.Combine(nmsCertificatesPath, "nms_certificate.pem"), ExportCertificateToPem(nmsCert));
// Export the public key from the NMS certificate
File.WriteAllText(Path.Combine(nmsCertificatesPath, "nms_public_key.pem"), ExportPublicKeyToPem(nmsKeyPair));
Console.WriteLine("NMS Certificate and Private Key saved successfully.");
}
private static AsymmetricCipherKeyPair GenerateRsaKeyPair(int keySize)
{
var keyGen = new RsaKeyPairGenerator();
keyGen.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
return keyGen.GenerateKeyPair();
}
private static Org.BouncyCastle.X509.X509Certificate GenerateSelfSignedCertificate(AsymmetricCipherKeyPair keyPair, string subjectName)
{
var certGen = new X509V3CertificateGenerator();
var secureRandom = new SecureRandom();
certGen.SetSerialNumber(BigInteger.ProbablePrime(120, secureRandom));
certGen.SetIssuerDN(new X509Name(subjectName));
certGen.SetSubjectDN(new X509Name(subjectName));
certGen.SetNotBefore(DateTime.UtcNow);
certGen.SetNotAfter(DateTime.UtcNow.AddYears(5));
certGen.SetPublicKey(keyPair.Public);
// Use a signature factory to sign the certificate
var signatureFactory = new Asn1SignatureFactory("SHA256WithRSA", keyPair.Private, secureRandom);
return certGen.Generate(signatureFactory);
}
private static Org.BouncyCastle.X509.X509Certificate GenerateCertificateSignedByKms(AsymmetricCipherKeyPair nmsKeyPair, AsymmetricCipherKeyPair kmsKeyPair, Org.BouncyCastle.X509.X509Certificate kmsCert, string subjectName)
{
var certGen = new X509V3CertificateGenerator();
var secureRandom = new SecureRandom();
certGen.SetSerialNumber(BigInteger.ProbablePrime(120, secureRandom));
certGen.SetIssuerDN(kmsCert.IssuerDN);
certGen.SetSubjectDN(new X509Name(subjectName));
certGen.SetNotBefore(DateTime.UtcNow);
certGen.SetNotAfter(DateTime.UtcNow.AddYears(5));
certGen.SetPublicKey(nmsKeyPair.Public);
// Use a signature factory to sign the certificate with the KMS private key
var signatureFactory = new Asn1SignatureFactory("SHA256WithRSA", kmsKeyPair.Private, secureRandom);
return certGen.Generate(signatureFactory);
}
private static string ExportPrivateKeyToPem(AsymmetricCipherKeyPair keyPair)
{
using (var stringWriter = new StringWriter())
{
var pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject(keyPair.Private);
pemWriter.Writer.Flush();
return stringWriter.ToString();
}
}
private static string ExportPublicKeyToPem(AsymmetricCipherKeyPair keyPair)
{
using (var stringWriter = new StringWriter())
{
var pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject(keyPair.Public); // Exporting the public key instead of the private key
pemWriter.Writer.Flush();
return stringWriter.ToString();
}
}
private static string ExportCertificateToPem(Org.BouncyCastle.X509.X509Certificate cert)
{
using (var stringWriter = new StringWriter())
{
var pemWriter = new PemWriter(stringWriter);
pemWriter.WriteObject(cert);
pemWriter.Writer.Flush();
return stringWriter.ToString();
}
}
private static AsymmetricCipherKeyPair LoadKeyPairFromPem(string path, string privateKeyFileName)
{
using (var reader = new StreamReader(Path.Combine(path, privateKeyFileName)))
{
var pemReader = new PemReader(reader);
return (AsymmetricCipherKeyPair)pemReader.ReadObject();
}
}
private static Org.BouncyCastle.X509.X509Certificate LoadCertificateFromPem(string path, string certificateFileName)
{
using (var reader = new StreamReader(Path.Combine(path, certificateFileName)))
{
var pemReader = new PemReader(reader);
return (Org.BouncyCastle.X509.X509Certificate)pemReader.ReadObject();
}
}
public static void StartTlsServer()
{
try
{
int port = int.Parse(ConfigurationManager.AppSettings["Port"]);
string kmsKeysPath = ConfigurationManager.AppSettings["KmsKeysPath"];
string nmsCertificatesPath = ConfigurationManager.AppSettings["NmsCertificatesPath"];
if (string.IsNullOrEmpty(kmsKeysPath) || string.IsNullOrEmpty(nmsCertificatesPath))
{
throw new ArgumentNullException("One or more configuration settings are missing or empty.");
}
// Load the KMS certificate and private key from PEM files
X509Certificate2 serverCertificate = LoadServerCertificateFromPem(kmsKeysPath);
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
Console.WriteLine($"Server started on port {port}");
while (true)
{
TcpClient client = listener.AcceptTcpClient();
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateClientCertificate), null);
try
{
// Authenticate the client
sslStream.AuthenticateAsServer(serverCertificate, true, System.Security.Authentication.SslProtocols.Tls12, true);
Console.WriteLine("Client connected and authenticated.");
// Handle further interaction with the client
// Here the server reads and validates the client certificate, sends the response
// ...
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
finally
{
sslStream.Close();
client.Close();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception during server start: {ex.Message}");
}
}
private static X509Certificate2 LoadServerCertificateFromPem(string kmsKeysPath)
{
string certFile = Path.Combine(kmsKeysPath, "kms_certificate.pem");
string privateKeyFile = Path.Combine(kmsKeysPath, "kms_private_key.pem");
if (File.Exists(certFile) && File.Exists(privateKeyFile))
{
try
{
using (var certReader = new StreamReader(certFile))
using (var keyReader = new StreamReader(privateKeyFile))
{
var pemReader = new PemReader(certReader);
var certObject = pemReader.ReadObject() as Org.BouncyCastle.X509.X509Certificate;
pemReader = new PemReader(keyReader);
var keyObject = pemReader.ReadObject() as AsymmetricCipherKeyPair;
var cert = new X509Certificate2(DotNetUtilities.ToX509Certificate(certObject));
var rsa = DotNetUtilities.ToRSA(keyObject.Private as RsaPrivateCrtKeyParameters);
Console.WriteLine("Successfully loaded certificate and private key.");
return cert.CopyWithPrivateKey(rsa);
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception while loading certificate and private key: {ex.Message}");
}
}
throw new FileNotFoundException("No matching certificate and private key pair found.");
}
private static bool ValidateClientCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors))
{
// Load the KMS public key
var kmsKeysPath = ConfigurationManager.AppSettings["KmsKeysPath"];
var kmsPublicKey = LoadPublicKeyFromPem(kmsKeysPath);
// Convert the client certificate to BouncyCastle format
var clientCert = DotNetUtilities.FromX509Certificate(new X509Certificate2(certificate));
try
{
// Verify the client certificate using the KMS public key
clientCert.Verify(kmsPublicKey);
Console.WriteLine("Client certificate verified successfully.");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Client certificate verification failed: {ex.Message}");
}
}
return false;
}
private static AsymmetricKeyParameter LoadPublicKeyFromPem(string kmsKeysPath)
{
string publicKeyFile = Path.Combine(kmsKeysPath, "kms_public_key.pem");
using (var reader = new StreamReader(publicKeyFile))
{
var pemReader = new PemReader(reader);
return (AsymmetricKeyParameter)pemReader.ReadObject();
}
}
}
}
Код на стороне клиента
`
using System;
using System.IO;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Configuration;
using System.Threading;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using BouncyCastleX509Certificate = Org.BouncyCastle.X509.X509Certificate;
namespace NmsClientApp
{
public class NmsClient
{
public static void ConnectToKms()
{
// Configuration parameters
string serverAddress = ConfigurationManager.AppSettings\["ServerAddress"\];
int port = int.Parse(ConfigurationManager.AppSettings\["Port"\]);
string pemDirectory = ConfigurationManager.AppSettings\["PemDirectory"\];
int retryInterval = int.Parse(ConfigurationManager.AppSettings\["RetryInterval"\]); // Retry interval from config (in ms)
while (true)
{
try
{
// Retrieve all PEM files in the specified directory
string[] pemFiles = Directory.GetFiles(pemDirectory, "*.pem");
if (pemFiles.Length == 0)
{
throw new FileNotFoundException("No PEM files found in the specified directory.");
}
foreach (string pemFile in pemFiles)
{
Console.WriteLine($"Processing PEM file: {pemFile}");
// Load the client certificate from the PEM file
X509Certificate2 clientCertificate = LoadCertificateFromPem(pemFile);
// Create a TCP client and establish a connection to the server
Console.WriteLine($"Connecting to KMS server at {serverAddress}:{port}...");
using (TcpClient client = new TcpClient(serverAddress, port))
{
SslStream sslStream = new SslStream(client.GetStream(), false, null);
try
{
// Authenticate SSL/TLS connection
sslStream.AuthenticateAsClient(serverAddress, new X509CertificateCollection { clientCertificate }, System.Security.Authentication.SslProtocols.Tls12, false);
Console.WriteLine("SSL/TLS authentication completed.");
// Read the request from the KMS server
Console.WriteLine("Reading request from KMS server...");
byte[] buffer = new byte[4096];
int bytesRead = sslStream.Read(buffer, 0, buffer.Length);
string requestMessage = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Received request: {requestMessage}");
// Respond to the server's request
if (requestMessage == "Send NMS Certificate")
{
// Send the NMS certificate to the KMS server
Console.WriteLine("Sending NMS certificate...");
byte[] certBytes = clientCertificate.Export(X509ContentType.Cert);
sslStream.Write(certBytes, 0, certBytes.Length);
sslStream.Flush();
Console.WriteLine("Sent NMS certificate.");
}
// Read the server's response
Console.WriteLine("Reading response from KMS server...");
bytesRead = sslStream.Read(buffer, 0, buffer.Length);
string responseMessage = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Response from KMS server: {responseMessage}");
// Exit the loop if connection and communication are successful
break;
}
catch (Exception ex)
{
Console.WriteLine($"SSL/TLS authentication failed: {ex.Message}");
throw;
}
finally
{
sslStream.Close();
client.Close();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
Console.WriteLine(ex.StackTrace);
Console.WriteLine($"Retrying in {retryInterval / 1000} seconds...");
Thread.Sleep(retryInterval);
}
}
}
// Load the certificate from the PEM file
private static X509Certificate2 LoadCertificateFromPem(string pemFilePath)
{
try
{
if (!File.Exists(pemFilePath))
{
throw new FileNotFoundException($"PEM file not found: {pemFilePath}");
}
var certParser = new Org.BouncyCastle.X509.X509CertificateParser();
BouncyCastleX509Certificate cert = certParser.ReadCertificate(File.ReadAllBytes(pemFilePath));
var x509Certificate = new X509Certificate2(cert.GetEncoded());
return x509Certificate;
}
catch (Exception ex)
{
Console.WriteLine($"Error loading certificate: {ex.Message}");
throw;
}
}
}
class Program
{
static void Main(string[] args)
{
// Call the ConnectToKms method to initiate the connection process
NmsClient.ConnectToKms();
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... ncy-castle
Как реализовать MTL между клиентом и сервером в C# с помощью Bouncy Castle? ⇐ C#
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Создание Public/Private Pare Pare с помощью Bouncy Castle или .NET RSACRYPTOServiceProvider
Anonymous » » в форуме C# - 0 Ответы
- 10 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Создание Public/Private Pare Pare с помощью Bouncy Castle или .NET RSACRYPTOServiceProvider
Anonymous » » в форуме C# - 0 Ответы
- 2 Просмотры
-
Последнее сообщение Anonymous
-