no-image

HTTPS分析與實戰

                                    

名稱解釋
https:一種安全的http協議,因此可以稱為安全的超文字傳輸協議,https提出在http和tcp之間新增一層加密層(SSL層),這一層負責資料的加密和解密。
數字證書:簡稱CA,它由權威機構給某網站頒發的一種認可憑證,是被瀏覽器所認可的,當然證書也可以自己生成,但是這樣就不被瀏覽器所認可,想想如果自己隨便生成一個證書就被瀏覽器認為是安全的,那也挺可怕的;最簡單的證書包含一個公開金鑰、名稱以及證書授權中心的數字簽名,證書都有一個有效期。
數字簽名:就是隻有資訊的傳送者才能產生的別人無法偽造的一段數字串,這段數字串同時也是對資訊的傳送者傳送資訊真實性的一個有效證明。
對稱加密:又叫共享金鑰加密,對稱金鑰在加密和解密的過程中使用的金鑰是相同的,常見的對稱加密演算法有DES,AES;優點是計算速度快,缺點是在資料傳送前,傳送方和接收方必須商定好祕鑰,然後使雙方都能儲存好祕鑰,如果一方的祕鑰被洩露,那麼加密資訊也就不安全了。
非對稱加密:服務端會生成一對金鑰,私鑰存放在伺服器端,公鑰可以釋出給任何人使用;優點就是比起對稱加密更加安全,但是加解密的速度比對稱加密慢太多了。

HTTPS握手流程
四次握手過程如下圖所示(圖片來源網上):

1.客戶端傳送ClientHello
具體資料包文如下:

*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1518006972 bytes = { 65, 176, 64, 48, 101, 197, 66, 45, 233, 201, 4, 212, 39, 207, 197, 221, 77, 28, 131, 219, 59, 92, 71, 77, 188, 128, 9, 85 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }

其中提供的資訊包括:TLS的協議版本,客戶端生成的隨機數,支援的加密方法,支援的壓縮方法;

2.伺服器返回SeverHello

*** ServerHello, TLSv1.2
RandomCookie:  GMT: 1518006972 bytes = { 103, 152, 99, 6, 122, 253, 175, 18, 69, 135, 32, 101, 52, 209, 212, 68, 77, 6, 58, 123, 185, 243, 135, 155, 70, 15, 167, 109 }
Session ID:  {90, 123, 243, 188, 13, 250, 24, 48, 62, 145, 5, 130, 84, 81, 156, 246, 107, 149, 19, 110, 245, 190, 163, 34, 163, 100, 83, 11, 192, 218, 82, 39}
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0

包括:確定使用的TLS協議版本,伺服器生成的隨機數,確認使用的加密方法和壓縮方法;

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
 
  Key:  Sun RSA public key, 1024 bits
  modulus: 110417951066462499519290586842003643785934040448390902213730986659030452849006148579719535028965929817510494601937968191944831386030431983435172428185729866570463573441808893521679337608573348913665909433691909748813081253262417347690775527606654237333218231191786649572821638992076667126149380228808647107891
  public exponent: 65537
  Validity: [From: Wed Feb 07 11:07:44 CST 2018,
               To: Sat Feb 05 11:07:44 CST 2028]
  Issuer: CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh
  SerialNumber: [    5123c55c]
  .....

主要提供的是伺服器的證書資訊,包括伺服器的名稱、受信任的證書頒發機構(CA)和伺服器的公鑰;

*** ECDH ServerKeyExchange
Signature Algorithm SHA512withRSA
Server key: Sun EC public key, 256 bits
  public x coord: 4476231809895366488986368567243788140155223860343081368472893244611384219615
  public y coord: 109050381572001188616470974606466310815898076295756191171855198021565796560756
  parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
[read] MD5 and SHA1 hashes:  len = 205

如果是DH演算法,這裡傳送伺服器使用的DH引數,RSA演算法沒有引數;

*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Cert Authorities:
<CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh>
<CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh>
[read] MD5 and SHA1 hashes:  len = 234

伺服器要求客戶端傳送客戶端證書,只有在伺服器配置了雙向認證的情況下才需要客戶端證書,我們大部分情況下訪問的網站都不需要客戶端證書,只會在一些對安全性要求很高的場景下才需要,比如銀行領域。

*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00 

告訴客戶端ServerHello結束。

3.客戶端返回

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
 
  Key:  Sun RSA public key, 1024 bits
  modulus: 143615763343122571917822119532441620876017906006239941866997048768454765923738744303428875033219696709502897773614201648358563889389642376484119370521476221520354430179070803876160761551600474650108009364863645371886530280727392383823083132045866415069619367905230488064564105401690196419355382565701271042183
  public exponent: 65537
  Validity: [From: Wed Feb 07 11:08:24 CST 2018,
               To: Sat Feb 05 11:08:24 CST 2028]
  Issuer: CN=localhost, OU=codingo, O=codingo, L=nj, ST=js, C=zh
  SerialNumber: [    06be5a82]

伺服器配置了雙向認證,所有客戶端需要傳送客戶端證書給伺服器,進行認證;

*** ECDHClientKeyExchange
ECDH Public value:  { 4, 76, 128, 114, 175, 136, 51, 128, 201, 195, 101, 5, 13, 104, 146, 150, 177, 126, 39, 78, 212, 106, 81, 93, 253, 171, 132, 162, 40, 118, 146, 65, 113, 156, 141, 114, 237, 127, 163, 208, 73, 9, 17, 235, 110, 166, 148, 93, 56, 159, 47, 6, 9, 119, 68, 109, 48, 4, 152, 89, 18, 54, 95, 214, 196 }
main, WRITE: TLSv1.2 Handshake, length = 684
SESSION KEYGEN:
PreMaster Secret:
......

客戶端收到伺服器的證書,首先會認證證書,認證證書是否是有效的或者可信任的,如果不是瀏覽器會顯示警告頁面;如果沒有問題客戶端會從伺服器端的證書中獲取公鑰,再生成一個隨機數,再用公鑰對這個隨機數加密生成PreMaster Secret,傳送給伺服器;這樣伺服器和客戶端都有了三個隨機數,在通過相同的演算法生成一個應用層通訊的對稱金鑰;

main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
[Raw write]: length = 6
0000: 14 03 03 00 01 01                                  ......
*** Finished
verify_data:  { 181, 183, 221, 94, 78, 87, 6, 226, 234, 202, 181, 9 }

Change Cipher Spec訊息客戶端通知服務端後面再傳送的訊息都會使用前面協商出來的祕鑰加密了;
Finished訊息客戶端將前面的握手訊息加密發出了第一條加密資料,伺服器如果可以解密說明金鑰是一致的;

4.伺服器返回

main, READ: TLSv1.2 Change Cipher Spec, length = 1
[Raw read]: length = 5
0000: 16 03 03 00 40     
*** Finished
verify_data:  { 54, 167, 25, 231, 214, 98, 110, 203, 169, 108, 148, 225 }
***

Change Cipher Spec訊息服務端通知客戶端後面再傳送的訊息都會使用前面協商出來的祕鑰加密了;
Finished訊息服務端將前面的握手訊息加密發出了第一條加密資料傳送給客戶端;
整個握手流程大致如此,接下來就是通過對稱加密來傳輸業務資料了。

雙向認證實現
正規的網站一般數字證書都是由權威機構釋出的證書,作業系統和瀏覽器都認可,但是價格比較昂貴(當然也有免費的比如:Let’s Encrypt);當然也可以使用自簽名證書,下面使用JDK自帶工具KeyTool來生成自簽名證書,並且在tomcat中使用;

1.生成伺服器端證書
使用keyTool命令生成伺服器端證書:

C:\Program Files\Java\jdk1.7.0_80\bin>keytool -genkey -alias tomcat -keypass 111111 -keyalg RSA -keysize 1024 -validity 3650 -keystore E:\tomcat.keystore -storepass 111111

引數說明:-alias:別名,-keypass:別名密碼,-keyalg:指定的加密演算法,-validity:有效期,-keystore:keystore儲存的目錄,-storepass:獲取keystroe的密碼
因為在本地測試,所以”您的名字與姓氏是什麼?” 填localhost,一步步新增資料完之後生成了tomcat.keystore檔案;

2.生成客戶端證書
使用keyTool命令生成客戶端證書:

C:\Program Files\Java\jdk1.7.0_80\bin>keytool -genkey -alias client -keypass 111111 -keyalg RSA -keysize 1024 -validity 3650 -storetype PKCS12 -keystore E:\client.p12 -storepass 111111

為了能夠匯入到瀏覽器中,證書格式應該是PKCS12,一步步新增資料完之後生成了client.p12檔案;

3.讓伺服器信任客戶端證書
讓伺服器信任客戶端證書,需要將客戶端證書匯入到伺服器證書中,新增為一個信任證書,但是首先需要把PKCS12格式轉為cer檔案:

C:\Program Files\Java\jdk1.7.0_80\bin>keytool -export -alias client -keystore E:\client.p12 -storetype PKCS12 -keypass 111111 -file E:\client.cer
C:\Program Files\Java\jdk1.7.0_80\bin>keytool -import -v -file E:\client.cer -keystore E:\tomcat.keystore -storepass 111111
C:\Program Files\Java\jdk1.7.0_80\bin>keytool -list -v -keystore E:\tomcat.keystore

最後的list命令,檢視一下當前您的金鑰庫是否包含2個條目;

4.讓客戶端信任伺服器
需要將伺服器證書匯入到瀏覽器的”受信任的根證書頒發機構”,匯入之前先將tomcat.keystore轉為server.cer,方便瀏覽器匯入;

C:\Program Files\Java\jdk1.7.0_80\bin>keytool -keystore E:\tomcat.keystore -export -alias tomcat -file E:\server.cer

瀏覽器匯入:設定->管理證書->受信任的根證書頒發機構->匯入server.cer;
因為是雙向認證,所有同樣需要在”個人”一欄中新增client.p12:設定->管理證書->個人->匯入client.p12;

5.配置tomcat

<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true" maxThreads="150" scheme="https" chemeecure="true" clientAuth="true"    sslProtocol="TLS" keystoreFile="E:\\tomcat.keystore" keystorePass="111111" truststoreFile="E:\\tomcat.keystore" truststorePass="111111"/>

keystoreFile:伺服器證書檔案路徑,keystorePass:伺服器證書密碼,truststoreFile:用來驗證客戶端證書的根證書,truststorePass:根證書密碼,clientAuth:是否雙向驗證

6.瀏覽器訪問
在chrome中輸入:https://localhost:8443,顯示如下圖所示:

雖然已經設定了伺服器端證書為受信任的,但是不是權威機構認證的,瀏覽器還是顯示為不安全的連線;

7.HttpClient4訪問
使用HttpClient4同樣需要認證伺服器端證書,並且需要傳送客戶端證書進行雙向認證,具體程式碼如下:

public class HttpClient4 {
 
    public static void main(String[] args) throws Exception {
        System.setProperty("javax.net.debug", "all");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(new FileInputStream(new File("E:\\client.p12")), "111111".toCharArray());
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        }).loadKeyMaterial(keyStore, "111111".toCharArray()).build();
 
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1.2" }, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        HttpGet get = new HttpGet();
        get.setURI(new URI("https://localhost:8443/"));
        httpClient.execute(get);
    }
}

如果不傳客戶端證書進行雙向認證,回出現如下錯誤:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate

總結
本文主要對https的握手過程介紹了一下,同時以自簽名的方式在tomcat上進行實際演示;由Let’s Encrypt機構生成的證書可以在瀏覽器上看到“安全”的標識,個人網站就使用了Let’s Encrypt的免費方式,具體可以看參考。

參考:
https://coolshell.cn/articles/18094.html
https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E6%80%A7%E5%8D%94%E5%AE%9A
https://www.bf361.com/system/centos-apache-LetsEncrypt

關聯文章