首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Win32中验证SSL证书的正确方法是什么?

在Win32中验证SSL证书的正确方法是什么?
EN

Stack Overflow用户
提问于 2011-09-08 05:03:54
回答 3查看 8.7K关注 0票数 22

我想使用C++在Win32中验证SSL证书。我想我想使用Cert* API,这样我就可以从Windows证书存储中获益。这就是我想出来的。

  • 这样做正确吗?
  • 有更好的方法吗?
  • 我做错了什么吗?

bool IsValidSSLCertificate( PCCERT_CONTEXT certificate, LPWSTR serverName )
{
    LPTSTR usages[] = { szOID_PKIX_KP_SERVER_AUTH };

    CERT_CHAIN_PARA params                           = { sizeof( params ) };
    params.RequestedUsage.dwType                     = USAGE_MATCH_TYPE_AND;
    params.RequestedUsage.Usage.cUsageIdentifier     = _countof( usages );
    params.RequestedUsage.Usage.rgpszUsageIdentifier = usages;

    PCCERT_CHAIN_CONTEXT chainContext = 0;

    if ( !CertGetCertificateChain( NULL,
                                   certificate,
                                   NULL,
                                   NULL,
                                   &params,
                                   CERT_CHAIN_REVOCATION_CHECK_CHAIN,
                                   NULL,
                                   &chainContext ) )
    {
        return false;
    }

    SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslPolicy = { sizeof( sslPolicy ) };
    sslPolicy.dwAuthType                       = AUTHTYPE_SERVER;
    sslPolicy.pwszServerName                   = serverName;

    CERT_CHAIN_POLICY_PARA policy = { sizeof( policy ) };
    policy.pvExtraPolicyPara      = &sslPolicy;

    CERT_CHAIN_POLICY_STATUS status = { sizeof( status ) };

    BOOL verified = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
                                                      chainContext,
                                                      &policy,
                                                      &status );

    CertFreeCertificateChain( chainContext );
    return verified && status.dwError == 0;
}
EN

回答 3

Stack Overflow用户

发布于 2014-02-28 21:53:10

您应该知道RFC3280 section 6.1RFC5280 section 6.1。两者都描述了验证证书路径的算法。尽管Win32应用程序接口为您处理了一些事情,但了解该过程的大体情况仍然是有价值的。

此外,这里有一个(在我看来)非常值得信赖的参考:Chromium certificate verification code

总的来说,我认为你的代码不是错误的。但是,如果我是你,我会考虑/改变一些事情:

1.单独的通用名称验证

铬与链分开验证证书通用名称。显然,他们注意到了其中的一些问题。请参阅评论以了解它们的基本原理:

cert_verify_proc.win.cc:731 // Certificate name validation happens separately, later, using an internal
cert_verify_proc.win.cc:732 // routine that has better support for RFC 6125 name matching.

2.使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT

of还使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT标志而不是CERT_CHAIN_REVOCATION_CHECK_CHAIN。实际上,在我找到他们的代码之前,我就已经开始研究这个问题了,这让我更加坚信你应该使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

