Ответ 1
Шаг 1: Создание самозаверяющего сертификата:
- Я загрузил класс Certificate.cs, опубликованный Doug Cook
-
Я использовал этот код для создания файла сертификата .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];
}