SSLSocket:为什么我们需要在访问服务器证书之前进行握手?

时间:2012-09-24 10:19:12

标签: java ssl

我正在通过SSLSocket连接到使用证书进行握手的远程主机。由于我们不使用具有所有证书颁发机构的默认JVM信任库,因此我需要将远程主机证书添加到我的信任库。

如何从SSLSocket获取我应该信任的证书?它似乎确实检索它们我需要使用似乎需要握手的SSLSession。 为什么我们需要执行握手才能检索证书?

是否有任何工具允许提取使用的远程主机证书?

2 个答案:

答案 0 :(得分:4)

实际上,握手期间会出示证书,以便服务器可以识别自己,并最终为客户端提供相同的信息。

当你这样做时:

SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
[...]
SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
try {
    socket.startHandshake();
    socket.close();
} catch (SSLException e) {
    e.printStackTrace(System.out);
}

如果你没有在startHandshake()上获得异常,则意味着证书由于某种原因已经被信任(直接出现在密钥库中,由可信实体签名)。

是否发生异常,您可以访问下载的链:

X509Certificate[] chain = tm.chain;
if (chain == null) {
   // error in downloading certificate chain
   return;
}

// loop through chain
for (int i = 0; i < chain.length; i++) {
    X509Certificate cert = chain[i];
    [....]
}

使用X509Certificate对象实例,您实际上可以更新您的第k个密钥库:

X509Certificate cert = chain[k];
String alias = host + "-" + (k + 1);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
[...]
ks.setCertificateEntry(alias, cert);
OutputStream out = new FileOutputStream("jssecacerts");
ks.store(out, passphrase);
out.close();

查看here以获取完整的样本。

或者,为您信任的服务器下载证书的另一种更安全的方法是使用openssl命令:

# openssl s_client -showcerts -connect $SERVER:$PORT  2>&1 | \
      sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >/tmp/$SERVERNAME.cert  

然后照常导入keytool

答案 1 :(得分:3)

通常,您不应该从SSLSocket获得您应该信任的证书,而应该是您独立获得的配置设置,作为您想要信任的参考。

您似乎想要做的是获取第一个连接的证书,希望没有拦截该连接,然后将其用作后续连接的参考(类似于SSH通常所做的事情,当您不必知道第一个连接上的服务器密钥的指纹,但检查以后是否得到相同的信息。

安全方面,这并不理想,因为初始连接可能被MITM攻击者截获(这会使所有后续连接都受到攻击),但这肯定是一种降低风险的方法。理想情况下,您应该将该证书与您以其他方式获得的已知参考进行比较。

您可以使用自定义X509TrustManager在握手期间访问远程证书(或者您可以禁用信任验证并稍后获取证书),然后您可以使用该证书初始化SSLContext,从中您可以获得SSLSocketFactory。在信任管理器中禁用信任验证通常是一个坏主意(因为它打开了与MITM攻击的连接),但是为此目的可以接受。您可能对InstallCert实用程序感兴趣,该实用程序应该或多或少地执行您所需要的工作。

  

为什么我们需要在访问服务器之前进行握手   证书?

这是在握手期间完成的,因为SSL / TLS套接字API的目的是为应用程序层提供一个可以认为是安全的套接字,并在该阶段或多或少地使用普通套接字。通常,对于JSSE(或通常是其他SSL / TLS堆栈)的大多数用途,作为使用该堆栈的应用程序开发人员,您不希望必须明确地进行验证。在握手期间检查证书也建议作为TLS specification

的一部分
  

收到服务器hello done消息后,客户端应该   验证服务器是否提供了有效证书(如果需要)   并检查服务器hello参数是否可以接受。