在 SSL/TLS 憑證的世界中,憑證鏈(Certificate Chain)是確保安全通訊的關鍵機制。本文將深入解析憑證鏈的運作原理、根憑證與中繼憑證的關係,以及常見問題的排解方法。
憑證鏈概念
什麼是憑證鏈?
憑證鏈是一系列數位憑證的連結,從終端實體憑證(End Entity Certificate,即您的伺服器憑證)一直延伸到受信任的根憑證授權中心(Root CA)。這條鏈建立了一條信任路徑,讓用戶端能夠驗證伺服器憑證的真實性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| 憑證鏈結構:
+---------------------------+
| 終端實體憑證 | ← 您的網站憑證(example.com)
| (End Entity Cert) |
+-----------+---------------+
|
| 由中繼 CA 簽發
↓
+---------------------------+
| 中繼 CA 憑證 | ← 中繼憑證授權中心
| (Intermediate CA) |
+-----------+---------------+
|
| 由根 CA 簽發
↓
+---------------------------+
| 根 CA 憑證 | ← 自簽憑證,預裝於作業系統/瀏覽器
| (Root CA) |
+---------------------------+
|
↓
信任錨點(Trust Anchor)
|
為什麼需要憑證鏈?
憑證鏈的設計解決了以下重要問題:
- 安全性分層:根 CA 的私鑰極為重要,將其離線保存可降低被盜用的風險
- 靈活性:中繼 CA 可以針對不同用途簽發不同類型的憑證
- 可擴展性:允許建立多層次的 CA 架構以應對大規模憑證簽發需求
- 風險隔離:若中繼 CA 遭到入侵,只需撤銷該中繼 CA,不影響根 CA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 信任模型比較:
直接簽發模式(不建議):
+--------+ 直接簽發 +--------+
| 根 CA | ---------------→ | 伺服器 |
+--------+ +--------+
↑
高風險:根 CA 頻繁使用
階層式模式(推薦):
+--------+ +--------+
| 根 CA | ----離線保存--→ | 中繼 CA | ---→ | 伺服器 |
+--------+ +--------+ +--------+
↑ ↑
低風險 日常操作
|
根憑證與中繼憑證
根憑證(Root Certificate)
根憑證是憑證鏈的起點,具有以下特性:
- 自簽憑證:由自己簽發給自己
- 預先安裝:內建於作業系統、瀏覽器和其他應用程式中
- 最高信任:作為信任的起點(Trust Anchor)
- 長有效期:通常有效期為 20-30 年
- 離線保存:私鑰存放於高度安全的離線環境(如 HSM)
1
2
3
4
5
6
7
8
| # 檢視根憑證資訊
openssl x509 -in rootCA.crt -text -noout
# 範例輸出(自簽憑證的特徵):
# Issuer: CN = Root CA ← 簽發者
# Subject: CN = Root CA ← 主體(與簽發者相同)
# X509v3 Basic Constraints: critical
# CA:TRUE ← 表示這是 CA 憑證
|
中繼憑證位於根憑證和終端實體憑證之間:
- 由上層 CA 簽發:由根 CA 或上層中繼 CA 簽發
- 可簽發憑證:具有簽發下層憑證的能力
- 線上使用:用於日常的憑證簽發作業
- 中等有效期:通常有效期為 5-10 年
- 可被撤銷:若遭入侵可被撤銷而不影響根 CA
1
2
3
4
5
6
7
8
| # 檢視中繼憑證資訊
openssl x509 -in intermediateCA.crt -text -noout
# 範例輸出(中繼憑證的特徵):
# Issuer: CN = Root CA ← 由根 CA 簽發
# Subject: CN = Intermediate CA ← 中繼 CA 的名稱
# X509v3 Basic Constraints: critical
# CA:TRUE, pathlen:0 ← CA 憑證,pathlen 限制層級
|
終端實體憑證(End Entity Certificate)
終端實體憑證是實際用於伺服器或用戶端的憑證:
- 由中繼 CA 簽發:不能自己簽發憑證
- 不具 CA 能力:無法簽發其他憑證
- 短有效期:通常為 1 年(最長 398 天)
- 包含服務資訊:如網域名稱、組織資訊等
1
2
3
4
5
6
7
8
| # 檢視終端實體憑證
openssl x509 -in server.crt -text -noout
# 範例輸出:
# Issuer: CN = Intermediate CA ← 由中繼 CA 簽發
# Subject: CN = www.example.com ← 網站網域名稱
# X509v3 Basic Constraints: critical
# CA:FALSE ← 非 CA 憑證
|
憑證層級比較
| 特性 | 根憑證 | 中繼憑證 | 終端實體憑證 |
|---|
| 自簽 | 是 | 否 | 否 |
| CA 能力 | 是 | 是 | 否 |
| 有效期 | 20-30 年 | 5-10 年 | 1 年 |
| 儲存方式 | 離線 HSM | 線上 HSM | 伺服器 |
| 預裝於系統 | 是 | 否 | 否 |
| 可被撤銷 | 困難 | 可以 | 可以 |
驗證流程
完整驗證過程
當用戶端(如瀏覽器)連接到 HTTPS 網站時,會進行以下驗證流程:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| 用戶端驗證憑證鏈流程:
第一步:接收憑證
+----------+ +----------+
| 瀏覽器 | ←---- 傳送憑證鏈 -------- | 伺服器 |
+----------+ +----------+
|
| 收到:伺服器憑證 + 中繼憑證
↓
第二步:建立憑證鏈
+------------------+
| 1. 伺服器憑證 |
| 2. 中繼 CA 憑證 |
| 3. 根 CA 憑證 | ← 從本地信任存放區取得
+------------------+
|
↓
第三步:逐層驗證
+--------------------------------------------------+
| 3.1 驗證伺服器憑證 |
| - 簽章驗證:使用中繼 CA 公鑰驗證 |
| - 有效期檢查:確認在有效期內 |
| - 網域匹配:確認 CN/SAN 與網址相符 |
| - 撤銷檢查:透過 CRL/OCSP 確認未被撤銷 |
+--------------------------------------------------+
|
↓
+--------------------------------------------------+
| 3.2 驗證中繼 CA 憑證 |
| - 簽章驗證:使用根 CA 公鑰驗證 |
| - 有效期檢查:確認在有效期內 |
| - CA 權限:確認具有簽發憑證的權限 |
| - 撤銷檢查:確認未被撤銷 |
+--------------------------------------------------+
|
↓
+--------------------------------------------------+
| 3.3 驗證根 CA 憑證 |
| - 信任檢查:確認存在於本地信任存放區 |
| - 自簽驗證:使用自己的公鑰驗證簽章 |
+--------------------------------------------------+
|
↓
第四步:驗證結果
+------------------+
| ✓ 憑證鏈完整 |
| ✓ 所有驗證通過 |
| → 建立安全連線 |
+------------------+
|
使用 OpenSSL 驗證憑證鏈
1
2
3
4
5
6
7
8
9
10
11
| # 下載網站的憑證鏈
openssl s_client -connect example.com:443 -showcerts
# 驗證憑證鏈完整性
openssl verify -CAfile ca-bundle.crt server.crt
# 分層驗證(指定中繼憑證)
openssl verify -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt
# 詳細驗證輸出
openssl verify -verbose -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt
|
驗證憑證鏈的腳本範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #!/bin/bash
# verify-chain.sh - 驗證憑證鏈完整性
DOMAIN=$1
PORT=${2:-443}
echo "=== 驗證 $DOMAIN 的憑證鏈 ==="
# 取得憑證資訊
echo -e "\n[1] 取得憑證鏈..."
echo | openssl s_client -connect "$DOMAIN:$PORT" -servername "$DOMAIN" 2>/dev/null | \
openssl x509 -noout -subject -issuer -dates
# 顯示完整憑證鏈
echo -e "\n[2] 憑證鏈結構..."
echo | openssl s_client -connect "$DOMAIN:$PORT" -servername "$DOMAIN" 2>/dev/null | \
grep -E "^( [0-9]|Certificate chain)"
# 驗證憑證鏈
echo -e "\n[3] 驗證結果..."
echo | openssl s_client -connect "$DOMAIN:$PORT" -servername "$DOMAIN" 2>/dev/null | \
grep "Verify return code"
|
常見問題與排解
問題一:憑證鏈不完整
症狀:
- 瀏覽器顯示「憑證不受信任」錯誤
- 某些用戶端可以連線,某些無法
- 使用 openssl 驗證時顯示「unable to get local issuer certificate」
1
2
| 錯誤訊息範例:
SSL certificate problem: unable to get local issuer certificate
|
原因:
解決方案:
1
2
3
4
5
6
7
8
9
10
| # 檢查伺服器憑證鏈
openssl s_client -connect example.com:443 -showcerts
# 正確的憑證鏈應包含:
# - Certificate 0: 伺服器憑證
# - Certificate 1: 中繼 CA 憑證
# (根憑證通常不需要傳送)
# 合併憑證建立完整的憑證鏈檔案
cat server.crt intermediate.crt > fullchain.crt
|
問題二:憑證鏈順序錯誤
症狀:
- 部分瀏覽器或程式無法驗證憑證
- SSL Labs 測試顯示 “Chain issues: Incorrect order”
正確順序:
1
2
3
4
5
6
7
8
9
10
| 正確的憑證鏈順序(由上到下):
1. 伺服器憑證(終端實體憑證)
2. 中繼 CA 憑證(最接近伺服器的)
3. 中繼 CA 憑證(較接近根 CA 的)
4. 根 CA 憑證(可選,通常不包含)
錯誤示範(順序顛倒):
1. 中繼 CA 憑證
2. 伺服器憑證
|
解決方案:
1
2
3
4
5
6
7
| # 檢查並修正憑證順序
# 使用 cat 按正確順序合併
cat server.crt intermediate1.crt intermediate2.crt > fullchain.crt
# 驗證合併後的憑證鏈
openssl crl2pkcs7 -nocrl -certfile fullchain.crt | \
openssl pkcs7 -print_certs -noout
|
問題三:中繼憑證過期
症狀:
- 伺服器憑證有效但仍顯示錯誤
- 錯誤訊息提到 “certificate has expired”
檢查方法:
1
2
3
4
5
| # 檢查所有憑證的有效期
for cert in server.crt intermediate.crt root.crt; do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -subject -dates
done
|
解決方案:
問題四:根憑證未受信任
症狀:
- 在特定系統或瀏覽器上無法驗證
- 錯誤訊息:“certificate signed by unknown authority”
原因:
- 使用私有 CA 簽發的憑證
- 作業系統或瀏覽器的信任存放區未更新
- 使用較新的根憑證(尚未廣泛分發)
解決方案:
1
2
3
4
5
6
7
8
9
| # 檢查系統的信任存放區(Ubuntu/Debian)
ls /etc/ssl/certs/
# 更新信任存放區
sudo update-ca-certificates
# 手動添加根憑證
sudo cp custom-root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
|
問題五:憑證與私鑰不匹配
症狀:
- 伺服器啟動時報錯
- 錯誤訊息:“key values mismatch”
檢查方法:
1
2
3
4
5
| # 比較憑證和私鑰的模數(modulus)
openssl x509 -in server.crt -noout -modulus | openssl md5
openssl rsa -in server.key -noout -modulus | openssl md5
# 兩者的 MD5 值應該相同
|
問題排解流程圖
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
30
| SSL/TLS 憑證問題排解流程:
開始排解
|
v
[檢查憑證是否過期] --是--> 更新憑證
|
否
v
[檢查憑證鏈是否完整] --否--> 添加缺少的中繼憑證
|
是
v
[檢查憑證順序是否正確] --否--> 重新排列憑證順序
|
是
v
[檢查私鑰是否匹配] --否--> 使用正確的私鑰
|
是
v
[檢查網域名稱是否匹配] --否--> 申請正確的憑證
|
是
v
[檢查根憑證是否受信任] --否--> 更新信任存放區或使用受信任的 CA
|
是
v
問題解決
|
設定範例
Nginx 憑證鏈設定
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
| # /etc/nginx/sites-available/example.com
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# 憑證鏈檔案(包含伺服器憑證 + 中繼憑證)
ssl_certificate /etc/nginx/ssl/fullchain.pem;
# 私鑰檔案
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# SSL 設定優化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# OCSP Stapling(憑證狀態查詢優化)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 其他設定...
root /var/www/example.com;
index index.html;
}
|
Apache 憑證鏈設定
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
30
31
32
33
34
| # /etc/apache2/sites-available/example.com-ssl.conf
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
# 伺服器憑證
SSLCertificateFile /etc/ssl/certs/server.crt
# 私鑰
SSLCertificateKeyFile /etc/ssl/private/server.key
# 中繼憑證鏈
SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
# 或使用合併的憑證鏈(Apache 2.4.8+)
# SSLCertificateFile /etc/ssl/certs/fullchain.crt
# SSL 設定
SSLEngine on
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder off
# OCSP Stapling
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
DocumentRoot /var/www/example.com
</VirtualHost>
# OCSP Stapling Cache(需放在 VirtualHost 外)
SSLStaplingCache shmcb:/var/run/ocsp(128000)
|
建立完整憑證鏈檔案
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
| #!/bin/bash
# create-fullchain.sh - 建立完整的憑證鏈檔案
# 憑證檔案路徑
SERVER_CERT="server.crt"
INTERMEDIATE_CERT="intermediate.crt"
ROOT_CERT="root.crt" # 可選
OUTPUT_FILE="fullchain.crt"
echo "建立憑證鏈檔案..."
# 合併憑證(正確順序)
cat "$SERVER_CERT" > "$OUTPUT_FILE"
cat "$INTERMEDIATE_CERT" >> "$OUTPUT_FILE"
# cat "$ROOT_CERT" >> "$OUTPUT_FILE" # 根憑證通常不需要
echo "憑證鏈已建立:$OUTPUT_FILE"
# 驗證憑證鏈
echo -e "\n驗證憑證鏈..."
openssl verify -CAfile "$ROOT_CERT" -untrusted "$INTERMEDIATE_CERT" "$SERVER_CERT"
# 顯示憑證鏈內容
echo -e "\n憑證鏈內容:"
openssl crl2pkcs7 -nocrl -certfile "$OUTPUT_FILE" | \
openssl pkcs7 -print_certs -noout | \
grep -E "(subject|issuer)="
|
Let’s Encrypt 憑證鏈
使用 Certbot 取得的 Let’s Encrypt 憑證會自動產生正確的憑證鏈:
1
2
3
4
5
6
7
8
9
10
| # Let's Encrypt 憑證檔案結構
/etc/letsencrypt/live/example.com/
├── cert.pem # 伺服器憑證
├── chain.pem # 中繼憑證鏈
├── fullchain.pem # 完整憑證鏈(cert.pem + chain.pem)
└── privkey.pem # 私鑰
# Nginx 設定(使用 Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
|
驗證設定是否正確
1
2
3
4
5
6
7
8
9
| # 使用 OpenSSL 測試連線
openssl s_client -connect example.com:443 -servername example.com
# 使用 curl 測試
curl -vI https://example.com
# 線上工具
# - SSL Labs: https://www.ssllabs.com/ssltest/
# - SSL Checker: https://www.sslshopper.com/ssl-checker.html
|
總結
憑證鏈是 SSL/TLS 安全通訊的核心機制,正確理解和設定憑證鏈對於維護網站安全至關重要。
重點回顧:
- 憑證鏈結構:終端實體憑證 → 中繼 CA → 根 CA
- 根憑證:自簽、預裝於系統、離線保存
- 中繼憑證:由上層 CA 簽發、日常簽發作業使用
- 驗證流程:逐層驗證簽章、有效期、撤銷狀態
- 常見問題:憑證鏈不完整、順序錯誤、憑證過期
最佳實務:
- 確保伺服器傳送完整的憑證鏈(包含中繼憑證)
- 使用正確的憑證順序(伺服器憑證在前)
- 啟用 OCSP Stapling 提升效能
- 定期監控憑證有效期
- 使用 SSL Labs 等工具定期檢測設定
參考資料