簡體   English   中英

Java和HTTPS url連接,無需下載證書

[英]Java and HTTPS url connection without downloading certificate

此代碼連接到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 //

您不必在本地加載證書的原因是您明確選擇不驗證證書,此信任管理器信任所有證書。

流量仍然會被加密,但您打開了與中間人攻擊的連接:您正在與某人秘密通信,您只是不確定它是您期望的服務器還是可能的攻擊者。

如果您的服務器證書來自着名的CA,與JRE捆綁在一起的默認CA證書捆綁包(通常是cacerts文件,請參閱JSSE參考指南),您可以使用默認的信任管理器,而不必使用在這里設置一切。

如果您具有特定證書(自簽名證書或來自您自己的CA),則可以使用默認信任管理器或可能使用特定信任庫初始化的證書,但您必須在信任存儲中明確導入證書(在獨立之后)驗證),如本答案所述 您可能也對此答案感興趣。

但為什么我不必在本地為該網站安裝證書?

那么您正在使用的代碼明確地設計為接受證書而不進行任何檢查。 這不是一個好的做法...但如果這是你想要做的,那么(顯然)不需要安裝你的代碼明確忽略的證書。

我不應該在本地安裝證書並為此程序加載它還是在封面下載?

不,不。 往上看。

客戶端到遠程站點之間的流量是否仍然在傳輸中加密?

是的。 但是,問題在於,由於您已經告訴它在不進行任何檢查的情況下信任服務器的證書,因此您不知道您是在與真實服務器進行通信,還是與正在偽裝成真實服務器的其他站點進行通信。 這是否是一個問題取決於具體情況。


如果我們以瀏覽器為例,通常瀏覽器不會要求用戶為每個訪問過的ssl站點顯式安裝證書。

瀏覽器預先安裝了一組受信任的根證書。 大多數情況下,當您訪問“https”站點時,瀏覽器可以驗證該站點的證書(最終通過證書鏈)是否受到其中一個受信任證書的保護。 如果瀏覽器無法將鏈起始處的證書識別為可信證書(或者證書已過期或無效/不合適),則會顯示警告。

Java的工作方式相同。 JVM的密鑰庫具有一組受信任的證書,並且使用相同的過程來檢查證書是否受可信證書的保護。

java https客戶端api是否支持某種類型的機制來自動下載證書信息?

允許應用程序從隨機位置下載證書,並在系統密鑰庫中安裝它們(如可信)將是一個安全漏洞。

使用最新的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

        }

    }

Java和HTTPS url連接,無需下載證書

如果您確實想避免下載服務器的證書,請使用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)在Web應用程序中咳嗽用戶的密碼。 這就是為什么我說每一方都證明了這個秘密的知識......

一個簡單的,但不是純Java解決方案,是掏出來curl從Java,它使您的要求是怎么做的完全控制。 如果你只是為了簡單的事情而這樣做,這允許你有時使用這種方法忽略證書錯誤。 此示例顯示如何針對具有有效或無效證書的安全服務器發出請求,傳入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);
    }
  }


}

如果您使用任何支付網關點擊任何網址只是為了發送消息,那么我使用webview遵循它: 如何在android webview中加載https網址而不使用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: 如何在android webview中加載https url而不使用ssl

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM