.NET操作数字证书

加密技术

如果用户 A 要和 B 进行数据交换,A 要通过网络发送一段文字给 B,那如何保证数据在传输的过程中是安全的呢?并且即使被别人截获,也无法知道数据的内容,这就用到加密技术。.

对称密码体制

发送方用一个密钥对数据进行加密,接收方使用相同的密钥进行解密。

• 加密和解密 的密钥相同

• 如何获取或交换密钥,保证密钥的私密性非常重要

• 密钥量级是参与者的平方级数,数量比较多

• 适合对大量数据进行加密,加密解密速度快

• 加解密易于通过硬件实现

非对称密码体制

每个网络参与者都有一对密钥 - 私钥和公钥。用户 A 的公钥是公开的,任何与 A 通信的人都可以获取,用来加密数据后发送给 A。A 的私钥只有自己知道,用来解密数据。

• 公钥用来加密,私钥用来解密。公私钥不相同也不相关

• 公钥的交换无需保密

• 密钥的量级为参与者的数目

• 加解密速度慢,不适合大量数据加密,常用于 对称密码 协商共享密钥

• 加解密操作难以通过硬件实现

数字签名

用户 A 发送给 B,B 如何确定数据是用户 A 发送的,而不是别人伪造的数据呢?数字签名可以鉴别消息的发送者

• 用户 A 先将要发送的数据进行 MD5 计算生成唯一的 消息摘要 a

• A 用私钥签名消息摘要 a

• A 把数据和消息摘要 a 组合起来发送给 B

• B 收到后用 A 的公钥对消息摘要验签得到 a

• B 用 MD5 算法对数据部分进行计算得到消息摘要 b

• B 对 a 和 b 进行比较。如果相同则证明是 A 发送过来的

A 计算数据的消息摘要,并用私钥进行加密的过程称为 签名算法。B 用 A 的公钥解密消息摘要,并与自己计算的消息摘要进行对比的过程称为 验证算法

如果直接对数据本身直接计算数字签名,会比较耗时。所以一般做法是先将原数据进行 Hash 运算,得到的 Hash 值就叫做“摘要”。

数字证书

用户 A 要给用户 B 发送数据,如何保证用户 A 拿到的一定是用户 B 的公钥呢?

数字证书是标志通讯各方身份信息的一串数字,不是数字身份证而是身份认证机构盖在数字身份证上的一个章或印。由权威机构-CA(Certificate Authority)发行的,用来识别对方的身份。

X.509是一种通用的证书规范。

常见的数字证书格式:

• .cer .crt - 用于存放证书,它是二进制形式存放的,不含私钥。

• .pfx .p12 - 存放个人证书/私钥,通常包含保护密码,2 进制方式

从证书文件获得证书对象:

X509Certificate2 cert = new X509Certificate2 (@"c:/myCert.crt" );

// 保护密码
String password = GetCertPassword();
X509Certificate2 cert = new X509Certificate2 (@"c:/myCert.pfx", password);

从本地证书容器获得证书对象:

private static X509Certificate2 GetCertificateFromStore(string certName)
{

    // Get the certificate store for the current user.
    X509Store store = new X509Store(StoreLocation.CurrentUser);
    try
    {
        store.Open(OpenFlags.ReadOnly);

        // Place all certificates in an X509Certificate2Collection object.
        X509Certificate2Collection certCollection = store.Certificates;
        // If using a certificate with a trusted root you do not need to FindByTimeValid, instead:
        // currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
        X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
        X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
        if (signingCert.Count == 0)
            return null;
        // Return the first certificate in the collection, has the right name and is current.
        return signingCert[0];
    }
    finally
    {
        store.Close();
    }
}
// 验证证书有效期
if (cert.NotAfter <= DateTime .Now)
{
    throw new ApplicationException (" 用户证书已经过期!" );
}
// 获取公钥
RSA publickKey = (RSA)cert.PublicKey.Key;
RSA privateKey = cert.GetRSAPrivateKey();
public class RSAHelper
{
    /// RSA加密
    /// </summary>
    /// <param name="xmlPublicKey">公钥</param>
    /// <param name="m_strEncryptString"></param>
    /// <returns></returns>
    public static string RSAEncrypt(string xmlPublicKey, string m_strEncryptString)
    {
        RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
        provider.FromXmlString(xmlPublicKey);
        byte[] bytes = new UnicodeEncoding().GetBytes(m_strEncryptString);
        return Convert.ToBase64String(provider.Encrypt(bytes, false));
    }

    /// <summary>
    /// RSA解密
    /// </summary>
    /// <param name="xmlPrivateKey">私钥</param>
    /// <param name="m_strDecryptString"></param>
    /// <returns></returns>
    public static string RSADecrypt(string xmlPrivateKey, string m_strDecryptString)
    {
        RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
        provider.FromXmlString(xmlPrivateKey);
        byte[] rgb = Convert.FromBase64String(m_strDecryptString);
        byte[] bytes = provider.Decrypt(rgb, false);
        return new UnicodeEncoding().GetString(bytes);
    }

}

X509Certificate2 具体使用方法可以参考 https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.x509certificates.x509certificate2?view=net-6.0