尽管上述两个RFC都指定自签名信任锚不被视为链的一部分,但CertGetCertificateChain (http://msdn.microsoft.com/en-us/library/windows/desktop/aa376078(v=vs.85).aspx )的文档表示,如果可能,它会构建一个链,直到信任的根证书为止。受信任的根证书(在同一页上)定义为受信任的自签名证书。

这消除了*EXCLUDE_ROOT可能跳过对非根信任锚的撤销检查的可能性(Win32实际上要求信任锚是自签名的,即使它不是任何RFC所必需的。虽然这并没有正式的文档)。

现在,由于根CA证书不能撤销自身( CRL不能被签名/验证),在我看来这两个标志是相同的。

我用谷歌搜索了一下,偶然发现了这个论坛帖子:http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/9f95882a-1a68-477a-80ee-0a7e3c7ae5cf/x509revocationflag-question?forum=windowssecurity。CDP产品组的一名成员(假设)声称,如果根是自签名的,那么实际上这些标志的作用是相同的(理论上,如果根证书包含.NET扩展,ENTIRE_CHAIN标志将检查根证书的吊销,但这不可能发生)。

他还建议使用*EXCLUDE_ROOT标志,因为如果自签名根CA包括CDP扩展,则其他标志可能会导致不必要的网络请求。

不幸的是:

  • 我找不到任何关于这两个标志之间差异的官方文档解释。
  • 尽管链接的讨论很可能适用于.NET背后的同一个Win32 API标志,但这不是保证。

为了完全确定可以使用CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,,我在谷歌上搜索了一下,在回复的顶部找到了我链接的Chromium SSL证书验证码。

作为额外的好处,Chromium cert_verify_proc_win.cc文件包含以下有关IE验证码的提示:

618: // IE passes a non-NULL pTime argument that specifies the current system
619: // time.  IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
620: // chain_flags argument.

不知道他们是怎么知道的,但在这一点上,我会觉得使用CERT_CHAIN_REVOCATION_CHECK_EXCLUDE_ROOT很舒服。

3.不同的接受证书用法

我注意到Chromium还指定了3种证书用法,而不是1种:

szOID_PKIX_KP_SERVER_AUTH,
szOID_SERVER_GATED_CRYPTO,
szOID_SGC_NETSCAPE

从我通过Google收集到的信息来看,其他用法可能是较旧的web浏览器所必需的,否则它们可能无法建立安全连接。

如果Chromium认为适合包含这些用法,我也会效仿。

请注意,如果更改代码,还应将params.RequestedUsage.dwType设置为USAGE_MATCH_TYPE_OR而不是USAGE_MATCH_TYPE_AND。

-

目前我想不出任何其他的评论。但如果我是你,我会自己去看看Chromium源码(也许还有Firefox )--只是为了确保我没有错过任何东西。

票数 16
EN

Stack Overflow用户

发布于 2014-02-22 14:14:02

我认为最好的答案取决于你到底想做什么。

我要提醒您的是,SSL是基于两个端点都需要安全连接的假设。如果任何一个端点对维护安全性不感兴趣,那么就没有端点。

将字节码放入您的分布式代码中,只需为此函数返回true,这是一项微不足道的工作。这就是为什么windows将大量的验证转移到内核中的原因。但他们没有预料到人们会在虚拟硬件上运行windows,这使得绕过操作系统变得如此微不足道。

现在,假设您希望从某个来源获得证书,但又假装无法从可靠的来源获得相同的信息。然后把它交给你。因此,您不能依赖证书来“证明”任何人是特定的人。

从证书获得的唯一保护是防止外部人员(而不是端点)破坏正在传输的消息的机密性。

任何其他的使用都是注定要失败的,它最终也会失败,并带来潜在的灾难性后果。

很抱歉发了这么大的帖子。评论部分有字数限制。

票数 1
EN

Stack Overflow用户

发布于 2015-01-27 19:37:14

函数CertGetCertificateChainCertVerifyCertificatePolicy一起使用。这部分是正确的。

对于CertGetCertificateChain,如果要检查吊销,可以将该标志设置为以下三项中的任意一项:

  • CERT_CHAIN_REVOCATION_CHECK_END_CERT
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

只能使用其中一个,这三个选项不能为ORed。除了这些标志之外,您还可以考虑应该如何创建链;使用local cache,或者只使用CRLOCSP。出于这些考虑,read this link

执行函数时出错,或者更简单地说,如果返回值为0,并不意味着证书无效,而是您无法执行操作。有关错误信息,请使用GetLastError()。所以你返回false的逻辑是错误的,它更像是抛出错误,让客户端代码决定是重试还是继续做其他事情。

this link中有一个名为“对错误进行分类”的部分,请阅读。基本上你应该检查一下certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS的msdn参考。因此,您可以在这里拥有您的业务逻辑。例如,如果您发现证书吊销检查无法执行的值的错误状态(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION),您可以选择您想要的证书(放弃证书或仍然将其标记为无效)。

因此,在调用CertVerifyCertificatePolicy之前,您可以选择放弃或已标记验证错误。

如果你选择使用CertVerifyCertificatePolicy,关于如何将policy_status.dwError映射到你的错误类/枚举,铬代码是一个很好的参考。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7340504

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档