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。
# 產生私鑰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.pem3. 透過 OpenSSL 為檔案做數位簽章
透過 openssl 指令能夠為任意格式的檔案做數位簽章,以 example.txt 為數位簽章的範例。
echo -n "example" > example.txt分別產生 P7S、P7M 兩種包含數位簽章的 PKCS7 格式,差別在於 P7M 包含了原始檔案,而 P7S 不包含。
3.1 產生 P7S
透過下面的指令,以憑證與私鑰為 example.txt 做數位簽章,可以輸出 PEM 格式的 example_pem.p7s,也可以輸出 DER 格式的 example_pem.p7s。
兩個格式都是國際通用的,則一產生即可。
# 產生 PEM 格式的 P7Sopenssl smime -sign -outform PEM -in example.txt -out example_pem.p7s \ -signer cert.pem -inkey key.pem
# 產生 DER 格式的 P7Sopenssl smime -sign -outform DER -in example.txt -out example_der.p7s \ -signer cert.pem -inkey key.pem3.2 產生 P7M
P7M 另外包含了原始檔案,產生的方式與 P7S 差不多,在指令最後加上 nodetach 即可。
# 產生 PEM 格式的 P7Mopenssl smime -sign -outform PEM -in example.txt -out example_pem.p7m \ -signer cert.pem -inkey key.pem -nodetach
# 產生 DER 格式的 P7Mopenssl smime -sign -outform DER -in example.txt -out example_der.p7m \ -signer cert.pem -inkey key.pem -nodetach4. 驗證數位簽章的原理
驗證數位簽章時,主要會驗證以下兩個部分:驗證簽章值、驗證憑證,當某一步錯誤,就會顯示驗證失敗。
P7S 與 P7M 兩個 PKCS7 格式中包含了以下資訊用於驗簽:
- Hash 演算法
- SignedAttributes,裡面包含簽章時間、原始檔案的 Hash 值
- SignedAttributes 的簽章值
- 公鑰憑證,裡面包含公鑰、序號、主體資訊、簽發者資訊、有效期限
- P7M 有原始檔案,P7S 則無
4.1 驗證簽章值
- 比對 PKCS7 中的 Hash 值是否與原始檔案的 Hash 值相同
- 將 PKCS7 中的簽章值,以公鑰進行解密,比對與 SignedAttributes 是否相同
4.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 -noverify5.2 驗簽 P7S
P7S 只包含了簽章值、憑證,驗證的時候要另外輸入原始檔
# 驗證 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 -noverify6. 透過 OpenSSL 查看 P7S、P7M 的憑證資訊
OpenSSL 可以用來查看憑證資訊,指令如下
openssl pkcs7 -in <PKCS7 file> -inform <PEM or DER> -print_certs -text會輸出以下結果

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

ㄗㄨ