[.NET]使用RSA憑証加/解密
最近客戶需要將資料以公開金鑰(public key)加密,然後用私密金鑰(private key)解密。
參考「C#使用RSA证书文件加密和解密示例」很快的就實作出來了,
但是當我將加密的內容增加時,卻發生了「長度錯誤」Invalid length的錯誤,
原來是因為長度大於允許的最大長度,
所以就改使用「RSA encryption exception」說的方式,改用blocks的方式。這樣就不怕要加密的內容會超過限制了!
但因為我們的Key Size是2048,所以_DecryptionBufferSize就變成了256(2048/8),_EncryptionBufferSize就變成了245。
程式如下,
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,如下圖所示。
參考資料
Java Encryption: RSA Block Size?
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^