Java和HTTPS的url连接无需下载证书

此代码连接到HTTPS网站,我假设我没有validation证书。 但为什么我不需要在本地安装证书? 我不应该在本地安装证书并加载该程序或将其下载到封面之后吗? 客户端到远程站点之间的stream量是否仍然在传输中encryption?

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 // 

您不必在本地加载证书的原因是,您明确select不validation证书,并且信任所有证书的信任pipe理器。

stream量仍然会被encryption,但是你打开了中间人攻击的连接:你和某人秘密通信,你只是不确定它是你期望的服务器还是可能的攻击者。

如果您的服务器证书来自知名CA(与JRE捆绑的默认CA证书捆绑包的一部分(通常为cacerts文件,请参阅JSSE参考指南)),则可以使用默认信任pipe理器,而不必这里设置任何东西

如果您有特定的证书(自签名或来自您自己的CA),则可以使用默认的信任pipe理器,也可以使用特定的信任库进行初始化,但必须在信任库中明确导入证书(独立后validation),如本答案所述 。 您可能也对这个答案感兴趣。

但为什么我不需要在本地安装证书?

那么你正在使用的代码明确地devise为接受证书,而不进行任何检查。 这不是一个好习惯……但是如果这是你想要做的,那么(显然)不需要安装一个你的代码明确忽略的证书。

我不应该在本地安装一个证书并加载这个程序,或者将它下载到封面之后吗?

不,不,不。 往上看。

客户端到远程站点之间的stream量是否仍然在传输中encryption?

是的。 然而,问题在于,既然你已经告诉它信任服务器的证书而不做任何检查,你不知道你是在跟真实的服务器,还是假装成真正的服务器的其他站点。 这是否是一个问题取决于具体情况。


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

浏览器有一组预先安装的可信根证书。 大多数情况下,当您访问“https”网站时,浏览器可以validation网站的证书(最终是通过证书链),由其中一个可信任的证书进行保护。 如果浏览器无法将链路启动时的证书认可为可信证书(或证书已过期或无效/不适当),则会显示警告。

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

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

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

Java和HTTPS的url连接无需下载证书

如果你真的想避免下载服务器的证书,那么使用匿名的Diffie-Hellman(ADH)这样的匿名协议。 服务器的证书不与ADH和朋友一起发送。

您可以使用setEnabledCipherSuitesselect一个匿名协议。 您可以看到getEnabledCipherSuites可用的密码套件列表。

相关:这就是为什么你必须在OpenSSL中调用SSL_get_peer_certificate 。 你将得到一个带有匿名协议的X509_V_OK ,这就是你如何检查交换中是否使用了证书。

但是正如Bruno和Stephed C所说,避免检查或使用匿名协议是一个坏主意。


另一种select是使用TLS-PSK或TLS-SRP。 他们也不需要服务器证书。 (但我不认为你可以使用它们)。

蹭的是,您需要在系统中进行预configuration,因为TLS-PSK是预共享密钥,而TLS-SRP是安全远程密码 。 authentication是相互的,而不仅仅是服务器。

在这种情况下,相互authentication是由双方知道共享密钥并到达相同的pipe理员密钥的财产提供的; 或者一个(或两个)不和通道设置失败。 每一方certificate这个秘密的知识是“共同的”部分。

最后,TLS-PSK或TLS-SRP不会做愚蠢的事情,比如在使用HTTP(或通过HTTPS)的Web应用程序中咳嗽用户的密码。 这就是为什么我说每个党都certificate了这个秘密的知识

一个简单的,但不是纯粹的Java解决scheme,是从Java中解脱出来,从而完全控制请求的完成方式。 如果您只是为了简单起见而这样做,那么您可以使用此方法有时忽略证书错误。 这个例子展示了如何使用有效或无效的证书向安全的服务器发出请求,传递一个cookie,并使用curl从java获取输出。

 import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; public class MyTestClass { public static void main(String[] args) { String url = "https://www.google.com"; String sessionId = "faf419e0-45a5-47b3-96d1-8c62b2a3b558"; // Curl options are: // -k: ignore certificate errors // -L: follow redirects // -s: non verbose // -H: add a http header String[] command = { "curl", "-k", "-L", "-s", "-H", "Cookie: MYSESSIONCOOKIENAME=" + sessionId + ";", "-H", "Accept:*/*", url }; String output = executeShellCmd(command, "/tmp", true, true); System.out.println(output); } public String executeShellCmd(String[] command, String workingFolder, boolean wantsOutput, boolean wantsErrors) { try { ProcessBuilder pb = new ProcessBuilder(command); File wf = new File(workingFolder); pb.directory(wf); Process proc = pb.start(); BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream())); StringBuffer sb = new StringBuffer(); String newLine = System.getProperty("line.separator"); String s; // read stdout from the command if (wantsOutput) { while ((s = stdInput.readLine()) != null) { sb.append(s); sb.append(newLine); } } // read any errors from the attempted command if (wantsErrors) { while ((s = stdError.readLine()) != null) { sb.append(s); sb.append(newLine); } } String result = sb.toString(); return result; } catch (IOException e) { throw new RuntimeException("Problem occurred:", e); } } } 

如下所示,使用最新的X509ExtendedTrustManager而不是X509Certificate: java.security.cert.CertificateException:证书不符合algorithm约束

  package javaapplication8; import java.io.InputStream; import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedTrustManager; /** * * @author hoshantm */ public class JavaApplication8 { /** * @param args the command line arguments * @throws java.lang.Exception */ public static void main(String[] args) throws Exception { /* * fix for * 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 */ TrustManager[] trustAllCerts = new TrustManager[]{ new X509ExtendedTrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } @Override public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException { } } }; 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() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); /* * end of the fix */ URL url = new URL("https://10.52.182.224/cgi-bin/dynamic/config/panel.bmp"); URLConnection con = url.openConnection(); //Reader reader = new ImageStreamReader(con.getInputStream()); InputStream is = new URL(url.toString()).openStream(); // Whatever you may want to do next } } 

如果您使用任何支付网关来打任何url只是为了发送消息,那么我使用了一个webview,如下所示: 如何在android webview中加载https url而不使用ssl

并在可见度消失的情况下在您的活动中制作网页视图。 你需要做的只是加载该webview ..就像这样:

  webViewForSms.setWebViewClient(new SSLTolerentWebViewClient()); webViewForSms.loadUrl(" https://bulksms.com/" + "?username=test&password=test@123&messageType=text&mobile="+ mobileEditText.getText().toString()+"&senderId=ATZEHC&message=Your%20OTP%20for%20A2Z%20registration%20is%20124"); 

简单。

你会得到这个:SSLTolerentWebViewClient从这个链接: 如何加载httpsurl,而不使用ssl在android webview