无需下载证书即可连接 Java 和 HTTPS URL

2022-08-31 19:37:40

此代码连接到HTTPS站点,我假设我没有验证证书。但是,为什么我不必在本地为站点安装证书呢?难道我不必在本地安装证书并为此程序加载它,还是将其下载到幕后?客户端到远程站点之间的流量在传输过程中是否仍加密?

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class TestSSL {

    public static void main(String[] args) throws Exception {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };
        // Install the all-trusting trust manager
        final SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

        URL url = new URL("https://www.google.com");
        URLConnection con = url.openConnection();
        final Reader reader = new InputStreamReader(con.getInputStream());
        final BufferedReader br = new BufferedReader(reader);        
        String line = "";
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }        
        br.close();
    } // End of main 
} // End of the class //

答案 1

您不必在本地加载证书的原因是,您已明确选择不验证证书,此信任管理器信任所有证书。

流量仍将加密,但您正在打开与中间人攻击的连接:您正在与某人秘密通信,只是不确定它是您期望的服务器,还是可能的攻击者。

如果您的服务器证书来自一个众所周知的 CA,即与 JRE 捆绑在一起的 CA 证书的默认捆绑包的一部分(通常是文件,请参阅 JSSE 参考指南),则只需使用默认信任管理器,而不必在此处设置任何内容。cacerts

如果您有特定的证书(自签名证书或来自您自己的 CA 的证书),则可以使用缺省信任管理器,也可以使用使用特定信任库初始化的信任管理器,但您必须在信任库中显式导入证书(经过独立验证后),如以下答案中所述。您可能也对此答案感兴趣。


答案 2

但是,为什么我不必在本地为站点安装证书呢?

好吧,您正在使用的代码被明确设计为接受证书而不进行任何检查。这不是很好的做法...但是,如果这是您要执行的操作,那么(显然)无需安装代码显式忽略的证书。

难道我不必在本地安装证书并为此程序加载它,还是将其下载到幕后?

不,也不。见上文。

客户端到远程站点之间的流量在传输过程中是否仍加密?

是的,它是。但是,问题是,由于您告诉它信任服务器的证书而不进行任何检查,因此您不知道您是在与真实服务器通信,还是与假装是真实服务器的其他站点通信。这是否是一个问题取决于具体情况。


如果我们以浏览器为例,通常浏览器不会要求用户为访问的每个 ssl 站点显式安装证书。

浏览器预安装了一组受信任的根证书。大多数情况下,当您访问“https”站点时,浏览器可以验证站点的证书(最终通过证书链)是否由这些受信任的证书之一保护。如果浏览器在链的开头没有将证书识别为受信任的证书(或者如果证书已过期或无效/不合适),则它将显示警告。

Java的工作方式相同。JVM 的密钥库具有一组可信证书,并且使用相同的过程来检查证书是否由可信证书保护。

java https 客户端 API 是否支持某种类型的机制来自动下载证书信息?

不。允许应用程序从随机位置下载证书,并将它们(作为受信任的)安装在系统密钥库中将是一个安全漏洞。