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

本文详细介绍了在.NET环境下使用AES和DES加密算法的实现方式,包括类别的选择、参数设置、编码与解码过程,以及如何处理文件加密与解密。通过实例演示了如何使用ASCII和UTF-8编码获取和转换数据,如何创建和使用CryptoStream进行加密和解密操作,并提供了AES与DES加密解密的范例代码。

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

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的來龍去脈後,就能寫出屬於自己的加解密類別,當然,這部份重構就得靠自己來了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值