Qua*_*nic 5 java ssl ssl-certificate
我的问题:
我想连接到服务器(不限于HTTPS协议 - 可能是LDAP-over-SSL,可能是SMTPS,可能是IMAPS等),可能正在使用Java默认不信任的证书(因为它们是自签名).
所需的工作流程是尝试连接,检索证书信息,将其呈现给用户,如果他接受,则将其添加到信任库,以便将其信任.
我被困在检索证书.我有代码(请参阅帖子的末尾),我已经从这里和从有关java SSL的问题的答案指向的站点中得到了解释.代码只是创建一个SSLSocket,启动SSL握手,并询问SSL会话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]
Run Code Online (Sandbox Code Playgroud)
如果我运行,-Djavax.net.debug=all我会看到JVM确实检索了自签名证书,但是在它返回证书之前,它会破坏连接以使用不受信任的证书.
看起来像鸡蛋问题.它不会让我看到证书,因为它们不受信任.但我需要看到证书能够将它们添加到信任库,以便它们可信.你怎么突破这个?
例如,如果我将程序运行为:
java SSLTest www.google.com 443
Run Code Online (Sandbox Code Playgroud)
我得到了Google正在使用的证书的打印输出.但如果我把它作为
java SSLTest my.imap.server 993
Run Code Online (Sandbox Code Playgroud)
我得到上面引用的异常.
代码:
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");
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
xon*_*nya 11
您可以这样做实现一个TrustManager接受所有证书的临时文件和一个HostnameVerifier验证所有名称的临时文件(显然您只需要使用它们来检索证书而不是发送私有数据).
以下代码从任意https URL检索证书并将其保存到文件中:
URL url = new URL("https://<yoururl>");
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(null, new TrustManager[]{ new X509TrustManager() {
private X509Certificate[] accepted;
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
accepted = xcs;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return accepted;
}
}}, null);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
connection.setSSLSocketFactory(sslCtx.getSocketFactory());
if (connection.getResponseCode() == 200) {
Certificate[] certificates = connection.getServerCertificates();
for (int i = 0; i < certificates.length; i++) {
Certificate certificate = certificates[i];
File file = new File("/tmp/newcert_" + i + ".crt");
byte[] buf = certificate.getEncoded();
FileOutputStream os = new FileOutputStream(file);
os.write(buf);
os.close();
}
}
connection.disconnect();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
14801 次 |
| 最近记录: |