憑證吊銷清單 CRL 原理與實作

Certificate Revocation List CRL Principles and Implementation

CRL 概述與用途

憑證吊銷清單(Certificate Revocation List,CRL)是公開金鑰基礎建設(PKI)中用於管理已失效憑證的重要機制。當憑證在有效期限內因各種原因(如私鑰外洩、持有者離職、憑證資訊變更等)需要提前作廢時,憑證授權中心(CA)會將該憑證的序號加入 CRL 中。

CRL 的主要用途包括:

  • 安全性維護:確保已被吊銷的憑證不再被信任
  • 合規性要求:符合各類安全標準對憑證管理的規範
  • 風險控管:及時處理憑證洩露或誤發的情況
  • 身份驗證:在驗證憑證時檢查其是否已被吊銷

CRL vs OCSP 比較

除了 CRL 之外,線上憑證狀態協定(OCSP)是另一種常見的憑證狀態檢查機制。以下是兩者的比較:

特性CRLOCSP
運作方式下載完整清單即時查詢單一憑證
頻寬消耗較高(需下載整份清單)較低(僅查詢特定憑證)
即時性較差(依發布週期)較佳(即時查詢)
離線支援支援(可快取)不支援
隱私性較佳(不洩露查詢行為)較差(CA 可追蹤查詢)
實作複雜度較簡單較複雜

CRL 結構與欄位

CRL 採用 X.509 標準格式,主要包含以下欄位:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
CRL 結構
├── tbsCertList(待簽名的憑證清單)
│   ├── version(版本,v2 為 1)
│   ├── signature(簽章演算法)
│   ├── issuer(發行者名稱)
│   ├── thisUpdate(本次更新時間)
│   ├── nextUpdate(下次更新時間)
│   ├── revokedCertificates(已吊銷憑證清單)
│   │   ├── userCertificate(憑證序號)
│   │   ├── revocationDate(吊銷日期)
│   │   └── crlEntryExtensions(擴充欄位)
│   └── crlExtensions(CRL 擴充欄位)
├── signatureAlgorithm(簽章演算法)
└── signatureValue(簽章值)

常見的吊銷原因代碼(CRL Reason Code):

代碼名稱說明
0unspecified未指定原因
1keyCompromise私鑰洩露
2cACompromiseCA 私鑰洩露
3affiliationChanged隸屬關係變更
4superseded憑證被取代
5cessationOfOperation停止運作
6certificateHold憑證暫停(可恢復)
9privilegeWithdrawn權限撤銷
10aACompromise屬性授權中心洩露

使用 OpenSSL 建立 CRL

準備 CA 環境

首先,建立 CA 所需的目錄結構和設定檔:

1
2
3
4
5
6
# 建立目錄結構
mkdir -p ~/ca/{certs,crl,newcerts,private}
chmod 700 ~/ca/private
touch ~/ca/index.txt
echo 1000 > ~/ca/serial
echo 1000 > ~/ca/crlnumber

建立 OpenSSL 設定檔 ~/ca/openssl.cnf

 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
[ ca ]
default_ca = CA_default

[ CA_default ]
dir               = /home/user/ca
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
crlnumber         = $dir/crlnumber
private_key       = $dir/private/ca.key
certificate       = $dir/certs/ca.crt
crl               = $dir/crl/ca.crl
default_md        = sha256
default_days      = 365
default_crl_days  = 30
policy            = policy_loose

[ policy_loose ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ crl_ext ]
authorityKeyIdentifier = keyid:always

建立 CA 憑證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 產生 CA 私鑰
openssl genrsa -aes256 -out ~/ca/private/ca.key 4096

# 產生 CA 憑證
openssl req -config ~/ca/openssl.cnf \
    -key ~/ca/private/ca.key \
    -new -x509 -days 3650 -sha256 \
    -extensions v3_ca \
    -out ~/ca/certs/ca.crt \
    -subj "/C=TW/ST=Taiwan/L=Taipei/O=Example Corp/CN=Example CA"

產生初始 CRL

1
2
3
4
5
6
# 產生空的 CRL
openssl ca -config ~/ca/openssl.cnf \
    -gencrl -out ~/ca/crl/ca.crl

# 檢視 CRL 內容
openssl crl -in ~/ca/crl/ca.crl -text -noout

吊銷憑證流程

簽發測試憑證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 產生伺服器私鑰
openssl genrsa -out ~/ca/private/server.key 2048

# 產生憑證簽署請求(CSR)
openssl req -new -key ~/ca/private/server.key \
    -out ~/ca/certs/server.csr \
    -subj "/C=TW/ST=Taiwan/L=Taipei/O=Example Corp/CN=www.example.com"

# CA 簽發憑證
openssl ca -config ~/ca/openssl.cnf \
    -days 365 -notext -md sha256 \
    -in ~/ca/certs/server.csr \
    -out ~/ca/certs/server.crt

吊銷憑證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 吊銷憑證(指定原因為私鑰洩露)
openssl ca -config ~/ca/openssl.cnf \
    -revoke ~/ca/certs/server.crt \
    -crl_reason keyCompromise

# 重新產生 CRL
openssl ca -config ~/ca/openssl.cnf \
    -gencrl -out ~/ca/crl/ca.crl

# 驗證憑證已被加入 CRL
openssl crl -in ~/ca/crl/ca.crl -text -noout | grep -A 2 "Serial Number"

CRL 分發點設定

CRL 分發點(CRL Distribution Point,CDP)用於告知用戶端從何處取得 CRL。在憑證中加入 CDP 擴充欄位:

1
2
3
4
5
6
7
# 在 openssl.cnf 中加入以下設定
[ server_cert ]
crlDistributionPoints = URI:http://crl.example.com/ca.crl

[ crl_info ]
URI.0 = http://crl.example.com/ca.crl
URI.1 = ldap://ldap.example.com/cn=CA,dc=example,dc=com?certificateRevocationList

簽發憑證時套用設定:

1
2
3
4
5
openssl ca -config ~/ca/openssl.cnf \
    -extensions server_cert \
    -days 365 -notext -md sha256 \
    -in ~/ca/certs/server.csr \
    -out ~/ca/certs/server.crt

驗證 CRL

檢視 CRL 資訊

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 檢視 CRL 詳細內容
openssl crl -in ~/ca/crl/ca.crl -text -noout

# 檢視 CRL 發行者
openssl crl -in ~/ca/crl/ca.crl -issuer -noout

# 檢視 CRL 有效期間
openssl crl -in ~/ca/crl/ca.crl -lastupdate -nextupdate -noout

# 轉換 CRL 格式(DER 轉 PEM)
openssl crl -in ca.crl -inform DER -out ca.pem -outform PEM

使用 CRL 驗證憑證

1
2
3
4
5
6
7
8
# 合併 CA 憑證與 CRL
cat ~/ca/certs/ca.crt ~/ca/crl/ca.crl > ~/ca/certs/ca-bundle.pem

# 驗證憑證狀態
openssl verify -crl_check -CAfile ~/ca/certs/ca-bundle.pem ~/ca/certs/server.crt

# 驗證憑證鏈(包含中繼 CA)
openssl verify -crl_check_all -CAfile ~/ca/certs/ca-bundle.pem ~/ca/certs/server.crt

Delta CRL

Delta CRL 是一種增量式 CRL,只包含自上次完整 CRL 發布後新增的吊銷憑證。這可以大幅減少頻寬消耗。

1
2
3
4
5
6
# 產生 Delta CRL
openssl ca -config ~/ca/openssl.cnf \
    -gencrl -crlexts crl_ext \
    -crl_lastupdate 20240401000000Z \
    -crl_nextupdate 20240408000000Z \
    -out ~/ca/crl/delta.crl

Delta CRL 的設定需要在 openssl.cnf 中加入:

1
2
3
4
5
6
7
8
[ crl_ext ]
authorityKeyIdentifier = keyid:always
issuingDistributionPoint = critical, @idp_section
deltaCRLIndicator = critical, 1000

[ idp_section ]
fullname = URI:http://crl.example.com/ca.crl
onlyuser = TRUE

CRL 的限制與挑戰

主要限制

  1. 延遲問題:CRL 有發布週期,在下次更新前無法反映最新的吊銷狀態
  2. 檔案大小:隨著吊銷憑證增加,CRL 檔案會持續增長
  3. 頻寬消耗:用戶端需要定期下載完整 CRL
  4. 可用性依賴:CRL 分發點若無法存取,可能導致驗證失敗

最佳實踐

  • 設定合理的 CRL 更新週期(通常 1-7 天)
  • 部署多個 CRL 分發點以提高可用性
  • 考慮搭配 OCSP 使用以提供即時驗證
  • 實施 CRL 快取機制以減少頻寬消耗
  • 定期監控 CRL 大小和發布狀態

自動化 CRL 更新腳本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# crl-update.sh - 自動更新 CRL 腳本

CA_DIR="/home/user/ca"
CRL_FILE="${CA_DIR}/crl/ca.crl"
WEB_DIR="/var/www/html/crl"

# 產生新的 CRL
openssl ca -config ${CA_DIR}/openssl.cnf -gencrl -out ${CRL_FILE}

# 複製到 Web 伺服器目錄
cp ${CRL_FILE} ${WEB_DIR}/

# 設定正確權限
chmod 644 ${WEB_DIR}/ca.crl

# 記錄更新時間
echo "CRL updated at $(date)" >> ${CA_DIR}/crl/update.log

可搭配 cron 排程自動執行:

1
2
# 每天凌晨 2 點更新 CRL
0 2 * * * /home/user/ca/scripts/crl-update.sh

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy