У меня есть рабочая реализация этого, но я хочу убедиться, что он безопасен. Целью является использование SSLStream и только прием сертификатов SSL с сервера, которые подписаны определенным ключом RSA.
Вот мой код подключения:
var client = new TcpClient("server_address", port_number);
var sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
sslStream.AuthenticateAsClient("SpeechGrid");
И вот моя реализация ValidateServerCertificate:
private static bool ValidateServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors) {
// Only accept our specific key pair
foreach (var cert in chain.ChainElements) {
if (cert.Certificate.GetPublicKeyString() == k_prodPublicKey) {
return true;
}
}
return false;
}
Из-за богатства объекта X509Chain я хочу убедиться, что мне не нужно проверять такие вещи, как X509ChainStatusFlags.NotSignatureValid и т.д.
Например, может ли злоумышленник "требовать" быть подписанным моим открытым ключом, отправить недопустимую подпись, и эта атака будет работать, потому что .NET предполагает, что я проверяю все эти флаги?
Спасибо!!
ОБНОВЛЕНИЕ: Хорошо, до сих пор я решил поставить следующие проверки выше оригинального foreach. Обратите внимание, что это несколько специфическое приложение; например, если мне нужны сертификаты для истечения срока действия, я бы проверял NotTimeValid и т.д.
foreach (var status in chain.ChainStatus) {
switch (status.Status) {
case X509ChainStatusFlags.Cyclic:
case X509ChainStatusFlags.NotSignatureValid:
case X509ChainStatusFlags.PartialChain:
return false;
}
}
Я бы отменил логику проверки, добавленной вами в обновлении, на ваш вопрос. Вместо того, чтобы искать то, что может быть неправильно, и принимать все остальное:
foreach (thing that I can think of that might be wrong)
return false;
if (public key matches regardless of other policy errors)
return true;
... Вместо этого я искал бы то, что может быть неправильным, но приемлемым, и отклонить любые другие ошибки политики:
if (policy errors)
{
foreach (error that is acceptable: remote name mismatch, untrusted root, etc.)
policy errors -= that particular error
}
if (any policy errors left)
return false;
else if (public key matches)
return true;
else
return false;
Что-то вроде этого для первой части (я не тестировал и не компилировал это):
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
{
sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
}
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
{
var otherFlagsFound =
from i in chain.ChainStatus
where (i.Status & ~X509ChainStatusFlags.UntrustedRoot) != X509ChainStatusFlags.NoError
select i;
if (otherFlagsFound.Count() == 0)
{
sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
}
}
Вы можете проверить параметр sslPolicyErrors для дополнительных ошибок, таких как истек, или если сертификатам не доверяют. Если все в порядке, оно должно вернуть SslPolicyErrors.None. Нецелесообразно выводить закрытый ключ из открытого ключа, поэтому вам не нужно беспокоиться о том, чтобы кто-то другой создал одну и ту же пару ключей и подписал его.