对称加密算法在C#中的踩坑日常

Posted dotNet框架学苑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对称加密算法在C#中的踩坑日常相关的知识,希望对你有一定的参考价值。

1.0 前言

有幸接触了一下传说中的对称加密算法3DES
感觉这些加密算法与我的工作是相去甚远的,一般没什么机会接触这些东西
今次了解了一下3DES这个对称算法
原理算不上明白,算是踩了C#中的一些坑吧
C#中对于密钥的处理比较奇怪,花费了一晚上一早上的时间才弄明白
期间偷窥了不少C#的源代码
下面由我娓娓道来

2.0 简介

2.1 3DES算法命名
定义算法最早期的标准被放在ANS X9.52中并在1998年发布并将其描述为三重数据加密算法(简称TDEA),在ANSI X3.92中定义了该算法的三个操作但是并没有使用DES或者3DES,直到1999年发布的FIPS PUB 46-3在正式命名三重数据加密算法,大概在2004到2005的样子才正式引入三重数据加密算法,之前一直以TDEA存在着,也就是说TDEA就是3DES,但是没有使用3DES作为标准术语。

2.2 基本逻辑
三重数据加密算法使用包括密钥K1,密钥K2和密钥约束K3,每一个包含56位不包含奇偶校验,算法实现公式如下:

 
   
   
 
  1. ciphertext = EK3(DK2(EK1(plaintext)))

 
   
   
 
  1. 密文 = EK3(DK2(EK1(平文)))

用K1对数据进行加密,用K2对数据进行解密,用K3对数据再加密。
解密公式为如下:

 
   
   
 
  1. plaintext = DK1(EK2(DK3(ciphertext)))

 
   
   
 
  1. 平文 = DK1(EK2(DK3(密文)))

用K3j对数据进行解密,用K2对数据进行加密,用K1对数据进行加密。每次加密都处理64位数据并形成一块。

2.3 3DES加密选项
定义了三种密钥选项。
(1)三个密钥相互独立。
(2)K1和K2密钥独立,但K1 = K3。
(3)三个密钥相等。
密钥选项1的强度最高,拥有3 x 56 = 168个独立的密钥位。
密钥选项2的安全性稍低,拥有2 x 56 = 112个独立的密钥位。该选项比简单的应用DES两次的强度较高,即使用K1和K2,因为它可以防御中途相遇攻击。
密钥选项3等同与DES,只有56个密钥位。这个选项提供了与DES的兼容性,因为第1和第2次DES操作相互抵消了。该选项不再为国家标准科技协会(NIST)所推荐,亦不为ISO/IEC 18033-3所支持。

2.4 C#实现
讲真简介里用来凑字数的这些内容我其实没怎么看明白
C#中使用 TripleDESCryptoServiceProvider类来实现相关功能

 
   
   
 
  1. public static string DesEncrypt(string input, string key)

  2. {

  3. byte[] inputArray = Encoding.UTF8.GetBytes(input);

  4. TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();

  5. tripleDES.Key = Encoding.UTF8.GetBytes(key);


  6. tripleDES.Mode = CipherMode.ECB;

  7. tripleDES.Padding = PaddingMode.PKCS7;

  8. ICryptoTransform cTransform = tripleDES.CreateEncryptor();

  9. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  10. tripleDES.Clear();

  11. return Convert.ToBase64String(resultArray, 0, resultArray.Length);

  12. }


  13. public static string DesDecrypt(string input, string key)

  14. {

  15. byte[] inputArray = Convert.FromBase64String(input);

  16. TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();

  17. tripleDES.Key = Encoding.UTF8.GetBytes(key);

  18. tripleDES.Mode = CipherMode.ECB;

  19. tripleDES.Padding = PaddingMode.PKCS7;

  20. ICryptoTransform cTransform = tripleDES.CreateDecryptor();

  21. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  22. tripleDES.Clear();

  23. return Encoding.UTF8.GetString(resultArray);

  24. }

从下面源码中看出,该类接收的Key为16位或24位

然后对于这个Key,C#似乎有自己的处理方式
以下为个人理解:
这个24位的key会被处理成3个8字节的独立密钥参与运算
当提供24位key时并没有什么不妥
但是当提供16位的key时 会把提供的key拆分成两个块(block) 并以第一个块作为第三个块组成一个24位的密钥
如下:

 
   
   
 
  1. 输入密钥:49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 50, 51, 52, 53, 54, 55

  2. 实际使用:49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 50, 51, 52, 53, 54, 55, 49, 50, 51, 52, 53, 54, 55, 56

