.NET Core加解密實戰系列之——對稱加密演算法

福祿網路技術團隊發表於2020-09-11

簡介

加解密現狀,編寫此係列文章的背景:

  • 需要考慮系統環境相容性問題(Linux、Windows)
  • 語言互通問題(如C#、Java等)(加解密本質上沒有語言之分,所以原則上不存在互通性問題)
  • 網上資料版本不一、或不全面
  • .NET官方庫密碼演算法提供不全面,很難針對其他語言(Java)進行適配

本系列文章主要介紹如何在 .NET Core 中使用非對稱加密演算法、編碼演算法、訊息摘要演算法、簽名演算法、對稱加密演算法、國密演算法等一系列演算法,如有錯誤之處,還請大家批評指正。

本系列文章旨在引導大家能快速、輕鬆的瞭解接入加解密,乃至自主組合搭配使用BouncyCastle密碼術包中提供的演算法。

本系列程式碼專案地址:https://github.com/fuluteam/ICH.BouncyCastle.git

上一篇文章《.NET Core加解密實戰系列之——訊息摘要與數字簽名演算法》:https://www.cnblogs.com/fulu/p/13209066.html

功能依賴

BouncyCastle(https://www.bouncycastle.org/csharp) 是一個開放原始碼的輕量級密碼術包;它支援大量的密碼術演算法,它提供了很多 .NET Core標準庫沒有的演算法。

支援 .NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core

功能 依賴
Portable.BouncyCastle Portable.BouncyCastle • 1.8.5

對稱加密演算法

演算法介紹

對稱加密演算法是應用較早的加密演算法,技術成熟。在對稱加密演算法中,資料發信方將明文(原始資料)和加密金鑰一起經過特殊加密演算法處理後,使其變成複雜的加密密文傳送出去。收信方收到密文後,若想解讀原文,則需要使用加密用過的金鑰及相同演算法的逆演算法對密文進行解密,才能使其恢復成可讀明文。在對稱加密演算法中,使用的金鑰只有一個,發收信雙方都使用這個金鑰對資料進行加密和解密,這就要求解密方事先必須知道加密金鑰。

具體演算法有:DES,3DES,TDEA,Blowfish,RC5,IDEA。常見的有:DES,AES,3DES等等。

演算法特點

對稱加密演算法的特點是演算法公開、計算量小、加密速度快、加密效率高。

不足之處是,交易雙方都使用同樣鑰匙,安全性得不到保證。

此外,每對使用者每次使用對稱加密演算法時,都需要使用其他人不知道的唯一鑰匙,這會使得發收信雙方所擁有的鑰匙數量呈幾何級數增長,金鑰管理成為使用者的負擔。

對稱加密演算法在分散式網路系統上使用較為困難,主要是因為金鑰管理困難,使用成本較高。而與公開金鑰加密演算法比起來,對稱加密演算法能夠提供加密和認證卻缺乏了簽名功能,使得使用範圍有所縮小。

對稱加密演算法的優點在於加解密的高速度和使用長金鑰時的難破解性。

對稱加密演算法相比非對稱加密演算法來說,加解密的效率要高得多。但是缺陷在於對於祕鑰的管理上,以及在非安全通道中通訊時,金鑰交換的安全性不能保障。所以在實際的網路環境中,會將兩者混合使用。

0ede9422ae5fe6121b7030ae71caa66d8bfcd15b

常見對稱加密演算法

DES

DES(資料加密標準)於 1976年推出,是最古老的對稱加密方法之一。它是由 IBM 開發的,用於保護敏感的,未分類的電子政府資料,並於 1977年被正式採用,以供聯邦機構使用。DES 使用 56 位加密金鑰,它基於 Feistel 結構,該結構由密碼學家 Horst Feistel 設計。DES 加密演算法是 TLS(傳輸層安全性)版本 1.0 和 1.1 中包含的演算法。

DES 通過將 64 位純文字資料塊劃分為兩個單獨的 32 位塊,並對每個塊分別應用加密過程,將 64 位純文字資料塊轉換為密文。這涉及 16 個回合的各種過程(例如擴充套件,置換,替換或使用回合金鑰的 XOR 操作),資料將在加密後經歷。最終,將生成 64 位加密文字塊作為輸出。

如今,DES 已不再使用,因為它已被許多安全研究人員破解。在 2005年,DES 被正式棄用,並被 AES 加密演算法所取代, 我們將在稍後進行討論。DES 的最大缺點是其加密金鑰長度短,這使暴力破解變得很容易。TLS 1.2 是當今使用最廣泛的 TLS 協議,它沒有使用 DES 加密方法。

3DES

顧名思義,3DES(也稱為 TDEA,代表三重資料加密演算法)是已釋出的 DES 演算法的升級版本。3DES 的開發是為了克服 DES 演算法的缺點,並於 1990年代後期開始投入使用。為此,它將 DES 演算法三次應用於每個資料塊。結果,此過程使 3DES 比其 DES 前身更難破解。它也已成為金融行業支付系統,標準和技術中廣泛使用的加密演算法。它也已成為 TLS,SSH,IPsec 和 OpenVPN 等加密協議的一部分。

所有加密演算法最終都屈從於時間的力量,而 3DES 也不例外。研究人員 Karthikeyan Bhargavan 和 GaëtanLeurent 發現的 Sweet32 漏洞消除了 3DES 演算法中存在的安全漏洞。這一發現導致安全行業考慮棄用該演算法,美國國家標準技術研究院(NIST)在 2019年釋出的指南草案中宣佈棄用該演算法。

根據該草案,到 2023年之後,將在所有新應用程式中廢棄 3DES 的使用。 值得注意的是,TLS 1.3(SSL / TLS 協議的最新標準)也停止了 3DES 的使用。

AES

AES代表“高階加密系統”,是最廣泛使用的加密演算法之一,並且是DES演算法的替代方法。AES也稱為Rijndael,在2001年經NIST批准後成為一種加密標準。與DES不同,AES是一組分組密碼,由不同金鑰長度和分組大小的密碼組成。

AES致力於替代和置換方法。首先,將明文資料轉換為塊,然後使用加密金鑰應用加密。加密過程由各種子過程組成,例如子位元組,移位行,混合列和新增回合金鑰。根據金鑰的大小,執行10、12或14次這樣的回合。值得注意的是,上一輪不包括混合列的子過程以及為加密資料而執行的所有其他子過程。

使用AES加密演算法的優勢

所有這些可以歸結為說AES是安全,快速和靈活的。與DES相比,AES是一種更快的演算法。多個金鑰長度選項是您最大的優勢,因為金鑰越長,破解它們的難度就越大。

如今,AES 是使用最廣泛的加密演算法,它已在許多應用程式中使用,包括:

  • 無線網路安全
  • 處理器安全性和檔案加密
  • SSL / TLS 協議(網站安全)
  • Wi-Fi 安全性
  • 移動應用加密
  • VPN(虛擬專用網)等
  • 許多政府機構,包括國家安全域性(NSA),都依靠 AES 加密演算法來保護其敏感資訊

示例程式碼

密文演算法

public class Algorithms
{
    public const string AES_CBC_NoPadding = "AES/CBC/NoPadding";
    public const string AES_CBC_PKCS7Padding = "AES/CBC/PKCS7Padding";
    public const string AES_CBC_ZerosPadding = "AES/CBC/ZerosPadding";
    public const string AES_CBC_ANSIX923Padding = "AES/CBC/ANSIX923Padding";
    public const string AES_CBC_ISO10126Padding = "AES/CBC/ISO10126Padding";

    public const string AES_ECB_NoPadding = "AES/ECB/NoPadding";
    public const string AES_ECB_PKCS7Padding = "AES/ECB/PKCS7Padding";
    public const string AES_ECB_ZerosPadding = "AES/ECB/ZerosPadding";
    public const string AES_ECB_ANSIX923Padding = "AES/ECB/ANSIX923Padding";
    public const string AES_ECB_ISO10126Padding = "AES/ECB/ISO10126Padding";

    public const string AES_OFB_NoPadding = "AES/OFB/NoPadding";
    public const string AES_OFB_PKCS7Padding = "AES/OFB/PKCS7Padding";
    public const string AES_OFB_ZerosPadding = "AES/OFB/ZerosPadding";
    public const string AES_OFB_ANSIX923Padding = "AES/OFB/ANSIX923Padding";
    public const string AES_OFB_ISO10126Padding = "AES/OFB/ISO10126Padding";

    public const string AES_CFB_NoPadding = "AES/CFB/NoPadding";
    public const string AES_CFB_PKCS7Padding = "AES/CFB/PKCS7Padding";
    public const string AES_CFB_ZerosPadding = "AES/CFB/ZerosPadding";
    public const string AES_CFB_ANSIX923Padding = "AES/CFB/ANSIX923Padding";
    public const string AES_CFB_ISO10126Padding = "AES/CFB/ISO10126Padding";

    public const string AES_CTS_NoPadding = "AES/CTS/NoPadding";
    public const string AES_CTS_PKCS7Padding = "AES/CTS/PKCS7Padding";
    public const string AES_CTS_ZerosPadding = "AES/CTS/ZerosPadding";
    public const string AES_CTS_ANSIX923Padding = "AES/CTS/ANSIX923Padding";
    public const string AES_CTS_ISO10126Padding = "AES/CTS/ISO10126Padding";

    public const string AES_CTR_NoPadding = "AES/CTR/NoPadding";
    public const string AES_CTR_PKCS7Padding = "AES/CTR/PKCS7Padding";
    public const string AES_CTR_ZerosPadding = "AES/CTR/ZerosPadding";
    public const string AES_CTR_ANSIX923Padding = "AES/CTR/ANSIX923Padding";
    public const string AES_CTR_ISO10126Padding = "AES/CTR/ISO10126Padding";

    public const string DES_CBC_NoPadding = "DES/CBC/NoPadding";
    public const string DES_CBC_PKCS7Padding = "DES/CBC/PKCS7Padding";
    public const string DES_CBC_ZerosPadding = "DES/CBC/ZerosPadding";
    public const string DES_CBC_ANSIX923Padding = "DES/CBC/ANSIX923Padding";
    public const string DES_CBC_ISO10126Padding = "DES/CBC/ISO10126Padding";

    public const string DES_ECB_NoPadding = "DES/ECB/NoPadding";
    public const string DES_ECB_PKCS7Padding = "DES/ECB/PKCS7Padding";
    public const string DES_ECB_ZerosPadding = "DES/ECB/ZerosPadding";
    public const string DES_ECB_ANSIX923Padding = "DES/ECB/ANSIX923Padding";
    public const string DES_ECB_ISO10126Padding = "DES/ECB/ISO10126Padding";

    public const string DES_OFB_NoPadding = "DES/OFB/NoPadding";
    public const string DES_OFB_PKCS7Padding = "DES/OFB/PKCS7Padding";
    public const string DES_OFB_ZerosPadding = "DES/OFB/ZerosPadding";
    public const string DES_OFB_ANSIX923Padding = "DES/OFB/ANSIX923Padding";
    public const string DES_OFB_ISO10126Padding = "DES/OFB/ISO10126Padding";

    public const string DES_CFB_NoPadding = "DES/CFB/NoPadding";
    public const string DES_CFB_PKCS7Padding = "DES/CFB/PKCS7Padding";
    public const string DES_CFB_ZerosPadding = "DES/CFB/ZerosPadding";
    public const string DES_CFB_ANSIX923Padding = "DES/CFB/ANSIX923Padding";
    public const string DES_CFB_ISO10126Padding = "DES/CFB/ISO10126Padding";

    public const string DES_CTS_NoPadding = "DES/CTS/NoPadding";
    public const string DES_CTS_PKCS7Padding = "DES/CTS/PKCS7Padding";
    public const string DES_CTS_ZerosPadding = "DES/CTS/ZerosPadding";
    public const string DES_CTS_ANSIX923Padding = "DES/CTS/ANSIX923Padding";
    public const string DES_CTS_ISO10126Padding = "DES/CTS/ISO10126Padding";

    public const string DESede_CBC_NoPadding = "DESede/CBC/NoPadding";
    public const string DESede_CBC_PKCS7Padding = "DESede/CBC/PKCS7Padding";
    public const string DESede_CBC_ZerosPadding = "DESede/CBC/ZerosPadding";
    public const string DESede_CBC_ANSIX923Padding = "DESede/CBC/ANSIX923Padding";
    public const string DESede_CBC_ISO10126Padding = "DESede/CBC/ISO10126Padding";

    public const string DESede_ECB_NoPadding = "DESede/ECB/NoPadding";
    public const string DESede_ECB_PKCS7Padding = "DESede/ECB/PKCS7Padding";
    public const string DESede_ECB_ZerosPadding = "DESede/ECB/ZerosPadding";
    public const string DESede_ECB_ANSIX923Padding = "DESede/ECB/ANSIX923Padding";
    public const string DESede_ECB_ISO10126Padding = "DESede/ECB/ISO10126Padding";

    public const string DESede_OFB_NoPadding = "DESede/OFB/NoPadding";
    public const string DESede_OFB_PKCS7Padding = "DESede/OFB/PKCS7Padding";
    public const string DESede_OFB_ZerosPadding = "DESede/OFB/ZerosPadding";
    public const string DESede_OFB_ANSIX923Padding = "DESede/OFB/ANSIX923Padding";
    public const string DESede_OFB_ISO10126Padding = "DESede/OFB/ISO10126Padding";

    public const string DESede_CFB_NoPadding = "DESede/CFB/NoPadding";
    public const string DESede_CFB_PKCS7Padding = "DESede/CFB/PKCS7Padding";
    public const string DESede_CFB_ZerosPadding = "DESede/CFB/ZerosPadding";
    public const string DESede_CFB_ANSIX923Padding = "DESede/CFB/ANSIX923Padding";
    public const string DESede_CFB_ISO10126Padding = "DESede/CFB/ISO10126Padding";

    public const string DESede_CTS_NoPadding = "DESede/CTS/NoPadding";
    public const string DESede_CTS_PKCS7Padding = "DESede/CTS/PKCS7Padding";
    public const string DESede_CTS_ZerosPadding = "DESede/CTS/ZerosPadding";
    public const string DESede_CTS_ANSIX923Padding = "DESede/CTS/ANSIX923Padding";
    public const string DESede_CTS_ISO10126Padding = "DESede/CTS/ISO10126Padding";
    
    ...
}

DES

程式碼

/// <summary>
/// 加密
/// </summary>
/// <param name="data">待加密原文資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>密文資料</returns>
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    if (algorithm == null)
    {
        throw new ArgumentNullException(nameof(algorithm));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(true, ParameterUtilities.CreateKeyParameter("DES", key));
    }
    else
    {
        cipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("DES", key), iv));
    }

    return cipher.DoFinal(data);
}

