github.com/emmansun/gmsm@v0.29.1/docs/pkcs7.md (about)

     1  # PKCS7应用指南
     2  本项目实现 PKCS#7/加密消息语法的子集([RFC2315](https://www.rfc-editor.org/rfc/rfc2315.html)、[RFC5652](https://www.rfc-editor.org/rfc/rfc5652.html)),以及相应国密支持《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。这是 [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 的一个分支,目前[mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7)已经是弃用状态,代码仓库也已经进入存档、只读状态。
     3  
     4  ## 支持的功能
     5  ### 数字信封数据(Enveloped Data)
     6  数字信封数据,使用对称加密算法加密数据,使用非对称加密加密数据密钥。支持的对称加密算法(以及模式)有
     7  * AES128-CBC
     8  * AES192-CBC
     9  * AES256-CBC
    10  * AES128-GCM
    11  * AES192-GCM
    12  * AES256-GCM
    13  * DES-CBC
    14  * 3DES-CBC
    15  * SM4-CBC
    16  * SM4-GCM
    17  
    18  支持的非对称加密算法为:
    19  * RSAPKCS1v15,目前尚不支持RSAOAEP
    20  * SM2
    21  
    22  #### 主要方法
    23  (是否国密是指OID也使用国密体系)
    24  
    25  | 是否国密 | 加密 | 解密(先调用```Parse```) |  
    26  | :--- | :--- | :--- |  
    27  | 否 | Encrypt | Decrypt |  
    28  | 否 | EncryptUsingPSK | DecryptUsingPSK |  
    29  | 是 | EncryptSM | Decrypt |  
    30  | 是 | EncryptCFCA | DecryptCFCA |  
    31  | 是 | EncryptSMUsingPSK | DecryptUsingPSK |  
    32  
    33  关于```EncryptSM / EncryptCFCA```的区别,请参考**CFCA互操作性指南**。  
    34  带PSK(Pre-shared key)后缀的方法,其对称加密密钥由调用者提供,而非随机生成。
    35  
    36  ### 加密数据(Encrypted Data)
    37  加密:对应本项目的```pkcs7.EncryptUsingPSK```和```pkcs7.EncryptSMUsingPSK```方法。  
    38  解密:对应本项目的```pkcs7.DecryptUsingPSK```方法(当然要先调用```pkcs7.Parse```)。
    39  
    40  ### 签名数据(Signed Data)
    41  签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
    42  
    43  #### 创建签名数据
    44  (是否国密是指OID也使用国密体系)
    45  
    46  | 是否国密 | 方法 | 默认签名算法 |    
    47  | :--- | :--- | :--- |
    48  | 否 | ```NewSignedData``` | SHA1 |  
    49  | 是 | ```NewSMSignedData``` | SM3 |  
    50  
    51  可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密不需要修改。    
    52  接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);  
    53  如果进行Detach签名,则调用```Detach```方法;  
    54  最后调用```Finish```方法,序列化输出结果。  
    55  
    56  #### Detach签名
    57  就是外部签名,**被签名数据**不包含在SignedData中(也就是其ContentInfo.Content为空)。
    58  
    59  In PKCS#7 SignedData, attached and detached formats are supported… In detached format, data that is signed is not embedded inside the SignedData package instead it is placed at some external location…
    60  
    61  可以参考[RFC2315](https://www.rfc-editor.org/rfc/rfc2315.html)的第7章 注3:  
    62  The optional omission of the content field makes it possible to construct "external signatures," for example, without modification to or replication of the content to which the signatures apply. In the case of external signatures, the content being signed would be omitted from the "inner" encapsulated ContentInfo value included in the signed-data content type.
    63  
    64  这种外部签名要验签的话,需要先提供**被签名数据**。以下代码片段来自**sign_test.go**中的**testSign**方法:  
    65  ```golang
    66  p7, err := Parse(signed)
    67  if err != nil {
    68  	t.Fatalf("test %s/%s/%s: cannot parse signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    69  }
    70  if testDetach {
    71    // Detached signature should not contain the content
    72    // So we should not be able to find the content in the parsed data
    73    // We should suppliment the content to the parsed data before verifying
    74  	p7.Content = content
    75  }
    76  if !bytes.Equal(content, p7.Content) {
    77      t.Errorf("test %s/%s/%s: content was not found in the parsed data:\n\tExpected: %s\n\tActual: %s", sigalgroot, sigalginter, sigalgsigner, content, p7.Content)
    78  }
    79  if err := p7.VerifyWithChain(truststore); err != nil {
    80  	t.Errorf("test %s/%s/%s: cannot verify signed data: %s", sigalgroot, sigalginter, sigalgsigner, err)
    81  }
    82  ```                    
    83  
    84  #### 验证签名
    85  而验证的话,流程如下:
    86  1. 调用```Parse```方法;
    87  2. 如果是Detach签名数据,则手动设置原始数据(参考```testSign```方法);
    88  3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```);
    89  4. 调用```Verify```或```VerifyWithChain```方法。
    90  
    91  #### 特殊方法
    92  ```DegenerateCertificate```,退化成签名数据中只包含证书,目前没有使用SM2 OID的方法,如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。
    93  
    94  
    95  ### 签名及数字信封数据(Signed and Enveloped Data)
    96  签名和数字信封数据,使用场景较少,有些实现用它来传输私钥(譬如www.gmcert.org)。具体请参考```sign_enveloped_test.go```。
    97  
    98  The "signed and enveloped data" content type is a part of the Cryptographic Message Syntax (CMS), which is used in various Internet Standards. However, it's not recommended for use due to several reasons:
    99  
   100  1. **Complexity**: The "signed and enveloped data" content type combines two operations - signing and enveloping (encryption). This increases the complexity of the implementation and can lead to potential security vulnerabilities if not handled correctly.
   101  
   102  2. **Order of Operations**: The "signed and enveloped data" content type first signs the data and then encrypts it. This means that to verify the signature, the data must first be decrypted. This could potentially expose sensitive data to unauthorized parties before the signature is verified.
   103  
   104  3. **Lack of Flexibility**: Combining signing and enveloping into a single operation reduces flexibility. It's often more useful to be able to perform these operations separately, as it allows for more varied use cases.
   105  
   106  Instead of using the "signed and enveloped data" content type, it's generally recommended to use separate "signed data" and "enveloped data" content types. This allows the operations to be performed in the order that best suits the application's needs, and also simplifies the implementation.
   107  
   108  #### 加密签名流程
   109  1. 调用```NewSignedAndEnvelopedData```或者```NewSMSignedAndEnvelopedData```创建```SignedAndEnvelopedData```数据结构,此过程包含了数据加密过程;
   110  2. 调用```AddSigner```或```AddSignerChain```方法,进行签名;
   111  3. 调用```AddRecipient```方法,用Recipient的公钥加密数据密钥;
   112  4. 最后调用```Finish```方法,序列化输出结果。  
   113  
   114  #### 解密验签流程
   115  1. 调用```Parse```方法;
   116  2. 调用```DecryptAndVerify```或者```DecryptAndVerifyOnlyOne```进行解密和验签。