PKCS7的數字信封格式分為兩種:帶簽名的數字信封和不帶簽名的數字信封。由於這個數字信封的生成過程比較復雜,所以這兩種格式比較容易記混,導致都搞不清楚一個數字信封里面到底是存儲的什么內容了。下面我就詳細的解釋一下,這兩種數字信封的生成過程和其詳細的數據結構。
不帶簽名的數字信封:
不帶簽名的數字信封內容類型由任意類型的加密內容和加密的一個/多個接收者的內容加密密鑰組成,其生成過程如下:
1. 發送方隨機產生一個對應於特定加密內容加密密鑰。
2. 發送方將內容加密密鑰用每個接收者的公鑰加密。
3. 對於每一個接收者,把加了密的內容加密密鑰和接收者的其他信息放入RecipientInfo值中。
4. 用內容加密密鑰加密內容。
5. 將所有接收者的RecipientInfo值和加了密的內容放入EnvelopedData值中。
發送給接收方后,解包的過程很簡單:接收者用自己的私鑰解開加密的內容加密密鑰,然后用該密鑰解密密文內容。整個過程參與非對稱運算的只有接收方的密鑰對。
帶簽名的數字信封:
帶簽名的數字信封由任意類型的加密內容、加了密的一個/多個接收者的內容加密密鑰和雙重加密的一個/多個簽名者的消息摘要。“雙重加密”由簽名者私鑰的加密和內容加密密鑰的加密組成。其生成過程如下:
1.發送方隨機產生一個對應於特定加密算法的內容加密密鑰。
2. 發送方將內容加密密鑰用每個接收者的公鑰加密。
3. 對於每一個接收者,把加了密的內容加密密鑰和接收者的其他信息放入RecipientInfo值中,參見數據結構2。
4. 對於每一個簽名者,他用自己的消息摘要算法計算出摘要值 (如果兩個簽名者使用同樣的算法,那么摘要值只需計算一次) 。
5. 對於每一個簽名者,消息摘要和相關的信息用自己的私鑰加密,結果再用內容加密密鑰加密。
6. 對於每一個簽名者,把雙重加密(即第5步中的先非對稱加密,再對稱加密的過程)的消息摘要和其他的簽名者特定信息放入SignerInfo 值中。
7. 用內容加密密鑰加密內容。
8. 把所有簽名者的消息摘要算法、所有簽名者的SignerInfo值、所有接收者的RecipientInfo值和加了密的內容一起放入SignedAndEnvelopedData 值中。
接收方接到數字信封后,解封操作會復雜些:
1.收方B接受到數字信封DE后,首先用自己的私鑰PVB解密數字信封,取出對稱密鑰SK;2.收方B用對稱密鑰SK通過DES算法解密加密信息E,還原出原文信息、數字簽名SD及發方A證書的公鑰PBA;3. 收方B驗證數字簽名,先用發方A的公鑰解密數字簽名得數字摘要MD;4.收方B同時將原文信息用同樣的哈希運算,求得一個新的數字摘要MD’;5.將兩個數字摘要MD和MD’進行比較,驗證原文是否被修改。如果二者相等,說明數據沒有被篡改,是保密傳輸的,簽名是真實的;否則拒絕該簽名。
下圖為簡單流程:
代碼示例:
/** * @param publicKey 公鑰對象 * @param orgData 原文 * @return Result if(code== 200)success else fail * encryptFileStr 加密后的字符串 * encryptKeyStr 加密后的秘鑰字符串 * @throws UnsupportedEncodingException * @throws NoSuchProviderException * @throws NoSuchAlgorithmException */ public static Result strEncrypt(PublicKey publicKey,String orgData) throws UnsupportedEncodingException { try { //創建對稱秘鑰對 byte[] key = Sm4Utils.generateKey(); //對加密 byte[] fileEncryptBytes = Sm4Utils.encrypt_ECB_Padding(key, orgData.getBytes(charset)); //對秘鑰對加密 byte[] encryptKey = SM2Util.encrypt((BCECPublicKey)publicKey, key); //將返回值base64加密 String encryptDataStr = Base64Utils.encode(fileEncryptBytes); String encryptKeyStr = Base64Utils.encode(encryptKey); return new Result(Result.CODE_SUCCESS,"加密成功", encryptDataStr,encryptKeyStr); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return new Result(Result.CODE_EXCEPTION, "NoSuchAlgorithmException,沒有找到該摘要算法"); } catch (NoSuchProviderException e) { e.printStackTrace(); return new Result(Result.CODE_EXCEPTION, "NoSuchProviderException,沒有找到該加密機"); }catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | InvalidCipherTextException e ) { e.printStackTrace(); return new Result(Result.CODE_EXCEPTION, "加密失敗:"+e.getMessage()); } }
參考:
https://blog.csdn.net/weixin_39636696/article/details/115780593
https://gitee.com/saxonkiku/encrypt_and_decrypt_file/blob/master/src/main/java/com/ymwk/StrEncrypt.java