顯示具有 CRL 標籤的文章。 顯示所有文章
顯示具有 CRL 標籤的文章。 顯示所有文章

2023年6月5日 星期一

Client Certificate 的 OCSP (Online Certificate Status Protocol) 設定

這篇是的
使用 OpenSSL 製做 Client Certificate 做客戶端 TLS 認證 - 配合 Nginx 和 Java 做示範
的 OCSP 設定補充。

如果要看 CRL 的設定可以看我的另一篇文章:

Client Certificate 的 CRL (Certificate Revocation List) 設定

OCSP 的全名是 Online Certificate Status Protocol,
跟 CRL 相同的地方是,它一樣可以用一個 ./db/index 來紀錄憑證的撤銷狀態。
跟 CRL 不同的地方是,它不是提供一個憑證撤銷清單,而是自行啟動一個伺服器,我們把它叫做 OCSP Responder,當想要對憑證狀態進行確認時,可以對 OCSP Responder 發起詢問的請求 (OCSP Request),OCSP Responder 再把憑證的狀態回傳回去 (OCSP Response)。

跟 CRL 比較起來,OCSP 有幾個優點:
  1. 不像 CRL 一次回傳多個憑證狀態,一次只回傳被詢問的憑證狀態,對於網路成本及詢問者的解析成本都比較低。
  2. 因為是由 OCSP Responder Server 即時回應,相較 CRL 可能一段時間才發出更新或才會被人詢問來說,能獲取比較即時的憑證狀態。
缺點也是有:
  1. OCSP Responder 的流量負擔可能比較大,可能可以用多台 Server 做 Revers Proxy 分流解決。
OCSP Responder 可以用任何技術實作,只要它能處理 OCSP Request 並回傳正確格式的 OCSP Response 即可,如何儲存憑證的撤銷狀態也可自行用任何方法實現,其實 CRL 也是一樣,只是上次我們就直接使用 OpenSSL 的 index 檔功能來幫助我們實現。

在這篇文中,我一樣會只使用 OpenSSL 來實現 OCSP Responder,OpenSSL 本身就可以開啟一個 OCSP Responder,並配合 OpenSSL index 檔來處理 OCSP Request。

---------------------------------------------------------------------------------------------
現在要來示範要做 Client Certificate 時,建立 OCSP Responder 和 Nginx 設定的方式。

Nginx 的設定非常簡單,接續這一篇文章的 Nginx 設定 使用 OpenSSL 製做 Client Certificate 做客戶端 TLS 認證 - 配合 Nginx 和 Java 做示範,只要再加上 ssl_ocsp ssl_ocsp_responder 設定即可。
server {
   ......
   ssl_client_certificate "ca.crt";
   ssl_verify_client optional;
   ssl_ocsp leaf; #值可以是 on, leaf, off,leaf 是指只對 client certificate 做 ocsp
   ssl_ocsp_responder http://127.0.0.1:9000; # 自行指定 OCSP Responder 的 url
   ......
}

為了之後管理方便,在這裡我先建了幾個資料夾和檔案 (./crlnumber 和 ./crl 資料夾就不用了):
  1. ./certs:存放憑證的地方。
  2. ./csr:存放 CSR 的地方。
  3. ./db/index:index 為我手動放置的一個空白檔案,作為一個紀錄憑證狀態的純文字檔,其實 CRL 可以不需要,主要給 OCSP 使用,但 CRL 也可從其中產生出 CRL 檔,也方便之後管理憑證狀態。
  4. ./keys:存放 Private Key 的地方。
  5. ./serial/serial:serial 為我手動放置的一個純文字檔案,其內容為一個單純的 serial number 序號,供憑證產生時的累加 Serial Key 使用,serial number 可視為對每一個 client certificate 的辨識唯一序號。
  6. ca.conf:供 openssl ca 指令用的配置檔,用以簡化指令參數長度及方便管理。