/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>未加密原文資料</returns>
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(false, ParameterUtilities.CreateKeyParameter("DES", key));
    }
    else
    {
        cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("DES", key), iv));
    }
    return cipher.DoFinal(data);
}

示例

private static void DES_Sample()
{
    Console.WriteLine("加密演算法:DES,加密模式:CBC,填充:PKCS7Padding,字符集:utf8 ");

    var keyBytes = DES.GenerateKey();

    var keyBase64Str = Base64.ToBase64String(keyBytes);  //key轉base64

    Console.WriteLine($"密碼長度:192bit,密碼(轉Base64):{keyBase64Str}");

    var ivStr = Str.GenerateRandom(8);
    var iv = Strings.ToByteArray(ivStr);      //模式為ECB時不支援初始化向量IV

    Console.WriteLine($"初始向量:{ivStr}");

    var content = "hello des";

    Console.WriteLine($"待加密文字:{content}");

    var cipherStr = Base64.ToBase64String(DES.Encrypt(Strings.ToUtf8ByteArray(content), Base64.Decode(keyBase64Str), iv, Algorithms.DES_CBC_PKCS7Padding));

    Console.WriteLine($"加密結果(輸出為Base64字串):{cipherStr}");

    var originalStr = Strings.FromUtf8ByteArray(DES.Decrypt(Base64.Decode(cipherStr), Base64.Decode(keyBase64Str), iv, Algorithms.DES_CBC_PKCS7Padding));

    Console.WriteLine($"解密結果(輸入為Base64字串):{originalStr}");

    Console.WriteLine();
}

