Шаг 1: Создание самозаверяющего сертификата:
- Я скачал класс Certificate.cs , опубликованный Дугом Кук
Я использовал этот код для создания файла сертификата .pfx:
byte[] c = Certificate.CreateSelfSignCertificatePfx(
"CN=yourhostname.com", //host name
DateTime.Parse("2000-01-01"), //not valid before
DateTime.Parse("2010-01-01"), //not valid after
"mypassword"); //password to encrypt key file
using (BinaryWriter binWriter = new BinaryWriter(
File.Open(@"testcert.pfx", FileMode.Create)))
{
binWriter.Write(c);
}
Шаг 2: Загрузка сертификата
X509Certificate cert = new X509Certificate2(
@"testcert.pfx",
"mypassword");
Шаг 3: Собираем все вместе
- Я основал его на этом очень простом примере SslStream
- Вы получите ошибку времени компиляции о перечислении SslProtocolType. Просто измените это с SslProtocolType.Default на SslProtocols.Default
- Было 3 предупреждения об устаревших функциях. Я заменил их все предлагаемыми заменами.
Я заменил эту строку в файле Server Program.cs строкой из шага 2:
X509Certificate cert = getServerCert ();
В файле Client Program.cs убедитесь, что вы указали serverName = yourhostname.com (и что оно соответствует имени в сертификате)
- В Client Program.cs функция CertificateValidationCallback завершается сбоем, поскольку sslPolicyErrors содержит RemoteCertificateChainErrors. Если копнуть немного глубже, то это потому, что центр выдачи, подписавший сертификат, не является доверенным корнем.
- Я не хочу, чтобы пользователь импортировал сертификаты в корневое хранилище и т. Д., Поэтому я сделал для этого особый случай и проверяю этот сертификат. GetPublicKeyString () равен открытому ключу, который я есть на файл для этого сервера. Если это совпадает, я возвращаю True из этой функции. Кажется, это работает.
Шаг 4: Аутентификация клиента
Вот как аутентифицируется мой клиент (он немного отличается от сервера):
TcpClient client = new TcpClient();
client.Connect(hostName, port);
SslStream sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(CertificateValidationCallback),
new LocalCertificateSelectionCallback(CertificateSelectionCallback));
bool authenticationPassed = true;
try
{
string serverName = System.Environment.MachineName;
X509Certificate cert = GetServerCert(SERVER_CERT_FILENAME, SERVER_CERT_PASSWORD);
X509CertificateCollection certs = new X509CertificateCollection();
certs.Add(cert);
sslStream.AuthenticateAsClient(
serverName,
certs,
SslProtocols.Default,
false); // check cert revokation
}
catch (AuthenticationException)
{
authenticationPassed = false;
}
if (authenticationPassed)
{
//do stuff
}
CertificateValidationCallback такой же, как и в случае сервера, но обратите внимание, как AuthenticateAsClient принимает коллекцию сертификатов, а не только один сертификат. Итак, вам нужно добавить LocalCertificateSelectionCallback, например, так (в этом случае у меня только один сертификат клиента, поэтому я просто возвращаю первый в коллекции):
static X509Certificate CertificateSelectionCallback(object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
return localCertificates[0];
}