[C#.NET] 利用 DES / AES 演算法加解密

来源:互联网 发布:网络mg老虎机技巧 编辑:程序博客网 时间:2024/05/21 19:49

維基對這兩種演算法的說明

http://zh.wikipedia.org/zh-hant/%E8%B3%87%E6%96%99%E5%8A%A0%E5%AF%86%E6%A8%99%E6%BA%96

在.NET裡

DES 演算法會用到 DSACryptoServiceProvider 類別

AES 演算法

2.0:RijndaelManaged 類別 (AES又叫Rijndael)

3.5:除了原本的 RijndaelManaged 類別 還多了 AesCryptoServiceProvider 類別,這兩個類別產生的結果相同。

AES與DES這兩種加密演算法都需要KEY與IV,AES/DES類別都有以下兩種屬性,必需要依照規則丟固定長度的Bit

  • LegalBlockSizes是表示IV的容許長度,單位Bit
  • LegalKeySizes表示KEY的容許長度,單位Bit

為了方便講解,我整理出下表:


PS.1 Byte=8 Bit, 所以 128 Bit = 16 Byte, 256 Bit = 32 Byte

所以我們在餵KEY跟IV的時候,就會有長度限制的問題要注意


接下來我們來看範例

下段比較值得注意的是編碼的部份,

1.下段是用Encoding.UTF8.GetBytes(source)來取得原始資料的Byte,所以解密的時候也要用Encoding.UTF8

2.另外加密後的Byte Convert String是用Convert.ToBase64String所以解密的時候也要用相對應的方式來處理

總之,要對應就對了

private string desEncryptBase64(string source){    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    byte[] dataByteArray = Encoding.UTF8.GetBytes(source);    des.Key = key;    des.IV = iv;    string encrypt = "";    using (MemoryStream ms = new MemoryStream())    using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))    {        cs.Write(dataByteArray, 0, dataByteArray.Length);        cs.FlushFinalBlock();        encrypt = Convert.ToBase64String(ms.ToArray());    }    return encrypt;}private string desDecryptBase64(string encrypt){    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    des.Key = key;    des.IV = iv;    byte[] dataByteArray = Convert.FromBase64String(encrypt);    using (MemoryStream ms = new MemoryStream())    {        using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))        {            cs.Write(dataByteArray, 0, dataByteArray.Length);            cs.FlushFinalBlock();            return Encoding.UTF8.GetString(ms.ToArray());        }    }}
執行結果如下:



再來看另外一資資料格式輸出,加密過程都一樣,只是資料產出的方式不一樣,也就是說,你可以依個人喜好選用不同的資料輸出樣式,參考之前寫過的,http://www.dotblogs.com.tw/yc421206/archive/2009/08/11/9984.aspx

private string desEncrypt(string source){    StringBuilder sb = new StringBuilder();    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    byte[] dataByteArray = Encoding.UTF8.GetBytes(source);    des.Key = key;    des.IV = iv;    string encrypt = "";    using (MemoryStream ms = new MemoryStream())    using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))    {        cs.Write(dataByteArray, 0, dataByteArray.Length);        cs.FlushFinalBlock();        //輸出資料        foreach (byte b in ms.ToArray())        {            sb.AppendFormat("{0:X2}", b);        }        encrypt = sb.ToString();    }    return encrypt;}private string desDecrypt(string encrypt){    byte[] dataByteArray = new byte[encrypt.Length / 2];    for (int x = 0; x < encrypt.Length / 2; x++)    {        int i = (Convert.ToInt32(encrypt.Substring(x * 2, 2), 16));        dataByteArray[x] = (byte)i;    }    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    des.Key = key;    des.IV = iv;    using (MemoryStream ms = new MemoryStream())    {        using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))        {            cs.Write(dataByteArray, 0, dataByteArray.Length);            cs.FlushFinalBlock();            return Encoding.UTF8.GetString(ms.ToArray());        }    }
}
加密字串跟Base64有很大的不同


接下來看看怎麼處理檔案,先準備好要處理的檔案


private void desEncryptFile(string sourceFile, string encryptFile){    if (string.IsNullOrEmpty(sourceFile) || string.IsNullOrEmpty(encryptFile))    {        return;    }    if (!File.Exists(sourceFile))    {        return;    }    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    des.Key = key;    des.IV = iv;    using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))    using (FileStream encryptStream = new FileStream(encryptFile, FileMode.Create, FileAccess.Write))    {        //檔案加密        byte[] dataByteArray = new byte[sourceStream.Length];        sourceStream.Read(dataByteArray, 0, dataByteArray.Length);        using (CryptoStream cs = new CryptoStream(encryptStream, des.CreateEncryptor(), CryptoStreamMode.Write))        {            cs.Write(dataByteArray, 0, dataByteArray.Length);            cs.FlushFinalBlock();        }    }}private void desDecryptFile(string encryptFile, string decryptFile){    if (string.IsNullOrEmpty(encryptFile) || string.IsNullOrEmpty(decryptFile))    {        return;    }    if (!File.Exists(encryptFile))    {        return;    }    DESCryptoServiceProvider des = new DESCryptoServiceProvider();    byte[] key = Encoding.ASCII.GetBytes("12345678");    byte[] iv = Encoding.ASCII.GetBytes("87654321");    des.Key = key;    des.IV = iv;    using (FileStream encryptStream = new FileStream(encryptFile, FileMode.Open, FileAccess.Read))    using (FileStream decryptStream = new FileStream(decryptFile, FileMode.Create, FileAccess.Write))    {        byte[] dataByteArray = new byte[encryptStream.Length];        encryptStream.Read(dataByteArray, 0, dataByteArray.Length);        using (CryptoStream cs = new CryptoStream(decryptStream, des.CreateDecryptor(), CryptoStreamMode.Write))        {            cs.Write(dataByteArray, 0, dataByteArray.Length);            cs.FlushFinalBlock();        }    }}
檔案加密的效果


解密後的效果,基本上要跟source.txt一樣


若你看到這裡還不太懂,只要記住一點不管加解密什麼都是處理Byte就對了。


Q:加密的KEY或IV能用中文嗎?

A:當然可以呀,只要能符合長度就可以


Q:每次都要計算byte的長度,很煩人,怎麼辦?

A:可以利用以下類別產生符合規格的Byte

Rfc2898DeriveBytes 類別 / PasswordDeriveBytes 類別:不管你打再長的密碼都會經過 Salt 亂數處理,以符合規則

private void generatorRfc(){    byte[] salt = new byte[] { 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xF1 };    byte[] key = Encoding.UTF8.GetBytes("我跟你說你不要跟別人說,你若跟別人說,不要跟別人說是我叫你不要跟別人說");    Rfc2898DeriveBytes rfcKey = new Rfc2898DeriveBytes(key, salt, 8);    Rfc2898DeriveBytes rfcIv = new Rfc2898DeriveBytes("0987654321", salt, 8);    byte[] keyData = rfcKey.GetBytes(8);    byte[] IVData = rfcIv.GetBytes(8);}



RNGCryptoServiceProvider 類別:用它來幫你產生符合規則的長度

public static byte[] generatorRng(int length){    byte[] randBytes;    if (length >= 1)    {        randBytes = new byte[length];    }    else    {        randBytes = new byte[8];    }    RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();    rand.GetBytes(randBytes);    return randBytes;}

Q:為什麼沒有AES的範例?

A:AES的用法跟DES一模一樣,在這裡就不多佔篇幅,AES比DES支援的長度還要長,所以它有比較多的選擇來處理KEY/IV,比如

SHA256CryptoServiceProvider 類別:將資料雜湊後產生32 Byte的資料

MD5CryptoServiceProvider 類別:將資料雜湊後產生16 Byte的資料

private void aes(){    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();    SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider();    AesCryptoServiceProvider provider = new AesCryptoServiceProvider();    byte[] keyData = sha256.ComputeHash(Encoding.UTF8.GetBytes("我跟你說你不要跟別人說,你若跟別人說,不要跟別人說是我叫你不要跟別人說"));    byte[] IVData = md5.ComputeHash(Encoding.UTF8.GetBytes("我跟你說你不要跟別人說,你若跟別人說,不要跟別人說是我叫你不要跟別人說"));    provider.Key = keyData;    provider.IV = IVData;}


這樣一來也就能處理掉長度的問題

Q:有更簡單的方法嗎?

A:當時可以,AES/DES初始化的時候就會自己產生KEY跟IV,只要將它產生的KEY/IV記下就好了

public void generatorKey(){    //var provider = new DESCryptoServiceProvider();    var provider = new AesCryptoServiceProvider();    var key = Convert.ToBase64String(provider.Key);    var iv = Convert.ToBase64String(provider.IV);}


後記:

這篇主要是在介紹怎麼使用AES/DES,所以程式碼寫的很亂,重覆的地方相當的多,瞭解了AES跟DES的來龍去脈後,就能寫出屬於自己的加解密類別,當然,這部份重構就得靠自己來了。

0 0
原创粉丝点击