C#.NET Framework 使用BC庫(BouncyCastle) RSA 公鑰加密 私鑰解密 ver:20230706

2023-07-06 12:00:47

C#.NET Framework 使用BC庫(BouncyCastle) RSA 公鑰加密 私鑰解密 ver:20230706

 

環境說明:

.NET Framework 4.6 的控制檯程式 。

 

2020年以後 ,有部分PKCS8私鑰(openssl生成)無法用RsaUtil.LoadPrivateKey(strPriPkcs8, "PKCS8")來解析 (https://www.cnblogs.com/runliuv/p/17474269.html)

 可以嘗試用BC庫來處理。

另外,在.NET中:私鑰加密,公鑰解密。也只能用BC庫實現。 

 

加密解析:

//假設私鑰長度為1024, 1024/8-11=117。
//如果明文的長度小於117,直接全加密,然後轉base64。(data.Length <= maxBlockSize)
//如果明文長度大於117,則每117分一段加密,寫入到另一個Stream中,最後轉base64。

 

如果要和其它語言互通,這個分段長度需要和其它語言約定好,不一是 私鑰長度/8-11。

 

解密解析:

//假設私鑰長度為1024, 1024/8 =128。
//如果明文的長度小於 128,直接全解密。(data.Length <= maxBlockSize)
//如果明文長度大於 128,則每 128 分一段解密,寫入到另一個Stream中,最後 GetString。

 

用「支付寶開放平臺開發助手」生成一組公私鑰:

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

 

BCSignUtil 輔助類,nuget 中參照 Portable.BouncyCastle。

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Text;

namespace CommonUtils
{
    public static class BCSignUtil
    {
        /// <summary>
        /// 簽名
        /// </summary>
        /// <param name="orgData">原資料字串</param>
        /// <param name="privateKeyPKCS8">必須是PKCS8的</param>
        /// <param name="algorithm">演演算法</param>
        /// <returns></returns>
        public static string SignData(string orgData, string privateKeyPKCS8, string algorithm)
        {
            if (string.IsNullOrEmpty(orgData))
                throw new Exception("字串不能為空!");

            if (string.IsNullOrEmpty(privateKeyPKCS8))
                throw new Exception("privateKeyPKCS8不能為空!");

            if (string.IsNullOrEmpty(privateKeyPKCS8))
                throw new Exception("algorithm 不能為空!");

            AsymmetricKeyParameter priKey = GetPrivateKeyParameter(privateKeyPKCS8);

            byte[] byteData = Encoding.UTF8.GetBytes(orgData);

            ISigner normalSig = SignerUtilities.GetSigner(algorithm);
            normalSig.Init(true, priKey);
            normalSig.BlockUpdate(byteData, 0, byteData.Length);//注意:是byte陣列和陣列長度,別寫成string的長度了
            byte[] normalResult = normalSig.GenerateSignature(); //簽名結果
            string sign = Convert.ToBase64String(normalResult);

            return sign;
        }

        private static AsymmetricKeyParameter GetPrivateKeyParameter(string privateKeyPem)
        {
            //獲取私鑰純字串
            privateKeyPem = privateKeyPem.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            byte[] privateInfoByte = Convert.FromBase64String(privateKeyPem);

            AsymmetricKeyParameter priKey = PrivateKeyFactory.CreateKey(privateInfoByte);
            return priKey;
        }

        /// <summary>
        /// 驗證簽名
        /// </summary>
        /// <param name="orgData">原資料字串</param>
        /// <param name="publicKeyPem">公鑰</param>
        /// <param name="responseSign">對方的簽名串</param>
        /// <param name="algorithm">演演算法</param>
        /// <returns></returns>
        public static bool VerifySignature(string orgData, string publicKeyPem, string responseSign, string algorithm)
        {

            AsymmetricKeyParameter pubKey = GetPublicKeyParameter(publicKeyPem);


            byte[] signBytes = Convert.FromBase64String(responseSign);
            byte[] plainBytes = Encoding.UTF8.GetBytes(orgData);

            ISigner verifier = SignerUtilities.GetSigner(algorithm);
            verifier.Init(false, pubKey);
            verifier.BlockUpdate(plainBytes, 0, plainBytes.Length);//注意:是byte陣列和陣列長度,別寫成string的長度了

            bool isOK = verifier.VerifySignature(signBytes); //驗簽結果
            return isOK;
        }

        private static AsymmetricKeyParameter GetPublicKeyParameter(string publicKeyPem)
        {
            //獲取公鑰純字串
            publicKeyPem = publicKeyPem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            byte[] publicInfoByte = Convert.FromBase64String(publicKeyPem);

            AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(publicInfoByte);
            return pubKey;
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="orgData">資料</param>
        /// <param name="key">私鑰、公鑰</param>
        /// <param name="isPublicKey">是否為公鑰</param>
        /// <param name="privateKeySize">私鑰長度,一般是1024或2048</param>
        /// <returns></returns>
        public static string EncryptByKey(string orgData, string key, bool isPublicKey, int privateKeySize)
        {
            //非對稱加密演演算法,加解密用  
            IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
            //加密  

            //1024長度是117,雙方協商好
            int maxBlockSize = privateKeySize / 8 - 11; //加密塊最大長度限制

            engine.Init(true, isPublicKey ? GetPublicKeyParameter(key) : GetPrivateKeyParameter(key));
            byte[] byteData = System.Text.Encoding.UTF8.GetBytes(orgData);

            int inputLen = byteData.Length;
            MemoryStream ms = new MemoryStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 對資料分段加密
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > maxBlockSize)
                {
                    cache = engine.ProcessBlock(byteData, offSet, maxBlockSize);
                }
                else
                {
                    cache = engine.ProcessBlock(byteData, offSet, inputLen - offSet);
                }
                ms.Write(cache, 0, cache.Length);
                i++;
                offSet = i * maxBlockSize;
            }
            byte[] encryptedData = ms.ToArray();

            //var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
            return Convert.ToBase64String(encryptedData);
            //Console.WriteLine("密文(base64編碼):" + Convert.ToBase64String(testData) + Environment.NewLine);

        }
        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="orgData">資料</param>
        /// <param name="key">私鑰、公鑰</param>
        /// <param name="isPublicKey">是否為公鑰</param>
        /// <param name="privateKeySize">私鑰長度,一般是1024或2048</param>
        /// <returns></returns>
        public static string DecryptByKey(string orgData, string key, bool isPublicKey, int privateKeySize)
        {
            orgData = orgData.Replace("\r", "").Replace("\n", "").Replace(" ", "");
            //非對稱加密演演算法,加解密用  
            IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());


            //解密  


            //1024長度是128,雙方協商好
            int maxBlockSize = privateKeySize / 8; //解密塊最大長度限制

            engine.Init(false, isPublicKey ? GetPublicKeyParameter(key) : GetPrivateKeyParameter(key));
            byte[] byteData = Convert.FromBase64String(orgData);

            int inputLen = byteData.Length;
            MemoryStream ms = new MemoryStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 對資料分段加密
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > maxBlockSize)
                {
                    cache = engine.ProcessBlock(byteData, offSet, maxBlockSize);
                }
                else
                {
                    cache = engine.ProcessBlock(byteData, offSet, inputLen - offSet);
                }
                ms.Write(cache, 0, cache.Length);
                i++;
                offSet = i * maxBlockSize;
            }
            byte[] encryptedData = ms.ToArray();

            //var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
            return Encoding.UTF8.GetString(ms.ToArray());
            //Console.WriteLine("密文(base64編碼):" + Convert.ToBase64String(testData) + Environment.NewLine);

        }

    }
}

 

使用程式碼:

 

using CommonUtils;
using System;

namespace ConsoleNetFxRsa加密簽名使用BC庫
{
    class Program
    {
        static void Main(string[] args)
        {
            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);                

                #region 一、私鑰簽名
                //string algorithm = "SHA1withRSA";
                string algorithm = "SHA256withRSA";
                string strSigned = BCSignUtil.SignData(strDJM, strPriPkcs8, algorithm); //PKCS8
                Console.WriteLine("簽名值:" + strSigned);
                #endregion

                #region 二、公鑰驗籤(驗證簽名)
                //二、公鑰驗籤(驗證簽名)
                //驗證簽名
                bool bCheck = BCSignUtil.VerifySignature(strDJM, strPub, strSigned, algorithm);//驗證簽名雙方要保持一致
                Console.WriteLine("驗證簽名:" + bCheck.ToString());
                #endregion

          //私鑰長度,常用 1024 或 2048 int privateKeySize = 1024; //公鑰加密 string strJiamihou= BCSignUtil.EncryptByKey(strDJM, strPub,true, privateKeySize); Console.WriteLine("加密後:" + strJiamihou); //私鑰解密 string strJiemihou = BCSignUtil.DecryptByKey(strJiamihou, strPriPkcs8, false, privateKeySize); Console.WriteLine("解密後:" + strJiemihou); //私鑰加密 strJiamihou = BCSignUtil.EncryptByKey(strDJM, strPriPkcs8, false, privateKeySize); Console.WriteLine("私鑰加密後:" + strJiamihou); //公鑰解密 strJiemihou = BCSignUtil.DecryptByKey(strJiamihou, strPub, true, privateKeySize); Console.WriteLine("公鑰解密後:" + strJiemihou); } catch (Exception ex) { Console.WriteLine("ex:" + ex.Message); } Console.WriteLine("hello END."); Console.ReadKey(); } } }

 

這裡演示了2組:

1:公鑰加密,私鑰解密。

2:私鑰加密,公鑰解密。