似乎我们无法配置我们的Java客户端以正确处理SSL连接。
我们可以使用具有指定客户端证书和curl工具的浏览器成功连接到HTTPS URL,但不能使用我们的java客户端或基于java的SOAP UI工具连接到HTTPS.Even当我们在浏览器中使用相同的* .p12证书时(工作正常)和SOAP UI工具(没有工作)。
所以,我们有以下信息:
我们将这些pem证书转换为jks和p12,并使用apache http client进行连接。正如之前提到的那样,相同的证书适用于浏览器,但对于自定义客户端或基于Java的SOAP UI都不起作用。
首先,我猜测我们有there所述的问题: 当服务器请求客户端证书(作为TLS握手的一部分)时,它还将提供可信CA的列表作为证书请求的一部分。当您希望提交用于身份验证的客户端证书未由其中一个CA签名时,它将无法在所有中显示
但似乎我们遇到了这样的问题它不会为浏览器解析此证书。因为默认浏览器默认情况下不允许使用不正确的(使用不同的颁发者)证书。
我根据以下tutorial创建的客户端:
所以,基本上我不得不提问:
我们尝试使用openssl s_client工具。输出显示服务器证书符合我们的预期,但客户端证书验证没有CA:
未发送客户端证书CA名称
openssl s_client的语法,如果使用连接到443而没有指定模式(https://)。所以我们决定在没有架构的情况下在浏览器中尝试我们的URL但使用443端口。服务器回复:
普通HTTP请求已发送到HTTPS端口
接下来,我们在此调查中的步骤是尝试wireshark。首先我们使用浏览器进行了正确的会话,然后我们尝试了我们的应用我们也尝试过openssl s_client。
差异在于服务器密钥交换数据包。虽然浏览器数据包有一个证书请求,但openssl和我们的应用程序在该数据包中没有这样的字段。 此外,如果浏览器没有发送任何证书,该字段仍然存在于数据包中。
因此只有浏览器才能获得证书请求。任何关于为什么会发生这种情况的想法都会受到赞赏。
openssl s_client -connect your.server.name:443
时,我们会收到有效的服务器证书,没有发送客户端证书CA名称。我们还得到验证错误:num = 2:无法获得颁发者证书。经过一些调查和研究,我们设法找出问题的根本原因。所以我们在服务器上启用了SNI(服务器名称指示),我们的应用程序正在使用apache httpclient 4.2.1。此版本的客户端不支持SNI扩展,也不会在Client Hello数据包中发送server_name扩展。之后,服务器不会通过客户端证书通知客户端进行身份验证。 This issue已在4.3.2版本中修复。
现在我们正在尝试检查4.3.2版本的httpclient是否正确发送server_name扩展名。
答案 0 :(得分:3)
我建议在发送请求时进行数据包捕获(例如,Wireshark或tcpdump)。在Wireshark中,您可以过滤“SSL”以及源和目标IP地址(SSL& ip.src == XXXX&& ip.dst == XXXX)以隔离感兴趣的流量,然后查找“信息”列中的以下SSL握手消息序列(或类似内容):
客户 - >服务器:客户端你好
服务器 - >客户:服务器Hello,证书,证书请求,服务器Hello完成
客户 - >服务器:证书,证书验证,客户密钥交换
要查找的关键事项是证书请求(确保服务器要求证书,并检查可信CA的列表),然后是客户端 - >服务器证书消息,包括客户端的证书。
您可以检查数据包捕获的其他事项:
*客户端支持的密码套件列表。如果服务器不接受任何连接,则连接将失败
* TLS版本。客户端和服务器必须支持通信版本才能进行通信。您似乎仅限于TLS v1.0,因此请检查另一端是否支持此功能
*其他“警报”消息,表示致命错误或警告。
答案 1 :(得分:3)
最后找到了解决这个问题的方法。
发生此问题的先决条件是:
根本原因的问题是在旧版本的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在正确的界面上收集数据包。