20200907143712

3DES

程式碼

/// <summary>
/// 加密
/// </summary>
/// <param name="data">待加密原文資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>密文資料</returns>
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(true, ParameterUtilities.CreateKeyParameter("DESEDE", key));
    }
    else
    {
        cipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("DESEDE", key), iv));
    }

    return cipher.DoFinal(data);
}

/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>未加密原文資料</returns>
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(false, ParameterUtilities.CreateKeyParameter("DESEDE", key));
    }
    else
    {
        cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("DESEDE", key), iv));
    }
    return cipher.DoFinal(data);
}

示例

private static void TripleDES_Sample()
{
    Console.WriteLine("加密演算法:3DES,加密模式:CBC,填充:PKCS7Padding,字符集:utf8 ");

    var keyBytes = TripleDES.GenerateKey(192);

    var keyBase64Str = Base64.ToBase64String(keyBytes);  //key轉base64

    Console.WriteLine($"密碼長度:192bit,密碼(轉Base64):{keyBase64Str}");

    var ivStr = Str.GenerateRandom(8);
    var iv = Strings.ToByteArray(ivStr);      //模式為ECB時不支援初始化向量IV

    Console.WriteLine($"初始向量:{ivStr}");

    var content = "hello 3des";

    Console.WriteLine($"待加密文字:{content}");

    var cipherStr = Base64.ToBase64String(TripleDES.Encrypt(Strings.ToUtf8ByteArray(content), Base64.Decode(keyBase64Str), iv, Algorithms.DESede_CBC_PKCS7Padding));

    Console.WriteLine($"加密結果(輸出為Base64字串):{cipherStr}");

    var originalStr = Strings.FromUtf8ByteArray(TripleDES.Decrypt(Base64.Decode(cipherStr), Base64.Decode(keyBase64Str), iv, Algorithms.DESede_CBC_PKCS7Padding));

    Console.WriteLine($"解密結果(輸入為Base64字串):{originalStr}");

    Console.WriteLine();
}

