[.NET]使用RSA憑証加/解密

  • 17006
  • 0
  • .NET
  • 2012-10-23

[.NET]使用RSA憑証加/解密

最近客戶需要將資料以公開金鑰(public key)加密,然後用私密金鑰(private key)解密。

參考「C#使用RSA证书文件加密和解密示例」很快的就實作出來了,

但是當我將加密的內容增加時,卻發生了「長度錯誤」Invalid length的錯誤,

原來是因為長度大於允許的最大長度,

所以就改使用「RSA encryption exception」說的方式,改用blocks的方式。這樣就不怕要加密的內容會超過限制了!

但因為我們的Key Size是2048,所以_DecryptionBufferSize就變成了256(2048/8),_EncryptionBufferSize就變成了245。Winking smile

image

 

程式如下,

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Net;

namespace RSASample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //公開金鑰加密
        private void btnEncrypt_Click(object sender, EventArgs e)
        {
            //txtPublicKeyPath.Text是公開金鑰的檔案路徑
            X509Certificate2 pubcrt = new X509Certificate2(txtPublicKeyPath.Text.Trim());
            RSACryptoServiceProvider pubkey = (RSACryptoServiceProvider)pubcrt.PublicKey.Key;
            //txtBody.Text是要加密的內容
            byte[] orgData = Encoding.UTF8.GetBytes(txtBody.Text.Trim());
            //公開金鑰加密
            byte[] encryptedData = RSAEncrypt(orgData, pubkey.ExportParameters(false), false);
            //將加密過的內容以Base64轉成字串
            txtEncryptBody.Text = Convert.ToBase64String(encryptedData);
        }

        //私密金鑰解密
        private void btnDecrypt_Click(object sender, EventArgs e)
        {
            //txtPrivateKeyPath.Text是私密金鑰解密的檔案路徑
            // txtPrivateKeyPassword.Text是私密金鑰解密的密碼
            X509Certificate2 prvcrt = new X509Certificate2(txtPrivateKeyPath.Text.Trim(), 
                txtPrivateKeyPassword.Text, X509KeyStorageFlags.Exportable);
            RSACryptoServiceProvider prvkey = (RSACryptoServiceProvider)prvcrt.PrivateKey;
            //將加密過的內容從Base64字串轉成Byte Array
            byte[] encryptedData = Convert.FromBase64String(txtEncryptBody.Text);
            System.Security.Cryptography.RSAParameters parms = prvkey.ExportParameters(true);
            //私密金鑰解密
            byte[] decryptedData = RSADecrypt(encryptedData, parms, false);
            //將解密出來的內容轉成字串
            txtDecryptBody.Text = Encoding.UTF8.GetString(decryptedData);
        }

        //The key size to use maybe 1024/2048
        private const int _EncryptionKeySize = 2048;
   
        // The buffer size to decrypt per set
        private const int _DecryptionBufferSize = (_EncryptionKeySize / 8);

        //The buffer size to encrypt per set
        private const int _EncryptionBufferSize = _DecryptionBufferSize - 11;

        static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //byte[] encryptedData;
                //Create a new instance of RSACryptoServiceProvider.
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    //Import the RSA Key information. This only needs
                    //toinclude the public key information.
                    RSA.ImportParameters(RSAKeyInfo);

                    ////Encrypt the passed byte array and specify OAEP padding.  
                    ////OAEP padding is only available on Microsoft Windows XP or
                    ////later.  
                    //encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
                    //2012/10/19 rm 改用block
                    using (MemoryStream ms = new MemoryStream())
                    {
                        byte[] buffer = new byte[_EncryptionBufferSize];
                        int pos = 0;
                        int copyLength = buffer.Length;
                        while (true)
                        {
                            //Check if the bytes left to read is smaller than the buffer size, 
                            //then limit the buffer size to the number of bytes left

                            if (pos + copyLength > DataToEncrypt.Length)

                                copyLength = DataToEncrypt.Length - pos;

                            //Create a new buffer that has the correct size

                            buffer = new byte[copyLength];

                            //Copy as many bytes as the algorithm can handle at a time, 
                            //iterate until the whole input array is encoded

                            Array.Copy(DataToEncrypt, pos, buffer, 0, copyLength);

                            //Start from here in next iteration

                            pos += copyLength;

                            //Encrypt the data using the public key and add it to the memory buffer

                            //_DecryptionBufferSize is the size of the encrypted data

                            ms.Write(RSA.Encrypt(buffer, false), 0, _DecryptionBufferSize);

                            //Clear the content of the buffer, 
                            //otherwise we could end up copying the same data during the last iteration

                            Array.Clear(buffer, 0, copyLength);

                            //Check if we have reached the end, then exit

                            if (pos >= DataToEncrypt.Length)

                                break;
                        }
                        return ms.ToArray();
                    }
                }
                //return encryptedData;
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);

                return null;
            }

        }

        static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
        {
            try
            {
                //byte[] decryptedData;
                //Create a new instance of RSACryptoServiceProvider.
                using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
                {
                    //Import the RSA Key information. This needs
                    //to include the private key information.
                    RSA.ImportParameters(RSAKeyInfo);

                    //Decrypt the passed byte array and specify OAEP padding.  
                    //OAEP padding is only available on Microsoft Windows XP or
                    //later.  
                    //decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
                    using (MemoryStream ms = new MemoryStream(DataToDecrypt.Length))
                    {

                        //The buffer that will hold the encrypted chunks

                        byte[] buffer = new byte[_DecryptionBufferSize];

                        int pos = 0;

                        int copyLength = buffer.Length;

                        while (true)
                        {

                            //Copy a chunk of encrypted data / iteration

                            Array.Copy(DataToDecrypt, pos, buffer, 0, copyLength);

                            //Set the next start position

                            pos += copyLength;

                            //Decrypt the data using the private key

                            //We need to store the decrypted data temporarily because we don't know the size of it; 
                            //unlike with encryption where we know the size is 128 bytes. 
                            //The only thing we know is that it's between 1-117 bytes

                            byte[] resp = RSA.Decrypt(buffer, false);

                            ms.Write(resp, 0, resp.Length);

                            //Cleat the buffers

                            Array.Clear(resp, 0, resp.Length);

                            Array.Clear(buffer, 0, copyLength);

                            //Are we ready to exit?

                            if (pos >= DataToDecrypt.Length)

                                break;

                        }

                        //Return the decoded data

                        return  ms.ToArray();

                    }
                }
                //return decryptedData;
            }
            //Catch and display a CryptographicException  
            //to the console.
            catch (CryptographicException e)
            {
                Console.WriteLine(e.ToString());

                return null;
            }

        }
    }
}

 

 

 


另外,如果使用TextBox測試的話,記得要將TextBox的MaxLength屬性值設定成0(無限制)哦! 預設長度是32727,如下圖所示。

參考資料

C#使用RSA证书文件加密和解密示例

RSA encryption exception

Java Encryption: RSA Block Size?

.NET 非對稱式加密與數位簽章程式設計入門

[C#.NET] RSA 的長度限制 

RSA加密演算法

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^