我的问题:
我想连接到可能使用 Java 默认不信任的证书(因为它们是自签名)。
所需的工作流程是尝试连接,检索证书信息,将其呈现给用户,如果他接受它,则将其添加到信任库,以便继续信任它。
我被困在检索证书上。我有代码(见文章末尾),这些代码是我从这里和从回答有关 java SSL 的问题所指向的站点中抄来的。SSLSocket
该代码只是创建一个Certificate[]
. 当我使用已经可信任的证书连接到服务器时,代码可以正常工作。但是当我使用自签名证书连接到服务器时,我得到了通常的结果:
Exception in thread "main" javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
[etc]
如果我运行,-Djavax.net.debug=all
我会看到 JVM 确实检索了自签名证书,但会在到达返回证书的地步之前破坏连接以使用不受信任的证书。
好像是先有鸡还是先有蛋的问题。它不会让我看到证书,因为它们不受信任。但我需要查看证书才能将它们添加到信任库中,以便它们受到信任。你如何摆脱这种状态?
例如,如果我将程序运行为:
java SSLTest www.google.com 443
我得到了 Google 正在使用的证书的打印输出。但是如果我运行它
java SSLTest my.imap.server 993
我得到了上面提到的异常。
编码:
import java.io.InputStream;
import java.io.OutputStream;
import java.security.cert.*;
import javax.net.SocketFactory;
import javax.net.ssl.*;
public class SSLTest
{
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: SSLTest host port");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
SocketFactory factory = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.startHandshake();
Certificate[] certs = socket.getSession().getPeerCertificates();
System.out.println("Certs retrieved: " + certs.length);
for (Certificate cert : certs) {
System.out.println("Certificate is: " + cert);
if(cert instanceof X509Certificate) {
try {
( (X509Certificate) cert).checkValidity();
System.out.println("Certificate is active for current date");
} catch(CertificateExpiredException cee) {
System.out.println("Certificate is expired");
}
}
}
}
}