使用自签名证书颁发机构验证服务器证书

7
我有自定义的服务器/客户端应用程序,它们使用SSL加密的TCP连接(自定义协议)进行通信。为了设置服务器证书,我创建了一个自签名证书颁发机构,并使用它来签署服务器要使用的证书。在客户端方面,我希望验证我连接到的服务器的证书是由我的自签名CA签署的。
我能够在C++中使用boost通过将自签名CA证书提供给ssl::context的load_verify_file()函数来使其工作。我希望在C#客户端中实现相同的功能,但.NET SSL似乎更严格地信任不在Windows信任存储中的证书。
我在搜索中找到了几个部分解决方案。显然 X509Chain 是用于验证 SSL 证书的类。我尝试了 像这样的代码,但是手动创建的链仍然抱怨根证书不受信任,就像传递到验证函数中的原始链一样。有一个选项可以 忽略未知的证书颁发机构,但那似乎意味着它将接受 任何 自签名证书,这绝对不是我想要的。

这段代码似乎最接近我想要的,但如上所述,我的问题是它仍然抱怨我添加到 ExtraStore 的证书是自签名的。有没有办法说服 X509Chain 相信我正在给它的证书?

bool remoteCertificateValidationCallback(
    object sender, X509Certificate certificate, X509Chain chain,
    SslPolicyErrors sslPolicyErrors)
{
    // make sure certificate was signed by our CA cert
    X509Chain verify = new X509Chain();
    verify.ChainPolicy.ExtraStore.Add(secureClient.CertificateAuthority); // add CA cert for verification
    //verify.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; // this accepts too many certificates
    verify.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // no revocation checking
    verify.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
    if (verify.Build(new X509Certificate2(certificate)))
    {
        return true; // success?
    }
    return false;
}
1个回答

9
我怀疑您的私有CA证书未安装在当前系统(运行代码的位置)上,或者安装不正确。根CA证书必须安装在计算机存储器中“受信任的根CA”容器中,而非当前用户存储器中。默认情况下,X509Chain使用计算机存储器查找可信锚点。
此外,您的代码无法实现您想要的功能。它将接受并通过任何公共信任的根CA。相反,您需要比较X509Chain.ChainElements中的最后一个元素,看其中包含的证书是否是您所期望的(通过比较指纹值)。下面是可以应用的修复方法:
if (verify.Build(new X509Certificate2(certificate)))
{
    return verify.ChainElements[verify.ChainElements.Count - 1]
        .Certificate.Thumbprint == cacert.thumbprint; // success?
}
return false;

其中cacert是您的根CA证书。


1
你说得对,我没有将CA证书安装到受信任的存储中。我希望避免这样做,但似乎我不能用.NET提供的内置工具实现我想要的功能。我刚刚注意到相关问题中的这个问题几乎完全相同(他们没有说明他们使用的CA是否是自签名的)。它没有一个好的答案。 - plasmoidia
1
X509Chain.Build 内部调用 CertCreateCertificateChainEngine 函数,该函数依赖于本地证书存储来建立信任。您可以通过移除外部 IF 语句并且不使用方法结果来避免这个要求。对于您的确切问题来说这是足够的。 - Crypt32
1
那么比较两个证书的Thumbprint属性将检查它们是否相同?这可能有效,我甚至可以使用传递的chain变量(其中包含来自服务器的正确链)。我可以只检查chain.ChainStatus是否仅包含UntrustedRoot,以确保该链也是有效的,而不仅仅是在末尾添加了CA吗? - plasmoidia
1
是的,指纹比较是比较证书最可靠的方法之一(最可靠的方法是精确的二进制比较)。 - Crypt32
3
请注意,在.NET 5中,现在可以通过将证书链策略的[TrustMode]设置为“X509ChainTrustMode.CustomRootTrust”来进行证书锁定。 - Stewart Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接