mTLS 概述與運作原理
mTLS(Mutual TLS)又稱為雙向 TLS 認證,是 TLS 協定的延伸應用。在標準的 TLS 連線中,只有伺服器需要提供憑證來證明身份;而在 mTLS 中,客戶端也必須提供憑證,讓雙方都能驗證對方的身份。
mTLS 握手流程
- 客戶端發起連線請求
- 伺服器回傳伺服器憑證
- 客戶端驗證伺服器憑證
- 伺服器要求客戶端憑證
- 客戶端回傳客戶端憑證
- 伺服器驗證客戶端憑證
- 雙方建立加密連線
單向 TLS vs 雙向 TLS
| 特性 | 單向 TLS | 雙向 TLS (mTLS) |
|---|
| 伺服器憑證 | 必要 | 必要 |
| 客戶端憑證 | 不需要 | 必要 |
| 身份驗證 | 僅驗證伺服器 | 雙向驗證 |
| 安全性 | 中等 | 高 |
| 適用場景 | 一般網站 | API、微服務、零信任架構 |
使用 OpenSSL 建立 CA
首先,我們需要建立自己的憑證授權中心(CA)來簽發伺服器和客戶端憑證。
建立 CA 私鑰
1
2
3
4
5
6
| # 建立工作目錄
mkdir -p ~/mtls-demo/{ca,server,client}
cd ~/mtls-demo
# 產生 CA 私鑰(4096 位元 RSA)
openssl genrsa -out ca/ca.key 4096
|
建立 CA 憑證
1
2
3
| # 產生 CA 自簽憑證(有效期 10 年)
openssl req -new -x509 -days 3650 -key ca/ca.key -out ca/ca.crt \
-subj "/C=TW/ST=Taiwan/L=Taipei/O=MyOrg/OU=IT/CN=MyCA"
|
驗證 CA 憑證
1
2
| # 檢視 CA 憑證資訊
openssl x509 -in ca/ca.crt -text -noout
|
產生伺服器憑證
建立伺服器私鑰與 CSR
1
2
3
4
5
6
| # 產生伺服器私鑰
openssl genrsa -out server/server.key 2048
# 建立伺服器憑證簽署請求(CSR)
openssl req -new -key server/server.key -out server/server.csr \
-subj "/C=TW/ST=Taiwan/L=Taipei/O=MyOrg/OU=Server/CN=localhost"
|
建立 SAN 擴展設定檔
1
2
3
4
5
6
7
8
9
10
11
12
| # 建立 server.ext 設定檔,加入 SAN(Subject Alternative Name)
cat > server/server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = *.example.com
IP.1 = 127.0.0.1
EOF
|
使用 CA 簽發伺服器憑證
1
2
3
4
| # 簽發伺服器憑證(有效期 1 年)
openssl x509 -req -in server/server.csr -CA ca/ca.crt -CAkey ca/ca.key \
-CAcreateserial -out server/server.crt -days 365 \
-extfile server/server.ext
|
產生客戶端憑證
建立客戶端私鑰與 CSR
1
2
3
4
5
6
| # 產生客戶端私鑰
openssl genrsa -out client/client.key 2048
# 建立客戶端 CSR
openssl req -new -key client/client.key -out client/client.csr \
-subj "/C=TW/ST=Taiwan/L=Taipei/O=MyOrg/OU=Client/CN=client01"
|
建立客戶端擴展設定檔
1
2
3
4
5
6
7
| # 建立 client.ext 設定檔
cat > client/client.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF
|
使用 CA 簽發客戶端憑證
1
2
3
4
| # 簽發客戶端憑證
openssl x509 -req -in client/client.csr -CA ca/ca.crt -CAkey ca/ca.key \
-CAcreateserial -out client/client.crt -days 365 \
-extfile client/client.ext
|
Nginx mTLS 設定
基本 mTLS 設定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| server {
listen 443 ssl;
server_name localhost;
# 伺服器憑證
ssl_certificate /path/to/server/server.crt;
ssl_certificate_key /path/to/server/server.key;
# CA 憑證(用於驗證客戶端)
ssl_client_certificate /path/to/ca/ca.crt;
# 啟用客戶端憑證驗證
ssl_verify_client on;
ssl_verify_depth 2;
# TLS 協定與加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
location / {
# 傳遞客戶端憑證資訊到後端
proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
return 200 "mTLS 驗證成功!客戶端 CN: $ssl_client_s_dn_cn\n";
add_header Content-Type text/plain;
}
}
|
選擇性驗證設定
1
2
3
4
5
6
7
8
9
10
| # 若要讓客戶端憑證為選擇性(非強制)
ssl_verify_client optional;
# 在 location 中檢查驗證結果
location /protected {
if ($ssl_client_verify != SUCCESS) {
return 403 "需要有效的客戶端憑證\n";
}
# ...其他設定
}
|
curl 測試 mTLS 連線
基本測試
1
2
3
4
5
6
7
8
9
10
11
| # 使用客戶端憑證連線
curl -v --cacert ca/ca.crt \
--cert client/client.crt \
--key client/client.key \
https://localhost/
# 簡化輸出
curl -s --cacert ca/ca.crt \
--cert client/client.crt \
--key client/client.key \
https://localhost/
|
測試無憑證連線(預期失敗)
1
2
3
| # 不提供客戶端憑證(應該被拒絕)
curl -v --cacert ca/ca.crt https://localhost/
# 預期錯誤:SSL peer certificate or SSH remote key was not OK
|
使用 PKCS#12 格式
1
2
3
4
5
6
7
8
9
10
11
12
| # 將憑證和私鑰合併為 PKCS#12 格式
openssl pkcs12 -export -out client/client.p12 \
-inkey client/client.key \
-in client/client.crt \
-certfile ca/ca.crt \
-passout pass:mypassword
# 使用 PKCS#12 檔案測試
curl --cacert ca/ca.crt \
--cert-type P12 \
--cert client/client.p12:mypassword \
https://localhost/
|
常見錯誤排解
憑證驗證失敗
錯誤訊息: SSL certificate problem: unable to get local issuer certificate
解決方案:
1
2
3
4
5
6
| # 確認 CA 憑證路徑正確
curl --cacert /correct/path/to/ca.crt ...
# 或將 CA 加入系統信任
sudo cp ca/ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
|
憑證過期
錯誤訊息: SSL certificate problem: certificate has expired
解決方案:
1
2
3
4
5
6
| # 檢查憑證有效期限
openssl x509 -in client/client.crt -noout -dates
# 重新簽發憑證
openssl x509 -req -in client/client.csr -CA ca/ca.crt -CAkey ca/ca.key \
-CAcreateserial -out client/client.crt -days 365 -extfile client/client.ext
|
私鑰不匹配
錯誤訊息: SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
解決方案:
1
2
3
4
| # 驗證憑證與私鑰是否匹配
openssl x509 -noout -modulus -in client/client.crt | openssl md5
openssl rsa -noout -modulus -in client/client.key | openssl md5
# 兩個 MD5 值應該相同
|
CN 或 SAN 不匹配
錯誤訊息: SSL: certificate subject name does not match target host name
解決方案:
1
2
3
| # 檢查憑證的 CN 和 SAN
openssl x509 -in server/server.crt -noout -text | grep -A1 "Subject:"
openssl x509 -in server/server.crt -noout -text | grep -A1 "Subject Alternative Name"
|
使用場景
微服務間通訊
在 Kubernetes 或微服務架構中,mTLS 可確保服務之間的通訊安全,防止未授權的服務存取。
API 安全
對於高安全性要求的 API,mTLS 提供比 API Key 或 JWT 更強的身份驗證。
零信任網路
在零信任架構中,mTLS 是實現「永不信任,始終驗證」原則的重要技術。
IoT 裝置認證
物聯網裝置可使用客戶端憑證來證明身份,確保只有授權裝置能連接到後端服務。
參考資料