1676 字
8 分鐘
OpenSSL 做 CAdES PKCS7 數位簽章與驗簽
2024-09-21
無標籤

OpenSSL 是一個強大的開源工具套件,主要用於實作各種加密技術,如 SSL/TLS 通訊協定、數位簽章和證書管理。PKCS7 是一種加密訊息語法標準,由 RSA 安全公司定義,通常用於數位簽章與加密。PKCS7 格式可以包含簽章、加密資料和憑證,使其適合在電子郵件、PDF 或其他應用程式中進行安全性資訊交換。OpenSSL 支援 PKCS7 格式,允許開發者使用它來處理加密與簽章的訊息封裝。

本篇文章將說明如何透過 OpenSSL 為檔案進行數位簽章,並產生 PKCS7,再進行驗簽。


1. CAdES 數位簽章#

CAdES(CMS Advanced Electronic Signatures)是一種基於 CMS/PKCS7 標準的數位簽章格式,提供更高層次的電子簽章功能。它不僅支援基本的簽章功能,還允許將簽章與簽署時間、簽署者憑證、驗證鏈等附加資訊整合,確保簽章檔案在長期保存和驗證時的完整性與有效性。CAdES 常用於法律和商業環境中,確保電子檔案的法律效力和安全性。

RFC 5126 是關於 CAdES 的技術規範,描述了如何在使用 CMS/PKCS7 格式的基礎上生成進階的數位簽章。它定義了多種 CAdES 簽章型別,從基本的簽章到支援長期驗證的簽章格式,提供時間戳、憑證鏈、撤銷資訊等附加元素,確保簽章的長期可驗證性。RFC 5126 的目標是使數位簽章在長時間內保持完整性與可驗證性,適合在需要高安全性和法律效力的電子交易中使用。

1.1 P7S 與 P7M#

在 RFC 5126 中,P7S 和 P7M 是基於 PKCS #7 標準的數位簽章格式,通常用於數位簽署電子檔案或訊息。這兩種格式的區別在於簽章與原始內容的存放方式:

P7S (.p7s)

  • P7S 格式僅包含簽章資訊,不包含原始檔案內容。
  • 這是「分離簽章」(Detached Signature)的情況,數位簽章與檔案是分開的。通常,檔案保持不變,簽章存儲在 .p7s 檔案中。
  • 例如,當你簽署電子郵件時,數位簽章會生成一個 .p7s 檔案,但原始郵件內容與簽章是分開處理的。

P7M (.p7m)

  • P7M 格式則包含簽章與被簽署的原始內容。
  • 這是「封裝簽章」(Enveloped Signature)的情況,數位簽章與檔案內容一起封裝並存放在 .p7m 檔案中。這使得檔案本身包含了簽章和數據,不需要另外的檔案來驗證簽章。
  • 常見於簽署完整的電子檔案或訊息,當你收到 .p7m 檔案時,該檔案同時包含了簽章及檔案。

這兩種格式的使用取決於應用需求,例如 .p7s 適合於檔案保持不變的情境,而 .p7m 則適用於需要整合內容與簽章的場景。

1.2 DER 或 PEM#

P7S 與 P7M 的檔案編碼可以分為 DER 與 PEM,DER 為 ASN.1 二進制檔, PEM 則為 DER 二進制檔的 base64 並在頭尾加上註解,DER 與 PEM 的差異參考本人過去的文章


2. 產生自簽憑證與私鑰#

範例中使用自簽憑證來為檔案進行數位簽章,因此需先產生自簽憑證與私鑰,指令如下,產生私鑰 key.pem 與憑證 cert.pem。

Terminal window
# 產生私鑰
openssl genpkey -algorithm RSA -out key.pem
# 產生憑證請求檔
openssl req -new -key key.pem -out request.csr
# 產生自簽憑證
openssl x509 -req -days 365 -in request.csr -signkey key.pem -out cert.pem

3. 透過 OpenSSL 為檔案做數位簽章#

透過 openssl 指令能夠為任意格式的檔案做數位簽章,以 example.txt 為數位簽章的範例。