可以看出使用了前8位来进行后面8位的补全
这时候你可能要问,如果提供一个不是16位也不是24位的密钥时会发生什么
会抛异常

以上理解都是在.NetFramework中的体现
如果换到NetCore中,效果就又不一样了

2.5 NetCore
在NetCore中不存在 TripleDESCryptoServiceProvider取而代之的是 TripleDES
所以此时我们的代码需要稍作修改

 
   
   
 
  1. public static string DesEncrypt(string input, string key)

  2. {


  3. byte[] inputArray = Encoding.UTF8.GetBytes(input);

  4. var tripleDES = TripleDES.Create();

  5. var byteKey = Encoding.UTF8.GetBytes(key);

  6. tripleDES.Key = byteKey;

  7. tripleDES.Mode = CipherMode.ECB;

  8. tripleDES.Padding = PaddingMode.PKCS7;

  9. ICryptoTransform cTransform = tripleDES.CreateEncryptor();

  10. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  11. return Convert.ToBase64String(resultArray, 0, resultArray.Length);

  12. }


  13. public static string DesDecrypt(string input, string key)

  14. {

  15. byte[] inputArray = Convert.FromBase64String(input);

  16. var tripleDES = TripleDES.Create();

  17. var byteKey = Encoding.UTF8.GetBytes(key);

  18. tripleDES.Key = byteKey;

  19. tripleDES.Mode = CipherMode.ECB;

  20. tripleDES.Padding = PaddingMode.PKCS7;

  21. ICryptoTransform cTransform = tripleDES.CreateDecryptor();

  22. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  23. return Encoding.UTF8.GetString(resultArray);

  24. }

NetCore中同样要求我们提供24位的Key
但是不在兼容16位的Key,如果你提供一个非24位的Key就会异常
不过没关系,对于16位的Key我们可以自行处理一下
同理使用前8位补全后8位

 
   
   
 
  1. public static string DesEncrypt(string input, string key)

  2. {


  3. byte[] inputArray = Encoding.UTF8.GetBytes(input);

  4. var tripleDES = TripleDES.Create();

  5. var byteKey = Encoding.UTF8.GetBytes(key);

  6. //复制前8位补全后8位

  7. byte[] allKey = new byte[24];

  8. Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);

  9. Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);

  10. tripleDES.Key = allKey;

  11. tripleDES.Mode = CipherMode.ECB;

  12. tripleDES.Padding = PaddingMode.PKCS7;

  13. ICryptoTransform cTransform = tripleDES.CreateEncryptor();

  14. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  15. return Convert.ToBase64String(resultArray, 0, resultArray.Length);

  16. }


  17. public static string DesDecrypt(string input, string key)

  18. {

  19. byte[] inputArray = Convert.FromBase64String(input);

  20. var tripleDES = TripleDES.Create();

  21. var byteKey = Encoding.UTF8.GetBytes(key);

  22. //复制前8位补全后8位

  23. byte[] allKey = new byte[24];

  24. Buffer.BlockCopy(byteKey, 0, allKey, 0, 16);

  25. Buffer.BlockCopy(byteKey, 0, allKey, 16, 8);

  26. tripleDES.Key = allKey;

  27. tripleDES.Mode = CipherMode.ECB;

  28. tripleDES.Padding = PaddingMode.PKCS7;

  29. ICryptoTransform cTransform = tripleDES.CreateDecryptor();

  30. byte[] resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);

  31. return Encoding.UTF8.GetString(resultArray);

至此就可以正常兼容NetFramework的代码了

3.0 小结

至此写下此文,也算是对3DES有了些许了解吧
需要记住

在.NET Core中利用3DES加密和解密必须要给出3个密钥即24个字节即使密钥3和密钥1相等,它不会像.NET Framework中会重用密钥1中的位数。


以上是关于对称加密算法在C#中的踩坑日常的主要内容,如果未能解决你的问题,请参考以下文章

Java项目生产环境部署,遇到FTP连接加密服务器的踩坑及爬坑过程

在Vue项目中使用Vant的踩坑日常(表单提交的一系列问题)

基于C#的RSA非对称加密算法

个人理解c#对称加密 非对称加密 散列算法的应用场景

冲量在线 | 非对称加密

JavaAES128对称加密算法实现