Fork me on GitHub
C#.NET CORE .NET6 RSA 私钥签名 公钥验签

C#.NET CORE .NET6 RSA 私钥签名 公钥验签

C#.NET CORE .NET6 RSA 私钥签名 公钥验签(验证签名) ver:20230614

 

环境说明:

.NET CORE 版本:.NET 6 。

 

.NET CORE 对于RSA的支持:

1. .NET 6 中内置了对 PKCS1,PKCS8 2种私钥格式的支持。

2.私钥字符串要去除"-----BEGIN RSA PRIVATE KEY-----"、"-----END RSA PRIVATE KEY-----"、"-----BEGIN PRIVATE KEY-----"、"-----END PRIVATE KEY-----"和换行符。

3.公钥要去除:"-----BEGIN PUBLIC KEY-----"、"-----END PUBLIC KEY-----"和换行符。

 

注意:.NET CORE 加载公钥为 rsaPub.ImportSubjectPublicKeyInfo。

 

核心重点是拿到 .NET CORE 的RSA  对象。

 

签名(双方要协商好编码):

1.将原始报文用UTF8转为byte数组。(参数为byte[] data) 原始报文的编码双方要协商好。

2.调用 SignData(byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) 方法,其中HashAlgorithmName 是HASH算法,双方要协商好,有:SHA256、SHA1、MD5。SHA256对应JAVA的 SHA256withRSA. (其它还有 SHA1withRSA,MD5withRSA),RSASignaturePadding 一般是Pkcs1.

3.将输出结果转为BASE64字符串。输出结果转字符串的编码,双方要协商好,不一定用BASE64。

 

公钥验签:

1.将原始报文用UTF8转为byte数组。(参数为byte[] data)

2.将签名串(BASE64字符串)用Convert.FromBase64String()转为byte数组。(参数为:byte[] signature)

3.使用  VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) ; 方法验签。其中HashAlgorithmName 是HASH算法,有:SHA256、SHA1、MD5。RSASignaturePadding 一般是Pkcs1.

 

用“支付宝开放平台开发助手”生成一组公私钥:

PKCS8私钥:

MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAMz0Czg6QUtTISa2pUkloeQB/TEpHdqrfyroWpKLW9B/LWFSOGH9nyTk1pPZaeadyEZQ6gay/C0pUAetLraq9bMA/Luxq68b87uG7WX7dKytEO2/87qGpGMRs97H+GlkzWil2QO2KK4cHnAcVicPsmi5aZ72U0BWJFyPhtd+qdmrAgMBAAECgYEAvW67iAbgHt0BASVD9C3iSjpEaVHVlC165o/IVzaTcEx8Bz3Ve0zN8W3JnvIO3ebsG4HiLLr2Nk++9rltOc0eNeGMv7F1e/OFot1wN0ON6s1g4bYh1z5Uz8FcYiMWcqHHICrx+oSFeK9x+I2Zge7enQXcsVnqEhm77ZE5YczSryECQQD9nB58e5efYchF+cYbmURioX18cUMuhQbB9Aq2N55cd689Lg35KZqT8JQTp/8tQSdCJG8d2nU8VKspUKTEAuaDAkEAzuKIIoc9PVJvy90LhIPA9c1S8BPCI7EMCaTZqJ5o3VaR2dqvUZDGX7kL3kYkQ+n7mq3KIECvkEFzA+FOP96XuQJBAJQTKHW0T/YeSKoayUHp/lS8R6F2HCy4PRbXn71+wfbpZqcJEd2OHhQM3tiPOV258esbjMlYeSUNppZL4LgVnXMCQQC7Lvs9Ql+GPDAqo7ToEM1lmICR906QPIBHuX+1sJ3wpYMROWumwPa7ZRH36j6ls+6R5OwcgmpWeuE1gYTrBNsBAkEAn2pEtAljX1foQff6CLozYg/J6J9RmVFcJ6qz0LX3052qNFBQYw8CMHB7VkVNzsDIDC8LX5uP2pzTrdPLew+pPA==

与之匹配的 PKCS1 私钥,用助手转换的:

MIICXwIBAAKBgQDM9As4OkFLUyEmtqVJJaHkAf0xKR3aq38q6FqSi1vQfy1hUjhh/Z8k5NaT2WnmnchGUOoGsvwtKVAHrS62qvWzAPy7sauvG/O7hu1l+3SsrRDtv/O6hqRjEbPex/hpZM1opdkDtiiuHB5wHFYnD7JouWme9lNAViRcj4bXfqnZqwIDAQABAoGBAL1uu4gG4B7dAQElQ/Qt4ko6RGlR1ZQteuaPyFc2k3BMfAc91XtMzfFtyZ7yDt3m7BuB4iy69jZPvva5bTnNHjXhjL+xdXvzhaLdcDdDjerNYOG2Idc+VM/BXGIjFnKhxyAq8fqEhXivcfiNmYHu3p0F3LFZ6hIZu+2ROWHM0q8hAkEA/ZwefHuXn2HIRfnGG5lEYqF9fHFDLoUGwfQKtjeeXHevPS4N+Smak/CUE6f/LUEnQiRvHdp1PFSrKVCkxALmgwJBAM7iiCKHPT1Sb8vdC4SDwPXNUvATwiOxDAmk2aieaN1Wkdnar1GQxl+5C95GJEPp+5qtyiBAr5BBcwPhTj/el7kCQQCUEyh1tE/2HkiqGslB6f5UvEehdhwsuD0W15+9fsH26WanCRHdjh4UDN7YjzldufHrG4zJWHklDaaWS+C4FZ1zAkEAuy77PUJfhjwwKqO06BDNZZiAkfdOkDyAR7l/tbCd8KWDETlrpsD2u2UR9+o+pbPukeTsHIJqVnrhNYGE6wTbAQJBAJ9qRLQJY19X6EH3+gi6M2IPyeifUZlRXCeqs9C199OdqjRQUGMPAjBwe1ZFTc7AyAwvC1+bj9qc063Ty3sPqTw=

与之匹配的公钥:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM9As4OkFLUyEmtqVJJaHkAf0xKR3aq38q6FqSi1vQfy1hUjhh/Z8k5NaT2WnmnchGUOoGsvwtKVAHrS62qvWzAPy7sauvG/O7hu1l+3SsrRDtv/O6hqRjEbPex/hpZM1opdkDtiiuHB5wHFYnD7JouWme9lNAViRcj4bXfqnZqwIDAQAB

 

 

示例代码:

// See https://aka.ms/new-console-template for more information


using System.Security.Cryptography;
using System.Text;

try
{
    //PKCS8格式私钥
    string strPriPkcs8 = "MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAMz0Czg6QUtTISa2pUkloeQB/TEpHdqrfyroWpKLW9B/LWFSOGH9nyTk1pPZaeadyEZQ6gay/C0pUAetLraq9bMA/Luxq68b87uG7WX7dKytEO2/87qGpGMRs97H+GlkzWil2QO2KK4cHnAcVicPsmi5aZ72U0BWJFyPhtd+qdmrAgMBAAECgYEAvW67iAbgHt0BASVD9C3iSjpEaVHVlC165o/IVzaTcEx8Bz3Ve0zN8W3JnvIO3ebsG4HiLLr2Nk++9rltOc0eNeGMv7F1e/OFot1wN0ON6s1g4bYh1z5Uz8FcYiMWcqHHICrx+oSFeK9x+I2Zge7enQXcsVnqEhm77ZE5YczSryECQQD9nB58e5efYchF+cYbmURioX18cUMuhQbB9Aq2N55cd689Lg35KZqT8JQTp/8tQSdCJG8d2nU8VKspUKTEAuaDAkEAzuKIIoc9PVJvy90LhIPA9c1S8BPCI7EMCaTZqJ5o3VaR2dqvUZDGX7kL3kYkQ+n7mq3KIECvkEFzA+FOP96XuQJBAJQTKHW0T/YeSKoayUHp/lS8R6F2HCy4PRbXn71+wfbpZqcJEd2OHhQM3tiPOV258esbjMlYeSUNppZL4LgVnXMCQQC7Lvs9Ql+GPDAqo7ToEM1lmICR906QPIBHuX+1sJ3wpYMROWumwPa7ZRH36j6ls+6R5OwcgmpWeuE1gYTrBNsBAkEAn2pEtAljX1foQff6CLozYg/J6J9RmVFcJ6qz0LX3052qNFBQYw8CMHB7VkVNzsDIDC8LX5uP2pzTrdPLew+pPA==";
    //PKCS1格式私钥
    string strPriPkcs1 = "MIICXwIBAAKBgQDM9As4OkFLUyEmtqVJJaHkAf0xKR3aq38q6FqSi1vQfy1hUjhh/Z8k5NaT2WnmnchGUOoGsvwtKVAHrS62qvWzAPy7sauvG/O7hu1l+3SsrRDtv/O6hqRjEbPex/hpZM1opdkDtiiuHB5wHFYnD7JouWme9lNAViRcj4bXfqnZqwIDAQABAoGBAL1uu4gG4B7dAQElQ/Qt4ko6RGlR1ZQteuaPyFc2k3BMfAc91XtMzfFtyZ7yDt3m7BuB4iy69jZPvva5bTnNHjXhjL+xdXvzhaLdcDdDjerNYOG2Idc+VM/BXGIjFnKhxyAq8fqEhXivcfiNmYHu3p0F3LFZ6hIZu+2ROWHM0q8hAkEA/ZwefHuXn2HIRfnGG5lEYqF9fHFDLoUGwfQKtjeeXHevPS4N+Smak/CUE6f/LUEnQiRvHdp1PFSrKVCkxALmgwJBAM7iiCKHPT1Sb8vdC4SDwPXNUvATwiOxDAmk2aieaN1Wkdnar1GQxl+5C95GJEPp+5qtyiBAr5BBcwPhTj/el7kCQQCUEyh1tE/2HkiqGslB6f5UvEehdhwsuD0W15+9fsH26WanCRHdjh4UDN7YjzldufHrG4zJWHklDaaWS+C4FZ1zAkEAuy77PUJfhjwwKqO06BDNZZiAkfdOkDyAR7l/tbCd8KWDETlrpsD2u2UR9+o+pbPukeTsHIJqVnrhNYGE6wTbAQJBAJ9qRLQJY19X6EH3+gi6M2IPyeifUZlRXCeqs9C199OdqjRQUGMPAjBwe1ZFTc7AyAwvC1+bj9qc063Ty3sPqTw=";
    //公钥
    string strPub = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM9As4OkFLUyEmtqVJJaHkAf0xKR3aq38q6FqSi1vQfy1hUjhh/Z8k5NaT2WnmnchGUOoGsvwtKVAHrS62qvWzAPy7sauvG/O7hu1l+3SsrRDtv/O6hqRjEbPex/hpZM1opdkDtiiuHB5wHFYnD7JouWme9lNAViRcj4bXfqnZqwIDAQAB";

    string strDJM = "泰酷拉!123ABC";//待签名字符串
    strDJM = "泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC泰酷拉!123ABC";//待加密字符串,超过117字符的测试

    Console.WriteLine("待签名字符串:" + strDJM);

    //一、私钥签名
    //获取私钥对象
    //转为纯字符串,不带格式
    strPriPkcs1 = strPriPkcs1.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
    strPriPkcs1 = strPriPkcs1.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
    RSA rsaPri = RSA.Create();
    rsaPri.ImportRSAPrivateKey(Convert.FromBase64String(strPriPkcs1), out _);
    //如果私钥是PKCS8格式:rsaPri.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyPem), out _);

    //待签名字符串 转为byte 数组
    byte[] byToSign = Encoding.UTF8.GetBytes(strDJM); // 编码要和其它语言一致,一般是:UTF8 
    byte[] bySigned = rsaPri.SignData(byToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);//SHA256,对应JAVA,SHA256withRSA,签名结果是 byte 数组
    string strSigned = Convert.ToBase64String(bySigned);//将byte 数组转为字符串,方便传输,一般是base64字符串,其它类型需和对方协商
    Console.WriteLine("签名值:" + strSigned);

    //二、公钥验签(验证签名)
    //获取公钥对象
    strPub = strPub.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
    RSA rsaPub = RSA.Create();
    rsaPub.ImportSubjectPublicKeyInfo(Convert.FromBase64String(strPub), out _);
    //将对方的签名值转为BYTE数组。
    byte[] byToCheckSign = Convert.FromBase64String(strSigned);
    //将原始报文转为byte数组
    byte[] byBody = Encoding.UTF8.GetBytes(strDJM);// 编码要和其它语言一致,一般是:UTF8 
                                                   //验证签名
    bool bCheck = rsaPub.VerifyData(byBody, byToCheckSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);//验证签名双方要保持一致
    Console.WriteLine("验证签名:" + bCheck.ToString());

}
catch (Exception ex)
{
    Console.WriteLine("ex:" + ex.Message);
}


Console.WriteLine("Hello, World!");
Console.ReadKey();

 

.NET CORE 与.NET FRAMEWORK的区别。

签名:

.NET CORE:SignData(byte[] data, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) ,默认3个参数,后2个得是枚举。

.NET FRAMEWORK:SignData(byte[] buffer, object halg),默认2参数,halg 可以是字符串。

验证签名:

.NET CORE:VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding),默认4个参数,后2个得是枚举。

.NET FRAMEWORK:VerifyData(byte[] buffer, object halg, byte[] signature),默认3参数,halg 可以是字符串。 data(buffer) 和 signature 的顺序有变化。

using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;
using RSAExtensions;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;

namespace ConsoleAppCICC.Net6.RSA
{
    internal class Program
    {
        static void Main(string[] args)
        {
             Console.WriteLine("Hello, World!");
            string message = "这是原始数据";
            byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
            // 可以从文件等读取证书私钥 
            //            string privateKey = @"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXuqAQz+MyeKuC
            //335rPshsHUFxLpZcBj97EiHq9qhvxMhLsMoa3YFvKlwktBoQWzZzgETls1N0H5E/
            //yhV+pljN4TIubfsOQ68TUfJkr9b+S1/25KwOwUmMndqaFb/vcYj1QtWTl65OXOWL
            //l3oKl7j2kwjmwtW8Smm97cEw15ijmRALP3qGvYyzL/YRIOHv+PmDpoMG0dedZRNz
            //u7hdhkU/UrlaRLvDLHyUJPkM+4SJHVUa2epYFMB7OX/b4ZQR0unxIBea2HAo8byG
            //qctOB41cXa6+t8dvRj2DhCXhMHGXKNU8DL9aALLmCbl2UTsJQyvNQ3FaT7TzQYsw
            //gOevjFttAgMBAAECggEBALltxOAukM91QZdAm917HbMwgmvS65M2G6Kht13LnoRT
            //1Qaac14cP4FaG9HArRaaEHe/LXlzZqxN5f+6qDcwrOxqQUP/bOiZ7QbiTUOWkWoL
            //R/VkITCdm6aumJQRVeVu5ZZLWGGA42agGv6duV0ZhPcM833GwwkRvUpv7rsY1i7n
            //ccUObhkv3ql8XjnCcz1rSSkf6W40+dp0pW2mCH6NPeT8kdtBH7mA/ZSK7ZMQbMGg
            //LNdCx069o91POKP/nZ9V9Ex2Bviic2TqpilC6Z/9EryxA0HszknUMX7Lnuhr7Fsb
            //n8wkvYLplebolTOrCy/athEZkGdwCrgdGFk7by/XsdECgYEA9BX0vnhr1d1A9rxz
            //YYt/Xt3DqqtnQ8ETe4CldI3/XIJ3CcMI4f52dAk+Jl9V4vHu4HRKODWjJ67FDExQ
            //Bu+FWUjk01rFSMmbsVJRenRyuGL4X5XTRwMCtcIcxfvYgttLVVpqCsDK2CredAxc
            //u+RZ5eI0/Y4gc+BKzDIbSy1LQ2sCgYEA4kJUQ4BlIQHD2CEj49ORL/WzWk01Wi9A
            //2ilQcVf/xbLeBJd04SemftHLF4ulKM6VZ4KyrTzewwM6rEw31fWo7dgxShwTdnMI
            //Hn3esTdZYK6O6zQ8PuEi/q4hTx4QYXDq63pKoDRTrkQAVZdTe0ujyczPXjdAKwYF
            //obd3sk606ocCgYA7tpdedZVkB7HZ6vaLOZZafgRdX3tu3vBGOY3AuvBrEFvQ3o7w
            //wtD1yEEVICOh1rAq5yg1IiIdFiuMu6qkV7qWwjeA4Wb8TJ4zPkFCpPRf2Y6Ly9qZ
            //N8pU7v0dDmTEdqOyaOEialH1ml87or6ZFN9NVw7BrSCRWKDT3bpBpP4RqwKBgQC/
            //i5u+kaILsWzVOikiJOgTDBTMa/Sw5rQmfTdm3TLhca3XgUknm+OuFXmz8zrdMxb0
            //zIbrdk64FbHET7otXo2m3ZHMxbwMWRIUjkbLDKqT2PORV2VxbjOsHEqCqlb/xxJ6
            //S8uFmQcGzvbhfvzjsoN0wfkEMA4Y3j5CTAn/X/Z1XQKBgGG4qDagG1unSkpNfHfu
            //qwjEjAkSR/pla4vJIaEaA94/gJ21upZfwjUUdRXJbk6W03OfSOlmy2zIeCguV+H/
            //UkMOcmBqVX/tbCQy7weCQwwHU4Dluy7xJIzrgYRXIuV+3zk2D63+LAK84+ksbL61
            //zCnaG86YfYxcClOZ/revxrnd";

            string privateKey = System.IO.File.ReadAllText(@"D:\Code\My\AspectCoreAopDemo\ConsoleAppCICC.Net6.RSA\etc\rsaprivatekey_pass.pem"); 

            //string publicKey = @"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANFVdaJmHSwAj+5ry0MD98sSZ4nH0hgaUWcUwHF7+ptiX8lrnVGeK4yMlHpS5bgaWFwmdwXRaoAzWBWkEg5yV2ECAwEAAQ==";

            //string publicKey = @"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
            //EQEPxnQpyBfDzB4lbEngM+yUxCdj";

            string publicKey = System.IO.File.ReadAllText(@"D:\Code\My\AspectCoreAopDemo\ConsoleAppCICC.Net6.RSA\etc\rsapublic_cert.cert");

            privateKey = privateKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKey = privateKey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            privateKey = privateKey.Replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "").Replace("-----END ENCRYPTED PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            publicKey = publicKey.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", "").Replace("\r", "").Replace("\n", "").Trim();
            publicKey = publicKey.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            string sign = string.Empty;
            var r = System.Security.Cryptography.RSA.Create();
            //privateKey = r.ExportPrivateKey(RSAKeyType.Pkcs8);//私钥
            //publicKey = r.ExportPublicKey(RSAKeyType.Pkcs8);//公钥
            //r.ImportPrivateKey(RSAKeyType.Pkcs8, privateKey); //私钥
            //r.ImportPublicKey(RSAKeyType.Pkcs8, publicKey); //公钥

            //r.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(Convert.FromBase64String(privateKey)), out _); //私钥
            //r.ImportPkcs8PublicKey(Convert.FromBase64String(publicKey)); //公钥

            //r.ImportEncryptedPkcs8PrivateKey(new ReadOnlySpan<byte>(Convert.FromBase64String("123456")), new ReadOnlySpan<byte>(Convert.FromBase64String(privateKey)), out _); //私钥
            //r.ImportPkcs8PublicKey(Convert.FromBase64String(publicKey)); //公钥

            byte[] keyData = Convert.FromBase64String(privateKey);

            // privatekey 私钥字符串,公钥字符串,
            int len = keyData.Length;
            r.ImportEncryptedPkcs8PrivateKey("123456".ToCharArray(), keyData, out len);

            //r.ImportSubjectPublicKeyInfo(Encoding.UTF8.GetBytes(publicKey), out len);
            //r.ImportPublicKey(RSAKeyType.Pkcs8, publicKey);
            //r.ImportPkcs8PublicKey(Convert.FromBase64String(publicKey)); //公钥

            //message 待加密字符串
            var encrypt = Extensions.ToHexString(r.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss));
            Console.WriteLine("System.Security.Cryptography.RSA.Create():" + encrypt);
            var res = r.VerifyData(data, Extensions.FromHexString(encrypt), HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
            Console.WriteLine("System.Security.Cryptography.RSA.Create():" + res);

            sign = Extensions.ToHexString(r.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss));
            Console.WriteLine(sign);
            //string encrypt1 = @"29972173A69BD1018ED99E6B1A0541AE7CEE646D43179AAB18306289F35067234EB9AAC28BF7128B7E7988EFE5378775026795BAE77CD3DA0ECBE3420DBFCB87D595840DF96D7262D5BCFD8BF0A8EF4B6051A4C8C2A970DE086CEA128CF519DD31A31EBF5FC8188A545EAD3D90BE6CE0125A4AE2D863947BF6E69EFDB5DC3BA32BA15D2F7DF2C3192D733B8211194566AAF3D6B95BD16CAC66CD832744004526B62218852B2F923B2CE3C73C92D65311FF80C2E08C79F76F3D949561E40CE776E505F5347B2FEC2C634935C8EF58BCD79B7C6CE27331192944A58353D736B4875C049E26CF111E0F2AEC614B4DF9CFA804DDF9CBED48D33C173D2B396C94B1C72767910BB3A1C3ED757F6410C5B9B72EFFCE63BC37B7AF3643D7721BC89C399A22421C0218E256CD2B5954247C71A2465035677DA3421EA7154964BAC9EE90DDF90921D2A33BAF8BBE2D97BFB36FA8FD35F4411713A1D33F30B26DE4727587F0E15C9F0DB7C7A83FB5058885DADD415F39982EA876D861159CA3ABAD0B7370F910F131AD0EB7644A7A9E8121ACFB714CD5A9CDC55C101B8B0452339BCDC0F04944C46F119396AF2C31EB524BE0C5D3F990D22E2E4CB444AD00B22DE2116BA307ED91AE0F49A2747F4FEC4039F9F57AF14C33FF3CA2962B5085F6F537CCDB98A0A6E4DC5CE20E23D0FD566F25694F7DF074C2031E098A6167CFD37EA0A8AA2BB8";



            // 使用私钥签名
            //using (CngKey cngKey = CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob))
            //using (RSACng rsa = new RSACng(cngKey))
            //{
            //    sign = Extensions.ToHexString(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss));
            //    Console.WriteLine(sign);
            //}

            // 使用公钥验签
            X509Certificate2 x509 = new X509Certificate2(Convert.FromBase64String(publicKey));
            using (var rsa = x509.GetRSAPublicKey())
            {
                bool result = rsa.VerifyData(data, Extensions.FromHexString(encrypt), HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
                Console.WriteLine("encrypt:" + result);

                result = rsa.VerifyData(data, Extensions.FromHexString(sign), HashAlgorithmName.SHA256, RSASignaturePadding.Pss);
                Console.WriteLine("sign:" + result);
            }

            Console.ReadKey();
        }
    }

    public class Extensions
    {
        public static string ToHexString(byte[] byteArray)
        {
            StringBuilder hexBuilder = new StringBuilder(byteArray.Length * 2);
            foreach (byte b in byteArray)
            {
                hexBuilder.AppendFormat("{0:X2}", b);
            }
            return hexBuilder.ToString();
        }

        public static byte[] FromHexString(string hexString)
        {
            int byteCount = hexString.Length / 2;
            byte[] byteArray = new byte[byteCount];

            for (int i = 0; i < byteCount; i++)
            {
                byteArray[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }

            return byteArray;
        }


        public static string RSA_Decrypt(byte[] value)
        {
            byte[] deciphered;

            using (var reader = System.IO.File.OpenText(@"D:\Code\My\AspectCoreAopDemo\ConsoleAppCICC.RSA\etc\private.pem"))
            {
                // Extracting the payload
                string privateKeyFile = reader.ReadToEnd();
                var exp = privateKeyFile.Split("\n\n");
                var privateKey = exp[1].Split("\n-----")[0];

                // using the payload to decrypt my data
                using (var rsa = System.Security.Cryptography.RSA.Create())
                {
                    rsa.ImportRSAPrivateKey(Convert.FromBase64String(privateKey), out _); //CryptographicException

                    deciphered = rsa.Decrypt(value, RSAEncryptionPadding.Pkcs1);
                }
            }

            return Encoding.Default.GetString(deciphered);
        }

        public static string RSA_Decrypt1(byte[] value)
        {
            string deciphered;

            using (var reader = File.OpenText(@"D:\Code\My\AspectCoreAopDemo\ConsoleAppCICC.RSA\etc\private.pem"))
            {
                string privateKeyFile = reader.ReadToEnd();
                var keyReader = new StringReader(privateKeyFile);

                var decryptEngine = new Pkcs1Encoding(new RsaEngine());

                object pemReader = new PemReader(keyReader).ReadObject();// , new PasswordFinder("azerty")
                var keyPair = (AsymmetricCipherKeyPair)pemReader;
                decryptEngine.Init(false, keyPair.Private);

                deciphered = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(value, 0, value.Length));
            }

            return deciphered;
        }
    }
}

  https://jpassing.com/2021/12/05/importing-rsa-public-keys-in-downlevel-dotnet-and-dotnet-framework-versions/

 

In .NET 5 and 6, we can use RSA.ImportFromPem to import a PEM-formatted RSA public key . Older .NET Core versions and .NET Framework don’t offer that functionality – but with some extension methods and a little help from CryptoAPI, we can fill that gap.

.NET Core 3.1 and below

Although .NET Core 3.1 and older versions don’t support RSA.ImportFromPem, they do support 2 other useful methods:

Recall that the header of a PEM file indicates whether the file contains a RSAPublicKey structure, a SubjectPublicKeyInfo structure, or something else entirely. Therefore, we can reimplement ImportFromPem by:

  1. Parsing the PEM header/footer to determine whether we’re dealing with a PKCS#1 RSAPublicKey structure (identified by BEGIN RSA PUBLIC KEY) or a X.509 SubjectPublicKeyInfo structure (identified by BEGIN PUBLIC KEY).
  2. Base64-decoding the body to get the DER blob.
  3. Importing the DER blob by using RSA.ImportRSAPublicKey or RSA.ImportSubjectPublicKeyInfo.

In C#, this looks like:

public static class RSAExtensions
{
  private const string RsaPublickeyPemHeader = "-----BEGIN RSA PUBLIC KEY-----";
  private const string RsaPublickeyPemFooter = "-----END RSA PUBLIC KEY-----";
  private const string SubjectPublicKeyInfoPemHeader = "-----BEGIN PUBLIC KEY-----";
  private const string SubjectPublicKeyInfoPemFooter = "-----END PUBLIC KEY-----";
    
  #if !(NET5_0 || NET5_0_OR_GREATER)
  //
  // Add missing method.
  //
  public static void ImportFromPem(
    this RSA key,
    string source)
    => ImportFromPem(key, source, out var _);
  
  #endif
  
  public static void ImportFromPem(
    this RSA key,
    string source,
    out RsaPublicKeyFormat format)
  {
    source = source.Trim();
  
    //
    // Inspect header to determine format.
    //
    if (source.StartsWith(SubjectPublicKeyInfoPemHeader) &&
        source.EndsWith(SubjectPublicKeyInfoPemFooter))
    {
      format = RsaPublicKeyFormat.SubjectPublicKeyInfo;
    }
    else if (source.StartsWith(RsaPublickeyPemHeader) &&
             source.EndsWith(RsaPublickeyPemFooter))
    {
      format = RsaPublicKeyFormat.RsaPublicKey;
    }
    else
    {
      throw new FormatException("Missing Public key header/footer");
    }
  
    //
    // Decode body to get DER blob.
    //
    var der = Convert.FromBase64String(string.Concat(
      source
        .Split('\n')
        .Select(s => s.Trim())
        .Where(line => !line.StartsWith("-----"))));
    if (format == RsaPublicKeyFormat.RsaPublicKey)
    {
      key.ImportRSAPublicKey(der, out var _);
    }
    else
    {
      key.ImportSubjectPublicKeyInfo(der, out var _);
    }
  }
  
  ...
}

public enum RsaPublicKeyFormat
{
  RsaPublicKey,
  SubjectPublicKeyInfo
}

.NET Framework

.NET Framework not only lacks ImportFromPem, but also RSA.ImportRSAPublicKey and RSA.ImportSubjectPublicKeyInfo. So we have a little more work to do.

To compensate for these missing methods, we could turn to BouncyCastle.NET. But why add another dependency if we can live off the land by P/Invoking into CryptoAPI instead? With a bit of help from CryptoAPI, we can reimplement RSA.ImportRSAPublicKey and RSA.ImportSubjectPublicKeyInfo on .NET Framework.

Reimplementing RSA.ImportRSAPublicKey

RSA.ImportRSAPublicKey takes a DER-encoded PKCS#1 RSAPublicKey structure as input. The first thing we need to do is convert that DER blob into a CSP Public Key blob. To do this, we can call CryptDecodeObjectEx and pass the flag RSA_CSP_PUBLICKEYBLOB.

The resulting CSP public key blob is understood by both CryptoAPI and CNG, but the process to import it differs slightly. Recall that RSA is an abstract base class, so we need to check whether we’re really dealing with a CNG key (RSACng) or Crypto API key (RSACryptoServiceProvider):

  • If it’s a CryptoAPI key, we can use RSACryptoServiceProvider.ImportCspBlob to import the blob, effectively overriding the existing key.
  • RSACng doesn’t provide an equivalent method, but we can work around that by first converting the blob into an RSAParameters object, and then importing that.

In C#, this looks like:

public static class RSAExtensions
{
  ...
  
  #if NET40_OR_GREATER
  
  private static void ImportCspBlob(
    RSA key,
    byte[] cspBlob)
  {
    if (key is RSACng)
    {
      //
      // RSACng.Key is private, so we can't import into
      // an existing key directly. But we can do so
      // indirectly.
      //
      var importedKey = CngKey.Import(cspBlob, CngKeyBlobFormat.GenericPublicBlob);
      var importedKeyParameters = new RSACng(importedKey).ExportParameters(false);
      key.ImportParameters(importedKeyParameters);
    }
    else if (key is RSACryptoServiceProvider cryptoApiKey)
    {
      cryptoApiKey.ImportCspBlob(cspBlob);
    }
    else
    {
      throw new ArgumentException("Unrecognized key type");
    }
  }

  public static void ImportRSAPublicKey(
    this RSA key,
    byte[] derBlob, 
    out int bytesRead)
  {
    using (var derBlobHandle = LocalAllocHandle.Alloc(derBlob.Length))
    {
      Marshal.Copy(
        derBlob,
        0,
        derBlobHandle.DangerousGetHandle(),
        derBlob.Length);
    
      //
      // Decode RSA PublicKey DER -> CSP blob.
      //
      if (UnsafeNativeMethods.CryptDecodeObjectEx(
          UnsafeNativeMethods.X509_ASN_ENCODING |
            UnsafeNativeMethods.PKCS_7_ASN_ENCODING,
          UnsafeNativeMethods.RSA_CSP_PUBLICKEYBLOB,
          derBlobHandle.DangerousGetHandle(),
          (uint)derBlob.Length,
          UnsafeNativeMethods.CRYPT_DECODE_ALLOC_FLAG,
          IntPtr.Zero,
          out var keyBlobHandle,
          out var keyBlobSize))
      {
        using (keyBlobHandle)
        {
          var keyBlobBytes = new byte[keyBlobSize];
          Marshal.Copy(
            keyBlobHandle.DangerousGetHandle(),
            keyBlobBytes,
            0,
            (int)keyBlobSize);
          
          bytesRead = derBlob.Length;
          ImportCspBlob(key, keyBlobBytes);
        }
      }
      else
      {
        throw new CryptographicException(
          "Failed to decode DER blob",
          new Win32Exception());
      }
    }
  }
  
  #endif
  
  ...
}

Note that the code uses a custom helper class LocalAllocHandle to handle memory allocations.

Reimplementing RSA.ImportSubjectPublicKeyInfo

As its name suggests, RSA.ImportSubjectPublicKeyInfo takes a DER-encoded X.509 SubjectPublicKeyInfo structure as input. To reimplement the method, we again have to convert that DER blob into a CSP Public Key blob first:

  1. Call CryptDecodeObjectEx with flag X509_PUBLIC_KEY_INFO to decode the DER blob into a CERT_PUBLIC_KEY_INFO structure.
  2. Verify that the structure really contains an RSA public key (and not something else, like an ECC key) by verifying that CERT_PUBLIC_KEY_INFO.Algorithm.pszObjId contains the RSA OID. If it does, we know that CERT_PUBLIC_KEY_INFO.PublicKey contains a DER-encoded PKCS#1 RSAPublicKey structure.
  3. Extract the RSAPublicKey from CERT_PUBLIC_KEY_INFO.PublicKey and call CryptDecodeObjectEx with flag RSA_CSP_PUBLICKEYBLOB to decode it into a public key blob that can be imported into CNG or CryptoAPI.

Once we have the CSP Public Key blob, we can import it using the ImportCspBlob method we looked at before.

public static class RSAExtensions
{
  private const string RsaOid = "1.2.840.113549.1.1.1";
  
  ...

  #if NET40_OR_GREATER
  
  public static void ImportSubjectPublicKeyInfo(
    this RSA key,
    byte[] certKeyInfoDer, 
    out int bytesRead)
  
  {
    using (var certKeyInfoDerHandle = LocalAllocHandle.Alloc(certKeyInfoDer.Length))
    {
      Marshal.Copy(
        certKeyInfoDer,
        0,
        certKeyInfoDerHandle.DangerousGetHandle(),
        certKeyInfoDer.Length);
  
      //
      // Decode DER -> CERT_PUBLIC_KEY_INFO.
      //
      if (UnsafeNativeMethods.CryptDecodeObjectEx(
          UnsafeNativeMethods.X509_ASN_ENCODING |
            UnsafeNativeMethods.PKCS_7_ASN_ENCODING,
          UnsafeNativeMethods.X509_PUBLIC_KEY_INFO,
          certKeyInfoDerHandle.DangerousGetHandle(),
          (uint)certKeyInfoDer.Length,
          UnsafeNativeMethods.CRYPT_DECODE_ALLOC_FLAG,
          IntPtr.Zero,
          out var certKeyInfoHandle,
          out var certKeyInfoSize))
      {
        using (certKeyInfoHandle)
        {
          //
          // Check that the CERT_PUBLIC_KEY_INFO contains an RSA public key.
          //
          var certInfo = Marshal.PtrToStructure<UnsafeNativeMethods.CERT_PUBLIC_KEY_INFO>(
            certKeyInfoHandle.DangerousGetHandle());
  
          if (certInfo.Algorithm.pszObjId != RsaOid)
          {
            throw new CryptographicException("Not an RSA public key");
          }
  
          //
          // Decode the RSA public key -> CSP blob.
          //
          if (UnsafeNativeMethods.CryptDecodeObjectEx(
              UnsafeNativeMethods.X509_ASN_ENCODING |
                UnsafeNativeMethods.PKCS_7_ASN_ENCODING,
              UnsafeNativeMethods.RSA_CSP_PUBLICKEYBLOB,
              certInfo.PublicKey.pbData,
              certInfo.PublicKey.cbData,
              UnsafeNativeMethods.CRYPT_DECODE_ALLOC_FLAG,
              IntPtr.Zero,
              out var cspKeyBlob,
              out var cspKeyBlobSize))
          {
            using (cspKeyBlob)
            {
              var keyBlobBytes = new byte[cspKeyBlobSize];
              Marshal.Copy(
                cspKeyBlob.DangerousGetHandle(),
                keyBlobBytes,
                0,
                (int)cspKeyBlobSize);
  
              bytesRead = certKeyInfoDer.Length;
              ImportCspBlob(key, keyBlobBytes);
            }
          }
          else
          {
            throw new CryptographicException(
              "Failed to decode RSA public key from CERT_PUBLIC_KEY_INFO",
              new Win32Exception());
          }
        }
      }
      else
      {
        throw new CryptographicException(
          "Failed to decode DER blob into CERT_PUBLIC_KEY_INFO",
          new Win32Exception());
      }
    }
  }

  #endif
}

With these extension methods in place, we can now use RSA.ImportFromPem on downlevel .NET and .NET Framework versions.

You can find the complete source code on GitHub.

posted on 2023-06-29 16:20  HackerVirus  阅读(1242)  评论(0编辑  收藏  举报