1.问题的出现
我们知道,C#的类库中提供了RSA加密、解密以及密钥(publickey&privatekey)的生成方法,使我们能够很方便的实现RSA的加密解密以及密钥生成的操作。然而,在我们做项目的时候,我们也会发现C#自带的RSA类有以下的局限性:
密钥生成的格式为XML,虽然这种标准的格式使得我们得以在C#程序中轻松将XML格式的密钥导入,但是这种XML格式的密钥却不能很好的兼容一些其他的项目(比如Java项目)
C#自带的RSA类只支持公钥加密私钥解密,却不支持私钥加密公钥解密(此文章将要讨论的问题),致使我们在开发某些项目(如用于软件加密、服务端数据私钥加密)时无法顺利进行
说到这里,就有必要说一下关于RSA的概念了。
2.关于RSA
RSA是一种不对称的加密算法,其安全性依赖于大数的因子分解,其生成的公钥/私钥对可以分别用于加密/解密或者解密/加密,也就是说,在理论上,RSA算法既可以实现公钥加密私钥解密,也可以实现私钥加密公钥解密。公钥加密私钥解密模式可以用于加密情形,这样,用户加密的数据在传输过程中被劫持也无法被第三方破解,从而保证用户与服务端握手的安全性。而私钥加密公钥解密则可以用于数字签名,这样,通过私钥加密产生的电子证书是不能被伪造的,而拿到公钥的用户可以使用公钥对其解密 ,从而可以校验此电子证书。
也就是说,在理论上,我们是可以实现私钥加密公钥解密的,那么我们首先就要了解一下C#中的RSA类。
3.C#中的RSA类
在C#中,RSACryptoServiceProvider类可以用于RSA加密解密以及密钥对生成。
实例化
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
导出密钥
RSAParameters privatekey = rsa.ExportParameters(true); //导出私钥
RSAParameters publickey = rsa.ExportParameters(false); //导出公钥
导入密钥
rsa.FromXmlString(str); //参数str为XML格式的字符串
公钥加密
byte[] arrSource = Encoding.UTF8.GetBytes(source); //source为要加密的字符串 byte[] arrEnc = rsa.Encrypt(arrSource, false); string Enc = Convert.ToBase64String(arrEnc);
私钥解密
byte[] arrEnc = Convert.FromBase64String(Enc); //Enc为需要解密的字符串 byte[] arrDec = rsa.Decrypt(arrEnc, false); string Dec = Encoding.UTF8.GetString(arrDec);
XML格式的密钥中,私钥包含Modulus、Exponent(生成密钥一般都是用的65537)、P、Q、DP、DQ、InverseQ、D,公钥包含Modulus、Exponent,其中Modulus和Exponent为RSA公钥,Modulus和D为RSA私钥。
4.实现私钥加密&公钥解密
考虑到使用大数运算自己编写RSA私钥加密以及公钥解密的类方法比较繁琐,我想到了BouncyCastle,我们首先下载BouncyCastle的dll,然后新建一个RSAHelper类
首先做引入以下的库,这些库可以用于字符串编码、使用C#自带的RSA类
using System.Text; using System; using System.Security.Cryptography;
然后,我们还需要引入BouncyCastle的相关库
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Parameters;
下面我们首先编写私钥加密函数
/// <summary> /// 用私钥给数据进行RSA加密 /// </summary> /// <param name="xmlPrivateKey"> 私钥(XML格式字符串)</param> /// <param name="strEncryptString"> 要加密的数据 </param> /// <returns> 加密后的数据 </returns> public static string PrivateKeyEncrypt(string xmlPrivateKey, string strEncryptString){ //加载私钥 RSACryptoServiceProvider privateRsa = new RSACryptoServiceProvider(); privateRsa.FromXmlString(xmlPrivateKey); //转换密钥 AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetKeyPair(privateRsa); IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); //使用RSA/ECB/PKCS1Padding格式 //第一个参数为true表示加密,为false表示解密;第二个参数表示密钥 c.Init(true, keyPair.Private); byte[] DataToEncrypt = Encoding.UTF8.GetBytes(strEncryptString); byte[] outBytes = c.DoFinal(DataToEncrypt);//加密 string strBase64 = Convert.ToBase64String(outBytes); return strBase64; }
接下来,编写公钥解密函数
/// <summary> /// 用公钥给数据进行RSA解密 /// </summary> /// <param name="xmlPublicKey"> 公钥(XML格式字符串) </param> /// <param name="strDecryptString"> 要解密数据 </param> /// <returns> 解密后的数据 </returns> public static string PublicKeyDecrypt(string xmlPublicKey, string strDecryptString){ //加载公钥 RSACryptoServiceProvider publicRsa = new RSACryptoServiceProvider(); publicRsa.FromXmlString(xmlPublicKey); RSAParameters rp = publicRsa.ExportParameters(false);//转换密钥 AsymmetricKeyParameter pbk = DotNetUtilities.GetRsaPublicKey(rp); IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); //第一个参数为true表示加密,为false表示解密;第二个参数表示密钥 c.Init(false, pbk); byte[] DataToDecrypt = Convert.FromBase64String(strDecryptString); byte[] outBytes = c.DoFinal(DataToDecrypt);//解密 string strDec = Encoding.UTF8.GetString(outBytes); return strDec; }
大功告成,现在我们就可以使用RSAHelper类的这两个静态方法来实现私钥加密以及公钥解密的功能了。