博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解决DESCryptoServiceProvider加解密时弱密钥异常
阅读量:6289 次
发布时间:2019-06-22

本文共 4920 字,大约阅读时间需要 16 分钟。

DES算法的密钥是8字节,由于其算法的特性所致,有一些密钥是不安全的,比如0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF,

用该密钥对数据进行加密与解密运算,得到的结果是一样的,这样的密钥被称为弱密钥。在.NET中我们一般用DESCryptoServiceProvider
来进行DES运算,但是该类会去检测传入的密钥是否为弱密钥,如果是,就直接抛出异常。先看下面的代码:

1         public static string DESEncode(byte[] str, byte[] key)  2         {
3 DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 4 des.Mode = CipherMode.ECB; 5 des.Padding = PaddingMode.None; 6 des.Key = key; 7 ICryptoTransform desCrypt = des.CreateEncryptor(); 8 byte[] result = desCrypt.TransformFinalBlock(str, 0, str.Length); 9 return BitConverter.ToString(result).Replace("-", ""); 10 }

上面的代码网上到处能找到,但是如果你传入0xFF0xFF0xFF0xFF0xFF0xFF0xFF0xFF这样的密钥,在des.Key = key;这一行就会抛出

“不能为弱密钥”的异常。这在正常情况下是没问题的,微软为我们考虑的很周到,强制我们不要用安全性低的弱密钥。但是有时需求是万变
的,比如你操作的是一个硬件设备,往它里面写入一个密钥,但是厂家规定了第一次写入时必须用8个0xFF密钥进行加密,后面就用原密钥加密后
写入(银行的金融POS机几乎都是这种模式的),那岂不是就没法用C#来操作了吗?
我之前也是这么认为的,甚至都决定用C写个DES算法(这个网上有现成的),编译成DLL后用C#去调用。后来想到了反射,去绕过弱密钥的检测。
于是我先反编译了DESCryptoServiceProvider.Key这个属性的set方法,看看里面做了什么操作:

1 public override byte[] Key  2 {
3 set 4 {
5 if (value == null) 6 {
7 throw new ArgumentNullException("value"); 8 } 9 if (!base.ValidKeySize(value.Length * 8)) 10 {
11 throw new ArgumentException(Environment.GetResourceString("Cryptography_InvalidKeySize")); 12 } 13 if (IsWeakKey(value)) 14 {
15 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKey_Weak"), "DES"); 16 } 17 if (IsSemiWeakKey(value)) 18 {
19 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKey_SemiWeak"), "DES"); 20 } 21 base.KeyValue = (byte[]) value.Clone(); 22 base.KeySizeValue = value.Length * 8; 23 } 24 }

Key这个属性是DESCryptoServiceProvider的父类DES类的属性。果然,我们在里面发现了检测弱密钥的代码if (IsWeakKey(value))和

if (IsSemiWeakKey(value)),后者是检测半弱密钥的。那么抛除前面的检测语句,真正有作用的只有这两句

1 base.KeyValue = (byte[]) value.Clone(); 2 base.KeySizeValue = value.Length * 8;

那么,下面的事情就明朗了,用反射去对这两个属性进行赋值

1         public static string DESEncode(byte[] str, byte[] key)  2         {
3 DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 4 des.Mode = CipherMode.ECB; 5 des.Padding = PaddingMode.None; 6 7 FieldInfo fi = des.GetType().GetField("KeyValue", BindingFlags.Instance | BindingFlags.NonPublic); 8 fi.SetValue(des, key); 9 fi = des.GetType().GetField("KeySizeValue", BindingFlags.Instance | BindingFlags.NonPublic); 10 fi.SetValue(des, key.Length * 8); 11 12 ICryptoTransform desCrypt = des.CreateEncryptor(); 13 byte[] result = desCrypt.TransformFinalBlock(str, 0, str.Length); 14 return BitConverter.ToString(result).Replace("-", ""); 15 }

这时发现des.CreateEncryptor()这句还是抛出弱密钥异常,继续反编译

1 public virtual ICryptoTransform CreateEncryptor() 2 {
3 return this.CreateEncryptor(this.Key, this.IV); 4 }
1 public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)  2 {
3 if (DES.IsWeakKey(rgbKey)) 4 {
5 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKey_Weak"), "DES"); 6 } 7 if (DES.IsSemiWeakKey(rgbKey)) 8 {
9 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKey_SemiWeak"), "DES"); 10 } 11 return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Encrypt); 12 }

它里面果然又进行了一次弱密钥判断,最终调用的是_NewEncryptor这个私有方法,于是就要反射去调用它。注意参数里的rgbIV是向量,

feedbackSize不清楚是干嘛的,但是这两个参数在ECB加解密模式下是不需要的,可以随便传。CryptoAPITransformMode这个枚举是internal的,
一样要用反射去得到。最终的代码如下:

1         public static string DESEncode(byte[] str, byte[] key)  2         {
3 DESCryptoServiceProvider des = new DESCryptoServiceProvider(); 4 des.Padding = PaddingMode.None; 5 6 Type t = Type.GetType("System.Security.Cryptography.CryptoAPITransformMode"); 7 object obj = t.GetField("Encrypt", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly).GetValue(t); 8 9 MethodInfo mi = des.GetType().GetMethod("_NewEncryptor", BindingFlags.Instance | BindingFlags.NonPublic); 10 ICryptoTransform desCrypt = (ICryptoTransform)mi.Invoke(des, new object[] { key, CipherMode.ECB, null, 0, obj }); 11 12 byte[] result = desCrypt.TransformFinalBlock(str, 0, str.Length); 13 return BitConverter.ToString(result).Replace("-", ""); 14 }

以上是DES的加密代码,DES的解密、3DES的加解密与之类似,就不一一写了。

 

 

转载于:https://www.cnblogs.com/jintianhu/archive/2011/11/26/2264375.html

你可能感兴趣的文章
淘宝API-类目
查看>>
virtualbox 笔记
查看>>
Git 常用命令
查看>>
驰骋工作流引擎三种项目集成开发模式
查看>>
SUSE11修改主机名方法
查看>>
jdk6.0 + Tomcat6.0的简单jsp,Servlet,javabean的调试
查看>>
Android:apk签名
查看>>
2(2).选择排序_冒泡(双向循环链表)
查看>>
MySQL 索引 BST树、B树、B+树、B*树
查看>>
微信支付
查看>>
CodeBlocks中的OpenGL
查看>>
短址(short URL)
查看>>
第十三章 RememberMe——《跟我学Shiro》
查看>>
mysql 时间函数 时间戳转为日期
查看>>
索引失效 ORA-01502
查看>>
Oracle取月份,不带前面的0
查看>>
Linux Network Device Name issue
查看>>
IP地址的划分实例解答
查看>>
如何查看Linux命令源码
查看>>
运维基础命令
查看>>