我的程序包含我知道并信任的2个根证书。 我必须验证信任中心的证书和信任中心颁发的“用户”证书,这些证书都来自这两个根证书。
我使用X509Chain类进行验证,但只有在根证书位于Windows证书库中时才有效。
我正在寻找一种方法来验证证书而不导入根证书 - 以某种方式告诉X509Chain类我相信这个根证书,它应该只检查链中的证书而不是其他任何东西。
实际代码:
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root); // i do trust this
chain.ChainPolicy.ExtraStore.Add(trust);
chain.Build(cert);
编辑:这是一个.NET 2.0 Winforms应用程序。
答案 0 :(得分:4)
我发现这个解决方案不依赖于构建方法的结果,而是检查ChainStatus属性。 (未在.NET 2.0上进行测试,但这是我找到的针对此常见问题的唯一解决方案)
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.Build(cert);
if (chain.ChainStatus.Length == 1 &&
chain.ChainStatus.First().Status == X509ChainStatusFlags.UntrustedRoot)
{
// chain is valid, thus cert signed by root certificate
// and we expect that root is untrusted which the status flag tells us
}
else
{
// not valid for one or more reasons
}
通过实验也发现了设置
ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
将允许构建方法返回true,即使您没有将证书添加到ExtraStore,这完全违背了检查的目的。我不建议出于任何原因使用此标志。
答案 1 :(得分:2)
我在dotnet / corefx上打开了Issue,他们回复如下:
如果AllowUnknownCertificateAuthority是唯一设置的标志 如果
,db = sql.connect("localhost","root","password","TEST") cursor = db.cursor() sql = "SELECT * FROM TEST.car_model" cursor.execute(sql) data = cursor.fetchall() for row in data: apiUrl = "http://webapi.nhtsa.gov/api/SafetyRatings/modelyear/" apiParams = str(row[1])+"/make/"+row[2].replace(" ","%20")+"/model/"+row[3].rstrip().replace(" ","%20") apiFormat = "?format=csv" link = apiUrl + apiParams + apiFormat response = urlopen(apiUrl + apiParams + apiFormat) f = open(link, 'rb') reader = csv.reader(f) for row in reader: print(row) db.close()
将返回true
链条在自签名证书中正确终止(通过 ExtraStore,或搜索持久存储)
根据请求的撤销,所有证书均无效 政策
所有证书在(可选)下有效 ApplicationPolicy或CertificatePolicy值
所有证书的NotBefore值都在或之前 VerificationTime和所有证书的NotAfter值都是 (在 - 或 - )VerificationTime之后。
如果未指定该标志,则添加其他约束:
自签名证书必须在系统上注册为受信任证书(例如,在LM \ Root商店中)。
因此,Build()返回true,你知道这是一个有时间有效的非撤销链 存在。那时要做的事情是阅读
chain.Build()
和 确定它是否是您信任的证书。我建议比较chain.ChainElements[chain.ChainElements.Count - 1].Certificate
代表您的证书chainRoot.RawData
信任作为上下文中的根(即,逐字节比较而不是 使用指纹值)。(如果设置了其他标志,则其他约束也会放宽)
所以你应该这样做:
byte[]
答案 2 :(得分:1)
获得此方法的方法是编写自定义验证。
如果您在WCF上下文中,则通过继承System.IdentityModel.Selectors.X509CertificateValidator
并在web.config中的serviceBehavior对象上指定自定义验证来完成此操作:
<serviceBehaviors>
<behavior name="IdentityService">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="SSOUtilities.MatchInstalledCertificateCertificateValidator, SSOUtilities"
certificateValidationMode="Custom" />
</clientCertificate>
<serviceCertificate findValue="CN=SSO ApplicationManagement"
storeLocation="LocalMachine" storeName="My" />
</serviceCredentials>
</behavior>
但是,如果您只是想从另一台主机接受SSL证书,可以修改web.config文件中的system.net设置:
以下是X509CertificateValidator的示例,该示例测试LocalMachine / Personal商店中是否存在客户端证书。 (这不是你需要的,但作为一个例子可能是有用的。
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Security.Cryptography.X509Certificates;
/// <summary>
/// This class can be injected into the WCF validation
/// mechanism to create more strict certificate validation
/// based on the certificates common name.
/// </summary>
public class MatchInstalledCertificateCertificateValidator
: System.IdentityModel.Selectors.X509CertificateValidator
{
/// <summary>
/// Initializes a new instance of the MatchInstalledCertificateCertificateValidator class.
/// </summary>
public MatchInstalledCertificateCertificateValidator()
{
}
/// <summary>
/// Validates the certificate. Throws SecurityException if the certificate
/// does not validate correctly.
/// </summary>
/// <param name="certificateToValidate">Certificate to validate</param>
public override void Validate(X509Certificate2 certificateToValidate)
{
var log = SSOLog.GetLogger(this.GetType());
log.Debug("Validating certificate: "
+ certificateToValidate.SubjectName.Name
+ " (" + certificateToValidate.Thumbprint + ")");
if (!GetAcceptedCertificates().Where(cert => certificateToValidate.Thumbprint == cert.Thumbprint).Any())
{
log.Info(string.Format("Rejecting certificate: {0}, ({1})", certificateToValidate.SubjectName.Name, certificateToValidate.Thumbprint));
throw new SecurityException("The certificate " + certificateToValidate
+ " with thumprint " + certificateToValidate.Thumbprint
+ " was not found in the certificate store");
}
log.Info(string.Format("Accepting certificate: {0}, ({1})", certificateToValidate.SubjectName.Name, certificateToValidate.Thumbprint));
}
/// <summary>
/// Returns all accepted certificates which is the certificates present in
/// the LocalMachine/Personal store.
/// </summary>
/// <returns>A set of certificates considered valid by the validator</returns>
private IEnumerable<X509Certificate2> GetAcceptedCertificates()
{
X509Store k = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
k.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
foreach (var cert in k.Certificates)
{
yield return cert;
}
}
finally
{
k.Close();
}
}
}
答案 3 :(得分:1)
如果您知道哪些证书可以是要检查的证书的根证书和中间证书,则可以在ChainPolicy.ExtraStore
对象的X509Chain
集合中加载根证书和中间证书的公钥。 / p>
我的任务还是编写一个Windows窗体应用程序来安装证书,只要它是根据我国政府的已知“国家根证书”发布的。还允许有限数量的CA颁发证书以验证与国家Web服务的连接,因此我有一组有限的证书可以在链中并且可能在目标计算机上丢失。我在应用程序的子目录“cert”中收集了CA的所有公钥和政府根证书:
在Visual Studio中,我将目录cert添加到解决方案中,并将此目录中的所有文件标记为嵌入资源。这使我能够在我的c#库代码中枚举“可信”证书的集合,即使没有安装颁发者证书,也可以构建一个链来检查证书。为此,我为X509Chain创建了一个包装类:
private class X509TestChain : X509Chain, IDisposable
{
public X509TestChain(X509Certificate2 oCert)
: base(false)
{
try
{
ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
if (!Build(oCert) || (ChainElements.Count <= 1))
{
Trace.WriteLine("X509Chain.Build failed with installed certificates.");
Assembly asmExe = System.Reflection.Assembly.GetEntryAssembly();
if (asmExe != null)
{
string[] asResources = asmExe.GetManifestResourceNames();
foreach (string sResource in asResources)
{
if (sResource.IndexOf(".cert.") >= 0)
{
try
{
using (Stream str = asmExe.GetManifestResourceStream(sResource))
using (BinaryReader br = new BinaryReader(str))
{
byte[] abResCert = new byte[str.Length];
br.Read(abResCert, 0, abResCert.Length);
X509Certificate2 oResCert = new X509Certificate2(abResCert);
Trace.WriteLine("Adding extra certificate: " + oResCert.Subject);
ChainPolicy.ExtraStore.Add(oResCert);
}
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
}
}
if (Build(oCert) && (ChainElements.Count > 1))
Trace.WriteLine("X509Chain.Build succeeded with extra certificates.");
else
Trace.WriteLine("X509Chain.Build still fails with extra certificates.");
}
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
public void Dispose()
{
try
{
Trace.WriteLine(string.Format("Dispose: remove {0} extra certificates.", ChainPolicy.ExtraStore.Count));
ChainPolicy.ExtraStore.Clear();
}
catch (Exception ex)
{
Trace.Write(ex);
}
}
}
在调用函数中,我现在可以成功检查未知证书是否来自国家根证书:
bool bChainOK = false;
using (X509TestChain oChain = new X509TestChain(oCert))
{
if ((oChain.ChainElements.Count > 0)
&& IsPKIOverheidRootCert(oChain.ChainElements[oChain.ChainElements.Count - 1].Certificate))
bChainOK = true;
if (!bChainOK)
{
TraceChain(oChain);
sMessage = "Root certificate not present or not PKI Overheid (Staat der Nederlanden)";
return false;
}
}
return true;
要完成图片:要检查根证书(通常因为它包含在Windows Update中而安装,但理论上也可能会丢失),我将友好名称和指纹与已发布的值进行比较:
private static bool IsPKIOverheidRootCert(X509Certificate2 oCert)
{
if (oCert != null)
{
string sFriendlyName = oCert.FriendlyName;
if ((sFriendlyName.IndexOf("Staat der Nederlanden") >= 0)
&& (sFriendlyName.IndexOf(" Root CA") >= 0))
{
switch (oCert.Thumbprint)
{
case "101DFA3FD50BCBBB9BB5600C1955A41AF4733A04": // Staat der Nederlanden Root CA - G1
case "59AF82799186C7B47507CBCF035746EB04DDB716": // Staat der Nederlanden Root CA - G2
case "76E27EC14FDB82C1C0A675B505BE3D29B4EDDBBB": // Staat der Nederlanden EV Root CA
return true;
}
}
}
return false;
}
我不确定此检查是否安全,但在我的情况下,Windows窗体应用程序的操作员确信可以访问要安装的有效证书。该软件的目标只是过滤证书列表,以帮助他只在计算机的机器商店中安装正确的证书(该软件还安装中间和根证书的公钥,以确保运行时的行为Web服务客户端是正确的。)
答案 4 :(得分:0)
我只是从@Tristan扩展了代码,检查根证书是添加到ExtraStore的证书之一。
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.ExtraStore.Add(root);
chain.Build(cert);
if (chain.ChainStatus.Length == 1 &&
chain.ChainStatus.First().Status == X509ChainStatusFlags.UntrustedRoot &&
chain.ChainPolicy.ExtraStore.Contains(chain.ChainElements[chain.ChainElements.Count - 1].Certificate))
{
// chain is valid, thus cert signed by root certificate
// and we expect that root is untrusted which the status flag tells us
// but we check that it is a known certificate
}
else
{
// not valid for one or more reasons
}