Java Https客户端证书的工作方式与浏览器或其他工具完全不同

时间:2014-07-28 19:27:18

标签: java ssl https certificate

似乎我们无法配置我们的Java客户端以正确处理SSL连接。

我们可以使用具有指定客户端证书和curl工具的浏览器成功连接到HTTPS URL,但不能使用我们的java客户端或基于java的SOAP UI工具连接到HTTPS.Even当我们在浏览器中使用相同的* .p12证书时(工作正常)和SOAP UI工具(没有工作)。

所以,我们有以下信息:

  1. 公共客户证书(pem格式)
  2. 客户的私钥(pem格式)
  3. 服务器用于验证客户端证书的CA;
  4. 我们将这些pem证书转换为jks和p12,并使用apache http client进行连接。正如之前提到的那样,相同的证书适用于浏览器,但对于自定义客户端或基于Java的SOAP UI都不起作用。

    首先,我猜测我们有there所述的问题: 当服务器请求客户端证书(作为TLS握手的一部分)时,它还将提供可信CA的列表作为证书请求的一部分。当您希望提交用于身份验证的客户端证书未由其中一个CA签名时,它将无法在所有中显示

    但似乎我们遇到了这样的问题它不会为浏览器解析此证书。因为默认浏览器默认情况下不允许使用不正确的(使用不同的颁发者)证书。

    我根据以下tutorial创建的客户端:

    所以,基本上我不得不提问:

    1. 如何知道客户端证书是否完全发送:我们使用org.apache.http.conn.ssl.SSLSocketFactory可能在什么地方进行调试;
    2. 问题可能是什么,可能是描述浏览器SSL配置和基于java的工具差异的任何好的参考(可能更严格地说是java SSL验证)?
    3. 更新1

      我们尝试使用openssl s_client工具。输出显示服务器证书符合我们的预期,但客户端证书验证没有CA:

      未发送客户端证书CA名称

      openssl s_client的语法,如果使用连接到443而没有指定模式(https://)。所以我们决定在没有架构的情况下在浏览器中尝试我们的URL但使用443端口。服务器回复:

      普通HTTP请求已发送到HTTPS端口

      接下来,我们在此调查中的步骤是尝试wireshark。首先我们使用浏览器进行了正确的会话,然后我们尝试了我们的应用我们也尝试过openssl s_client。

      差异在于服务器密钥交换数据包。虽然浏览器数据包有一个证书请求,但openssl和我们的应用程序在该数据包中没有这样的字段。 此外,如果浏览器没有发送任何证书,该字段仍然存在于数据包中。

      因此只有浏览器才能获得证书请求。任何关于为什么会发生这种情况的想法都会受到赞赏。

      评论中的问题解答

      • 我们正在使用java 1.7
      • 是初始客户端证书身份验证
      • 当我们使用openssl s_client -connect your.server.name:443时,我们会收到有效的服务器证书,没有发送客户端证书CA名称。我们还得到验证错误:num = 2:无法获得颁发者证书
      • 我们从s_client和浏览器获得的服务器证书是相同的
      • 没有公布的CA证书列表未发送客户端证书CA名称

      更新2

      经过一些调查和研究,我们设法找出问题的根本原因。所以我们在服务器上启用了SNI(服务器名称指示),我们的应用程序正在使用apache httpclient 4.2.1。此版本的客户端不支持SNI扩展,也不会在Client Hello数据包中发送server_name扩展。之后,服务器不会通过客户端证书通知客户端进行身份验证。 This issue已在4.3.2版本中修复。

      现在我们正在尝试检查4.3.2版本的httpclient是否正确发送server_name扩展名。

2 个答案:

答案 0 :(得分:3)

我建议在发送请求时进行数据包捕获(例如,Wireshark或tcpdump)。在Wireshark中,您可以过滤“SSL”以及源和目标IP地址(SSL& ip.src == XXXX&& ip.dst == XXXX)以隔离感兴趣的流量,然后查找“信息”列中的以下SSL握手消息序列(或类似内容):

客户 - >服务器:客户端你好 服务器 - >客户:服务器Hello,证书,证书请求,服务器Hello完成
客户 - >服务器:证书,证书验证,客户密钥交换

要查找的关键事项是证书请求(确保服务器要求证书,并检查可信CA的列表),然后是客户端 - >服务器证书消息,包括客户端的证书。

您可以检查数据包捕获的其他事项:
*客户端支持的密码套件列表。如果服务器不接受任何连接,则连接将失败 * TLS版本。客户端和服务器必须支持通信版本才能进行通信。您似乎仅限于TLS v1.0,因此请检查另一端是否支持此功能 *其他“警报”消息,表示致命错误或警告。

答案 1 :(得分:3)

最后找到了解决这个问题的方法。

发生此问题的先决条件是:

  • Java版本< 7
  • Apache httpclient版本< 4.3.2
  • 服务器上已启用SNI

根本原因的问题是在旧版本的httpclient中使用过。版本4.3.2之前的客户端无法处理SNI(服务器名称指示),也不能在客户端Hello数据包(字段名称:server_name)中发送必要的信息。这是一个问题link

基本上,SNI用于在同一个IP地址上运行多个虚拟域,每个域使用不同的服务器证书。当启用SNI的服务器获取没有server_name的Client Hello数据包时,它无法确定它必须使用哪个证书,因此不会通过CA列表通告客户端。

解决方案是使用Java版本> = 7和apache httpclient版本> = 4.3.2。
另外wireshark对于调试此类问题非常有用,并且与其一起使用的过滤器示例由 juhraffe 发布:

ssl && (ip.dst == ip_or_domain_of_server || ip.src == ip_or_domain_of_server)

同样在使用它时,不要忘记让wireshark在正确的界面上收集数据包。