SSL/TLS中的双向认证

4

我对SSL身份验证比较陌生,但我有一个要求需要通过SSL在信任边界上验证两个架构组件(我可以控制这两个组件)。我认为我需要双向SSL身份验证,服务器和客户端都需要证书。

证书可以是自签名的吗?(即由供应商签名,这样使用SSL就不会失效了吗?还是需要第三方验证服务来确保证书的身份?)

在公钥和私钥方面,服务器和客户端的握手是如何工作的?

我将不得不在IIS中配置服务器以使用证书,并通过请求发送客户端证书(最可能是通过WCF进行配置),但我还需要完成哪些其他任务才能使其正常工作?


我认为其中一个SOAP扩展中内置了这样的功能(用于PKI客户端身份验证或类似功能),但我无法回忆起来了.. - user166390
3个回答

3
我同意EJP的答案,但由于你已经接受了不同的答案,这里进一步介绍一下使用自签名证书可能会遇到的问题。
在服务器上使用自签名证书是可行的。您只需要配置所有潜在客户端以信任此证书。这可以通过将证书(没有私钥)导入每个机器的受信任存储或浏览器的受信任证书存储库来完成(例如,Firefox使用其自己的存储库,独立于其运行的操作系统)。这里的自签名服务器证书主要是自定义CA证书的特例,其中每个客户端都需要知道它可能使用的每个服务器(缺点是自签名证书无法撤销)。这没问题,但如果您有几台机器,这很快就会成为实际问题。
您的主要问题将是关于自签名证书作为客户端证书的问题。
客户端证书身份验证由服务器发起,服务器向客户端发送TLS证书请求消息。该消息包含愿意接受的认证机构名称列表。然后,客户端使用此列表选择要发送的证书:它们查找具有Issuer DN匹配此CA列表中一个名称的证书(如果需要中间CA证书来链接到Issuer DN,则还可以使用证书链)。如果找不到与这些条件匹配的证书,大多数客户端将根本不发送任何客户端证书。当尝试使用自签名客户端证书时,这将是您最大的问题。
解决此问题的方法是让服务器发送一个空列表。这为客户端提供了更多选择证书的自由(然后由服务器选择是否接受它,但无论如何都是如此)。这在TLS 1.1和以前的版本中明确允许(SSLv3 / TLS 1.0对此主题保持沉默),尽管在实践中,据我所知,大多数SSLv3 / TLS 1.0堆栈也可以正常工作。 (例如,当浏览器执行自动证书选择时,这仍可能导致笨拙的交互,因为它使正确进行自动选择变得困难。)
Java的X509TrustManager可以使用getAcceptedIssuers()自定义,以在TLS证书请求消息中返回空的CA列表。据我所知,在C#/ .Net中,SslStream的RemoteCertificateValidationCallback没有其等效项。据我所知,在使用IIS / SslStream时,该列表是从机器的受信任证书列表构建的。(请注意,这与证书验证本身无关,只涉及服务器广告可能接受的证书。)
此外,如果您想使用自签名证书进行客户端证书认证,您需要在服务器中实现自己的验证回调来执行此认证。一个简单的例子是从您获得的证书中提取公钥,将其与服务器预定义的列表进行比较,然后使用该预定义列表中的用户名。除非您有一种明确的方法检查其内容与您知道的内容相匹配,否则您无法信任自签名证书所说的任何内容。实现一个只说“return true;”的回调不会执行任何身份验证。任何人都可以构建具有相同名称或属性但不同密钥的证书。(在TLS证书请求消息中,信任您服务器受信任的证书存储中的每个客户端证书可能更好,尽管这可能导致长的CA列表。)
如果您不想依赖商业CA,请构建自己的CA。这可能需要一些工作(主要是管理),但至少您将拥有一个更清洁的系统。通过使用该CA证书配置您的客户端和服务器,您应该能够扩展到更多的客户端和服务器而无需进行更改,并且当请求客户端证书时,您还将能够受益于正常的CA列表行为。

2

自签名证书实际上更加复杂。除非您完全控制双方,否则不要这样做。

私钥用于在握手中签署发送的证书,以便对等方可以检查您是否真正拥有您发送的证书。服务器私钥还在某些密码中生成共享密钥时发挥作用。


我对这两个组件都有控制权。使用自签名证书会有哪些复杂性问题? - theringostarrs
1
如果您想使用任何自签名证书进行客户端证书身份验证(您需要以某种方式验证它们),则需要让服务器在其Certificate Request消息中发送一个空的证书颁发机构列表。您可以通过从 X509TrustManager.getAcceptedIssuers() 返回大小为0的数组来在Java中执行此操作。我从未在C#的 SslStream(或IIS)世界中找到相应的等价物。据我所知, RemoteCertificateValidationCallback 是与Java的 X509TrustManager 最接近的等价物,但它不允许这样做。 - Bruno
@theringostarrs 另外,这更加复杂,因为您必须在两端执行操作,以使它们信任彼此的证书,并安装自己的证书。对于签名证书,每端只需配置自己的证书即可。 - user207421
@theringostarrs,如果您可以控制所有服务器和客户端,您可以尝试设置自己的CA。您仍然需要显式导入CA证书,并且需要一些管理工作,但是与使用自签名证书相比,它应该更可管理,也不会有我之前提到的问题。 - Bruno

-4

使用客户端和服务器证书的双向身份验证可以实现。您也可以使用自签名证书。但是,如果两个证书都是自签名的,则需要在客户端和服务器上绕过证书验证,因为它们没有由受信任的机构签署。

就证书的安装而言,应按以下方式进行:

服务器端

  1. 将ServerCert.pfx安装到LocalMachine --> 个人存储区

  2. 将ClientCert.cer安装到Local Machine --> 可信任的人员存储区

客户端

  1. 将ClientCert.pfx安装到当前用户 --> 个人存储区

  2. 将ServerCert.cer安装到Local Machine --> 可信任的存储区

您需要将ServerCert.cer文件发送给客户端,客户端需要将ClientCert.cer发送给您。

现在,如果您正在使用自签名证书,并且您的客户端正在访问您的服务,则他需要绕过证书验证。以下是C#中的示例代码:

        System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) =>
                                                                                 {
                                                                                     return true;
                                                                                 };

希望能有所帮助。

注意: 从安全角度考虑,在生产环境中不建议使用自签名证书或绕过证书验证。


2
我并没有建议绕过证书验证,无论是在任何一方。我说过,你需要让服务器发送一个空的CA列表,并且当使用自签名证书时,客户端证书验证必须被定制化(即手动完成,但必须以某种方式完成)。让客户端检查服务器证书是为了防止中间人攻击。让服务器检查客户端证书是为了进行客户端身份验证。如果服务器不验证其证书,那么你最好不要使用任何客户端证书。你所建议的方法在使用自签名客户端证书时行不通。 - Bruno
提供一个下投票的理由会很有帮助。 - Rajesh

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