HTTPS訪問中證書的處理

現在基本大家都在使用HTTPS,比如REST API, 或者其他類似的SOAP啥的。因爲HTTP是明文傳輸太不安全了。使用HTTPS會有個比較麻煩的問題就是證書的處理。這裏有幾種證書的問題:

1. 如果判斷HTTPS中的證書是否是正確的?所謂的正確有2種意思:

a. 證書是假的,並非權威機構簽發的,比如自己做的測試證書。

        b. 證書也是權威機構簽發的,但並不是自己公司的。比如某個人向verisign公司買了一個證書,然後通過域名劫持啥的把某https站點引導到自己的服務器,並且使用自己購買的證書,從而收集用戶的密碼啥的。

2. 通常在開發階段用的都是測試證書,那麼很多https訪問庫,一看這個證書並不在當前機器的信任區,可能會直接丟出錯誤。

對於這些問題,其實解決起來還是蠻簡單的,基本思路就是:獲取證書信息,然後自己根據需求來進行處理。


這裏使用C#做一個簡單演示:

static public string ReadHTTPorHTTPSWithGet(string URL)
        {
            try
            {
                

                // combine HTTP request
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);

                // HTTP request header
                request.Method = "get";
                request.ContentType = "text/plain,charset=UTF-8";

                // try to get response from HTTP/HTTPS
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream ss = response.GetResponseStream();
                using (StreamReader reader = new StreamReader(ss, Encoding.UTF8))
                {
                    string data = reader.ReadToEnd();

                    return data;
                }
            }
            catch (Exception e)
            {
                string err = e.Message;
            }

            return "";
        }
如果使用上面的代碼來訪問https://www.baidu.com,沒有問題可以成功得到結果,因爲百度的證書是正式的,它是由verisign公司簽發的,而verisign公司的根證書都是默認在機器的信任區的,大家可以打開證書管理器看一下(certmgr.msc)

可以看到這裏有一堆的信任根證書,其中就有verisign公司的根證書(verisign公司是權威的,世界上用的大多數證書都是他們簽發的,當然微軟,蘋果,谷歌等大公司也有一些權威根證書)。

百度的證書也是verisign公司簽發的,所以沒有問題。

我們可以創建來看一下百度的證書。爲了查看百度的證書,需要自己提供一個System.Net.ICertificatePolicy的子類,重寫虛函數

public bool CheckValidationResult(ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert,
            WebRequest req, int problem)

如:

    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
    {
        public TrustAllCertificatePolicy()
        { }

        public bool CheckValidationResult(ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert,
            WebRequest req, int problem)
        {
            System.Console.WriteLine("name: " + cert.Subject);
            System.Console.WriteLine("issuer name: " + cert.Issuer);
            
            return true;
        }
    }

我們可以打出證書信息。

在訪問https之前,替換成我們自己的類實例:

System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

運行一下,就可以看到百度的證書信息:

可以看到百度的證書確實是verisign公司簽發的,而verisign公司的根證書在信任區。所以驗證沒問題。

那麼如果是自己的測試證書呢?

如果我們不提供自己的certificatePolicy類,那麼怕是會丟出異常,說是證書不合法啥的。

其實原因很簡單,因爲默認的CertificatePolicy類會去檢查證書是不是合法機構簽發的。看上去類似:

        public bool CheckValidationResult(ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert,
            WebRequest req, int problem)
        {
            System.Console.WriteLine("name: " + cert.Subject);
            System.Console.WriteLine("issuer name: " + cert.Issuer);

            if (cert is in trusted area) return true;
            else return false;
            
        }

然後就出錯了。

如果我們自己重寫了CheckValidationResult,如:

        public bool CheckValidationResult(ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert,
            WebRequest req, int problem)
        {
            System.Console.WriteLine("name: " + cert.Subject);
            System.Console.WriteLine("issuer name: " + cert.Issuer);
            
            return true;
        }
如何什麼證書都返回true那就ok了。如果我們在測試開發階段使用測試證書,那麼就可以這麼做。然後等正式發佈的時候(有正式證書了),就把這段代碼去掉,這樣程序就只認權威機構簽發的證書了。另外還有個問題,如果爲了防止域名被劫持,然後使用一個權威機構簽發的另外一個證書。解決問題也很簡單,就是重寫CheckValidationResult函數,然後在裏面得到證書的信息,看一下subject裏面是不是自己公司的,如果是就返回true,否則返回false,這樣就防止公司的https被中途攔截了。

完整代碼:

這個例子裏實現了GET和POST。可以直接運行,並且看百度的證書信息。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace TestHTTPS
{
    public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
    {
        public TrustAllCertificatePolicy()
        { }

        public bool CheckValidationResult(ServicePoint sp,
            System.Security.Cryptography.X509Certificates.X509Certificate cert,
            WebRequest req, int problem)
        {
            System.Console.WriteLine("name: " + cert.Subject);
            System.Console.WriteLine("issuer name: " + cert.Issuer);
            
            return true;
        }
    }

    class Program
    {
        static public string ReadHTTPorHTTPSWithGet(string URL)
        {
            try
            {
                System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

                // combine HTTP request
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);

                // HTTP request header
                request.Method = "get";
                request.ContentType = "text/plain,charset=UTF-8";

                // try to get response from HTTP/HTTPS
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream ss = response.GetResponseStream();
                using (StreamReader reader = new StreamReader(ss, Encoding.UTF8))
                {
                    string data = reader.ReadToEnd();

                    return data;
                }
            }
            catch (Exception e)
            {
                string err = e.Message;
            }

            return "";
        }

        static public string ReadHTTPorHTTPSWithPost(string URL, string strPostdata)
        {
            try
            {
                System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

                // combine HTTP request
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);

                // HTTP request header
                request.Method = "post";
                request.ContentType = "text/plain,charset=UTF-8";

                // HTTP request body
                byte[] buffer = Encoding.UTF8.GetBytes(strPostdata);
                request.ContentLength = buffer.Length;
                request.GetRequestStream().Write(buffer, 0, buffer.Length);

                // try to get response from HTTP/HTTPS
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream ss = response.GetResponseStream();
                using (StreamReader reader = new StreamReader(ss, Encoding.UTF8))
                {
                    string data = reader.ReadToEnd();

                    return data;
                }
            }
            catch (Exception e)
            {
                string err = e.Message;
            }

            return "";
        }

        static void Main(string[] args)
        {
            string ret = ReadHTTPorHTTPSWithGet("https://www.baidu.com");
            string ret2 = ReadHTTPorHTTPSWithGet("http://www.baidu.com");

            if (ret.Equals(ret2))
            {
                System.Console.WriteLine("success, results for http and https are same.");
            }
            else
            {
                System.Console.WriteLine("failed");
            }
        }
    }
}





發佈了140 篇原創文章 · 獲贊 49 · 訪問量 64萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章