强制 SSL 客户端套接字发送证书
Posted
技术标签:
【中文标题】强制 SSL 客户端套接字发送证书【英文标题】:Forcing SSL Client Socket to Send Certificate 【发布时间】:2012-03-27 09:28:40 【问题描述】:我正在尝试编写一个客户端(实际上是一个中间件,它是实体的客户端,但也充当其他人的服务器)。它应该以客户端身份与另一台服务器(VMware 的 VirtualCenter)通信,并要求它代表它做一些事情。
为了提供更多上下文,VirtualCenter 允许应用程序注册为扩展。所述应用程序可以在注册时注册其证书(setCertificate)。之后,应用程序可以使用其证书(loginExtensionByCertificate() 方法)登录到 VirtualCenter,因此不需要存储用户名和密码。但是,要使其正常工作,客户端(我的应用程序)必须发送证书作为其 SSL 连接的一部分,即使服务器(VirtualCenter)并没有特别要求它。
我正在用 Java 编写我的应用程序。创建我自己的密钥管理器,将其连接到我的密钥库并指定要使用的别名。然后初始化我的 ssl 上下文以使用该密钥管理器。在创建的套接字中,我确实看到他们的 SSLContext 中有我的密钥管理器。但是,我没有看到曾经调用密钥管理器来获取证书。出于某种原因,套接字觉得它不需要发送证书。
我了解服务器可能会要求客户端出示其证书。在这种情况下,它不会发生。我想知道是否有办法强制创建的套接字提供证书,而不管服务器是否要求它。
【问题讨论】:
我建议使用-Djavax.net.debug=ssl
或至少Djavax.net.debug=ssl:handshake
来调试握手,看看你是否得到CertificateRequest
并检查它包含的certificate_authorities
列表。
【参考方案1】:
安装 vCenter 时,它在端口 443 上使用反向代理,通过 HTTP 将请求转发到实际服务。代理从不要求客户端证书,也无法转发它。
尝试找到运行服务的实际端口并连接到该端口。
【讨论】:
【参考方案2】:终于解决了问题。看来 VMware 走的是一条截然不同的道路。这个问题被误解了。
首先,我使用 WireShark 跟踪连接,服务器在任何时候都不会询问(需要或想要)证书。但是,VirtualCenter (VC) 确实提供了一种方法来注册证书作为连接的一部分,然后使用该证书进行身份验证。为此,它们提供了一种隧道连接方式。
我使用的顺序是:
首先将要连接的 URL 指定为 https://:(注意协议是安全的,但端口不是)。
初始套接字将打开到明文端口。但是将 https 作为协议将强制调用我自己的 SSLSocketFactory。在我的套接字工厂中,我收到了对 public Socket createSocket(Socket s, String host, int port, boolean autoClose)
方法的调用,该方法允许我根据自己的喜好将套接字转换为 SSLSocket。
我的代码基本上看起来像:
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException
s.setKeepAlive(true);
String connectReq = "CONNECT /sdkTunnel HTTP/1.1\r\n\r\n";
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
writer.write(connectReq, 0, connectReq.length());
writer.flush();
// In this initial response, all that matters is the http code. If
// it is 200, the rest can be ignored.
READ_AND_VERIFY_INITIAL_RESPONSE();
SSLSocket sock = (SSLSocket) origSslSockFactory
.createSocket(s, s.getInetAddress().getHostName(), s.getPort(), true);
// Depending on whether the caller actually does the handshake
// or not, you may need to call handshake here. In my case, I
// did not need to, since HTTPClient I am using calls the
// handshake. Axis does not call, so you may have to in that
// case.
// sock.startHandshake();
return sock;
在上面的代码块中,origSslSockFactory 是被你的实现替换的 SSLSocketFactory。在我的例子中,它是 sun.security.ssl.SSLSocketFactoryImpl 的一个实例。
在该过程之后,调用 ExtensionManager.setExtensionCertificate() 成功。随后,对 SessionManager.loginExtensionByCertificate() 的所有调用也成功。
我想发布这个,因为似乎有很多关于这个的问题。
【讨论】:
【参考方案3】:只有在服务器请求时才能发送客户端证书。见TLS Spec. - Client Certificate message (RFC 4346, Section 7.4.6):
这是客户端在收到一个消息后可以发送的第一条消息 服务器你好完成消息。此消息仅在以下情况下发送 服务器请求证书。
如果服务器没有请求,您不能强制客户端发送客户端证书。
编辑:当然,您还需要确保您的密钥管理器/密钥库设置正确。您可能会遇到类似于why doesn't java send the client certificate during SSL handshake?
中描述的问题【讨论】:
【参考方案4】:VMware VC 将始终要求提供客户端的 SSL 证书,因为这是登录 VC 的经过身份验证的扩展的数量。所以,你在正确的轨道上。
我怀疑您只是在 SSL/Java 交互方面遇到了问题,而这并不是 VMware 问题(尽管它总是很难用 SSL 来判断......)。
我想你想听听这里的建议:Java client certificates over HTTPS/SSL
【讨论】:
【参考方案5】:但是,要使其正常工作,客户端(我的应用)必须发送证书 作为其 SSL 连接的一部分,即使服务器 (VirtualCenter) 并不是特别要求它
为此,服务器必须请求它。这就是 SSL 协议的定义方式。如果您可以安排您的客户端不请自来地发送它,而您不能这样做,那么服务器将必然会断开连接。
解决方案是在服务器端。你100%确定不是在问吗?也许它在询问但您的客户没有发送?例如,因为证书不是由服务器信任的 CA 签署的。
【讨论】:
您确定drop the connection
吗? AFAIK IIS 可以设置为忽略客户端证书。
@desaivv RFC 4346 'This message is sent only if the server requests a certificate' 看起来很清楚;也许它没有义务断开连接,但除非请求,否则 JSSE 肯定不会发送证书。我对 IIS 一无所知。
第 7.4 节还说“握手协议消息按照它们必须发送的顺序在下面显示;以意外的顺序发送握手消息会导致致命错误”。我会考虑将未经请求的ClientCertificate
消息作为“意外订单”发送(因此这会中断连接)。 @desaivv,“可以将IIS设置为忽略客户端证书”是什么意思? (任何文档/指针?)这里的“忽略”不就是不使用它们对后面的应用程序进行身份验证/授权吗?
感谢您的提示。我会深入挖掘。至于“不是特别要”的俏皮话:我的意思是,它不需要它。即,如果它不来,它不会断开连接。我认为这是 Java 的 SSLSocket.setWantClientAuth() 和 SSLSocket.setNeedClientAuth() 之间的区别。我认为 VirtualCenter 已将标志设置为需要,但不需要。将深入挖掘并报告。
@VirtuallyReal 这确实是区别。在这两种情况下,服务器都通过CertificateRequest
消息请求证书:在need
情况下,如果客户端不发送它,它会中止连接;在want
情况下,如果客户端不发送它,它会继续,但如果服务器要求对等证书,则会抛出PeerUnverifiedException
。 IIS
会在同样的情况下做类似的事情,不要问我什么。这些都不允许客户端发送未经请求的证书。以上是关于强制 SSL 客户端套接字发送证书的主要内容,如果未能解决你的问题,请参考以下文章
在TLS / SSL握手(证书等)中泄露了哪些信息? [关闭]