54

此代码连接到 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 //
4

6 回答 6

33

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

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

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

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

于 2012-10-23T02:31:53.403 回答
12

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

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

我不应该在本地安装证书并为该程序加载它还是在幕后下载?

不,也不。往上看。

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

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


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

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

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

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

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

于 2012-10-23T02:34:52.327 回答
7

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

    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

        }

    }
于 2016-10-04T11:32:10.437 回答
1

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

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

您使用 选择匿名协议setEnabledCipherSuites。您可以查看可用的密码套件列表getEnabledCipherSuites

相关:这就是你必须调用SSL_get_peer_certificateOpenSSL 的原因。您将获得一个X509_V_OK带有匿名协议的证书,这就是您检查证书是否在交换中使用的方式。

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


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

问题是,您需要在系统中预先配置,因为 TLS-PSK 是Pres-shared Secret而 TLS-SRP 是Secure Remote Password。身份验证是相互的,而不仅仅是服务器。

在这种情况下,相互认证由双方都知道共享秘密并达到相同的预主秘密的属性提供;或其中一个(或两者)没有,并且通道设置失败。每一方都证明知道秘密是“相互”的部分。

最后,TLS-PSK 或 TLS-SRP 不会做愚蠢的事情,例如在使用 HTTP(或通过 HTTPS)的 Web 应用程序中咳出用户密码。这就是为什么我说每一方都证明知道这个秘密......

于 2014-03-24T08:15:30.513 回答
1

一个简单但不是纯 java 的解决方案是curl从 java 中脱壳,这使您可以完全控制请求的完成方式。如果您只是为了简单的事情而这样做,则可以使用此方法有时忽略证书错误。此示例显示如何使用有效或无效证书向安全服务器发出请求,传入 cookie,并使用curlfrom java.util.get 获取输出。

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);
    }
  }


}
于 2016-08-11T19:52:17.843 回答
0

如果您使用任何支付网关点击任何 url 只是为了发送消息,那么我使用 webview 并遵循它: How can load https url without using 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 using ssl in android webview

于 2017-07-13T11:46:14.427 回答