需求

由于公司所从事行业的性质,部门需要一个能对数字的格式、有效性进行检查的工具,特别是需要支持国密SM2证书。

一、开发环境及相关库的选择

鉴于本菜鸡比较熟悉C#而且在学校也写过SM2加密的程序,因此选用VS 2019进行开发Winform项目。选择C#的原因有三:首先,界面相对友好(不像古老的MFC);其次,C#中自带X509Certificate2类; 最后,C#还有BouncyCastle这一密码学库。介绍完了,开搞!!!

二、遇到的问题及解决方法

1.CRL分发点的获取

这个问题困扰我了很久,由于数字证书是以ASN1编码存储的,直接获取并不能通过ToString()方法转换成可读的字符串,试过多种方法无果后,某天google到相关的代码,才解决此问题。

代码如下:

1
2
3
4
//获取CRL分发点
var crlinfo = cert.Extensions["2.5.29.31"];
var crlDistribitionPoints = new AsnEncodedData(crlinfo.Oid, crlinfo.RawData).Format(true);
listView1.Items[11].SubItems.Add(crlDistribitionPoints);

2.证书签名值的获取

仅通过C#的X509Certificate2类并不能获取到证书的签名值,需要结合BouncyCastle的方法进行获取,而获取证书签名值主要是为了后续验证国密SM2证书的签名。

代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//签名值
if (cert.SignatureAlgorithm.Value == "1.2.156.10197.1.501")
{
    //SM2证书
    string sig_str_tmp = cert2.CertificateStructure.Signature.ToString();
    string sig_r;
    string sig_s;
    if (sig_str_tmp[15] == '0' && sig_str_tmp[16] == '0')
    {
        sig_r = sig_str_tmp.Substring(17, 64);
    }
    else
    {
        sig_r = sig_str_tmp.Substring(15, 64);
    }
    sig_s = sig_str_tmp.Substring(sig_str_tmp.Length - 64);
    //sig_value = sig_r + sig_s;
    listView1.Items[16].SubItems.Add(sig_r + sig_s);
}
else
{
    //国际算法证书
    listView1.Items[16].SubItems.Add(cert2.CertificateStructure.Signature.ToString());
}

3.SM2证书签名值的验证

一个证书的签名值往往是由它上级CA的私钥对其基本证书域进行签名而得来的(自签名证书及顶级根CA除外),由于Windows不识别SM2算法,无法通过构建证书链验证证书,因此需要手动验证。

对于SM2签名验签的原理,此处不再赘述。验签函数具体实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <summary>
/// SM2证书签名验证
/// </summary>
/// <param name="pubkey"></param>
/// <param name="sourceData"></param>
/// <param name="sign"></param>
/// <returns></returns>
public bool Verify(string pubkey, byte[] sourceData,byte[] sign) 
{
    ECPoint userKey;
    X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
    ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve, sm2ECParameters.G, sm2ECParameters.N);
    //公钥
    userKey = sm2ECParameters.Curve.DecodePoint(Hex.Decode(pubkey));
    ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(userKey, domainParameters);

    SM2Signer sm2Signer = new SM2Signer();
    var param = new ParametersWithID(publicKeyParameters, Encoding.Default.GetBytes("1234567812345678"));
    sm2Signer.Init(false, param);
    sm2Signer.BlockUpdate(sourceData, 0, sourceData.Length);
    bool verify = sm2Signer.VerifySignature(sign);
    return verify;
}

调用验签函数后,根据返回值判断是否验签成功;函数的参数分别是公钥(上级CA的)、证书的基本证书域、签名值。

而国际算法(如RSA)数字证书的验证就简单许多,直接调用X509Certificate2类的Verify()方法即可。

三、最终效果

开发完成后的程序界面如下:

数字证书验证工具