檔案內容例如可用以下指定 (自己用編輯器輸入也可以):
touch ./db/index
openssl rand -hex 16 > serial/serial
因為會用到
openssl ca 指令,為了方便我們先來建立一個 config 配置檔供 openssl ca 指令參考,
這樣 openssl ca 指令就可以少打一些參數。

ca.conf:
[default]
name                    = root-ca
default_ca              = ca_config

[ca_config]
database                = db/index
serial                  = serial/serial
crlnumber               = crlnumber/crlnumber
default_crl_days        = 1
certificate             = certs/ca.crt
private_key             = keys/ca.key
new_certs_dir           = certs
default_md              = sha256
policy                  = ca_policy
unique_subject          = no

[ca_policy]
countryName             = match
stateOrProvinceName     = optional
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ocsp_ext]
basicConstraints        = critical,CA:false
extendedKeyUsage        = OCSPSigning
noCheck                 = yes
keyUsage                = critical,digitalSignature
subjectKeyIdentifier    = hash
接著各項指令如下,
#產生 CA private key
openssl genrsa -out ./keys/ca.key 4096

#產生 CA CSR
openssl req -new -key ./keys/ca.key -out ./csr/ca.csr

#產生 CA 憑證
openssl x509 -req -days 365 -in ./csr/ca.csr -signkey ./keys/ca.key -CAcreateserial -out ./certs/ca.crt

# OCSP Responder 也需要自己的憑證
#產生 OCSP Responder 的 private key
openssl genrsa -out ./keys/ocsp.key 4096

#產生 OCSP Responder 的 CSR
openssl req -new -key ./keys/ocsp.key -out ./csr/ocsp.csr

#產生 OCSP Responder Server 的 Certificate,這裡使用了 ocsp_ext 區塊
#不使用 config 檔裡的 ocsp_ext 區塊設定的話,之後向 OCSP Responder Server 驗證 Certificate 時會有 missing ocspsigning usage 錯誤訊息)
#此時 ./db/index 會被更新,可以看到有一筆 OCSP Certificate 的憑證資料被寫入
openssl ca -config ca.conf -in ./csr/ocsp.csr -out ./certs/ocsp.crt -extensions ocsp_ext -days 365

#啟動 OCSP Responder Server,這邊使用了 locahost 的 9000 port
#啟動成功後應該可以看到 console 顯示
#ocsp: waiting for OCSP client connections...
#的訊息
openssl ocsp -port 9000 -index ./db/index -rsigner ./certs/ocsp.crt -rkey ./keys/ocsp.key -CA ./certs/ca.crt -text

#產生 Client private key:
openssl genrsa -out ./keys/client.key 4096

#產生 Client CSR:
openssl req -new -key ./keys/client.key -out ./csr/client.csr

#產生 Client Certificate,
#此時 ./db/index 會被更新,可以看到有一筆 Client Certificate 的憑證資料被寫入
openssl ca -config ca.conf -in ./csr/client.csr -out ./certs/client.crt -days 365

#用 OpenSSL 對 OCSP Responder Server 發起 OCSP Request 進行憑證的驗證
#對 OCSP Certificate 做驗證
openssl ocsp -issuer ./certs/ca.crt -CAfile ./certs/ca.crt -cert ./certs/ocsp.crt -url http://127.0.0.1:9000
#對 Client Certificate 做驗證
openssl ocsp -issuer ./certs/ca.crt -CAfile ./certs/ca.crt -cert ./certs/client.crt -url http://127.0.0.1:9000
#如果驗證結果是合法 (Valid) 的話,應該會看到如下訊息:
Response verify OK
./certs/client.crt: good
        This Update: Jun
#被 Revoke 的憑證查詢結果會像這樣:
#(Reason 只會在 Revoke 時有加入 crl_reason 時才會出現,手動修改 ./db/index 內容也可以,就是在 Revoke date 欄位加上 Reason 並用逗號分隔)
Response verify OK
./certs/client.crt: revoked
        This Update: Jun  6 04:55:19 2023 GMT
		Reason: unspecified
        Revocation Time: Jun  6 04:53:37 2023 GMT

