參考資料
1.https://tangyuxian.com/2021/05/19/%E5%90%8E%E7%AB%AF/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95-TLS-SSL%E6%8F%A1%E6%89%8B%E8%BF%87%E7%A8%8B/
2. https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html
TLS or SSL ?
SSL(Secure Socket Layer 安全套接層)是基于HTTPS下的一個(gè)協(xié)議加密層,最初是由網(wǎng)景公司(Netscape)研發(fā),后被IETF(The Internet Engineering Task Force - 互聯(lián)網(wǎng)工程任務(wù)組)標(biāo)準(zhǔn)化后寫入(RFCRequest For Comments 請(qǐng)求注釋),RFC里包含了很多互聯(lián)網(wǎng)技術(shù)的規(guī)范。
起初是因?yàn)镠TTP在傳輸數(shù)據(jù)時(shí)使用的是明文(雖然說POST提交的數(shù)據(jù)時(shí)放在報(bào)體里看不到的,但是還是可以通過抓包工具竊取到)是不安全的,為了解決這一隱患網(wǎng)景公司推出了SSL安全套接字協(xié)議層,SSL是基于HTTP之下TCP之上的一個(gè)協(xié)議層,是基于HTTP標(biāo)準(zhǔn)并對(duì)TCP傳輸數(shù)據(jù)時(shí)進(jìn)行加密,所以HPPTS是HTTP+SSL/TCP的簡稱。
由于HTTPS的推出受到了很多人的歡迎,在SSL更新到3.0時(shí),IETF對(duì)SSL3.0進(jìn)行了標(biāo)準(zhǔn)化,并添加了少數(shù)機(jī)制(但是幾乎和SSL3.0無差異),標(biāo)準(zhǔn)化后的IETF更名為TLS1.0(Transport Layer Security 安全傳輸層協(xié)議),可以說TLS就是SSL的新版本3.1。
TLS(Transport Layer Security)是更為安全的升級(jí)版 SSL。但 SSL 這一術(shù)語更為常用,實(shí)際上 MySQL 使用的就是 TLS協(xié)議,而不是 SSL 協(xié)議。
1. TLS握手過程
想弄清楚下面這一大堆文件的作用是什么嗎?那就必須先了解 TLS 握手過程
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── server-cert.pem
└── server-key.pem
1.1 TLS 握手過程
TLS 握手的過程,其實(shí)就是秘鑰交換的過程,這也是整個(gè)TLS加密技術(shù)里最復(fù)雜的一個(gè)環(huán)節(jié),參考一張網(wǎng)上的圖如下:1.2 密鑰算法
對(duì)稱密鑰算法:數(shù)據(jù)加密和解密時(shí)使用相同的密鑰。
非對(duì)稱密鑰算法:數(shù)據(jù)加密和解密時(shí)使用不同的密鑰,一個(gè)是公開的公鑰,一個(gè)是由用戶秘密保存的私鑰。利用公鑰(或私鑰)加密的數(shù)據(jù)只能用相應(yīng)的私鑰(或公鑰)才能解密。
與非對(duì)稱密鑰算法相比,對(duì)稱密鑰算法具有計(jì)算速度快的優(yōu)點(diǎn),通常用于對(duì)大量信息進(jìn)行加密(如對(duì)所有報(bào)文加密);而非對(duì)稱密鑰算法,一般用于數(shù)字簽名和對(duì)較少的信息進(jìn)行加密。
注意:SSL/TLS 協(xié)議就是使用對(duì)稱密鑰算法進(jìn)行數(shù)據(jù)加密,使用非對(duì)稱密鑰算法進(jìn)行”對(duì)稱密鑰“的加密。其過程為:
- 上圖中,Server端發(fā)送了公鑰給客戶端,私鑰自己保存,這是非對(duì)稱密鑰算法中的公鑰、私鑰對(duì);
- 客戶端會(huì)創(chuàng)建一個(gè)密鑰,這個(gè)就是對(duì)稱加密算法中的密鑰。然后用 Server 端的公鑰對(duì)這個(gè)密鑰加密,發(fā)送給 Server 端;
- Server 端收到客戶到發(fā)送過來的加密過的密鑰,使用自己的私鑰進(jìn)行解密,得到加密前的密鑰;
- 接下來傳輸數(shù)據(jù)則使用“對(duì)稱密鑰”進(jìn)加密和解密。
這個(gè)就是利用非對(duì)稱密鑰算法保證對(duì)稱密鑰本身的安全。
1.3 數(shù)字證書-如何保證公鑰的真實(shí)性?
如果有攻擊者偽造了 Server 端的公鑰并發(fā)了客戶端,客戶端會(huì)訪問到假網(wǎng)站被竊取信息。顯然保證客戶端收到的 Server 端的公鑰是真實(shí)的,是保證整個(gè)加密連接的可靠性的第一道防線。
數(shù)字證書由權(quán)威機(jī)構(gòu)CA(Certification Authority)簽發(fā),簽發(fā)過程為:
- 用戶首先產(chǎn)生自己的密鑰對(duì),并將公鑰及部分個(gè)人身份信息傳送給CA;
- CA 核實(shí)用戶身份(將執(zhí)行一些必要的步驟,以確信請(qǐng)求確實(shí)由用戶發(fā)送而來);
- CA 對(duì)用戶的所有信息(公鑰、所有者、有效期...)進(jìn)行 Hash 計(jì)算,得到一個(gè) Hash 值,然后再使用私鑰對(duì) Hash 值進(jìn)行加密得到簽名,就得到了數(shù)字證書。該證書包含:用戶的公鑰、所有者、有效期等信息,同時(shí)還附有CA的簽名信息。
數(shù)字證書的驗(yàn)證流程:
- 客戶端會(huì)使用同樣的 Hash 算法獲取該數(shù)字證書的 Hash 值 H1;
- 通常瀏覽器和操作系統(tǒng)中集成了 CA 證書(包含CA公鑰、所有者),客戶端取得這個(gè)CA證書,使用其中的 CA 公鑰解密簽名,得到一個(gè) Hash 值 H2 ;
- 比較 H1 和 H2,如果值相同,則數(shù)字證書可信。
如果 CA 證書不在瀏覽器和操作系統(tǒng)的可信任區(qū),這種 CA 證書通常被稱為自簽名 CA 證書(MySQL自動(dòng)生成的就是自簽名證書,詳見下文)。要完成數(shù)字證書的驗(yàn)證,則必須事先將自簽名 CA 證書放到客戶端,并在客戶端發(fā)起連接時(shí)指定這個(gè) CA 證書文件;或者事先將自簽名 CA 證書導(dǎo)入到客戶端的操作系統(tǒng)可信任區(qū),這樣在 TLS 握手過程中也能自動(dòng)獲取到這個(gè) CA 證書。
另外:驗(yàn)證證書在 SSL/TLS 協(xié)議中不一定是必須的,比如 mysql 客戶端只有指定 --ssl-mode=VERIFY_CA 或者 --ssl-mode=VERIFY_IDENTITY 時(shí)才驗(yàn)證 CA 證書。如果 --ssl-mode=REQUIRED,則不驗(yàn)證 CA 證書,只要求 MySQL Server 端發(fā)送公鑰給客戶端,這就無法保證服務(wù)端公鑰是真實(shí)屬于 MySQL server 的。詳細(xì)見下文。
2. MySQL SSL 加密連接
2.1 MySQL服務(wù)端的配置
啟動(dòng)參數(shù):
- --ssl:表示 MySQL 服務(wù)端允許加密連接,這個(gè)啟動(dòng)參數(shù)MySQL8.0默認(rèn)啟用
系統(tǒng)變量:
- require_secure_transport:指定是否要求客戶端使用加密連接。默認(rèn)值為 OFF,如果 ON,則表示客戶端必須使用加密連接,如果客戶端關(guān)閉 ssl ,則連接會(huì)報(bào)錯(cuò)。
以下參數(shù)指定加密連接時(shí)使用的證書和密鑰文件:
ssl_ca=ca.pem
ssl_cert=server-cert.pem
ssl_key=server-key.pem
MySQL8.0在啟動(dòng)時(shí)會(huì)自動(dòng)生成 SSL 證書、密鑰文件,以及 RSA 密鑰對(duì)文件;或者使用 mysql_ssl_rsa_setup 程序生成上述文件。也可以手工生成:
## SSL證書和密鑰文件
certs
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── server-cert.pem
└── server-key.pem
# Create CA certificate
# 創(chuàng)建CA證書(包含CA公鑰)和CA私鑰
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3600 \
-key ca-key.pem -out ca.pem
# Create server certificate, remove passphrase, and sign it
# server-cert.pem = public key, server-key.pem = private key
# 先生成服務(wù)器公鑰、私鑰
# 使用CA私鑰對(duì)服務(wù)器公鑰簽名,得到服務(wù)器證書 server-cert.pem,證書中包含公鑰、所有者、有效期等明文信息,也有經(jīng)過 CA 私鑰加密對(duì)公鑰、所有者、有效期...加密后的簽名
openssl req -newkey rsa:2048 -days 3600 \
-nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3600 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# Create client certificate, remove passphrase, and sign it
# client-cert.pem = public key, client-key.pem = private key
# 先生成客戶端公鑰、私鑰
# 使用CA私鑰對(duì)客戶端公鑰簽名,得到客戶端證書 client-cert.pem,一般不需要驗(yàn)證客戶端身份,這些文件就不需要用到。當(dāng)然如果要同時(shí)驗(yàn)證 MySQL Server 身份和客戶端身份,就需要用到這些文件了。
openssl req -newkey rsa:2048 -days 3600 \
-nodes -keyout client-key.pem -out client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3600 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem
# 查看證書內(nèi)容
openssl x509 -text -in ca.pem
# 驗(yàn)證CA證書(“使用CA證書驗(yàn)證數(shù)字證書”更恰當(dāng)?)
openssl verify -CAfile ca.pem server-cert.pem
# 輸出結(jié)果:server-cert.pem: OK
2.2 MySQL客戶端配置
MySQL 客戶端連接 Server 時(shí),通過 --ssl-mode 參數(shù)指定:
- --ssl-mode=PREFFERED,默認(rèn)行為,client 端嘗試使用加密進(jìn)行連接,如果無法構(gòu)建加密連接,則會(huì)退回到未加密的連接
- --ssl-mode=REQUIRED時(shí),Client 端需要加密連接,如果無法構(gòu)建連接,則 Client 端將失敗
- --ssl-mode=DISABLED,Client 端使用未加密的連接
- --ssl-mode=VERIFY_CA,Client 端需要加密連接,并且還對(duì) CA 證書進(jìn)行驗(yàn)證
- --ssl-mode=VERIFY_IDENTITY,Client 端需要加密的連接,并且還針對(duì) CA 證書和其證書中的服務(wù)器主機(jī)名執(zhí)行驗(yàn)證
注意:主機(jī)名身份驗(yàn)證 VERIFY_IDENTITY 不適用于由服務(wù)器自動(dòng)創(chuàng)建或使用 mysql_ssl_rsa_setup 手動(dòng)創(chuàng)建的自簽名CA證書。
測試如下:
##當(dāng)指定 --ssl-mode=REQUIRED,僅要求加密連接,不需要驗(yàn)證 MySQL Server 身份,所以會(huì)直接信任 MySQL Server 發(fā)送給客戶端的公鑰(即 server-cert.pem 數(shù)字證書中的明文公鑰,忽略其中的數(shù)字簽名信息)
[root@172-16-21-5 /]# /opt/mysql/base/8.0.21/bin/mysql -h172.16.21.4 -P3306 -utest -ptestpass --ssl-mode=REQUIRED -e "select 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---+
| 1 |
+---+
| 1 |
+---+
##當(dāng)指定 --ssl-mode=VERIFY_CA,需要驗(yàn)證 CA 證書,因?yàn)檫@個(gè) CA 證書是自簽發(fā)的,所以不在瀏覽器和操作系統(tǒng)的可信任區(qū),無法從瀏覽器和操作系統(tǒng)的可信任區(qū)這個(gè)公共渠道獲取 CA 證書,所以報(bào)錯(cuò):
[root@172-16-21-5 /]# /opt/mysql/base/8.0.21/bin/mysql -h172.16.21.4 -P3306 -utest -ptestpass --ssl-mode=VERIFY_CA
ERROR 2026 (HY000): SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY
##將MySQL服務(wù)端的 ca.pem 拷貝到客戶端
scp ca.pem 172.16.21.5:/tmp/
##--ssl-mode=VERIFY_CA,指定需要驗(yàn)證 CA 證書,因?yàn)檫@個(gè) CA 證書是自簽發(fā)的,所以不在瀏覽器和操作系統(tǒng)的可信任區(qū),則必須要將 CA 證書拷貝到客戶端,并指定 CA 證書文件
##TLS 握手過程中,MySQL Server 發(fā)送服務(wù)器數(shù)字證書 server-cert.pem 給客戶端,客戶端使用 CA 證書中的 CA 公鑰解密 server-cert.pem 中的簽名,驗(yàn)證通過,才可以正常登陸:
[root@localhost ~]# mysql -h10.186.61.173 -P3308 -uhucq -p'1qaz@WSX' \
--ssl-ca="/tmp/ca.pem" \
--ssl-mode=VERIFY_CA \
-e "select 1"
+---+
| 1 |
+---+
| 1 |
+---+
##由于MySQL自動(dòng)生成的CA證書是自簽名證書,而 --ssl-mode=VERIFY_IDENTITY 不適用于由服務(wù)器自動(dòng)創(chuàng)建或使用 mysql_ssl_rsa_setup手動(dòng)創(chuàng)建的自簽名CA證書,即使指定本地的CA證書文件,連接也會(huì)失敗
[root@localhost ~]# mysql -h10.186.61.173 -P3308 -uhucq -p'1qaz@WSX' \
--ssl-ca="/tmp/ca.pem" \
--ssl-mode=VERIFY_IDENTITY \
-e "select 1"
ERROR 2026 (HY000): SSL connection error: Failed to verify the server certificate via X509 certificate matching functions
2.3 MySQL SSL 連接中的 TLS 握手過程
上述示例已有詳細(xì)說明,這里再簡要總結(jié)一下:
- 客戶端發(fā)起 ssl 連接請(qǐng)求;
- MySQL Server 發(fā)送數(shù)字證書 server-cert.pem 給客戶端(server-cert.pem包含:服務(wù)器公鑰、CA簽名信息);
- 客戶端使用CA 證書 ca.pem(由于這是 MySQL 自簽名的CA證書,無法從操作系統(tǒng)的可信任區(qū)獲取(壓根不在這里邊),所以事先必須在客戶端本地保存 CA 證書文件)中的 CA 公鑰解密 server-cert.pem 中的簽名,進(jìn)行驗(yàn)證;
- 驗(yàn)證通過后,生成對(duì)稱密鑰,使用 server-cert.pem 中的公鑰加密“對(duì)稱密鑰”,發(fā)送給 MySQL Server;
- MySQL Server 使用自己保留的私鑰 server-key.pem 解密,得到“對(duì)稱密鑰”;
- 接下來傳輸數(shù)據(jù)則使用“對(duì)稱密鑰”進(jìn)加密和解密。
如果僅指定 --ssl-mode=REQUIRED,不指定 --ssl-mode=VERIFY_CA 或者 --ssl-mode=VERIFY_IDENTITY,則不需要步驟3。
3. JDBC 如何設(shè)置SSL連接
首先 MySQL Server 端必須生成 SSL 證書和密鑰文件,并且在啟動(dòng)時(shí)指定啟動(dòng)參數(shù):--ssl(一般將其寫到 my.cnf 中)。MySQL8.0 啟動(dòng)時(shí)會(huì)自動(dòng)生成SSL 證書和密鑰文件,并默認(rèn)使用 --ssl 參數(shù)。
JDBC 關(guān)閉 ssl 連接示例:jdbc:mysql://localhost:3306/hucq?useSSL=false
如果 MySQL Server 使用 caching_sha2_password(MySQL8.0默認(rèn)的認(rèn)證插件)、sha256_password 認(rèn)證插件,則還必須指定 AllowPublicKeyRetrieval=True,因?yàn)?caching_sha2_password 插件要求交換密碼時(shí)必須使用 RSA 公鑰加密(在沒有使用SSL加密連接的情況下),AllowPublicKeyRetrieval=True 參數(shù)作用是請(qǐng)求 MySQL Server 端發(fā)送 RSA 公鑰給客戶端,如果不請(qǐng)求 RSA 公鑰并且又沒有指定客戶端本地RSA公鑰文件(先從 MySQL 服務(wù)器上拷貝 RSA 公鑰到本地),則連接會(huì)報(bào)錯(cuò)。應(yīng)該配置:jdbc:mysql://localhost:3306/hucq?useSSL=false&AllowPublicKeyRetrieval=True
JDBC 開啟 SSL 連接,意味著要求安全連接,則應(yīng)該開啟 CA 證書認(rèn)證,跟 mysql 客戶端一樣,需要將 MySQL 的自簽名 CA 證書導(dǎo)入到客戶端,或者放到 ftp 上,再通過 JDBC 參數(shù)指定 CA 證書路徑,比較復(fù)雜,請(qǐng)參考官方文檔:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html