Я новичок в безопасности и шифровании, поэтому, если я только что сделал очень глупую ошибку, заранее извините. Мне нужен сервер и клиент, общение через безопасное соединение с использованием SslStream. Но мои сертификаты не работают. Я получаю следующую ошибку: System.NotSupportedException: 'The server mode SSL must use a certificate with the associated private key.'
Мой код был примером Microsoft, приведенным в документации: https://docs.microsoft.com/en-us/dotnet/api/system.net.security.sslstream?view=netframework-4.8ss
Я пытался:
Все, кроме последнего, выдало исключение System.NotSupportedException: 'The server mode SSL must use a certificate with the associated private key.'
. Означает ли это, что самоподписанные сертификаты не работают? Нужно ли покупать сертификат?
Редактировать: вот код, который я использовал. Это модифицированный пример (извините, если он ужасно закодирован с моей стороны), он исполняемый, имитирует сервер и клиента и выдает исключение:
class Program
{
static void Main(string[] args)
{
//Temporarily added the arguments here for you to see
args = new string[2] { @"C:\Users\jacke\Documents\CA\TempCert.cer", "FakeServerName" };
Console.WriteLine("Starting server in seperate thread...");
Task t = Task.Run(() => { Server.Initialize(args[0]); });
Task.Delay(500).Wait();
Client.RunClient(args[1]);
}
}
public static class Server
{
private static X509Certificate cert;
private static TcpListener server;
public static void Initialize(string certificate)
{
cert = X509Certificate.CreateFromCertFile(certificate);
server = new TcpListener(IPAddress.Any, 12321);
server.Start();
while (true)
{
Console.WriteLine("Waiting for a client to connect...");
TcpClient client = server.AcceptTcpClient();
ProcessClient(client);
}
}
private static void ProcessClient(TcpClient client)
{
SslStream sslStream = new SslStream(client.GetStream(), false);
try
{
sslStream.AuthenticateAsServer(cert, clientCertificateRequired: false, checkCertificateRevocation: true);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
Console.WriteLine("Waiting for client message...");
string messageData = Helpers.ReadMessage(sslStream);
byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
Console.WriteLine("Sending hello message.");
sslStream.Write(message);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
sslStream.Close();
client.Close();
return;
}
finally
{
sslStream.Close();
client.Close();
}
}
}
public static class Client
{
private static Hashtable certificateErrors = new Hashtable();
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
public static void RunClient(string serverName)
{
TcpClient client = new TcpClient("localhost", 12321);
Console.WriteLine("Client connected.");
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
try
{
sslStream.AuthenticateAsClient(serverName);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
sslStream.Write(messsage);
string serverMessage = Helpers.ReadMessage(sslStream);
Console.WriteLine("Server says: {0}", serverMessage);
client.Close();
Console.WriteLine("Client closed.");
}
}
public static class Helpers
{
public static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
}
А вот как я создал сертификаты (как описано в пост, который я связал выше):
makecert -sv RootCATest.pvk -r -n "CN=FakeServerName" RootCATest.cer
makecert -ic RootCATest.cer -iv RootCATest.pvk -n "CN=FakeServerName" -sv
TempCert.pvk -pe -sky exchange TempCert.cer
cert2spc TempCert.cer TempCert.spc
pvkimprt -pfx TempCert.spc TempCert.pvk
Дополнительная информация, которую я ввел с помощью команд выше:
- Когда меня спросили по паролю первых 2 команд, я оставил его пустым
- Я проверил закрытый ключ экспорта и установил 'A' в качестве пароля для последней команды
Затем я импортировал файл .pfx в локальное хранилище сертификатов (ранее я также пробовал весь компьютер) и пусть программа выберет правильный магазин. Он предупредил меня, что все сертификаты CA будут доверенными, и я должен связаться с CA, чтобы проверить, действительно ли это их сертификат, но я продолжил. Затем я запустил код (используя только что созданный файл 'TempCert.cer') и получил ошибку. Любой совет высоко ценится!