一、问题表现与根本原因

1.1  典型错误表现

国密CSR生成时,"Signature  Algorithm"字段错误通常表现为以下几种情况:

  签名算法OID不正确:CSR中显示的签名算法为  `1.2.840.10045.4.3.2`(SHA256withECDSA)或RSA相关OID,而非国密标准的  `1.2.156.10197.1.501`

  公钥算法OID错误:SubjectPublicKeyInfo中算法为  `1.2.840.10045.2.1`(id-ecPublicKey),而非国密的  `1.2.156.10197.1.301`

  Unknown  named  curve  错误:系统无法识别SM2曲线OID(`1.2.156.10197.1.301`)

  CA校验失败:CSR提交给国密CA时因签名算法不匹配被拒绝

1.2  根本原因

国密SM2算法使用的OID体系与标准RSA/ECDSA完全不同,而多数开发工具和语言标准库默认不识别国密OID:

字段      标准RSA/ECDSA  OID      国密SM2  OID      说明  

签名算法      1.2.840.113549.1.1.11  (RSA)  /  1.2.840.10045.4.3.2  (ECDSA)          1.2.156.10197.1.501            SM2  with  SM3  

公钥算法      1.2.840.10045.2.1  (id-ecPublicKey)            1.2.156.10197.1.301            SM2公钥  

曲线OID      1.2.840.10045.3.1.7  (P-256)          1.2.156.10197.1.301        SM2曲线  

核心问题:OpenSSL  1.1.1及部分版本默认不内置SM2曲线OID映射表,导致生成CSR时使用了错误的OID。

二、通用解决方案

2.1  必须使用支持国密的工具

OpenSSL官方版本不支持国密算法,直接用  `openssl`  命令生成国密证书会失败。必须使用以下替代方案:

工具    说明  

GmSSL            国密专用开源工具,支持SM2/SM3/SM4  

铜锁/Tongsuo          阿里等维护的国密增强OpenSSL分支  

openHiTLS            华为等国密适配版本  

商业信创发行版          华为OpenEuler的openssl-gm包等  

2.2  GmSSL生成CSR的正确命令

使用GmSSL生成国密CSR时,必须指定  `-sm3`  签名算法和  `-sigopt`  参数:

```bash

#  1.  生成SM2私钥

gmssl  ecparam  -genkey  -name  sm2p256v1  -out  sm2_private.key


#  2.  生成CSR(必须指定-sm3)

gmssl  req  -new  -key  sm2_private.key  -out  sm2.csr  -sm3  -sigopt  "distid:1234567812345678"

```

2.3  手动修正已生成的CSR

如果已经生成了错误的CSR,需要重写TBSCertificate中的算法字段:

  重写  SubjectPublicKeyInfo.algorithm  为  SM2  公钥OID(`1.2.156.10197.1.301`)

  同步更新签名算法OID  为  `1.2.156.10197.1.501`

三、各开发环境具体解决方案

3.1  Go语言

Go标准库  `crypto/x509`  默认不识别国密OID,需要**手动注册签名算法**:

```go

import  "crypto/x509"

import  "encoding/asn1"


func  init()  {

        //  注册SM2签名算法OID

        x509.RegisterSignatureAlgorithm(

                x509.SM2WithSM3,    //  自定义常量

                x509.SignatureAlgorithm(0),

                asn1.ObjectIdentifier{1,  2,  156,  10197,  1,  501},  //  SM2  OID

        )

}

```

解析证书时还需注意:

  Certificate.PublicKey  应为  `*sm2.PublicKey`  类型,而非  `*ecdsa.PublicKey`

  需自定义  `UnmarshalPKIXPublicKey`  解析SM2公钥

3.2  Java  /  Bouncy  Castle

Java标准库同样不识别SM2  OID,必须**显式注册Bouncy  Castle  Provider**:

```java

import  org.bouncycastle.jce.provider.BouncyCastleProvider;

import  java.security.Security;

static  {

        if  (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)  ==  null)  {

                Security.addProvider(new  BouncyCastleProvider());

        }

}

```

获取签名实例时使用正确的算法名称:`"SM3withSM2"`

3.3  OpenSSL  API(C语言)

使用OpenSSL  API生成SM2  CSR时,需注意以下要点:

```c

//  创建SM2密钥上下文

pkctx  =  EVP_PKEY_CTX_new_id(EVP_PKEY_SM2,  NULL);

EVP_PKEY_keygen_init(pkctx);

EVP_PKEY_generate(pkctx,  &pkey);

//  设置用户ID(SM2标准要求)

EVP_PKEY_CTX_set1_id(pkctx,  SM2_DEFAULT_USER_ID,  SM2_DEFAULT_USER_ID_LEN);

//  使用SM3签名

X509_REQ_sign(x509Req,  pkey,  EVP_sm3());

```

四、验证方法

生成CSR后,使用以下命令验证Signature  Algorithm字段是否正确:

```bash

#  查看CSR详情,确认Signature  Algorithm为  1.2.156.10197.1.501

gmssl  req  -in  sm2.csr  -noout  -text

#  或使用openssl查看

openssl  req  -in  sm2.csr  -noout  -text

```

正确输出应显示:

```

Signature  Algorithm:  sm2sign-with-sm3  (1.2.156.10197.1.501)

```

五、总结与最佳实践

1.  工具选择:使用GmSSL、铜锁/Tongsuo等支持国密的工具,而非原生OpenSSL

2.  OID精确性:确保签名算法OID为  `1.2.156.10197.1.501`,公钥算法OID为  `1.2.156.10197.1.301`

3.  Provider注册:Java环境必须注册Bouncy  Castle  Provider

4.  Go环境扩展:Go需手动注册签名算法OID

5.  用户ID设置:SM2签名时需传入正确的用户ID(默认  `1234567812345678`)

6.  证书链一致性:证书外层的  `signatureAlgorithm`  与  `TBSCertificate.signature`  必须保持一致