[C#.NET] 字串及檔案,利用 RSA 演算法加解密

  • 37049
  • 0
  • RSA
  • 2020-04-13

[C#.NET] 字串及檔案,利用 RSA 演算法加解密

維基對非對稱RSA演算法的說明

http://zh.wikipedia.org/zh-hant/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

.NET提供了 RSACryptoServiceProvider 類別 讓我們來處理RSA加解密,我們雖然不用知道演算法的實作過程,但必須要知道公開金鑰(公開金鑰)跟私密金鑰(Private Key)的使用規則

1.使用公開金鑰加密,使用私密金鑰解密。

2.公開金鑰可對外公開,任何人都能取得,反之私密金鑰就必須保管好不能外洩。

3.非對稱私密金鑰絕對不應一字不漏或以純文字格式儲存在本機電腦中。http://msdn.microsoft.com/zh-tw/library/tswxhw92%28VS.80%29.aspx

 

使用金鑰容器:

//使用金鑰容器
this._cspParameters = new CspParameters(BLOCK_SIZE);
this._cspParameters.KeyContainerName = this.KeyContainerName;
this._cspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
this._cspParameters.ProviderName = "Microsoft Strong Cryptographic Provider";
this._cspParameters.ProviderType = 1;

KeyContainerName :把它想成檔案名稱

Flags:儲存等級,http://msdn.microsoft.com/zh-tw/library/f5cs0acs.aspx

ProviderType:請參考 http://msdn.microsoft.com/zh-tw/library/system.security.cryptography.cspparameters.providertype.aspx

 

當然你也將Xml金鑰保存下來,然後把公開金鑰給相關的人員,但也別忘了私密金鑰要妥當保存並且加密。

 

產生金鑰:

public void GenerateKey()
{
    //使用金鑰容器
    this._cspParameters = new CspParameters(BLOCK_SIZE);
    this._cspParameters.KeyContainerName = this.KeyContainerName;
    this._cspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
    this._cspParameters.ProviderName = "Microsoft Strong Cryptographic Provider";
    this._cspParameters.ProviderType = 1;
 
    if (this._rsaCrypto != null)
    {
        this._rsaCrypto.PersistKeyInCsp = false;
        this._rsaCrypto.Clear();
    }
 
    this._rsaCrypto = new RSACryptoServiceProvider(this._cspParameters);
    this.PrivateKey = this._rsaCrypto.ToXmlString(true);
    this.PublicKey = this._rsaCrypto.ToXmlString(false);
}
this.PrivateKey = rsa.ToXmlString(true);
this.PublicKey = rsa.ToXmlString(false);

由下圖可得知私密金鑰包含了公開金鑰

image

image

整個加解密的流程如下:

image

 


接下來來看怎麼實做。

加解密都是處理資料的byte[],只要將資料轉成byte[],然後餵給相關的方法就可以了

加密:

private byte[] encryptor(byte[] OriginalData)
{
    if (OriginalData == null || OriginalData.Length <= 0)
    {
        throw new NotSupportedException();
    }
    if (this._rsaCrypto == null)
    {
        throw new ArgumentNullException();
    }
    this._rsaCrypto.FromXmlString(this.PublicKey);
 
    var encryptData = this._rsaCrypto.Encrypt(OriginalData, false);
    return encryptData;
}

 

解密:

private byte[] decryptor(byte[] EncryptDada)
{
    if (EncryptDada == null || EncryptDada.Length <= 0)
    {
        throw new NotSupportedException();
    }
 
    this._rsaCrypto.FromXmlString(this.PrivateKey);
    var decrtpyData = this._rsaCrypto.Decrypt(EncryptDada, false);
    return decrtpyData;
}


 

字串加密:

public string EncryptString(string OriginalString)
{
    if (string.IsNullOrEmpty(OriginalString))
    {
        throw new NotSupportedException();
    }
    var originalData = this.Encode.GetBytes(OriginalString);
    var encryptData = this.encryptor(originalData);
    var base64 = Convert.ToBase64String(encryptData);
    return base64;
}

 

檔案加密:

public void EncryptFile(string OriginalFile, string EncrytpFile)
{
    using (FileStream originalStream = new FileStream(OriginalFile, FileMode.Open, FileAccess.Read))
    using (FileStream encrytpStream = new FileStream(EncrytpFile, FileMode.Create, FileAccess.Write))
    {
        //加密
        var dataByteArray = new byte[originalStream.Length];
        originalStream.Read(dataByteArray, 0, dataByteArray.Length);
        var encryptData = encryptor(dataByteArray);
        //寫檔
        encrytpStream.Write(encryptData, 0, encryptData.Length);
    }
}

 

字串解密:

public string DecryptString(string EncryptString)
{
    if (string.IsNullOrEmpty(EncryptString))
    {
        throw new NotSupportedException();
    }
    var encryptData = Convert.FromBase64String(EncryptString);
    var decryptData = this.decryptor(encryptData);
    var decryptString = this.Encode.GetString(decryptData);
    return decryptString;
}

 

檔案解密:

public void DecryptFile(string EncrytpFile, string DecrytpFile)
{
    using (FileStream encrytpStream = new FileStream(EncrytpFile, FileMode.Open, FileAccess.Read))
    using (FileStream decrytpStream = new FileStream(DecrytpFile, FileMode.Create, FileAccess.Write))
    {
        //解密
        var dataByteArray = new byte[encrytpStream.Length];
        encrytpStream.Read(dataByteArray, 0, dataByteArray.Length);
        var decryptData = this.decryptor(dataByteArray);
        //寫檔
        decrytpStream.Write(decryptData, 0, decryptData.Length);
    }
}

數位簽章:用來判斷訊息是否遭到篡改

1.使用私密金鑰產生簽章,使用公開金鑰驗証簽章。(這跟加密方式不太一樣)

 

訊息內容經過雜湊演算法處理(比如SHA1),得到一組簽章。

public string GetSignature(string OriginalString)
{
    if (string.IsNullOrEmpty(OriginalString))
    {
        throw new ArgumentNullException();
    }
    this._rsaCrypto.FromXmlString(this.PrivateKey);
    var originalData = this.Encode.GetBytes(OriginalString);
    var singData = this._rsaCrypto.SignData(originalData, new SHA1CryptoServiceProvider());
    var SignatureString = Convert.ToBase64String(singData);
    return SignatureString;
}

 

然後再把簽章跟原始訊息做比對。

public bool VerifySignature(string OriginalString, string SignatureString)
{
    if (string.IsNullOrEmpty(OriginalString))
    {
        throw new ArgumentNullException();
    }
 
    this._rsaCrypto.FromXmlString(this.PublicKey);
    var originalData = this.Encode.GetBytes(OriginalString);
    var signatureData = Convert.FromBase64String(SignatureString);
    var isVerify = this._rsaCrypto.VerifyData(originalData, new SHA1CryptoServiceProvider(), signatureData);
    return isVerify;
}

 

 

使用情境:

當A廠商發佈公開金鑰後,其他廠商就能拿著A廠商提供的公開金鑰加密,再拋回給A廠商解密,當越來越多廠商有A廠商的公開金鑰後,A廠商可能會接收到冒名的資料,A廠商要如何判斷是某廠商所傳來的訊息?這時候加上數位簽章,就可以解決。

原本的加密流程再加上簽章,可以變成這樣

image


實戰演練:

假設兩家廠商有各自的Key

image

 

B:拿著A發佈的公開金鑰加密,並用自己的私密金鑰產生簽章

image

 

A:拿著B加密後的資料,用自己的私密金鑰解密。

image

 

A:拿著B發佈的公開金鑰。

image

 

A:用B給的公開金鑰驗証簽章資料,驗証成功表示該A拿到的資料確實是B給的。

image


範例下載:

https://github.com/yaochangyu/sample.dotblog/tree/master/Crypto/Simple.RsaCrypto

RsaCrypto

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo