名稱解釋
https:一種安全的http協(xié)議,因此可以稱為安全的超文本傳輸協(xié)議,https提出在http和tcp之間添加一層加密層(SSL層),這一層負責數(shù)據(jù)的加密和解密。
數(shù)字證書:簡稱CA,它由權(quán)威機構(gòu)給某網(wǎng)站頒發(fā)的一種認可憑證,是被瀏覽器所認可的,當然證書也可以自己生成,但是這樣就不被瀏覽器所認可,想想如果自己隨便生成一個證書就被瀏覽器認為是安全的,那也挺可怕的;最簡單的證書包含一個公開密鑰、名稱以及證書授權(quán)中心的數(shù)字簽名,證書都有一個有效期。
數(shù)字簽名:就是只有信息的發(fā)送者才能產(chǎn)生的別人無法偽造的一段數(shù)字串,這段數(shù)字串同時也是對信息的發(fā)送者發(fā)送信息真實性的一個有效證明。
對稱加密:又叫共享密鑰加密,對稱密鑰在加密和解密的過程中使用的密鑰是相同的,常見的對稱加密算法有DES,AES;優(yōu)點是計算速度快,缺點是在數(shù)據(jù)傳送前,發(fā)送方和接收方必須商定好秘鑰,然后使雙方都能保存好秘鑰,如果一方的秘鑰被泄露,那么加密信息也就不安全了。
非對稱加密:服務端會生成一對密鑰,私鑰存放在服務器端,公鑰可以發(fā)布給任何人使用;優(yōu)點就是比起對稱加密更加安全,但是加解密的速度比對稱加密慢太多了。
HTTPS握手流程
四次握手過程如下圖所示(圖片來源網(wǎng)上):
1.客戶端發(fā)送ClientHello
具體數(shù)據(jù)報文如下:
*** 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的協(xié)議版本,客戶端生成的隨機數(shù),支持的加密方法,支持的壓縮方法;
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協(xié)議版本,服務器生成的隨機數(shù),確認使用的加密方法和壓縮方法;
*** 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] .....
主要提供的是服務器的證書信息,包括服務器的名稱、受信任的證書頒發(fā)機構(gòu)(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算法,這里發(fā)送服務器使用的DH參數(shù),RSA算法沒有參數(shù);
*** 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
服務器要求客戶端發(fā)送客戶端證書,只有在服務器配置了雙向認證的情況下才需要客戶端證書,我們大部分情況下訪問的網(wǎng)站都不需要客戶端證書,只會在一些對安全性要求很高的場景下才需要,比如銀行領(lǐng)域。
*** ServerHelloDone [read] MD5 and SHA1 hashes: len = 4 0000: 0E 00 00 00
告訴客戶端ServerHello結(jié)束。
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]
服務器配置了雙向認證,所有客戶端需要發(fā)送客戶端證書給服務器,進行認證;
*** 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: ......
客戶端收到服務器的證書,首先會認證證書,認證證書是否是有效的或者可信任的,如果不是瀏覽器會顯示警告頁面;如果沒有問題客戶端會從服務器端的證書中獲取公鑰,再生成一個隨機數(shù),再用公鑰對這個隨機數(shù)加密生成PreMaster Secret,發(fā)送給服務器;這樣服務器和客戶端都有了三個隨機數(shù),在通過相同的算法生成一個應用層通訊的對稱密鑰;
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消息客戶端通知服務端后面再發(fā)送的消息都會使用前面協(xié)商出來的秘鑰加密了;
Finished消息客戶端將前面的握手消息加密發(fā)出了第一條加密數(shù)據(jù),服務器如果可以解密說明密鑰是一致的;
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消息服務端通知客戶端后面再發(fā)送的消息都會使用前面協(xié)商出來的秘鑰加密了;
Finished消息服務端將前面的握手消息加密發(fā)出了第一條加密數(shù)據(jù)發(fā)送給客戶端;
整個握手流程大致如此,接下來就是通過對稱加密來傳輸業(yè)務數(shù)據(jù)了。
雙向認證實現(xiàn)
正規(guī)的網(wǎng)站一般數(shù)字證書都是由權(quán)威機構(gòu)發(fā)布的證書,操作系統(tǒng)和瀏覽器都認可,但是價格比較昂貴(當然也有免費的比如:Let’s Encrypt);當然也可以使用自簽名證書,下面使用JDK自帶工具KeyTool來生成自簽名證書,并且在Tomcat中使用;
1.生成服務器端證書
使用keyTool命令生成服務器端證書:
C:Program FilesJAVAjdk1.7.0_80bin>keytool -genkey -alias tomcat -keypass 111111 -keyalg RSA -keysize 1024 -validity 3650 -keystore E:tomcat.keystore -storepass 111111
參數(shù)說明:-alias:別名,-keypass:別名密碼,-keyalg:指定的加密算法,-validity:有效期,-keystore:keystore存儲的目錄,-storepass:獲取keystroe的密碼
因為在本地測試,所以”您的名字與姓氏是什么?” 填localhost,一步步添加數(shù)據(jù)完之后生成了tomcat.keystore文件;
2.生成客戶端證書
使用keyTool命令生成客戶端證書:
C:Program FilesJavajdk1.7.0_80bin>keytool -genkey -alias client -keypass 111111 -keyalg RSA -keysize 1024 -validity 3650 -storetype PKCS12 -keystore E:client.p12 -storepass 111111
為了能夠?qū)氲綖g覽器中,證書格式應該是PKCS12,一步步添加數(shù)據(jù)完之后生成了client.p12文件;
3.讓服務器信任客戶端證書
讓服務器信任客戶端證書,需要將客戶端證書導入到服務器證書中,添加為一個信任證書,但是首先需要把PKCS12格式轉(zhuǎn)為cer文件:
C:Program FilesJavajdk1.7.0_80bin>keytool -export -alias client -keystore E:client.p12 -storetype PKCS12 -keypass 111111 -file E:client.cer C:Program FilesJavajdk1.7.0_80bin>keytool -import -v -file E:client.cer -keystore E:tomcat.keystore -storepass 111111 C:Program FilesJavajdk1.7.0_80bin>keytool -list -v -keystore E:tomcat.keystore
最后的list命令,查看一下當前您的密鑰庫是否包含2個條目;
4.讓客戶端信任服務器
需要將服務器證書導入到瀏覽器的”受信任的根證書頒發(fā)機構(gòu)”,導入之前先將tomcat.keystore轉(zhuǎn)為server.cer,方便瀏覽器導入;
C:Program FilesJavajdk1.7.0_80bin>keytool -keystore E:tomcat.keystore -export -alias tomcat -file E:server.cer
瀏覽器導入:設(shè)置->管理證書->受信任的根證書頒發(fā)機構(gòu)->導入server.cer;
因為是雙向認證,所有同樣需要在”個人”一欄中添加client.p12:設(shè)置->管理證書->個人->導入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,顯示如下圖所示:
雖然已經(jīng)設(shè)置了服務器端證書為受信任的,但是不是權(quán)威機構(gòu)認證的,瀏覽器還是顯示為不安全的連接;
7.HttpClient4訪問
使用HttpClient4同樣需要認證服務器端證書,并且需要發(fā)送客戶端證書進行雙向認證,具體代碼如下:
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); } }
如果不傳客戶端證書進行雙向認證,回出現(xiàn)如下錯誤:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
總結(jié)
本文主要對https的握手過程介紹了一下,同時以自簽名的方式在tomcat上進行實際演示;由Let’s Encrypt機構(gòu)生成的證書可以在瀏覽器上看到“安全”的標識,個人網(wǎng)站就使用了Let’s Encrypt的免費方式,具體可以看參考。