#撤銷 (Revoke) 一個 Certificate (可以看到跟 CRL 的 Revoke 指令相同,-crl_reason 參數可自選要不要加上)
openssl ca -config ca.conf -revoke ./certs/client.crt

#加進一個未撤銷 (Valid) 狀態的 Certificate 到 OCSP DB 中 (自己要先手動把 OCSP DB 中相同序號的 Certificate 紀錄刪掉),
#或是自己打開 OCSP DB 檔把 Certificate 的狀態從 R (Revoke 的意思) 改成 V (Valid 的意思) 也可以
openssl ca -valid ./certs/client.crt -config ca.conf

參考資料:

Client Certificate 的 CRL (Certificate Revocation List) 設定

 這篇是
使用 OpenSSL 製做 Client Certificate 做客戶端 TLS 認證 - 配合 Nginx 和 Java 做示範
這篇文章的 CRL 設定補充。

如果要看 OCSP 的設定可以看我的另一篇文章:

Client Certificate 的 OCSP (Online Certificate Status Protocol) 設定

CRL 的全名是 Certificate Revocation List,
顧名思意是一個憑證撤銷狀態的列表,
有時雖然憑證的日期沒有過期、簽名驗證也都正確,
但是因為一些其他因素,例如 Private Key 被駭客偷走的憑證安全性問題等,
憑證可能會被 CA 認為需要強制撤銷 (Revoke),
這時我們除了認證憑證合法以外,還需要有方法來確認憑證的 Revoke 狀態,
CRL 就是其中一種方法,其他還有像是 OCSP 的方法,會在我的另一篇文XXXXX 中做介紹。

在 CRL 方法中,CA 會定期更新憑證的撤銷列表到一個 CRL 檔案中,通常會以 pem 格式儲存,並且以 CA 來進行簽章 (Sign) 來證明其是由 CA 頒發的。

當我們要確認憑證的撤銷狀態時,通常會在憑證中找到 CRL 檔案的位置 (通常是一個網路 url 的位置),憑證中也有可能沒有寫上 CRL 資訊,這時有可能是 client, server 端已經彼些講好的位置,
拿到 CRL 檔案後,就可以在其中查找要確認的憑證撤銷狀態。

CRL 檔案或是憑證中可能還會有其他資訊,例如有效期限、下一次 CRL 更新日期等,我們可以在期限到後主動查找新發佈版本的 CRL。

CRL 有一些優點:
  1. 其中一個優點就是架構簡單、實現方便,CA 只要定時的更新 CRL 即可。
  2.  CRL 檔案容易取得,可能只是一個網路上的檔案,使用 HTTP Request 即可取得,取得後甚至可以把它在本地端,不用每次都去網路上抓取,等下次更新日或 CA 發表重大更新要求時再抓新版的 CRL。
但相對地其反面就是它的缺點:
  1. CA 如果不及時更新 CRL 檔 或是 詢問者不即時取得最新的 CRL 的話,即意味著 CRL 可能不是最新的,如果有憑證有安全問題即時的被 CA 公告需要 Revoke,沒更新 CRL 可能會無法即时知道。
  2. 另一個缺點是,因為 CRL 可能包含許多其他可能用不到的憑證狀態資訊,如果 CRL 檔很大,網路的傳送成本就會提高,也會讓我們在其中查找要確認的憑證時花較多的時間。

---------------------------------------------------------------------------------------------
現在要來示範要做 Client Certificate 時,產生 CRL 檔案和 Nginx 設定的方式。

Nginx 的設定非常簡單,接續這一篇文章的 Nginx 設定 使用 OpenSSL 製做 Client Certificate 做客戶端 TLS 認證 - 配合 Nginx 和 Java 做示範,只要再加上 ssl_crl 設定即可。
server {
   ......
   ssl_client_certificate "ca.crt";
   ssl_verify_client optional;
   ssl_crl "D:\xxx\xxx\ca.crl"; # CRL 檔案的位置,也可以是網路上的 url
   ......
}

