此代码连接到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 //
答案 0 :(得分:29)
您不必在本地加载证书的原因是您已明确选择不验证证书,并且此信任管理器信任所有证书。
流量仍然会被加密,但是您打开了与中间人攻击的连接:您正在与某人秘密通信,您只是不确定它是否是您期望的服务器,或者是可能的攻击者。
如果您的服务器证书来自着名的CA,与JRE捆绑在一起的默认CA证书捆绑(通常为cacerts
文件,请参阅JSSE参考指南),您可以使用默认的信任管理器,你不必在这里设置任何东西。
如果您有特定证书(自签名证书或来自您自己的CA),您可以使用默认信任管理器或可能使用特定信任库初始化的证书,但您必须在信任库中明确导入证书(经过独立核实后),如this answer所述。您可能也对this answer感兴趣。
答案 1 :(得分:12)
但为什么我不必在本地为该网站安装证书?
您正在使用的代码明确设计为接受证书而不进行任何检查。这不是一个好的做法...但如果这是你想要做的,那么(显然)不需要安装你的代码明确忽略的证书。
我不应该在本地安装证书并为此程序加载它还是在封面下载?
不,不。见上文。
客户端到远程站点之间的流量是否仍然在传输中加密?
是的。但是,问题在于,由于您已经告诉它在不进行任何检查的情况下信任服务器的证书,因此您不知道您是在与真实服务器进行通信,还是在与假装的其他网站进行通信成为真正的服务器。这是否是一个问题取决于具体情况。
如果我们以浏览器为例,通常浏览器不会要求用户为每个访问过的ssl站点明确安装证书。
浏览器预先安装了一组受信任的根证书。大多数情况下,当您访问“https”站点时,浏览器可以验证该站点的证书(最终通过证书链)是否受到其中一个受信任证书的保护。如果浏览器无法将链起始处的证书识别为可信证书(或者证书已过期或无效/不合适),则会显示警告。
Java的工作方式相同。 JVM的密钥库具有一组受信任的证书,并且使用相同的过程来检查证书是否受可信证书的保护。
java https客户端api是否支持某种类型的机制来自动下载证书信息?
没有。允许应用程序从随机位置下载证书,并在系统密钥库中安装它们(如可信)将是一个安全漏洞。
答案 2 :(得分:3)
使用最新的X509ExtendedTrustManager而不是X509Certificate,如下所示:java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
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
}
}
答案 3 :(得分:1)
不下载证书的Java和HTTPS网址连接
如果您确实想避免下载服务器的证书,请使用Anonymous Diffie-Hellman(ADH)等匿名协议。服务器的证书不会与ADH和朋友一起发送。
您选择setEnabledCipherSuites
的匿名协议。您可以使用getEnabledCipherSuites
查看密码套件列表。
相关:这就是为什么你必须在OpenSSL中调用SSL_get_peer_certificate
的原因。您将获得一个具有匿名协议的X509_V_OK
,以及您如何检查证书是否在交换中使用。
但正如Bruno和Stephed C所说,避免检查或使用匿名协议是一个坏主意。
另一种选择是使用TLS-PSK或TLS-SRP。他们也不需要服务器证书。 (但我不认为你可以使用它们。)
问题是,您需要在系统中预先配置,因为TLS-PSK是预共享密钥,而TLS-SRP是安全远程密码。身份验证只是相互而非服务器。
在这种情况下,相互认证由双方知道共享秘密并到达相同的预主密钥的属性提供;或者一个(或两个)没有,并且通道设置失败。每一方都证明了这个秘密的知识是“共同的”。一部分。
最后,TLS-PSK或TLS-SRP不会做愚蠢的事情,比如在使用HTTP(或通过HTTPS)的网络应用中咳出用户的密码。这就是为什么我说各方证明了秘密的知识......
答案 4 :(得分:1)
一个简单但不是纯粹的java解决方案是从Java中发送到curl
,这使您可以完全控制请求的完成方式。如果你只是为了简单的事情而这样做,这允许你有时使用这种方法忽略证书错误。此示例显示如何针对具有有效或无效证书的安全服务器发出请求,传入cookie并使用java中的curl
获取输出。
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);
}
}
}
答案 5 :(得分:0)
如果您使用任何支付网关点击任何网址只是为了发送消息,那么我按照它使用了webview: How can load https url without use of ssl in android webview
并在您的活动中制作网络视图,其中可见性已消失。您需要做什么:只需加载该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: How can load https url without use of ssl in android webview