.Net HttpWebRequest ClientCertificates未发送到API

时间:2019-05-21 06:51:38

标签: .net ssl ssl-certificate

我正在尝试通过HTTPS调用API。 API希望发送客户端证书,然后将对其进行验证。该API不会返回某些已接受CA的列表(例如在标准的Web服务器TLS流中),它只是希望证书可以验证。

在运行我的应用程序的客户端计算机上,客户端证书安装在本地计算机\个人存储中(通过在certlm.msc中进行检查,它也可以正常运行)。我可以像这样HttpWebRequest一样从商店中获取证书并将其添加到我的request.ClientCertificates.Add(cert)中。在运行时检查应用程序代码中的证书,它具有私钥(HasPrivateKeytrue,并且PrivateKey属性包含数据)。为了进行测试,我授予了everyone对证书的读取权限,以确保可以从应用程序中使用证书来检索它们的私钥,这似乎可行。

但是,API抱怨没有发送证书。使用Wireshark检查请求会显示TLSv1.2类型的Certificate握手,但是显示为Certificate Length: 0的握手。因此确实没有发送证书。

使用Postman调用具有完全相同证书的完全相同的API(从文件中,因为Postman需要一个文件,该文件是在机器\个人存储中导入证书的.pfx)发送证书。 Wireshark显示Certificate Length: 770,我可以查看证书详细信息。

所以看来,由于某种原因,即使我将证书附加到HttpWebRequest.ClientCertificates上,它也不会从应用程序代码中发送出去。也不例外,它不会被发送。

回顾:

  • HttpWebRequest.ClientCertificates包含从计算机\个人存储中检索的证书
  • 证书具有私钥
  • certlm.msc中的证书看起来不错(受信任的CA等)
  • 证书未设置为API
  • 但是从邮递员调用API时会设置相同的证书
  • 该应用程序是.Net 4.6.1 wpf应用程序

有什么主意我想念的吗?谢谢!

(稍后编辑)

我在框架中调试了SecureChannel类,该类是处理证书发送的类,并发现了一些令人担忧的东西。首先,一些评论:

Here is how we work:
                case 0: Cert Selection delegate is present
                        Alwasys use its result as the client cert answer.
                        Try to use cached credential handle whenever feasible.
                        Do not use cached anonymous creds if the delegate has returned null
                        and the collection is not empty (allow responding with the cert later).

                case 1: Certs collection is empty
                        Always use the same statically acquired anonymous SSL Credential

                case 2: Before our Connection with the Server
                        If we have a cached credential handle keyed by first X509Certificate
                        **content** in the passed collection, then we use that cached
                        credential and hoping to restart a session.

                        Otherwise create a new anonymous (allow responding with the cert later).

                case 3: After our Connection with the Server (ie during handshake or re-handshake)
                        The server has requested that we send it a Certificate then
                        we Enumerate a list of server sent Issuers trying to match against
                        our list of Certificates, the first match is sent to the server.

                        Once we got a cert we again try to match cached credential handle if possible.
                        This will not restart a session but helps miminizing the number of handles we create.

调试代码,注释似乎成立:

  • 首先,它检查证书选择委托:没有证书选择委托,并且证书集合包含我的证书,因此移至步骤2
  • 在这里获取我的证书,检查有效的私钥,是否成功,然后发出新的评论
// (see VsWhidbey#363953) For some (probably good) reason IIS does not renegotiate a restarted session if client cert is needed.
                    // So we don't want to reuse **anonymous** cached credential for a new SSL connection if the client has passed some certificate.
                    // The following block happens if client did specify a certificate but no cached creds were found in the cache
                    // Since we don't restart a session the server side can still challenge for a client cert.

...此代码之后...

   if ((object)clientCertificate != (object)selectedCert)
                        selectedCert.Reset();
                    guessedThumbPrint = null;
                    selectedCert = null;
                    clientCertificate = null;

因此基本上,它将null对我传入的证书的引用。如果我正确理解它,则可以解决某些IIS问题,这意味着它希望服务器将发证人列表发回给筛选证书的依据(这是注释中的情况3),这的确是我在代码中看到的。仅当此发行者列表从服务器中删除时,代码才会通过客户端证书并由发行者过滤,然后向上发送匹配的证书。

现在我不禁要问:如果服务器要求颁发者列表,是否只能发送客户端证书?因为就我而言,我正在调用的API并没有做到这一点,所以它只需要一个证书即可。我希望我是错的,并且有人可以阐明这一点。

(甚至以后进行编辑)

我正在调用的API现在返回CA列表。我的测试CA已添加到此处,并且在SSL握手期间返回了它。现在调试SecureChannel表明我之前的假设是正确的:

  • 第一次通过时,我添加的证书未发送
  • 但是,从服务器获取CA列表后,会检查它是否与我的证书的颁发者相匹配,并且确实找到了匹配项!
  • 跟随此成功匹配SecureChannel成功将其m_SelectedClientCertificate设置为我的证书,但是...
  • ...不幸的是,协议的下一步仍未将其发送到服务器:(

一旦设置了SecureChannel.m_SelectedClientCertificate,似乎SecureChannel本身就不再使用它了,但是它被暴露了:

internal X509Certificate LocalClientCertificate {
            get {
                return m_SelectedClientCertificate;
            }
        }

不幸的是,在我的情况下,从未调用过此属性:(

1 个答案:

答案 0 :(得分:1)

发现问题:我的证书已与SHA1签署。用SHA256辞职并看到它被发送了!