Terminal window
echo -n "example" > example.txt

分別產生 P7S、P7M 兩種包含數位簽章的 PKCS7 格式,差別在於 P7M 包含了原始檔案,而 P7S 不包含。

3.1 產生 P7S#

透過下面的指令,以憑證與私鑰為 example.txt 做數位簽章,可以輸出 PEM 格式的 example_pem.p7s,也可以輸出 DER 格式的 example_pem.p7s。

兩個格式都是國際通用的,則一產生即可。

Terminal window
# 產生 PEM 格式的 P7S
openssl smime -sign -outform PEM -in example.txt -out example_pem.p7s \
-signer cert.pem -inkey key.pem
# 產生 DER 格式的 P7S
openssl smime -sign -outform DER -in example.txt -out example_der.p7s \
-signer cert.pem -inkey key.pem

3.2 產生 P7M#

P7M 另外包含了原始檔案,產生的方式與 P7S 差不多,在指令最後加上 nodetach 即可。

Terminal window
# 產生 PEM 格式的 P7M
openssl smime -sign -outform PEM -in example.txt -out example_pem.p7m \
-signer cert.pem -inkey key.pem -nodetach
# 產生 DER 格式的 P7M
openssl smime -sign -outform DER -in example.txt -out example_der.p7m \
-signer cert.pem -inkey key.pem -nodetach

4. 驗證數位簽章的原理#

驗證數位簽章時,主要會驗證以下兩個部分:驗證簽章值、驗證憑證,當某一步錯誤,就會顯示驗證失敗。

P7S 與 P7M 兩個 PKCS7 格式中包含了以下資訊用於驗簽:

  • Hash 演算法
  • SignedAttributes,裡面包含簽章時間、原始檔案的 Hash 值
  • SignedAttributes 的簽章值
  • 公鑰憑證,裡面包含公鑰、序號、主體資訊、簽發者資訊、有效期限
  • P7M 有原始檔案,P7S 則無

4.1 驗證簽章值#

  1. 比對 PKCS7 中的 Hash 值是否與原始檔案的 Hash 值相同
  2. 將 PKCS7 中的簽章值,以公鑰進行解密,比對與 SignedAttributes 是否相同

4.2 驗證憑證#

  1. 檢查憑證有效期限
  2. 檢查憑證是否能夠與主機上信任的根憑證、中繼憑證的憑證鏈相符

5. 透過 OpenSSL 驗證數位簽章#

透過 OpenSSL 指令可以為 P7S 與 P7M 進行驗簽,當驗證成功時會第一行會輸出 Verification successful。

由於我用自簽憑證當範例,就不驗證憑證鏈。

5.1 驗簽 P7M#

由於 P7M 包含了原始檔、簽章值、憑證,只需要 P7M 就可以驗簽。

  • 參數 noverify:不用驗證憑證鏈。
  • 參數 out:從 P7M 中匯出原始檔
# 驗證 PEM 格式的 P7M,不驗證憑證鏈
openssl smime -verify -inform PEM -in example_pem.p7m -out output.txt -noverify
# 驗證 DER 格式的 P7M,不驗證憑證鏈
openssl smime -verify -inform DER -in example_der.p7m -out output.txt -noverify

5.2 驗簽 P7S#

P7S 只包含了簽章值、憑證,驗證的時候要另外輸入原始檔

Terminal window
# 驗證 PEM 格式的 P7M,不驗證憑證鏈
openssl smime -verify -inform PEM -in example_pem.p7s -content example.txt -noverify
# 驗證 DER 格式的 P7M,不驗證憑證鏈
openssl smime -verify -inform DER -in example_der.p7s -content example.txt -noverify

6. 透過 OpenSSL 查看 P7S、P7M 的憑證資訊#

OpenSSL 可以用來查看憑證資訊,指令如下

Terminal window
openssl pkcs7 -in <PKCS7 file> -inform <PEM or DER> -print_certs -text

會輸出以下結果

滑到最下面可以看到憑證的 PEM 格式

ㄗㄨ

ㄗㄨ