介绍
有朋友叫帮忙做有一个RSA加解密的帮助类,后面发现是与JAVA做交互,对方提供了公钥cer文件。由于C#默认RSA只支持公钥加密,私钥解密。而现在需求正好相反,只能使用公钥加密,公钥解密,而且C#与Java并不互通,所以采用第三方类库 BouncyCastle来进行实现。
安装
通过VS自带NuGet搜索BouncyCastle即可安装
加密过程
加密流程为
1.将待加密字符串通过 UTF-8 字符编码转换为二进制数组;
2.将输入二进制数据按 117 字节分组(最后一组大小可小于 117 字节) ;
3.将各分组分别进行 RSA 加密操作后,将加密结果(二进制数组)顺次拼接在一起;
4.对加密后得到的二进制数组进行Base64编码处理后得到加密结果
1.从文件中获取公钥
/// <summary>
/// 从文件中获取公钥
/// </summary>
/// <param name="_data"></param>
/// <returns></returns>
public static PublicKey GetPublicKey()
{
try
{
X509Certificate2 x5092 = new X509Certificate2(CER_PATH);
return x5092.PublicKey;
}
catch (Exception ex)
{
throw ex;
}
}
2.对数据加密过程
/// <summary>
/// 对数据进行加密
/// </summary>
/// <param name="key">加密密钥</param>
/// <param name="data">待加密数据</param>
/// <returns>加密结果</returns>
public static byte[] RsaEncrypt(PublicKey key, byte[] data)
{
// -- 初始化RSA加密算法
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.ImportParameters(((RSA)key.Key).ExportParameters(false));
using (MemoryStream bout = new MemoryStream())
{
for (int i = 0; i < data.Length; i += MAX_ENCRYPT_BLOCK)
{ // 由于RSA加密一次最多只能从117字节到128字节,需要多次进行
// 当前循环的加密块大小
int encryptBlockSize = ((i + MAX_ENCRYPT_BLOCK) <= data.Length) ? MAX_ENCRYPT_BLOCK : (data.Length - i); // 对最后一块数据的逻辑处理 // 加密数据块
byte[] chunk = new byte[encryptBlockSize];
Array.Copy(data, i, chunk, 0, encryptBlockSize);
byte[] encryptedChunk = provider.Encrypt(chunk, false);
bout.Write(encryptedChunk, 0, encryptedChunk.Length);
}
return bout.ToArray();
}
}
解密过程
解密流程为
1.将加密字符串通过Base64解码为二进制数组
2.将输入二进制数据按 128 字节分组(最后一组大小可小于 128 字节) ;
3.将各分组分别进行 RSA 解密操作后,将解密结果(二进制数组)顺次拼接在一起;
4.通过 UTF-8 字符编码将最终解密结果的二进制数组转换为字符串。
1.从文件中获取公钥 xml格式
/// <summary>
/// 从证书中获取公钥
/// </summary>
/// <param name="cerPath"></param>
/// <returns></returns>
private string GetPublicKeyFromCer(string cerPath)
{
RSACryptoServiceProvider pubkey = (RSACryptoServiceProvider)GetPublicKey().Key;
return pubkey.ToXmlString(false);
}
2.将C#格式公钥转成Java格式公钥
/// <summary>
/// 将C#格式公钥转成Java格式公钥
/// </summary>
/// <param name="publicKey"></param>
/// <returns></returns>
public RsaKeyParameters RSAPublicKeyDotNet2Java(string publicKey)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(publicKey);
BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
return pub;
}
3.解密数据
/// <summary>
/// 密文解密
/// </summary>
/// <param name="xmlPublicKey"></param>
/// <param name="strEncryptString"></param>
/// <returns></returns>
public string RSADecryptByPublicKey(string xmlPublicKey, string strEncryptString)
{
//得到公钥
RsaKeyParameters keyParams = RSAPublicKeyDotNet2Java(xmlPublicKey);
//参数与Java中加密解密的参数一致
IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");
//第一个参数 true-加密,false-解密;第二个参数表示密钥
c.Init(false, keyParams);
//对密文进行base64解码
byte[] dataFromEncrypt = Convert.FromBase64String(strEncryptString);
//解密
string result = "";
for (int i = 0; i < dataFromEncrypt.Length; i += MAX_DECRYPT_BLOCK)
{
int encryptBlockSize = ((i + MAX_DECRYPT_BLOCK) <= dataFromEncrypt.Length) ? MAX_DECRYPT_BLOCK : (dataFromEncrypt.Length - i); // 对最后一块数据的逻辑处理
byte[] chunk = new byte[encryptBlockSize];
Array.Copy(dataFromEncrypt, i, chunk, 0, encryptBlockSize);
byte[] encryptedChunk = c.DoFinal(chunk, 0, encryptBlockSize);
result += Encoding.UTF8.GetString(encryptedChunk);
}
return result;
}
其他
MAX_ENCRYPT_BLOCK 定义最大加密长度 为117
MAX_DECRYPT_BLOCK 定义最大解密长度 为128
这里还要注意base64的编码格式,如果编码格式不对,会报Unknown block type错误
Comments NOTHING