你可以使用该回调函数,无需重新发明轮子。
如果你仔细观察,
回调函数具有多个参数。
public delegate bool RemoteCertificateValidationCallback(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors
)
这些参数是通过.Net实现检查的结果。例如,您可以使用以下代码检查唯一的问题是否是缺少根CA:
// Check if the only error of the chain is the missing root CA,
// otherwise reject the given certificate.
if (chain.ChainStatus.Any(statusFlags => statusFlags.Status != X509ChainStatusFlags.UntrustedRoot))
return false;
它将遍历整个证书链并检查所有状态。如果任何状态不是受信任的根(注意:枚举具有标志属性),则失败。但是,如果唯一的问题是不受信任的根,那么您可以使用它。
但是现在您面临的另一个问题是,您知道该证书具有不受信任的根,但您不知道是否真的是您的证书(或任何其他证书)。
为了确保这一点,您必须读取已存储在应用程序中的服务器证书的公共部分,并将其与给定的证书链进行比较:
var now = DateTime.UtcNow;
var certificateAuthority = new X509Certificate(_ServerCertificateLocation);
var caEffectiveDate = DateTime.Parse(certificateAuthority.GetEffectiveDateString());
var caExpirationDate = DateTime.Parse(certificateAuthority.GetExpirationDateString());
if (now <= caEffectiveDate
|| now > caExpirationDate)
return false;
return chain.ChainElements.Cast<X509ChainElement>()
.Select(element => element.Certificate)
.Where(chainCertificate => chainCertificate.Subject == certificateAuthority.Subject)
.Where(chainCertificate => chainCertificate.GetRawCertData().SequenceEqual(certificateAuthority.GetRawCertData()))
.Any();
也许在函数开始时添加一个快速退出会很明智,如果服务器提供的证书由已安装的根CA签名(可能不是你的!):
if (sslPolicyErrors == SslPolicyErrors.None
&& chain.ChainStatus.All(statusFlags => statusFlags.Status == X509ChainStatusFlags.NoError))
return true
如果需要(根据您的需求),您可以允许或禁止使用证书,即使服务器名称和证书名称不匹配。例如,如果证书是为本地主机制作的,而您从另一台计算机访问该服务器,则会发生这种情况。或者在局域网中,您只使用http://myserver
而不是http://myserver.domain.com
,但证书是在全限定名称上制作的(反之亦然):
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch)
return false;
就是这样。你仍然依赖默认实现,只需另外检查你的部分。
AllowUnknownCertificateAuthority
标志的“自定义”X509Chain
实际上是我正在寻找的。它将大部分检查推迟到了“框架”。但我必须问一下:如果找不到CRL或CRL指示我们的“内置”根证书已被吊销,您绝对确定Build()会在这里失败吗?虽然我们的“内置”根证书现在是完全受信任的,但我们需要能够在将来撤销它 - 无论出于什么原因。 - dtr84