接下來要來產生 CRL 檔案,

為了之後管理方便,在這裡我先建了幾個資料夾和檔案:
  1. ./certs:存放憑證的地方。
  2. ./csr:存放 CSR 的地方。
  3. ./crlnumber:存放 CRL 檔版本序號的地方。
  4. ./crl:存放 CRL 檔的地方。
  5. ./db/index:index 為我手動放置的一個空白檔案,作為一個紀錄憑證狀態的純文字檔,其實 CRL 可以不需要,主要給 OCSP 使用,但 CRL 也可從其中產生出 CRL 檔,也方便之後管理憑證狀態。
  6. ./keys:存放 Private Key 的地方。
  7. ./serial/serial:serial 為我手動放置的一個純文字檔案,其內容為一個單純的 serial number 序號,供憑證產生時的累加 Serial Key 使用,serial number 可視為對每一個 client certificate 的辨識唯一序號。
  8. ca.conf:供 openssl ca 指令用的配置檔,用以簡化指令參數長度及方便管理。
檔案內容例如可用以下指定 (自己用編輯器輸入也可以):
touch ./db/index
openssl rand -hex 16 > serial/serial
echo 1001 > crlnumber/crlnumber
因為會用到
openssl ca 指令,為了方便我們先來建立一個 config 配置檔供 openssl ca 指令參考,
這樣 openssl ca 指令就可以少打一些參數。

ca.conf:
[default]
name                    = root-ca
default_ca              = ca_config

[ca_config]
database                = db/index
serial                  = serial/serial
crlnumber               = crlnumber/crlnumber
default_crl_days        = 1
certificate             = certs/ca.crt
private_key             = keys/ca.key
new_certs_dir           = certs
default_md              = sha256
policy                  = ca_policy
unique_subject          = no

[ca_policy]
countryName             = match
stateOrProvinceName     = optional
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
接著各項指令如下,
#產生 CA private key
openssl genrsa -out ./keys/ca.key 4096

#產生 CA CSR
openssl req -new -key ./keys/ca.key -out ./csr/ca.csr

#產生 CA 憑證
openssl x509 -req -days 365 -in ./csr/ca.csr -signkey ./keys/ca.key -CAcreateserial -out ./certs/ca.crt

#產生 CRL
openssl ca -gencrl -config ./ca.conf -out ./crl/ca.crl

#觀看 CRL 檔中的資訊
openssl crl -in ./crl/ca.crl -text -noout

#產生 Client private key
openssl genrsa -out ./keys/client.key 4096

#產生 Client CSR
openssl req -new -key ./keys/client.key -out ./csr/client.csr

#使用 ca.conf 產生 CA 憑證,這時 client 憑證可得到一個可識別的唯一序號, serial 裡的 serial number 也會改變,
#此時也會發現 db/index 被加進了一條此 client 憑證的資料,db/index 有變動時可以手動下指令再製作一次新版本的 CRL
openssl ca -config ./ca.conf -in ./csr/client.csr -out ./certs/client.crt -days 365

#向 CRL 檢查憑證是否 Valid or Revoke
openssl verify -crl_check -CRLfile ./crl/ca.crl -CAfile ./certs/ca.crt ./certs/client.crt

#撤銷 (Revoke) Client 憑證 -crl_reason 參數是 Revoke reason
#撤銷後 db/index 會更新,憑證資訊欄位的 V (Valid 的意思) 會變成 R (Revoke) 的意思,並且會被標上 Revoke Date 訊息
#記得再下指令產生一下新版本的 CRL
openssl ca -config ca.conf -revoke ./certs/client.crt -crl_reason unspecified
# Revoke reason 可選列表如下:
unspecified
keyCompromise
CACompromise
affiliationChanged
superseded
cessationOfOperation
certificateHold
removeFromCRL

參考資料: