2012-03-09 127 views
5

我正在尝试编写一个客户端(实际上是一个中间件,它是一个实体的客户端,但也充当了其他服务器的服务器)。在它的客户端容量中,它应该与另一台服务器(VMware的VirtualCenter)交谈,并要求它代表它做些事情。强制SSL客户端套接字发送证书

为了给您更多的上下文,VirtualCenter允许应用程序注册为扩展。所述申请可以在注册时注册其证书(setCertificate)。之后,应用程序可以使用其证书(loginExtensionByCertificate()方法)登录VirtualCenter,因此不需要存储用户名和密码。然而,为了这个工作,即使服务器(VirtualCenter)没有特别要求,客户端(我的应用)也必须发送证书作为其SSL连接的一部分。

我正在用Java编写我的应用程序。创建我自己的密钥管理器,将它连接到我的密钥库并指定要使用的别名。然后初始化我的SSL上下文以使用该密钥管理器。在创建的套接字中,我确实看到他们的SSLContext中有我的密钥管理器。但是,我没有看到该密钥管理器曾被称为获取证书。出于某种原因,套接字不认为需要发送证书。

我知道服务器可能会要求客户提交其证书。在这种情况下,它不会发生。我在想,是否有办法强制创建的套接字提交证书,而不管服务器是否要求它。

+0

我建议使用'-Djavax.net.debug = ssl'或至少'Djavax.net.debug = ssl:handshake'来调试握手以查看您是否获得'CertificateRequest'并检查'certificate_authorities'列表它包含。 – Bruno 2012-03-10 15:31:38

回答

2

但是那样的问题,对于这个工作,客户端(我的应用程序)都必须发送证书 作为SSL连接的一部分,即使服务器(VirtualCenter的) 不是要求它特别

对于工作,服务器必须提出要求。这就是SSL协议的定义。如果你可以安排你的客户端主动发送它,你不能这样做,那么服务器就会被拒绝连接。

解决方法是在服务器端。你100%确定它没有问?也许它是问,但你的客户没有发送?例如,因为证书未由服务器信任的CA签名。

+0

你确定要放弃连接吗? AFAIK IIS可以设置为忽略客户端证书。 – 2012-03-10 06:54:46

+0

@desaivv RFC 4346'只有当服务器请求证书时才会发送此消息'似乎很清楚;也许它没有义务放弃连接,但JSSE肯定不会发送证书,除非有要求。我对IIS一无所知。 – EJP 2012-03-10 07:38:45

+1

第7.4节还指出“*握手协议消息按它们必须发送的顺序在下面给出;以意外的顺序发送握手消息会导致致命错误*”。我会考虑发送一个未被请求的'ClientCertificate'消息作为“意外命令”(所以这会破坏连接)。 @desaivv,你是什么意思的“IIS可以设置为忽略客户端证书”? (任何文档/指针?)这里不是“忽略”,而仅仅是不使用它们来认证/授权坐在后面的应用程序? – Bruno 2012-03-10 15:27:54

4

客户端证书只能在服务器请求时发送。见TLS Spec. - Client Certificate message (RFC 4346, Section 7.4.6)

这是客户端可以接收 服务器问候结束消息后发送的第一条消息。该消息仅在 服务器请求证书时才会发送。

您不能强制客户端发送客户端证书是服务器没有请求的。

编辑:当然,您还需要确保您的keymanager/keystore设置正确。你可能会面临相似于why doesn't java send the client certificate during SSL handshake?

0

VMware VC将始终要求客户端的SSL证书,因为这是多少个经过身份验证的扩展登录到VC。所以,你在正确的轨道上。

我怀疑你只是在SSL/Java交互方面遇到问题,它并不特别是VMware的问题(尽管它总是很难用SSL来分析......)。

我想你想跟随在这里建议:Java client certificates over HTTPS/SSL

0

最后,解决了这个问题。看起来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()的所有调用也都成功。

我想发布这个,因为这似乎有很多问题。

0

当安装vCenter时,它会在端口443上使用反向代理,通过HTTP将请求转发给实际服务。代理从不要求客户端证书也不能转发它。

尝试查找服务正在运行的实际端口并连接到该端口。