需求
由于公司所从事行业的性质,部门需要一个能对数字的格式、有效性进行检查的工具,特别是需要支持国密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()方法即可。
三、最终效果
开发完成后的程序界面如下: