mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 20:26:19 +08:00
doc typo
This commit is contained in:
parent
ea592fe3d0
commit
02becc69c5
20
docs/cfca.md
20
docs/cfca.md
@ -38,9 +38,9 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
||||
|
||||
| encryptedType | 输出格式 | 用本软件库如何解密 |
|
||||
| :--- | :--- | :--- |
|
||||
| 0 | ASN.1编码格式 ```EncryptUtil.encrypt``` 方法默认 | 正常解密 |
|
||||
| 0 | ASN.1编码格式 `EncryptUtil.encrypt` 方法默认 | 正常解密 |
|
||||
| 2 | C1C3C2 格式,带0x04这个点非压缩标识 | 正常解密 |
|
||||
| 4 | C1C3C2 格式,不带0x04这个点非压缩标识 (```EncryptUtil.encryptMessageBySM2 / EncryptUtil.encryptFileBySM2``` 方法默认) | 添加0x04前缀后解密 |
|
||||
| 4 | C1C3C2 格式,不带0x04这个点非压缩标识 (`EncryptUtil.encryptMessageBySM2 / EncryptUtil.encryptFileBySM2` 方法默认) | 添加0x04前缀后解密 |
|
||||
| 8 | C1C2C3 格式,带0x04这个点非压缩标识 | 指定解密Opts后解密 |
|
||||
| 16 | C1C2C3 格式,不带0x04这个点非压缩标识 | 添加0x04前缀,同时指定解密Opts后解密 |
|
||||
|
||||
@ -57,8 +57,8 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
||||
### SM2数字信封加解密
|
||||
互操作性问题主要出在:
|
||||
1. 数据对称加密所用密钥的SM2密文格式。
|
||||
2. 对称加密算法的OID。```public static final ASN1ObjectIdentifier id_sm4_CBC = new ASN1ObjectIdentifier("1.2.156.10197.1.104");```。
|
||||
3. 如果需要用本软件库去解密CFCA生成的SM2数字信封,目前会有问题(从**v0.29.3**开始可以解密)。CFCA实现不符合《GB/T 35275-2017:信息安全技术 SM2密码算法加密签名消息语法规范》,它的**RecipientInfo**默认使用SubjectKeyIdentifier而不是IssuerAndSerialNumber。在SADK 3.7.1.0中,需要指定recipientPolicyType=2(0:从证书扩展中获取SubjectKeyID,找不到抛异常;1:根据公钥数据直接计算SubjectKeyID;2:使用证书的IssuerAndSerialNumber)才会使用IssuerAndSerialNumber。正常情况下,只有CA证书才一定会在证书扩展中有SubjectKeyID信息。如果要产生和CFCA一样的加密信封,请使用```pkcs7.EnvelopeMessageCFCA```方法。
|
||||
2. 对称加密算法的OID。`public static final ASN1ObjectIdentifier id_sm4_CBC = new ASN1ObjectIdentifier("1.2.156.10197.1.104");`。
|
||||
3. 如果需要用本软件库去解密CFCA生成的SM2数字信封,目前会有问题(从**v0.29.3**开始可以解密)。CFCA实现不符合《GB/T 35275-2017:信息安全技术 SM2密码算法加密签名消息语法规范》,它的**RecipientInfo**默认使用SubjectKeyIdentifier而不是IssuerAndSerialNumber。在SADK 3.7.1.0中,需要指定recipientPolicyType=2(0:从证书扩展中获取SubjectKeyID,找不到抛异常;1:根据公钥数据直接计算SubjectKeyID;2:使用证书的IssuerAndSerialNumber)才会使用IssuerAndSerialNumber。正常情况下,只有CA证书才一定会在证书扩展中有SubjectKeyID信息。如果要产生和CFCA一样的加密信封,请使用`pkcs7.EnvelopeMessageCFCA`方法。
|
||||
|
||||
**v0.29.6**之后,请直接使用
|
||||
* `cfca.EnvelopeMessage`
|
||||
@ -70,22 +70,22 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
||||
1. 数据对称加密密钥的密文格式为**C1C2C3 格式,不带0x04这个点非压缩标识**。这个不符合《GM/T 0010-2012 SM2密码算法加密签名消息语法规范》以及《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
|
||||
2. SM4-CBC的OID,使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。
|
||||
|
||||
本软件库的```pkcs7.EncryptCFCA```方法```DecryptCFCA```方法提供了SADK 3.2之前版本的信封加解密兼容性,记得cipher参数选择```pkcs.SM4```。但是```pkcs7.EncryptCFCA```方法产生的加密信封依然使用IssuerAndSerialNumber作为RecipientInfo。
|
||||
本软件库的`pkcs7.EncryptCFCA`方法`DecryptCFCA`方法提供了SADK 3.2之前版本的信封加解密兼容性,记得cipher参数选择`pkcs.SM4`。但是`pkcs7.EncryptCFCA`方法产生的加密信封依然使用IssuerAndSerialNumber作为RecipientInfo。
|
||||
|
||||
#### SADK 3.2+版本
|
||||
1. 数据对称加密密钥的密文格式为**ASN.1编码格式**,这个符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
|
||||
2. SM4-CBC的OID,使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。
|
||||
|
||||
本软件库的```pkcs7.EncryptSM```方法```Decrypt```方法提供了SADK 3.2+版本的信封加解密兼容性。使用时,请确保`cipher`参数选择```pkcs.SM4```。```pkcs7.EncryptSM```方法符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》,CFCA的SADK可实现相应数据的解密。
|
||||
本软件库的`pkcs7.EncryptSM`方法`Decrypt`方法提供了SADK 3.2+版本的信封加解密兼容性。使用时,请确保`cipher`参数选择`pkcs.SM4`。`pkcs7.EncryptSM`方法符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》,CFCA的SADK可实现相应数据的解密。
|
||||
|
||||
本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现,也就是recipientPolicyType=0。
|
||||
本软件库的`pkcs7.EnvelopeMessageCFCA`方法提供了CFCA SADK更兼容的实现,也就是recipientPolicyType=0。
|
||||
|
||||
从SADK 的向下兼容性来看,SADK 3.2+能够解密SADK 3.2之前版本的数字信封加密数据,反之不行。
|
||||
|
||||
### SM2 PKCS7签名数据
|
||||
```cfca.sadk.util.p7SignMessageAttach / cfca.sadk.util.p7SignMessageDetach```,对应```pkcs7.SignWithoutAttr```,如果要Detach签名,调用```Finish```之前调用```Detach```就行。
|
||||
`cfca.sadk.util.p7SignMessageAttach / cfca.sadk.util.p7SignMessageDetach`,对应`pkcs7.SignWithoutAttr`,如果要Detach签名,调用`Finish`之前调用`Detach`就行。
|
||||
|
||||
```cfca.sadk.util.p7SignFileAttach / cfca.sadk.util.p7SignFileDetach```类似,只是本软件库不提供对应方法,您可以通过```pkcs7.SignWithoutAttr```自己实现。
|
||||
`cfca.sadk.util.p7SignFileAttach / cfca.sadk.util.p7SignFileDetach`类似,只是本软件库不提供对应方法,您可以通过`pkcs7.SignWithoutAttr`自己实现。
|
||||
|
||||
参考[cfca sadk 3.0.2.0](https://github.com/emmansun/gmsm/issues/260)
|
||||
|
||||
@ -108,4 +108,4 @@ SADK 3.2之后的版本,支持下列SM2密文格式(encryptedType):
|
||||
使用`cfca.ParseEscrowPrivateKey`解析CFCA返回的加密用私钥。
|
||||
|
||||
### SM2私钥、证书的解析
|
||||
这个是CFCA自定义的,未见相关标准,可以通过```cfca.ParseSM2```来解析。```cfca.ParseSM2```函数只接受**DER**编码的二进制数据,如果你的数据是**base64**编码的,请先自行解码。
|
||||
这个是CFCA自定义的,未见相关标准,可以通过`cfca.ParseSM2`来解析。`cfca.ParseSM2`函数只接受**DER**编码的二进制数据,如果你的数据是**base64**编码的,请先自行解码。
|
||||
|
@ -9,9 +9,9 @@
|
||||
|
||||
| 方法 | 适用 | 具体说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| ```DecodeChain``` | 抽取出一个私钥、一个相应证书以及证书链 | 私钥和相应证书必须存在,否则报错 |
|
||||
| ```Decode``` | 抽取出一个私钥、一个相应证书 | 私钥和相应证书必须存在,否则报错;并且**不能有证书链存在**。 |
|
||||
| ```DecodeTrustStore``` | 抽取出证书链 | 只支持java的TrustStore, [Difference Between a Java Keystore and a Truststore](https://www.baeldung.com/java-keystore-truststore-difference) |
|
||||
| `DecodeChain` | 抽取出一个私钥、一个相应证书以及证书链 | 私钥和相应证书必须存在,否则报错 |
|
||||
| `Decode` | 抽取出一个私钥、一个相应证书 | 私钥和相应证书必须存在,否则报错;并且**不能有证书链存在**。 |
|
||||
| `DecodeTrustStore` | 抽取出证书链 | 只支持java的TrustStore, [Difference Between a Java Keystore and a Truststore](https://www.baeldung.com/java-keystore-truststore-difference) |
|
||||
|
||||
### 解码能处理的算法
|
||||
|
||||
@ -58,13 +58,13 @@ PBES1属于老旧遗留算法,目前版本未实现。
|
||||
## PKCS#12的生成
|
||||
目前只支持下列几种,不支持自由定义:
|
||||
|
||||
* ```LegacyRC2```,加密使用PKCS12特有算法;对证书使用RC2加密,对私钥使用3DES加密,一致性保证使用HMAC-SHA1。
|
||||
* ```LegacyDES```,加密使用PKCS12特有算法;对证书和私钥都是用3DES加密,一致性保证使用HMAC-SHA1。
|
||||
* ```Passwordless```,无加密、一致性保证模式。
|
||||
* ```Modern2023```,对应OpenSSL 3+ 默认,加密使用AES-256-CBC with PBKDF2,一致性保证使用HMAC-SHA256。
|
||||
* ```ShangMi2024```,这个估计目前没什么互操作性。
|
||||
* `LegacyRC2`,加密使用PKCS12特有算法;对证书使用RC2加密,对私钥使用3DES加密,一致性保证使用HMAC-SHA1。
|
||||
* `LegacyDES`,加密使用PKCS12特有算法;对证书和私钥都是用3DES加密,一致性保证使用HMAC-SHA1。
|
||||
* `Passwordless`,无加密、一致性保证模式。
|
||||
* `Modern2023`,对应OpenSSL 3+ 默认,加密使用AES-256-CBC with PBKDF2,一致性保证使用HMAC-SHA256。
|
||||
* `ShangMi2024`,这个估计目前没什么互操作性。
|
||||
|
||||
目前的全局函数```Encode``` / ```EncodeTrustStore```使用**LegacyRC2**编码器。
|
||||
目前的全局函数`Encode` / `EncodeTrustStore`使用**LegacyRC2**编码器。
|
||||
|
||||
```go
|
||||
// LegacyRC2 encodes PKCS#12 files using weak algorithms that were
|
||||
@ -180,7 +180,7 @@ var ShangMi2024 = &Encoder{
|
||||
```
|
||||
|
||||
## 解析加密的PKCS#8私钥
|
||||
[go-pkcs12](https://github.com/emmansun/go-pkcs12) 也提供了```ParsePKCS8PrivateKey```方法,相比**pkcs8**的类似方法,这里特别支持**PBES-PKCS12**加密算法。
|
||||
[go-pkcs12](https://github.com/emmansun/go-pkcs12) 也提供了`ParsePKCS8PrivateKey`方法,相比**pkcs8**的类似方法,这里特别支持**PBES-PKCS12**加密算法。
|
||||
* PBE-SHA1-RC2-128
|
||||
* PBE-SHA1-RC2-40
|
||||
* PBE-SHA1-3DES
|
||||
|
@ -22,7 +22,7 @@
|
||||
#### 主要方法
|
||||
(是否国密是指OID也使用国密体系)
|
||||
|
||||
| 是否国密 | 加密 | 解密(先调用```Parse```) |
|
||||
| 是否国密 | 加密 | 解密(先调用`Parse`) |
|
||||
| :--- | :--- | :--- |
|
||||
| 否 | Encrypt | Decrypt |
|
||||
| 否 | EncryptUsingPSK | DecryptUsingPSK |
|
||||
@ -30,12 +30,12 @@
|
||||
| 是 | EncryptCFCA | DecryptCFCA |
|
||||
| 是 | EncryptSMUsingPSK | DecryptUsingPSK |
|
||||
|
||||
关于```EncryptSM / EncryptCFCA```的区别,请参考**CFCA互操作性指南**。
|
||||
关于`EncryptSM / EncryptCFCA`的区别,请参考**CFCA互操作性指南**。
|
||||
带PSK(Pre-shared key)后缀的方法,其对称加密密钥由调用者提供,而非随机生成。
|
||||
|
||||
### 加密数据(Encrypted Data)
|
||||
加密:对应本项目的```pkcs7.EncryptUsingPSK```和```pkcs7.EncryptSMUsingPSK```方法。
|
||||
解密:对应本项目的```pkcs7.DecryptUsingPSK```方法(当然要先调用```pkcs7.Parse```)。
|
||||
加密:对应本项目的`pkcs7.EncryptUsingPSK`和`pkcs7.EncryptSMUsingPSK`方法。
|
||||
解密:对应本项目的`pkcs7.DecryptUsingPSK`方法(当然要先调用`pkcs7.Parse`)。
|
||||
|
||||
### 签名数据(Signed Data)
|
||||
签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
|
||||
@ -46,15 +46,15 @@
|
||||
|
||||
| 是否国密 | 数据是否是哈希值 | 方法 | 默认签名算法 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| 否 | 否 | ```NewSignedData``` | SHA1 |
|
||||
| 否 | 是 | ```NewSignedDataWithDigest``` | SHA1 |
|
||||
| 是 | 否 | ```NewSMSignedData``` | SM3 |
|
||||
| 是 | 是 | ```NewSMSignedDataWithDigest``` | SM3 |
|
||||
| 否 | 否 | `NewSignedData` | SHA1 |
|
||||
| 否 | 是 | `NewSignedDataWithDigest` | SHA1 |
|
||||
| 是 | 否 | `NewSMSignedData` | SM3 |
|
||||
| 是 | 是 | `NewSMSignedDataWithDigest` | SM3 |
|
||||
|
||||
2. 可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密**不需要**修改。
|
||||
3. 接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);
|
||||
4. 如果进行Detach签名,则调用```Detach```方法;
|
||||
5. 最后调用```Finish```方法,序列化输出结果。
|
||||
2. 可选步骤:调用`SetDigestAlgorithm`设置想要的签名算法,通常国密**不需要**修改。
|
||||
3. 接着调用`AddSigner`或`AddSignerChain`方法,进行签名;可以通过`SignerInfoConfig.SkipCertificates`指定忽略证书项(最终签名数据中不包含证书项);
|
||||
4. 如果进行Detach签名,则调用`Detach`方法;
|
||||
5. 最后调用`Finish`方法,序列化输出结果。
|
||||
|
||||
**注意**:
|
||||
1. 如果是直接对哈希值签名,一定是Detach签名。
|
||||
@ -90,17 +90,17 @@ if err := p7.VerifyWithChain(truststore); err != nil {
|
||||
|
||||
#### 验证签名
|
||||
而验证的话,流程如下:
|
||||
1. 调用```Parse```方法;
|
||||
2. 如果是Detach签名数据,则手动设置原始数据(参考```testSign```方法);
|
||||
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```);
|
||||
4. 如果Content是原始数据,调用```Verify```或```VerifyWithChain```方法;如果Content是哈希值,调用```VerifyAsDigest```或```VerifyAsDigestWithChain```方法。
|
||||
1. 调用`Parse`方法;
|
||||
2. 如果是Detach签名数据,则手动设置原始数据(参考`testSign`方法);
|
||||
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考`TestSkipCertificates`);
|
||||
4. 如果Content是原始数据,调用`Verify`或`VerifyWithChain`方法;如果Content是哈希值,调用`VerifyAsDigest`或`VerifyAsDigestWithChain`方法。
|
||||
|
||||
#### 特殊方法
|
||||
```DegenerateCertificate```,退化成签名数据中只包含证书,目前没有使用SM2 OID的方法,如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。
|
||||
`DegenerateCertificate`,退化成签名数据中只包含证书,目前没有使用SM2 OID的方法,如果需要可以请求添加。可以参考`TestDegenerateCertificate`和`TestParseSM2CertificateChain`。
|
||||
|
||||
|
||||
### 签名及数字信封数据(Signed and Enveloped Data)
|
||||
签名和数字信封数据,使用场景较少,有些实现用它来传输私钥(譬如www.gmcert.org)。具体请参考```sign_enveloped_test.go```。
|
||||
签名和数字信封数据,使用场景较少,有些实现用它来传输私钥(譬如www.gmcert.org)。具体请参考`sign_enveloped_test.go`。
|
||||
|
||||
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:
|
||||
|
||||
@ -113,11 +113,11 @@ The "signed and enveloped data" content type is a part of the Cryptographic Mess
|
||||
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.
|
||||
|
||||
#### 加密签名流程
|
||||
1. 调用```NewSignedAndEnvelopedData```或者```NewSMSignedAndEnvelopedData```创建```SignedAndEnvelopedData```数据结构,此过程包含了数据加密过程;
|
||||
2. 调用```AddSigner```或```AddSignerChain```方法,进行签名;
|
||||
3. 调用```AddRecipient```方法,用Recipient的公钥加密数据密钥;
|
||||
4. 最后调用```Finish```方法,序列化输出结果。
|
||||
1. 调用`NewSignedAndEnvelopedData`或者`NewSMSignedAndEnvelopedData`创建`SignedAndEnvelopedData`数据结构,此过程包含了数据加密过程;
|
||||
2. 调用`AddSigner`或`AddSignerChain`方法,进行签名;
|
||||
3. 调用`AddRecipient`方法,用Recipient的公钥加密数据密钥;
|
||||
4. 最后调用`Finish`方法,序列化输出结果。
|
||||
|
||||
#### 解密验签流程
|
||||
1. 调用```Parse```方法;
|
||||
2. 调用```DecryptAndVerify```或者```DecryptAndVerifyOnlyOne```进行解密和验签。
|
||||
1. 调用`Parse`方法;
|
||||
2. 调用`DecryptAndVerify`或者`DecryptAndVerifyOnlyOne`进行解密和验签。
|
||||
|
40
docs/sm2.md
40
docs/sm2.md
@ -34,7 +34,7 @@ ECIES_DH_SHA_1_XOR_HMAC:遵循[SEC 1: Elliptic Curve Cryptography, Version 2.0
|
||||
SM2公私钥对的话,要么是自己产生,要么是别的系统产生后通过某种方式传输给您的。
|
||||
|
||||
### SM2公私钥对的生成
|
||||
您可以通过调用```sm2.GenerateKey```方法产生SM2公私钥对,SM2的私钥通过组合方式扩展了```ecdsa.PrivateKey```,用于定义一些SM2特定的方法:
|
||||
您可以通过调用`sm2.GenerateKey`方法产生SM2公私钥对,SM2的私钥通过组合方式扩展了`ecdsa.PrivateKey`,用于定义一些SM2特定的方法:
|
||||
```go
|
||||
// PrivateKey represents an ECDSA SM2 private key.
|
||||
// It implemented both crypto.Decrypter and crypto.Signer interfaces.
|
||||
@ -43,7 +43,7 @@ type PrivateKey struct {
|
||||
...
|
||||
}
|
||||
```
|
||||
SM2的公钥类型沿用了```ecdsa.PublicKey```结构。注意:Go从v1.20开始,```ecdsa.PublicKey```增加了```func (k *PublicKey) ECDH() (*ecdh.PublicKey, error)```方法,这个方法对SM2的公钥不适用,SM2公钥请使用```func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error)```。
|
||||
SM2的公钥类型沿用了`ecdsa.PublicKey`结构。注意:Go从v1.20开始,`ecdsa.PublicKey`增加了`func (k *PublicKey) ECDH() (*ecdh.PublicKey, error)`方法,这个方法对SM2的公钥不适用,SM2公钥请使用`func PublicKeyToECDH(k *ecdsa.PublicKey) (*ecdh.PublicKey, error)`。
|
||||
|
||||
### SM2公钥的解析、构造
|
||||
通常情况下,公钥是通过PEM编码的文本传输的,您可以通过两步获得公钥:
|
||||
@ -59,7 +59,7 @@ func getPublicKey(pemContent []byte) (any, error) {
|
||||
return smx509.ParsePKIXPublicKey(block.Bytes)
|
||||
}
|
||||
```
|
||||
由于```smx509.ParsePKIXPublicKey```返回any类型,您需要通过```pub, ok := publicKey.(*ecdsa.PublicKey)```转型。
|
||||
由于`smx509.ParsePKIXPublicKey`返回any类型,您需要通过`pub, ok := publicKey.(*ecdsa.PublicKey)`转型。
|
||||
|
||||
有些应用可能会直接存储公钥的曲线点X, Y 坐标值,这时候,您可以通过以下类似方法构造公钥(假设输入的是点的非压缩序列化字节数组):
|
||||
```go
|
||||
@ -73,7 +73,7 @@ func ExampleNewPublicKey() {
|
||||
// Output: 048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1
|
||||
}
|
||||
```
|
||||
当然,您也可以使用ecdh包下的方法```ecdh.P256().NewPublicKey```来构造,目前只支持非压缩方式。
|
||||
当然,您也可以使用ecdh包下的方法`ecdh.P256().NewPublicKey`来构造,目前只支持非压缩方式。
|
||||
|
||||
### SM2私钥的解析、构造
|
||||
私钥的封装格式主要有以下几种,[相关讨论](https://github.com/emmansun/gmsm/issues/104):
|
||||
@ -90,12 +90,12 @@ func ExampleNewPublicKey() {
|
||||
|
||||
| 封装格式 | 解析方法 |
|
||||
| :--- | :--- |
|
||||
| RFC 5915 / SEC1 | ```smx509.ParseSM2PrivateKey``` |
|
||||
| RFC 5915 / SEC1 | `smx509.ParseSM2PrivateKey` |
|
||||
| PKCS#12 | 使用 github.com/emmansun/go-pkcs12 解析 |
|
||||
| PKCS#8 | ```smx509.ParsePKCS8PrivateKey```可以处理未加密的;```pkcs8.ParsePKCS8PrivateKeySM2```可以处理未加密的,也可以处理加密的 |
|
||||
| PKCS#7 | Cryptographic Message Syntax, 可以参考github.com/emmansun/pkcs7/sign_enveloped_test.go中的```TestParseSignedEvnvelopedData```,测试数据来自 https://www.gmcert.org/ |
|
||||
| CFCA自定义封装 | 顾名思义,这个封装是CFCA特定的,修改自PKCS#12,使用```cfca.ParseSM2```方法来解析 |
|
||||
|《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》| 这个规范还比较新,使用```sm2.ParseEnvelopedPrivateKey```解析。典型的应用场景是CA机构返回CSRResponse, 里面包含签名证书、CA生成的SM2加密私钥以及相应的SM2加密证书,其中SM2加密私钥就用该规范定义的方式加密封装。请参考《GM/T 0092-2020 基于SM2算法的证书申请语法规范》 |
|
||||
| PKCS#8 | `smx509.ParsePKCS8PrivateKey`可以处理未加密的;`pkcs8.ParsePKCS8PrivateKeySM2`可以处理未加密的,也可以处理加密的 |
|
||||
| PKCS#7 | Cryptographic Message Syntax, 可以参考github.com/emmansun/pkcs7/sign_enveloped_test.go中的`TestParseSignedEvnvelopedData`,测试数据来自 https://www.gmcert.org/ |
|
||||
| CFCA自定义封装 | 顾名思义,这个封装是CFCA特定的,修改自PKCS#12,使用`cfca.ParseSM2`方法来解析 |
|
||||
|《GB/T 35276-2017 信息安全技术 SM2密码算法使用规范》| 这个规范还比较新,使用`sm2.ParseEnvelopedPrivateKey`解析。典型的应用场景是CA机构返回CSRResponse, 里面包含签名证书、CA生成的SM2加密私钥以及相应的SM2加密证书,其中SM2加密私钥就用该规范定义的方式加密封装。请参考《GM/T 0092-2020 基于SM2算法的证书申请语法规范》 |
|
||||
|
||||
有些系统可能会直接存储、得到私钥的字节数组,那么您可以使用如下方法来构造私钥:
|
||||
```go
|
||||
@ -119,7 +119,7 @@ func ExampleNewPrivateKeyFromInt() {
|
||||
// Output: 123456
|
||||
}
|
||||
```
|
||||
当然,你也可以使用ecdh包的方法```ecdh.P256().NewPrivateKey```来构造私钥,您要确保输入的字节数组是256位(32字节)的,如果不是,请先自行处理。
|
||||
当然,你也可以使用ecdh包的方法`ecdh.P256().NewPrivateKey`来构造私钥,您要确保输入的字节数组是256位(32字节)的,如果不是,请先自行处理。
|
||||
|
||||
### 关于《GM/T 0091-2020 基于口令的密钥派生规范》
|
||||
这个规范就是[RFC8018 PKCS#5](https://datatracker.ietf.org/doc/html/rfc8018) 国密定制版,其中PBES/PBKDF/PBMAC使用了不同的OID,但是这些OID似乎没有注册过。而且表A.1 中**id-hmacWithSM3**的OID为没有注册过的**1.2.156.10197.1.401.3.1**,和我们常用的**1.2.156.10197.1.401.2**不一致,也与该文档本身附录C不一致。不知道哪个产品遵从了这个行业规范。
|
||||
@ -138,7 +138,7 @@ func ExampleNewPrivateKeyFromInt() {
|
||||
4. 附录 **C. ASN.1 结构定义**,**id-hmacWithSM3**的OID又是**1.2.156.10197.1.401.2**。
|
||||
|
||||
## 数字签名算法
|
||||
您可以直接使用sm2私钥的签名方法```Sign```:
|
||||
您可以直接使用sm2私钥的签名方法`Sign`:
|
||||
```go
|
||||
// This is a reference method to force SM2 standard with SDK [crypto.Signer].
|
||||
func ExamplePrivateKey_Sign_forceSM2() {
|
||||
@ -162,11 +162,11 @@ func ExamplePrivateKey_Sign_forceSM2() {
|
||||
fmt.Printf("%x\n", sig)
|
||||
}
|
||||
```
|
||||
我们通过```SignerOpts```参数来指示```toSign```已经是hash值,还是需要进行处理的原始信息。通常情况下,```toSign```传入原始信息、```SignerOpts```传入```sm2.DefaultSM2SignerOpts```。如果将来标准支持自定义的uid,那么您可以通过调用```sm2.NewSM2SignerOption```来构造一个自定义的```SignerOpts```。
|
||||
我们通过`SignerOpts`参数来指示`toSign`已经是hash值,还是需要进行处理的原始信息。通常情况下,`toSign`传入原始信息、`SignerOpts`传入`sm2.DefaultSM2SignerOpts`。如果将来标准支持自定义的uid,那么您可以通过调用`sm2.NewSM2SignerOption`来构造一个自定义的`SignerOpts`。
|
||||
|
||||
当然,您也可以通过调用SM2私钥的```SignWithSM2```方法,区别在于,```Sign```方法是```crypto.Singer```接口中定义的方法,而```SignWithSM2```方法是```sm2.Signer```接口中定义的方法。
|
||||
当然,您也可以通过调用SM2私钥的`SignWithSM2`方法,区别在于,`Sign`方法是`crypto.Singer`接口中定义的方法,而`SignWithSM2`方法是`sm2.Signer`接口中定义的方法。
|
||||
|
||||
您可以使用```sm2.VerifyASN1WithSM2```来校验SM2签名:
|
||||
您可以使用`sm2.VerifyASN1WithSM2`来校验SM2签名:
|
||||
```go
|
||||
func ExampleVerifyASN1WithSM2() {
|
||||
// real public key should be from cert or public key pem file
|
||||
@ -188,7 +188,7 @@ func ExampleVerifyASN1WithSM2() {
|
||||
|
||||
### 如何处理不用UID的签名、验签?
|
||||
#### 签名
|
||||
也是使用sm2私钥的`Sign`方法,只是```SignerOpts```传入`nil`或者其它非`SM2SignerOption`即可,那么,你自己负责预先计算杂凑值,当然如何计算杂凑值,由你自己说了算了。
|
||||
也是使用sm2私钥的`Sign`方法,只是`SignerOpts`传入`nil`或者其它非`SM2SignerOption`即可,那么,你自己负责预先计算杂凑值,当然如何计算杂凑值,由你自己说了算了。
|
||||
|
||||
#### 验签
|
||||
调用`sm2.VerifyASN1`方法,同样,你自己负责预先计算杂凑值,确保杂凑算法和签名时使用的杂凑算法保持一致。
|
||||
@ -232,9 +232,9 @@ func ExampleEncryptASN1() {
|
||||
fmt.Printf("Ciphertext: %x\n", ciphertext)
|
||||
}
|
||||
```
|
||||
如果您需要普通拼接编码输出,您可以调用```sm2.Encrypt```方法,其中```EncrypterOpts```类型参数可以传入nil,表示默认C1C3C2。
|
||||
如果您需要普通拼接编码输出,您可以调用`sm2.Encrypt`方法,其中`EncrypterOpts`类型参数可以传入nil,表示默认C1C3C2。
|
||||
|
||||
sm2包也提供了辅助方法用于密文输出编码格式转换:您可以通过```sm2.ASN1Ciphertext2Plain```方法把ASN.1密文转换为简单拼接输出;反过来,您也可以通过```sm2.PlainCiphertext2ASN1```将简单拼接密文输出转换为ASN.1密文。你还可以通过```sm2.AdjustCiphertextSplicingOrder```方法来改变串接顺序。
|
||||
sm2包也提供了辅助方法用于密文输出编码格式转换:您可以通过`sm2.ASN1Ciphertext2Plain`方法把ASN.1密文转换为简单拼接输出;反过来,您也可以通过`sm2.PlainCiphertext2ASN1`将简单拼接密文输出转换为ASN.1密文。你还可以通过`sm2.AdjustCiphertextSplicingOrder`方法来改变串接顺序。
|
||||
|
||||
SM2公钥加密算法解密示例:
|
||||
```go
|
||||
@ -258,7 +258,7 @@ func ExamplePrivateKey_Decrypt() {
|
||||
// Output: Plaintext: send reinforcements, we're going to advance
|
||||
}
|
||||
```
|
||||
这个SM2私钥的解密方法```Decrypt```,通常情况下,对```crypto.DecrypterOpts```类型参数,您只需传入nil,系统会自己检测输入密文是ASN.1还是普通拼接,但是,如果密文是老旧的C1||C2||C3拼接,请传入相应的```crypto.DecrypterOpts```类型参数,或者您可以先通过上面介绍的辅助函数转换一下。
|
||||
这个SM2私钥的解密方法`Decrypt`,通常情况下,对`crypto.DecrypterOpts`类型参数,您只需传入nil,系统会自己检测输入密文是ASN.1还是普通拼接,但是,如果密文是老旧的C1||C2||C3拼接,请传入相应的`crypto.DecrypterOpts`类型参数,或者您可以先通过上面介绍的辅助函数转换一下。
|
||||
|
||||
具体API文档请参考:[API Document](https://godoc.org/github.com/emmansun/gmsm)
|
||||
|
||||
@ -298,7 +298,7 @@ So, while omitting the form indicator might simplify the implementation or provi
|
||||
从**v0.27.0**开始,对大数据量的加解密做了优化处理,尤其是KDF并行计算。详情请参考[SM2加解密性能](https://github.com/emmansun/gmsm/wiki/SM2%E5%8A%A0%E8%A7%A3%E5%AF%86%E6%80%A7%E8%83%BD)。
|
||||
|
||||
## 与KMS集成
|
||||
国内云服务商的KMS服务大都提供SM2密钥,我们一般调用其API进行签名和解密,而验签和加密操作,一般在本地用公钥即可完成。不过需要注意的是,KMS提供的签名通常需要您在本地进行hash操作,而sm2签名的hash又比较特殊,下面示例供参考(自版本**v0.24.0**开始,您可以直接使用函数```sm2.CalculateSM2Hash```):
|
||||
国内云服务商的KMS服务大都提供SM2密钥,我们一般调用其API进行签名和解密,而验签和加密操作,一般在本地用公钥即可完成。不过需要注意的是,KMS提供的签名通常需要您在本地进行hash操作,而sm2签名的hash又比较特殊,下面示例供参考(自版本**v0.24.0**开始,您可以直接使用函数`sm2.CalculateSM2Hash`):
|
||||
```go
|
||||
func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
|
||||
if len(uid) == 0 {
|
||||
@ -332,7 +332,7 @@ func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
|
||||
**注意**:
|
||||
1. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`方法通常用于对哈希值作签名,最好遵从以下实现逻辑:检查`opts`是否是`*sm2.SM2SignerOption`类型,如果是,则把传入的`digest`作为原始数据进行处理,具体实现可以参考`sm2.SignASN1`函数。当然,在大多数情况下,直接将数据视为原始数据是可行的。实施者可以根据具体的应用场景灵活处理。
|
||||
2. 如果密码硬件有自己的随机数源,可以忽略传入的`rand`。
|
||||
3. 很多设备签名函数通常只接收哈希值,需要调用```sm2.CalculateSM2Hash```或者**SDF**提供的哈希函数计算哈希值。
|
||||
3. 很多设备签名函数通常只接收哈希值,需要调用`sm2.CalculateSM2Hash`或者**SDF**提供的哈希函数计算哈希值。
|
||||
|
||||
SDF API请参考《GB/T 36322-2018 密码设备应用接口规范》
|
||||
|
||||
|
28
docs/sm4.md
28
docs/sm4.md
@ -6,15 +6,15 @@
|
||||
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
|
||||
|
||||
## 概述
|
||||
SM4分组密码算法,其地位类似NIST中的AES分组密码算法,密钥长度128位(16字节),分组大小也是128位(16字节)。在本软件库中,SM4的实现与Go语言中的AES实现一致,也实现了```cipher.Block```接口,所以,所有Go语言中实现的工作模式(CBC/GCM/CFB/OFB/CTR),都能与SM4组合使用。
|
||||
SM4分组密码算法,其地位类似NIST中的AES分组密码算法,密钥长度128位(16字节),分组大小也是128位(16字节)。在本软件库中,SM4的实现与Go语言中的AES实现一致,也实现了`cipher.Block`接口,所以,所有Go语言中实现的工作模式(CBC/GCM/CFB/OFB/CTR),都能与SM4组合使用。
|
||||
|
||||
## [工作模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation)
|
||||
Go语言实现的工作模式,主要有三类:
|
||||
* 基于分组的工作模式 ```cipher.BlockMode```,譬如CBC。
|
||||
* 带有关联数据的认证加密工作模式```cipher.AEAD```,譬如GCM。
|
||||
* 流加密工作模式```cipher.Stream```,譬如CTR、CFB、OFB。
|
||||
* 基于分组的工作模式 `cipher.BlockMode`,譬如CBC。
|
||||
* 带有关联数据的认证加密工作模式`cipher.AEAD`,譬如GCM。
|
||||
* 流加密工作模式`cipher.Stream`,譬如CTR、CFB、OFB。
|
||||
|
||||
在实际加解密操作中,我们一般不会直接使用```cipher.Block```,必须结合分组密码算法的工作模式使用。除了Go语言自带的工作模式(CBC/GCM/CFB/OFB/CTR),本软件库也实现了下列工作模式:
|
||||
在实际加解密操作中,我们一般不会直接使用`cipher.Block`,必须结合分组密码算法的工作模式使用。除了Go语言自带的工作模式(CBC/GCM/CFB/OFB/CTR),本软件库也实现了下列工作模式:
|
||||
* ECB - 电码本模式
|
||||
* BC - 分组链接模式
|
||||
* HCTR - 带泛杂凑函数的计数器模式
|
||||
@ -34,14 +34,14 @@ Go语言实现的工作模式,主要有三类:
|
||||
|
||||
|
||||
## 填充(padding)
|
||||
有些分组密码算法的工作模式(譬如实现了```cipher.BlockMode```接口的模式)的输入要求是其长度必须是分组大小的整数倍。《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》附录C中列出了以下几种填充模式:
|
||||
* 填充方式 1,对应本软件库的```padding.NewPKCS7Padding```
|
||||
* 填充方式 2,对应本软件库的```padding.NewISO9797M2Padding```
|
||||
有些分组密码算法的工作模式(譬如实现了`cipher.BlockMode`接口的模式)的输入要求是其长度必须是分组大小的整数倍。《GB/T 17964-2021 信息安全技术 分组密码算法的工作模式》附录C中列出了以下几种填充模式:
|
||||
* 填充方式 1,对应本软件库的`padding.NewPKCS7Padding`
|
||||
* 填充方式 2,对应本软件库的`padding.NewISO9797M2Padding`
|
||||
* 填充方式 3,目前没有实现,它对应ISO/IEC_9797-1 padding method 3
|
||||
|
||||
本软件库也实现了ANSI X9.23标准中定义的填充方式```padding.NewANSIX923Padding```,**用的最广的还是填充方式 1:PKCS7填充**。
|
||||
本软件库也实现了ANSI X9.23标准中定义的填充方式`padding.NewANSIX923Padding`,**用的最广的还是填充方式 1:PKCS7填充**。
|
||||
|
||||
您如果使用实现了```cipher.BlockMode```接口的分组加密工作模式,那您也必须与相关方协调好填充模式。JAVA库的对称加密算法字符串名就包含了所有信息,譬如**AES/CBC/PKCS7Padding**。
|
||||
您如果使用实现了`cipher.BlockMode`接口的分组加密工作模式,那您也必须与相关方协调好填充模式。JAVA库的对称加密算法字符串名就包含了所有信息,譬如**AES/CBC/PKCS7Padding**。
|
||||
|
||||
## 密文及其相关参数的传输和存储
|
||||
如果是自描述的,那肯定有相关标准,定义相关ASN.1结构,并且给分组密码算法、工作模式、填充方式都赋予一个OID。或者如hashicorp vault,一个对称密钥确定了分组密码算法、工作模式、填充方式,最终输出密文是密钥ID和原始密文的组合。
|
||||
@ -195,9 +195,9 @@ func Example_decryptCBC() {
|
||||
}
|
||||
```
|
||||
|
||||
需要注意一下,```cipher.AEAD```对```dst```参数的要求:
|
||||
需要注意一下,`cipher.AEAD`对`dst`参数的要求:
|
||||
|
||||
```cipher.AEAD```是**追加**结果,所以如果要重用切片,要注意一下。而且```Seal```的结果要比plaintext长(加上tag),所以只有```cap(plaintext)>=len(plaintext)+tagSize```时才会重用,否则还是会新建一个切片。
|
||||
`cipher.AEAD`是**追加**结果,所以如果要重用切片,要注意一下。而且`Seal`的结果要比plaintext长(加上tag),所以只有`cap(plaintext)>=len(plaintext)+tagSize`时才会重用,否则还是会新建一个切片。
|
||||
```go
|
||||
// AEAD is a cipher mode providing authenticated encryption with associated
|
||||
// data. For a description of the methodology, see
|
||||
@ -234,7 +234,7 @@ type AEAD interface {
|
||||
Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)
|
||||
}
|
||||
```
|
||||
而```cipher.BlockMode```和```cipher.Stream```的话,则是直接覆盖。
|
||||
而`cipher.BlockMode`和`cipher.Stream`的话,则是直接覆盖。
|
||||
|
||||
## 性能
|
||||
SM4分组密码算法的软件高效实现,不算CPU指令支持的话,已知有如下几种方法:
|
||||
@ -247,7 +247,7 @@ SM4分组密码算法的软件高效实现,不算CPU指令支持的话,已
|
||||
当然,这些与有CPU指令支持的AES算法相比,性能差距依然偏大,要是工作模式不支持并行,差距就更巨大了。
|
||||
|
||||
### 混合方式
|
||||
从**v0.25.0**开始,AMD64/ARM64 支持AES-NI的CPU架构下,**默认会使用混合方式**,即```cipher.Block```的方法会用纯Go语言实现,而对于可以并行的加解密模式,则还是会尽量采用AES-NI和SIMD并行处理。您可以通过环境变量```FORCE_SM4BLOCK_AESNI=1```来强制都使用AES-NI实现(和v0.25.0之前版本的行为一样)。请参考[SM4: 单block的性能问题](https://github.com/emmansun/gmsm/discussions/172)。
|
||||
从**v0.25.0**开始,AMD64/ARM64 支持AES-NI的CPU架构下,**默认会使用混合方式**,即`cipher.Block`的方法会用纯Go语言实现,而对于可以并行的加解密模式,则还是会尽量采用AES-NI和SIMD并行处理。您可以通过环境变量`FORCE_SM4BLOCK_AESNI=1`来强制都使用AES-NI实现(和v0.25.0之前版本的行为一样)。请参考[SM4: 单block的性能问题](https://github.com/emmansun/gmsm/discussions/172)。
|
||||
|
||||
**注意**:目前的纯Golang SM4实现(查表实现)是以可变时间运行的!
|
||||
|
||||
|
10
docs/sm9.md
10
docs/sm9.md
@ -22,17 +22,17 @@ SM9算法是一种基于双线性对的标识密码算法(简称“IBC”)
|
||||
|
||||
## 主公私钥对
|
||||
SM9标识密码算法用于签名和加密的主公私钥对是分开的,需要各自独立生成:
|
||||
* ```sm9.GenerateSignMasterKey```用于生成签名主密钥对。
|
||||
* ```sm9.GenerateEncryptMasterKey```用于生成加密主密钥对。
|
||||
* `sm9.GenerateSignMasterKey`用于生成签名主密钥对。
|
||||
* `sm9.GenerateEncryptMasterKey`用于生成加密主密钥对。
|
||||
|
||||
其中签名主公钥是G2上的点,加密主公钥是G1上的点,而签名、加密主私钥都是一个随机大整数。
|
||||
|
||||
主公私钥的ASN.1数据格式定义请参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》,和椭圆曲线的公私钥ASN.1数据格式类似。本软件实现了相应的Marshal/Unmarshal方法。
|
||||
|
||||
## 用户私钥
|
||||
用户的签名私钥由签名主私钥、用户标识生成:```(master *SignMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*SignPrivateKey, error)```,它是G1上的点。
|
||||
用户的签名私钥由签名主私钥、用户标识生成:`(master *SignMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*SignPrivateKey, error)`,它是G1上的点。
|
||||
|
||||
用户的加密私钥由加密主私钥、用户标识生成:```func (master *EncryptMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*EncryptPrivateKey, error)```,它是G2上的点。
|
||||
用户的加密私钥由加密主私钥、用户标识生成:`func (master *EncryptMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*EncryptPrivateKey, error)`,它是G2上的点。
|
||||
|
||||
《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》中 hid 定义如下:
|
||||
* hid = 1,签名
|
||||
@ -42,7 +42,7 @@ SM9标识密码算法用于签名和加密的主公私钥对是分开的,需
|
||||
|
||||
用户签名、加密私钥的ASN.1数据格式定义请参考《GB/T 41389-2022 信息安全技术 SM9密码算法使用规范》,和椭圆曲线点的ASN.1数据格式类似。本软件实现了相应的Marshal/Unmarshal方法。
|
||||
|
||||
目前```smx509```中实现的```MarshalPKCS8PrivateKey/ParsePKCS8PrivateKey```没有相关标准,只是为了和[gmssl](https://github.com/guanzhi/GmSSL)互操作验证,请参考[sm9:【feature】是否考虑支持 pem 格式的公私钥输出](https://github.com/emmansun/gmsm/issues/86)。
|
||||
目前`smx509`中实现的`MarshalPKCS8PrivateKey/ParsePKCS8PrivateKey`没有相关标准,只是为了和[gmssl](https://github.com/guanzhi/GmSSL)互操作验证,请参考[sm9:【feature】是否考虑支持 pem 格式的公私钥输出](https://github.com/emmansun/gmsm/issues/86)。
|
||||
```go
|
||||
func TestMarshalPKCS8SM9SignPrivateKey(t *testing.T) {
|
||||
masterKey, err := sm9.GenerateSignMasterKey(rand.Reader)
|
||||
|
@ -9,7 +9,7 @@
|
||||
您可以从[国家标准全文公开系统](https://openstd.samr.gov.cn/)在线阅读这些标准。
|
||||
|
||||
## 保密性算法
|
||||
保密性算法EEA实现了```cipher.Stream```接口,所以和其它流密码算法使用类似,只是创建方法不同而已。
|
||||
保密性算法EEA实现了`cipher.Stream`接口,所以和其它流密码算法使用类似,只是创建方法不同而已。
|
||||
|
||||
| | ZUC-128 | ZUC-256 |
|
||||
| :--- | :--- | :--- |
|
||||
@ -101,7 +101,7 @@ func ExampleNewCipher_zuc256() {
|
||||
```
|
||||
|
||||
## 完整性算法
|
||||
完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。
|
||||
完整性算法实现了`hash.Hash`接口,所以其使用方法和其它哈希算法类似。
|
||||
|
||||
| | ZUC-128 | ZUC-256 |
|
||||
| :--- | :--- | :--- |
|
||||
@ -187,7 +187,7 @@ func ExampleNewHash256_tagSize16() {
|
||||
}
|
||||
```
|
||||
|
||||
要支持位为单位的话,可以调用```Finish```方法。
|
||||
要支持位为单位的话,可以调用`Finish`方法。
|
||||
```go
|
||||
func ExampleZUC128Mac_Finish() {
|
||||
key := make([]byte, 16)
|
||||
|
Loading…
x
Reference in New Issue
Block a user