1. 程式人生 > >C#的RSA私鑰加密&公鑰解密類

C#的RSA私鑰加密&公鑰解密類

1.問題的出現


        我們知道,C#的類庫中提供了RSA加密、解密以及金鑰(publickey&privatekey)的生成方法,使我們能夠很方便的實現RSA的加密解密以及金鑰生成的操作。然而,在我們做專案的時候,我們也會發現C#自帶的RSA類有以下的侷限性:

  1. 金鑰生成的格式為XML,雖然這種標準的格式使得我們得以在C#程式中輕鬆將XML格式的金鑰匯入,但是這種XML格式的金鑰卻不能很好的相容一些其他的專案(比如Java專案)

  2. 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類的這兩個靜態方法來實現私鑰加密以及公鑰解密的功能了。