我在服务器和客户端之间建立了一个https连接,其中客户端是一个java程序,服务器是一个servlet。我使用以下代码从服务器打印证书详细信息。
URL url = new URL("https://localhost:8443/cert");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslsocketfactory);
connection.setDoOutput(true);
if(connection!=null){
Certificate[] certs = connection.getServerCertificates();// #1
System.out.println("Cert Type : " + certs[0].getType());
System.out.println("Cert Hash Code : " + certs[0].hashCode());
System.out.println("Cert Public Key Algorithm : " + certs[0].getPublicKey().getAlgorithm());
System.out.println("Cert Public Key Format : " + certs[0].getPublicKey().getFormat());
System.out.println("\n");
}
但我得到以下异常。
java.lang.IllegalStateException: connection not yet open
我认为握手应该在调用theurl.openconnection()方法时立即进行。
这有什么问题?
异常抛出行号'#1'(参见上面代码中的注释)
答案 0 :(得分:3)
您正在尝试连接到自签名证书主机。按照this answer答案的选项2的说明进行操作,这样可以连接。
我将connection.setDoOutput()
替换为connection.connect()
,代码正常运行。
请勿将此机制用于测试以外的任何其他内容 - 您应使用有效签名的证书
答案 1 :(得分:3)
SSLSocket
" [...]的握手可以通过以下三种方式之一启动:"
- 调用startHandshake明确开始握手,或
- 在此套接字上读取或写入应用程序数据的任何尝试都会导致隐式握手,或
- 如果没有当前有效的会话,则调用getSession会尝试建立会话,并完成隐式握手。
似乎唯一适用于HttpsURLConnection
的是尝试读取/写入某些内容(因为您无法直接获取底层SSLSocket
实例以启动握手或获取会议)。
如果您在尝试获取证书之前只是致电connection.getInputStream();
,那么应该启动握手,之后您将获得证书。
请注意,只有当您SSLContext
/ SSLSocketFactory
信任您要查看的证书时,才能达到这一点。为此,您可以构建包含该证书的a keystore (which you will use as a truststore),如this answer中所述。您可以在自己的SSLContext
中使用if,也可以通过javax.net.ssl.trustStore*
系统属性全局使用(在任何SSL使用之前设置)。避免TrustManagers
不检查任何内容的示例(这里有一些关于SO):它将完全禁用证书验证,使连接可能容易受到MITM攻击。