20200907143736

AES

程式碼

/// <summary>
/// 加密
/// </summary>
/// <param name="data">待加密原文資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>密文資料</returns>
public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(true, ParameterUtilities.CreateKeyParameter("AES", key));
    }
    else
    {
        cipher.Init(true, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv));
    }

    return cipher.DoFinal(data);
}

/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密資料</param>
/// <param name="key">金鑰</param>
/// <param name="iv">偏移量,ECB模式不用填寫!</param>
/// <param name="algorithm">密文演算法</param>
/// <returns>未加密原文資料</returns>
public static byte[] Decrypt(byte[] data, byte[] key, byte[] iv, string algorithm)
{
    if (data == null)
    {
        throw new ArgumentNullException(nameof(data));
    }

    if (key == null)
    {
        throw new ArgumentNullException(nameof(key));
    }

    var cipher = CipherUtilities.GetCipher(algorithm);
    if (iv == null)
    {
        cipher.Init(false, ParameterUtilities.CreateKeyParameter("AES", key));
    }
    else
    {
        cipher.Init(false, new ParametersWithIV(ParameterUtilities.CreateKeyParameter("AES", key), iv));
    }
    return cipher.DoFinal(data);
}

示例

private static void AES_Sample()
{
    Console.WriteLine("加密演算法:AES,加密模式:CBC,填充:PKCS7Padding,字符集:utf8 ");

    var keyBytes = AES.GenerateKey();

    var keyBase64Str = Base64.ToBase64String(keyBytes);  //key轉base64

    Console.WriteLine($"密碼長度:192bit,密碼(轉Base64):{keyBase64Str}");

    var ivStr = Str.GenerateRandom(16);
    var iv = Strings.ToByteArray(ivStr);      //模式為ECB時不支援初始化向量IV

    Console.WriteLine($"初始向量:{ivStr}");

    var content = "hello aes";

    Console.WriteLine($"待加密文字:{content}");

    var cipherStr = Base64.ToBase64String(AES.Encrypt(Strings.ToUtf8ByteArray(content), Base64.Decode(keyBase64Str), iv, Algorithms.AES_CBC_PKCS7Padding));

    Console.WriteLine($"加密結果(輸出為Base64字串):{cipherStr}");

    var originalStr = Strings.FromUtf8ByteArray(AES.Decrypt(Base64.Decode(cipherStr), Base64.Decode(keyBase64Str), iv, Algorithms.AES_CBC_PKCS7Padding));

    Console.WriteLine($"解密結果(輸入為Base64字串):{originalStr}");

    Console.WriteLine();
}

20200907143631

下期預告

下一篇將介紹證照相關操作,敬請期待。。。

福祿ICH·架構組 福祿娃

相關文章