【问题标题】:Making SSL connections without intermediate certificates using HttpUrlConnection使用 HttpUrlConnection 建立没有中间证书的 SSL 连接
【发布时间】:2012-04-12 18:02:53
【问题描述】:

是否可以使用 SSL 连接到客户端只有根证书但服务器同时拥有根证书和中间证书的站点?

我正在尝试使用 HttpUrlConnection 与包含我的根的 TrustManager 进行连接,但我得到了通常的握手错误:

javax.net.ssl.SSLHandshakeException:
  sun.security.validator.ValidatorException:
  Certificate chaining error
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379)

我知道一般的解决方案是安装中间证书,但我想避免一次性获得供应商 X 的新中间证书。

我熟悉使用接受所有内容的 TrustManager,但这不是一种选择。

【问题讨论】:

    标签: java ssl ssl-certificate


    【解决方案1】:

    是否可以使用 SSL 连接到只有客户端的站点 具有根证书,但服务器同时具有根证书和 中间证书?

    这不仅是可能的,而且实际上是 TLS 工作的常用方式:

    1. TLS 客户端只有根证书。 Java - 至少是 Oracle JVM - 从 JRE 安装目录中的lib/security/cacerts 读取它们。浏览器要么有自己的内部列表,要么使用操作系统提供的列表。
    2. 当客户端启动 TLS 连接时,服务器应该发回自己的证书以及任何中间证书(如果它使用任何中间证书) - 此列表称为 证书链。对于当前版本 1.2。 section 7.4.2 of RFC 5246 中指定的 TLS(请参阅下面的报价)。
    3. 客户端接收这些证书,并验证签名,从服务器证书到它知道的根证书之一。这称为认证路径构建,并在RFC 4158 中进行了描述。
    4. 如果客户端可以建立有效的证书路径,它会继续进行连接设置。否则,它会中止并显示错误消息 - 类似于可怕的“此连接不受信任”错误。

    我知道一般的解决方法是安装中间 证书

    不,实际上不是,如上所述,服务器应该自动发送所有中间证书。

    但是:

    服务器上一个相当常见的配置错误是未在服务器上安装所有中间证书。在这种情况下,服务器将在连接设置期间发送不完整的证书链。然后客户端将无法建立有效的证书路径,并将中止连接。

    一些并发症会使这个问题难以诊断:

    • 某些客户端(尤其是浏览器)可能仍能成功连接,因为它们有丢失的中间证书的缓存副本。
    • 大多数客户端不会或无法区分缺少的中间证书(在服务器发送的链中)、缺少的根证书(在它们自己的列表中)和自签名证书,因此您必须弄清楚找出问题所在。

    诊断这些问题的有用工具:

    • OpenSSL 的命令行客户端可以显示服务器发送的证书链:openssl s_client -showcerts -connect google.com:443
    • Qualys 的SSL Server Test 将列出证书链以及任何验证错误

    来自section 7.4.2 of RFC 5246的相关引用:

    7.4.2。服务器证书

    何时发送此消息:

    服务器必须在约定的任何时候发送证书消息 密钥交换方法使用证书进行身份验证(此 包括本文档中定义的所有密钥交换方法,除了 DH_anon)。此消息将始终紧跟 ServerHello 消息。

    此消息的含义:

    此消息将服务器的证书链传送给客户端。

    [...]

    此消息的结构:

    [...]

    证书列表

    这是一个证书序列(链)。发件人证明 必须在列表中排在第一位。以下每个证书必须直接 证明它前面的那个。因为证书验证需要 根密钥是独立分发的,自签名的 指定根证书颁发机构的证书可以是 从链中省略,假设远程端必须 已经拥有它以便在任何情况下验证它。

    【讨论】:

    • 是的。这。对。在这里。
    【解决方案2】:

    服务器应在Certificate 消息中发送整个 证书链,包括中间证书。客户端将检查整个链并找到它信任的根证书。所以你所描述的应该有效。

    当然,有可能(错误)配置服务器不发送整个链 - 在这种情况下,客户端的检查可能会失败。

    【讨论】:

    • 我同意,从逻辑上讲它应该可以工作。客户端可以验证整个链,它只需要检查受信任的根是否签署了接收链中的最后一个人。如果我们在这里遗漏了什么,我想知道什么。唯一的事情是确保将服务器配置为发送完整的链,而不仅仅是它自己的证书。不同意接受的答案。
    • @AmitNaidu:没错。问题是某些服务器(错误)配置并且不发送完整的链。我试图在我的回答中解释这一点。我冒昧地将其编辑为答案。
    【解决方案3】:

    this answer中所述:

    使用 Oracle JRE,您可以启用具有 Java 系统属性 -Dcom.sun.security.enableAIAcaIssuers=true 的中间证书的自动下载

    为此,服务器的证书必须提供中间证书(证书的颁发者)的 URI。

    【讨论】:

      【解决方案4】:

      当服务器拥有由根 CA(例如 Verisign)签名的 sub-ca-A 签名的证书时,服务器会将所有证书作为服务器 hello 的一部分发送,以便服务器的证书可以验证。

      在您的情况下,您在信任库中只有根 CA。

      因此,由于您缺少 sub-ca 证书,因此无法建立信任验证链。
      不仅不可能,而且能够做到这一点是错误的/不安全的。因此,您正在做的事情偏离了正确的道路。

      所以你只有两个选择。
      服务器的实际证书作为信任放入信任库中。
      整个链放入信任库,即中间 CA 证书和根。

      您有什么顾虑?中间证书过期?

      【讨论】:

      • 我担心的是需要不断添加中间证书。有时,客户会有一个调用外部服务器的脚本,结果将是 SSL 握手错误。然后,我们将不得不放弃我们正在做的所有事情来添加新的中间证书。
      • 如果我正确理解您的问题,您的设置有一个根 CA,例如X 委托子 CA。因此,您希望避免将子 CA 证书存储在信任库中,而只保留根 CA 的 1 个证书。我能想到的只有一种方法,而且更难做到正确并取决于在很多事情上。如果您的服务器证书包含一个可以下载证书颁发者的地址,则可以动态获取它并在现场构建链。但老实说,我不确定它是否很容易做对
      • 是的,我曾考虑使用“不信任”密钥库进行连接以获取证书,并检查它们是否使用我们的根之一,并使用 getCertificates 调用构建链。中间证书也存在信任问题。
      • 我不明白这个答案。服务器将发送所有证书,包括中间证书和根证书。客户端有根。因此,客户端可以验证整个链,并找到一个受信任的锚点。所以 OP 描述的应该是可行的。
      • @EJP:是的,这个答案没有意义。大概是某种误会吧。你的答案是正确的:-)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-09
      • 1970-01-01
      • 2019-03-09
      • 2022-10-19
      • 1970-01-01
      • 2011-06-20
      相关资源
      最近更新 更多