c# 验证 X509Certificate2:我这样做对吗?
c# Validating an X509Certificate2: am I doing this right?
使用框架 4.5.1 和以下要求,我这样做对吗?
- 证书中的 URL 必须匹配给定的 URL
- 证书必须有效且可信
- 证书不能过期
以下通过,但这是否足够?
特别是对 chain.Build(cert) 的调用是否满足上面的#2?
protected bool ValidateDigitalSignature(Uri uri)
{
bool isValid = false;
X509Certificate2 cert = null;
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
response.Close();
}
isValid = (request.ServicePoint.Certificate != null);
if(isValid)
cert = new X509Certificate2(request.ServicePoint.Certificate);
if (isValid)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.Build(cert);
isValid = (chain.ChainStatus.Length == 0);
}
if (isValid)
{
var dnsName = cert.GetNameInfo(X509NameType.DnsName, false);
isValid = (Uri.CheckHostName(dnsName) == UriHostNameType.Dns
&& uri.Host.Equals(dnsName, StringComparison.InvariantCultureIgnoreCase));
}
if (isValid)
{
//The certificate must not be expired
DateTimeOffset today = DateTimeOffset.Now;
isValid = (today >= cert.NotBefore && today <= cert.NotAfter);
}
return isValid;
}
如果您要验证 HTTPS 证书是否有效,HttpWebRequest 可以为您完成。
要让 HttpWebRequest 检查撤销状态,您需要在调用 GetResponse()
之前设置全局 ServicePointManager.CheckCertificateRevocationList = true
(我 认为 它是 GetResponse,而不是调用 Create()).
然后将检查:
- 证书链接到受信任的根
- 证书没有过期(以及其他类似的东西)
- 请求主机名与其应有的相符
你问的三点全部是哪一个。最难的是正确匹配主机名,因为
- 可能有多个 SubjectAlternativeName DNS 条目,但在 .NET 中没有很好的方法来询问它们。
- 任何 SubjectAlternativeName DNS 条目都允许在其中包含通配符 (*)。但主题 CN 值不是(并且 .NET API 不指示您返回的名称类型)。
- IDNA 等的名称规范化。
事实上,HttpWebRequest 唯一不会自动为您做的事情(除非您设置全局)是检查撤销。你可以通过
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
request.ServerCertificateValidationCallback = ValidationCallback;
private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// Since you want to be more strict than the default, reject it if anything went wrong.
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
// If the chain didn't suppress any type of error, and revocation
// was checked, then it's okay.
if (chain.ChainPolicy.VerificationFlags == X509VerificationFlags.None &&
chain.ChainPolicy.RevocationMode == X509RevocationMode.Online)
{
return true;
}
X509Chain newChain = new X509Chain();
// change any other ChainPolicy options you want.
X509ChainElementCollection chainElements = chain.ChainElements;
// Skip the leaf cert and stop short of the root cert.
for (int i = 1; i < chainElements.Count - 1; i++)
{
newChain.ChainPolicy.ExtraStore.Add(chainElements[i].Certificate);
}
// Use chainElements[0].Certificate since it's the right cert already
// in X509Certificate2 form, preventing a cast or the sometimes-dangerous
// X509Certificate2(X509Certificate) constructor.
// If the chain build successfully it matches all our policy requests,
// if it fails, it either failed to build (which is unlikely, since we already had one)
// or it failed policy (like it's revoked).
return newChain.Build(chainElements[0].Certificate);
}
而且,请注意,正如我在此处放入示例代码时,您只需要检查 chain.Build() 的 return 值,因为如果任何证书过期,那将是错误的或诸如此类的东西。您可能还想检查构建链中的根证书(或中间证书或其他证书)是否为预期值(证书固定)。
如果 ServerCertificateValidationCallback return 为假,则在 GetResponse() 上抛出异常。
您应该试用您的验证器以确保其有效:
- 选择您最喜欢的 https 站点并确保它通过。
- 所有这些都应该失败:
使用框架 4.5.1 和以下要求,我这样做对吗?
- 证书中的 URL 必须匹配给定的 URL
- 证书必须有效且可信
- 证书不能过期
以下通过,但这是否足够?
特别是对 chain.Build(cert) 的调用是否满足上面的#2?
protected bool ValidateDigitalSignature(Uri uri)
{
bool isValid = false;
X509Certificate2 cert = null;
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
response.Close();
}
isValid = (request.ServicePoint.Certificate != null);
if(isValid)
cert = new X509Certificate2(request.ServicePoint.Certificate);
if (isValid)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.Build(cert);
isValid = (chain.ChainStatus.Length == 0);
}
if (isValid)
{
var dnsName = cert.GetNameInfo(X509NameType.DnsName, false);
isValid = (Uri.CheckHostName(dnsName) == UriHostNameType.Dns
&& uri.Host.Equals(dnsName, StringComparison.InvariantCultureIgnoreCase));
}
if (isValid)
{
//The certificate must not be expired
DateTimeOffset today = DateTimeOffset.Now;
isValid = (today >= cert.NotBefore && today <= cert.NotAfter);
}
return isValid;
}
如果您要验证 HTTPS 证书是否有效,HttpWebRequest 可以为您完成。
要让 HttpWebRequest 检查撤销状态,您需要在调用 GetResponse()
之前设置全局 ServicePointManager.CheckCertificateRevocationList = true
(我 认为 它是 GetResponse,而不是调用 Create()).
然后将检查:
- 证书链接到受信任的根
- 证书没有过期(以及其他类似的东西)
- 请求主机名与其应有的相符
你问的三点全部是哪一个。最难的是正确匹配主机名,因为
- 可能有多个 SubjectAlternativeName DNS 条目,但在 .NET 中没有很好的方法来询问它们。
- 任何 SubjectAlternativeName DNS 条目都允许在其中包含通配符 (*)。但主题 CN 值不是(并且 .NET API 不指示您返回的名称类型)。
- IDNA 等的名称规范化。
事实上,HttpWebRequest 唯一不会自动为您做的事情(除非您设置全局)是检查撤销。你可以通过
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
request.ServerCertificateValidationCallback = ValidationCallback;
private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// Since you want to be more strict than the default, reject it if anything went wrong.
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
// If the chain didn't suppress any type of error, and revocation
// was checked, then it's okay.
if (chain.ChainPolicy.VerificationFlags == X509VerificationFlags.None &&
chain.ChainPolicy.RevocationMode == X509RevocationMode.Online)
{
return true;
}
X509Chain newChain = new X509Chain();
// change any other ChainPolicy options you want.
X509ChainElementCollection chainElements = chain.ChainElements;
// Skip the leaf cert and stop short of the root cert.
for (int i = 1; i < chainElements.Count - 1; i++)
{
newChain.ChainPolicy.ExtraStore.Add(chainElements[i].Certificate);
}
// Use chainElements[0].Certificate since it's the right cert already
// in X509Certificate2 form, preventing a cast or the sometimes-dangerous
// X509Certificate2(X509Certificate) constructor.
// If the chain build successfully it matches all our policy requests,
// if it fails, it either failed to build (which is unlikely, since we already had one)
// or it failed policy (like it's revoked).
return newChain.Build(chainElements[0].Certificate);
}
而且,请注意,正如我在此处放入示例代码时,您只需要检查 chain.Build() 的 return 值,因为如果任何证书过期,那将是错误的或诸如此类的东西。您可能还想检查构建链中的根证书(或中间证书或其他证书)是否为预期值(证书固定)。
如果 ServerCertificateValidationCallback return 为假,则在 GetResponse() 上抛出异常。
您应该试用您的验证器以确保其有效:
- 选择您最喜欢的 https 站点并确保它通过。
- 所有这些都应该失败: