前言
這兩天主要是公司同僚用到了RSA加密,事後也看了下,以為很簡單,最終利用RSACryptoServiceProvider來實作RSA加密,然後大緻了解到RSACryptoServiceProvider不支援跨平台,此類原先存在于.NET Framework中,本文我講講利用RSA.Create來實作各種加密模式統一封裝。
RSA加密或解密
主要是在查找資料時看到了dudu老大寫的有關RSA加密的文章(http://www.cnblogs.com/dudu/p/dotnet-core-rsa-openssl.html),剛好找到了我們項目中需要用到的openssl加密模式對應.NET Core中的Pkcs1,這裡實作代碼實在有點多,然後自己也遇到如評論中所說加密長度超出的情況,這個時候隻能采取分段加密的方式。接下來我們來看看。首先看看如下代碼:
var privateKey = "<RSAKeyValue><Modulus>0wE26IHp4U9OLtPhJ+fT8ej6aWORFP8pd++MjUuhkQQm/zhcImbxQbjxtSAftz+kkDwGDFJpSldQPyigOGcUx7PofTc6VhiFik9E9SsxV9n0iEEtqUndDfmBJfPAWt+4UDMwKakgZqFoapDuwjKlTErFvKCyKCs+qN9OZvZwKWk=</Modulus><Exponent>AQAB</Exponent><P>8Ei6NIsZtgV3DQjuGHfGLS6o1O+IUXxzjqLxdMm77yhEPUxR9YPIxODJ2VVTddXSAHxViJJt30yJ7JhVz6cpQw==</P><Q>4M49NrmalgVQFMsea2RMB1qN8fAPfIw5G9q9hzsLcWSCmkeRRIQlvPYflVEKAYKiDVVzENETbnnduFXWBABx4w==</Q><DP>t+JQbemN0Zi5FQaif6MZzHYKynpNTl75aE0Wj5Pa+RlNr8N6bXNe8Bw/HM2Jw4HQ5oJASvYUk3DVlHS4JuP8VQ==</DP><DQ>lT62iv9brp9mU/epgVh71SH8PJPIZEJfo6tryjyb0zMMNcqvmZI1z6aCv0mm3+vPFBUXqCF1yhFj7n4l8FAvSw==</DQ><InverseQ>flrvgxHvf4l+fdymEVDgKjsfGqshOpppoNgZj9kpeWBto3o8z++Ki6eSLQT3nVnpx2QCZeTWkxTED4nhSLKscw==</InverseQ><D>cQTCg1Eqk7sltmFYxUYgOP/AOPjSufteG9acYwYymPkvZh6rAuY+rSRBmvGE62NUYskzuB/gM6iG2/2HrA5SixfNgCvZ+nsK+kX5pzQRsYdD71ViQW0hOanXwj45I2zHRgBiuTtCUP0fs5pISmQkaeJkDL5pO2l+wvlgl+wunj0=</D></RSAKeyValue>";
var publicKey = "<RSAKeyValue><Modulus>0wE26IHp4U9OLtPhJ+fT8ej6aWORFP8pd++MjUuhkQQm/zhcImbxQbjxtSAftz+kkDwGDFJpSldQPyigOGcUx7PofTc6VhiFik9E9SsxV9n0iEEtqUndDfmBJfPAWt+4UDMwKakgZqFoapDuwjKlTErFvKCyKCs+qN9OZvZwKWk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
using (var rsa = RSA.Create())
{
rsa.FromXmlString(publicKey);
}
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5COwAzN4YTMwcTMtgjN5QDM2IjMwYjMwEDOxAjMtIDN2kDO18CXwEDOxAjMvwlM0YTO4UzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
經到github上查找解決方案,我們需要手動設定值,如下:
public static void FromXmlString(RSA rsa, string xmlString)
{
RSAParameters parameters = new RSAParameters();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
{
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
switch (node.Name)
{
case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
}
}
}
else
{
throw new Exception("Invalid XML RSA key.");
}
rsa.ImportParameters(parameters);
}
其中經過很多嘗試,要麼是Base64格式不正确,要麼是待解密的資料長度和Key Size無效,我第一次嘗試是傳回的加密字元串,然後進行解密,就是有問題,最後還是傳回位元組數組來進行加密和解密,直接看代碼,這裡解決了加密資料長度超出問題以及各種加密模式的統一,如下RSAHelper。
public static class RSAHelper
{
private const string privateKey = "<RSAKeyValue><Modulus>0wE26IHp4U9OLtPhJ+fT8ej6aWORFP8pd++MjUuhkQQm/zhcImbxQbjxtSAftz+kkDwGDFJpSldQPyigOGcUx7PofTc6VhiFik9E9SsxV9n0iEEtqUndDfmBJfPAWt+4UDMwKakgZqFoapDuwjKlTErFvKCyKCs+qN9OZvZwKWk=</Modulus><Exponent>AQAB</Exponent><P>8Ei6NIsZtgV3DQjuGHfGLS6o1O+IUXxzjqLxdMm77yhEPUxR9YPIxODJ2VVTddXSAHxViJJt30yJ7JhVz6cpQw==</P><Q>4M49NrmalgVQFMsea2RMB1qN8fAPfIw5G9q9hzsLcWSCmkeRRIQlvPYflVEKAYKiDVVzENETbnnduFXWBABx4w==</Q><DP>t+JQbemN0Zi5FQaif6MZzHYKynpNTl75aE0Wj5Pa+RlNr8N6bXNe8Bw/HM2Jw4HQ5oJASvYUk3DVlHS4JuP8VQ==</DP><DQ>lT62iv9brp9mU/epgVh71SH8PJPIZEJfo6tryjyb0zMMNcqvmZI1z6aCv0mm3+vPFBUXqCF1yhFj7n4l8FAvSw==</DQ><InverseQ>flrvgxHvf4l+fdymEVDgKjsfGqshOpppoNgZj9kpeWBto3o8z++Ki6eSLQT3nVnpx2QCZeTWkxTED4nhSLKscw==</InverseQ><D>cQTCg1Eqk7sltmFYxUYgOP/AOPjSufteG9acYwYymPkvZh6rAuY+rSRBmvGE62NUYskzuB/gM6iG2/2HrA5SixfNgCvZ+nsK+kX5pzQRsYdD71ViQW0hOanXwj45I2zHRgBiuTtCUP0fs5pISmQkaeJkDL5pO2l+wvlgl+wunj0=</D></RSAKeyValue>";
private const string publicKey = "<RSAKeyValue><Modulus>0wE26IHp4U9OLtPhJ+fT8ej6aWORFP8pd++MjUuhkQQm/zhcImbxQbjxtSAftz+kkDwGDFJpSldQPyigOGcUx7PofTc6VhiFik9E9SsxV9n0iEEtqUndDfmBJfPAWt+4UDMwKakgZqFoapDuwjKlTErFvKCyKCs+qN9OZvZwKWk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
public static byte[] Encrypt(byte[] encryptBytes, RSAEncryptionPadding padding)
{
using (var rsa = RSA.Create())
{
FromXmlString(rsa, publicKey);
var maxBlockSize = GetMaxBlockSize(rsa, padding);
if (encryptBytes.Length <= maxBlockSize)
{
var @bytes = rsa.Encrypt(encryptBytes, padding);
return @bytes;
}
using (var memoryStream = new MemoryStream(encryptBytes))
{
using (var readStream = new MemoryStream())
{
byte[] buffer = new byte[maxBlockSize];
int blockSize = memoryStream.Read(buffer, 0, maxBlockSize);
while (blockSize > 0)
{
var blockByte = new byte[blockSize];
Array.Copy(buffer, 0, blockByte, 0, blockSize);
var encrypts = rsa.Encrypt(blockByte, padding);
readStream.Write(encrypts, 0, encrypts.Length);
blockSize = memoryStream.Read(buffer, 0, maxBlockSize);
}
return readStream.ToArray();
}
}
}
}
public static byte[] Decrypt(byte[] decryptBytes, RSAEncryptionPadding padding)
{
using (var rsa = RSA.Create())
{
FromXmlString(rsa, privateKey);
var maxBlockSize = rsa.KeySize / 8;
if (decryptBytes.Length <= maxBlockSize)
{
var @bytes = rsa.Decrypt(decryptBytes, padding);
return @bytes;
}
using (var memoryStream = new MemoryStream(decryptBytes))
{
using (var readStream = new MemoryStream())
{
var buffer = new byte[maxBlockSize];
var blockSize = memoryStream.Read(buffer, 0, maxBlockSize);
while (blockSize > 0)
{
var blockByte = new byte[blockSize];
Array.Copy(buffer, 0, blockByte, 0, blockSize);
var decrypts = rsa.Decrypt(blockByte, padding);
readStream.Write(decrypts, 0, decrypts.Length);
blockSize = memoryStream.Read(buffer, 0, maxBlockSize);
}
return readStream.ToArray();
}
}
}
}
public static void FromXmlString(RSA rsa, string xmlString)
{
RSAParameters parameters = new RSAParameters();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
{
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
switch (node.Name)
{
case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
}
}
}
else
{
throw new Exception("Invalid XML RSA key.");
}
rsa.ImportParameters(parameters);
}
static int GetMaxBlockSize(RSA rsa, RSAEncryptionPadding padding)
{
var offset = 0;
if (padding.Mode == RSAEncryptionPaddingMode.Pkcs1)
{
offset = 11;
}
else
{
if (padding.Equals(RSAEncryptionPadding.OaepSHA1))
{
offset = 42;
}
if (padding.Equals(RSAEncryptionPadding.OaepSHA256))
{
offset = 66;
}
if (padding.Equals(RSAEncryptionPadding.OaepSHA384))
{
offset = 98;
}
if (padding.Equals(RSAEncryptionPadding.OaepSHA512))
{
offset = 130;
}
}
return rsa.KeySize / 8 - offset;
}
}
我們開始進行如下測試,加密資料長度為6890,如下。
static void Main(string[] args)
{
var encryptString = "0";
for (int i = 1; i < 2000; i++)
{
encryptString += i;
}
Console.WriteLine(encryptString.Length);
var encryptBytes = Encoding.UTF8.GetBytes(encryptString);
//加密後位元組數組
var resultBytes = RSAHelper.Encrypt(encryptBytes, RSAEncryptionPadding.Pkcs1);
//解密後位元組數組
var decryptBytes = RSAHelper.Decrypt(resultBytes, RSAEncryptionPadding.Pkcs1);
//解密結果
var result = Encoding.UTF8.GetString(decryptBytes);
//比較加密字元串和解密結果是否相等
Console.WriteLine(encryptString == result);
Console.ReadKey();
}
總結
本文通過實作RSA加密和解密,同時也對加密資料超出采取分段加密的方式,測試在windows上通過,由于沒有linux和mac環境未經測試,不知是否好使,一試見分曉。
你所看到的并非事物本身,而是經過诠釋後所賦予的意義