【问题标题】:Which certificate should be use in SSLContext when using self-signed certificate? Self-sign certificate or the ca certificate使用自签名证书时应在 SSLContext 中使用哪个证书?自签名证书或ca证书
【发布时间】:2019-11-04 13:58:54
【问题描述】:

我对 android 上的 ssl 中使用的证书感到非常困惑。通常,应该使用 ca.crt(ca certificate) 来验证客户端中的 server.crt(certificate)。但是,在 android 上使用 ssl 时,似乎 ca.crt 和 server.crt 都可以工作。这很奇怪。有人可以解释发生了什么吗?

提前致谢。

如下所示,我可以使用 ca.crt 或 server.crt 来构建 client.bks。他们都可以成功连接到服务器。顺便说一句,我用其他信息在服务器端重建 server.crt 后,我​​只能使用 ca.crt 连接服务器。


KeyStore trustKeyStore = KeyStore.getInstance("BKS");
InputStream keyStream = xxxApplication.getInstance().getAssets().open("client.bks");

trustKeyStore.load(keyStream, "".toCharArray());
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustKeyStore);
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

另一方面,我想知道 android 中的 SSL 如何使用 TrustManagers 验证服务器证书。我们将用于构建 trustManager 的证书称为“client.crt”。下面的代码似乎允许服务器证书(证书 client.crt 和可由 client.crt 验证的证书)。

【问题讨论】:

  • 这取决于你想做什么。您想只信任特定的服务器证书还是信任特定 CA 颁发的所有证书?
  • 如果它们都有效,则它不是自签名证书。您应该使用 CA 证书。您不能“重建”证书。
  • @SteffenUllrich 感谢您的回复。实际上,我想信任由特定 CA 颁发的所有证书。您能告诉我如何信任特定证书以及如何信任 Android 中特定 CA 颁发的所有证书吗?我很困惑为什么上面显示的代码可以同时使用 server.crt 和 ca.crt。
  • @user207421 感谢您的回复。上面显示的代码实际上适用于两个证书。我知道我应该使用 CA 证书来信任该 CA 颁发的所有证书。我很困惑为什么我在客户端使用 server.crt 可以工作(服务器端在握手中返回自签名的 server.crt)。顺便说一句,我在服务器端“重新构建”证书意味着我使用 server.key 和 ca.key 生成新证书。
  • 它可以工作因为它不是自签名证书。我已经说过了。

标签: android ssl


【解决方案1】:

当您进行 TLS 握手时,每一方都可以拥有一个证书(带有关联的私钥),该证书向另一方公开以识别自己。

通常只有服务器证书很重要,就像在 HTTPS 中一样,但一般来说,如果服务器请求,客户端也可以提供证书。

从广义上讲,交流是这样的:

  • 客户:你好
  • 服务器:我有证书 X
  • (如果服务器希望客户端出示证书)服务器:客户端,请将您的证书发给我,我识别 CA A、B、C、D
  • (如果服务器要求提供证书)客户端:这是我的证书 Y

这是什么意思?

当服务器向客户端询问其证书时,它会列出它知道的 CA,也就是说,它会引导客户端选择适当的证书(客户端可以在其信任库中拥有多个证书,因此它需要选择一个 - 它只能发送一个 - 通过某种自动方式或最后通过要求某人选择一个)

这只是一个指导,客户端可以发送它想要的任何证书,由它喜欢的任何 CA 签名......但是有可能证书被服务器拒绝。

因此,当您配置客户端部分时,要构建“SSL 上下文”,您需要指定您的证书、与证书关联的私钥、可选的称为“链”或“中间”的证书列表,作为所有这个没有私钥需要发送到服务器,并单独配置你方将某些 CA 识别为完全受信任的,以便你使用它们来验证服务器证书。

链/中间证书是因为最终用户/系统证书很少(从不?)直接由 CA 证书签名(除非自签名,当然在这种情况下最终证书也是 CA 证书),所以客户端实际上需要发送其最终证书和将其最终证书链接到 CA 证书之一所需的整个证书链(不需要发送“根”CA 证书)。使用所有这些信息,服务器将能够使用其证书和所有中间证书以及服务器在其自己的本地信任存储中拥有的 CA 证书对客户端进行身份验证。

如果您使用“真实”CA(公共或私有)配置您的一方,这意味着您将接受任何服务器(对于这部分检查,还有其他检查,例如日期、签名值、名称包含在证书等中),它能够提供由该 CA 颁发的证书(再次通过一些中间证书)。相反,如果您直接在那里使用服务器自签名证书,这意味着您实际上将只允许此证书,而不是其他任何人(因此,一旦您更改其自签名证书,就不允许其他服务器,甚至不允许该特定服务器)。

请注意,在第一步 (clientHello) 中,客户端还可以列出它知道的 CA(参见 RFC8446 的 §4.2.4),但我认为这种情况很少见。

【讨论】:

  • 客户端无法发送不符合CertificateRequest消息中参数的证书。
  • @user207421 对不起,但它可以,正如我亲眼所见。在一种特定的设置中,服务器希望为客户端证书接受许多 CA,但由于其大小限制,它们并不都适合 CertificateRequest。因此,它尽可能多地发送其中的“一些”,但仍然接受来自客户端与其他 CA 的返回证书,因为请求中的 CA 列表更多的是让客户端能够选择适当的证书作为一些语言(Java)依赖于此。
  • @PatrickMevzek 感谢您的回复。我只想让客户端验证服务器(所以我不需要客户端的提供者密钥和客户端证书)。您提到“单独配置您方以将某些 CA 识别为完全受信任的,以便您将使用它们来验证服务器证书”。但是,在 android 中,我可以使用“server.crt”(我认为用它来验证自己是不可能的)或“ca.crt”来使用相同的代码验证服务器证书(server.crt)。
  • 也许某些实现会这样做,但它违反了 RFC,因此不能依赖。
  • @user207421 我不会这么确定,原因很简单,因为消息大小有限。那么如果客户端和服务器的 CA 列表太大而无法放入其中,客户端和服务器应该怎么做呢?
【解决方案2】:

根据我观察到的结果,我相信 android 中默认的 trustManagerFactory 将接受两种类型的证书。一个包含我们在 trustManagerFactory 中加载的证书。另一个包含可以通过我们加载的证书正确验证的证书。

所以一个人可以加载服​​务器证书来信任一个特定的证书,或者他可以加载 ca 证书来信任一组可以由该 ca 证书验证的证书。

【讨论】:

    猜你喜欢
    • 2021-01-09
    • 2020-07-30
    • 1970-01-01
    • 2016-07-08
    • 2016-01-22
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多