【问题标题】:TripleDES: Specified key is a known weak key for 'TripleDES' and cannot be usedTripleDES:指定的密钥是“TripleDES”的已知弱密钥,不能使用
【发布时间】:2009-04-13 16:43:58
【问题描述】:

我正在使用 .NET 3.0 类 System.Security.Cryptography.MACTripleDES 类来生成 MAC 值。不幸的是,我正在使用使用“1111111111111111”(作为十六进制)作为单长度 DES 密钥的硬件设备。 System.Security.Cryptography 库会对密钥进行一些完整性检查,如果您尝试使用加密弱密钥,则会返回异常。

例如:

byte[] key = new byte[24];
for (int i = 0; i < key.Length; i++)
  key[i] = 0x11;

byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] computedMac = null;
using (MACTripleDES mac = new MACTripleDES(key))
{
  computedMac = mac.ComputeHash(data);
}

抛出异常

System.Security.Cryptography.CryptographicException : Specified key is a known weak key for 'TripleDES' and cannot be used.

我知道这不是安全密钥。在生产中,设备将使用新的安全密钥进行刷新。同时,有什么方法可以抑制这个异常被抛出?也许是app.config 或注册表设置?

编辑:由于算法强制奇校验,密钥实际上是 101010...。我不确定这是否适用于 DES 算法,或者只是我所做的支付处理工作中的一个要求。

编辑 2:Daniel 在下面的回答中提供了一些关于黑客攻击 .NET 的非常好的信息。不幸的是,我无法使用这种技术解决我的问题,但仍然有一些有趣的阅读。

【问题讨论】:

  • (回复:您的编辑)DES 和 3DES 仅使用密钥中每个字节的前 7 位。最低有效位通常用作奇偶校验位。
  • 我会这样回家。在图书馆级别强制执行这样的事情似乎很奇怪。使用加密的人应该知道使用难以猜测的密钥,图书馆不应该强制执行这些事情。
  • @Kibbee,在这种情况下,弱键并不意味着“难以猜测”。这意味着指定算法表现出一些削弱它的退化行为的密钥。
  • 关于奇偶校验:每个字节的最后一位实际上并没有被算法使用,所以这并不重要。通常将其用作奇偶校验位,但每个实现都可以按照自己的意愿进行操作。

标签: c# .net cryptography cryptographicexception


【解决方案1】:

我不会真的推荐它,但您应该能够使用Reflector 和插件ReflexIL 修改检查弱密钥的IL 代码

编辑:

抱歉,我花了一段时间才将所有内容加载到我的虚拟机(运行 Ubuntu)中,并且不想弄乱 Mono。

  • 安装 ReflexIL 插件:查看 -> 插件 -> 添加
  • 打开 ReflexIL:工具 -> ReflexIL v0.9
  • 找到 IsWeakKey() 函数。 (您可以使用搜索:F3)
  • 会出现两个函数,双击 System.Security.Cryptography.TripleDES 中的那个
  • ReflexIL 也应该出现。在“说明”选项卡中,一直向下滚动到第 29 行(偏移量 63)。
  • 将 ldc.i4.1 更改为 ldc.i4.0,这意味着该函数将始终返回 false。

在您的程序集窗格(左侧)中,您现在可以向上滚动并单击“公共语言运行时库”,ReflexIL 窗格将为您提供保存它的选项。

重要提示:

  • 首先备份您的原始程序集! (mscorlib.dll)
  • mscorlib.dll 是一个签名程序集,您需要 ReflexIL 的 .NET SDK(sn.exe 工具)使其跳过验证。我刚刚自己检查了这个,你应该已经安装了 Visual C#。只需在系统提示时点击“注册以跳过验证(在此计算机上)”即可。
  • 我认为我不必告诉您只能在您的开发机器上使用它:)

祝你好运!如果您需要更多说明,请随时使用评论框。

编辑2:

我很困惑!

我从 mscorlib 程序集中的 set_Key 函数中完全删除了 IsWeakKey 检查。我绝对确定我修改了正确的功能,并且我做得正确。 Reflector 的反汇编程序不再显示该检查。然而有趣的是,Visual C# 仍然抛出同样的异常。

这让我相信 mscorlib 必须以某种方式仍然缓存在某个地方。但是,将 mscorlib.dll 重命名为 mscorlib.dll_ 会导致 MSVC# 崩溃,因此它必须仍然依赖于原始 dll。

这是非常有趣的东西,但我想我已经到了不知道发生了什么的地步,它只是没有任何意义!见附图。 :(

编辑3:

我在 Olly 中注意到,与 mscoree、mscorsec 和 mscorwks 等程序集不同; mscorlib.dll 实际上并不位于: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

但是,在一个似乎不存在的位置: C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\6d667f19d687361886990f3ca0f49816\mscorlib.ni.dll

我想我在这里遗漏了一些东西 :) 将对此进行更多调查。

编辑4:

即使修补了 IsWeakKey 中的所有内容,并使用“ngen.exe”删除和生成 mscorlib.dll 的新本机图像 (x.ni.dll),我仍然得到同样的例外。必须注意的是,即使卸载了本机 mscorlib 映像,它仍然在使用 mscorlib.ni.dll ......嗯。

我放弃了。我希望有人能够回答到底发生了什么,因为我当然不知道。 :)

【讨论】:

  • 非常有趣的解决方案!我修补了我的 mscorlib.dll,但函数的行为没有变化。这个 dll 是否缓存在某个地方?我要改变的只是 ....\Microsoft.NET\Framework\v2.0.50737\mscorlib.dll。
  • 我刚刚测试了代码,你是对的。我会调查并报告:)
  • 这至少可以说很奇怪。我现在修改了 set_Key() 上的实际 IsWeakKey 检查,它也在做同样的事情。它必须缓存在某个地方。我会调查更多:)
  • 我不熟悉这部分 .NET 内部结构。我认为 C:\WINDOWS\assembly 是 GAC 中的程序集所在的位置。使用 Process Explorer,我看到我的应用程序有一个打开的 mscorlib.ni.dll 句柄。 blogs.msdn.com/junfeng/archive/2004/11/11/256122.aspx.. 有一个旧博客条目
  • 是的。它使用 mscorlib.dll 的本机映像,可以使用 ngen.exe 工具(ngen.exe install mscorlib.dll)生成。第一次运行它时,它会告诉您本机映像已经是最新的。 (事实并非如此)。即使您删除本机映像,它也会加载 mscorlib.ni.dll。
【解决方案2】:

我知道你需要做什么。幸运的是,有一种方法可以创建不检查弱密钥的 ICryptoTranforms。您还需要注意基类,因为它还会进行完整性检查。通过反射简单地调用 _NewEncryptor 方法(你需要做更多的反射,但这就是想法)。

幸运的是,MACTripleDES 有一个 TripleDES 类型的字段,因此从 MACTripleDES 派生并通过构造函数中的反射替换它。我已经为你完成了所有的工作。

我无法验证是否生成了正确的 MAC,但没有引发异常。此外,您可能希望对代码进行文档注释并进行异常处理(反射失败 - 例如,如果字段/方法不存在) - 但这是如此;所以我没有打扰。

using System;
using System.Reflection;
using System.Security.Cryptography;
using System.IO;

namespace DesHack
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] key = new byte[24];
            for (int i = 0; i < key.Length; i++)
                key[i] = 0x11;

            byte[] data = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            byte[] computedMac = null;
            using (MACTripleDES mac = new MACTripleDESHack(key))
            {
                computedMac = mac.ComputeHash(data);
            }
        }
    }

    class MACTripleDESHack : MACTripleDES
    {
        TripleDES _desHack = new DesHack();

        static FieldInfo _cspField = typeof(MACTripleDES).GetField("des", BindingFlags.Instance | BindingFlags.NonPublic);

        public MACTripleDESHack()
            : base()
        {
            RewireDes();
        }

        public MACTripleDESHack(byte[] rgbKey)
            : base(rgbKey)
        {
            RewireDes();
        }

        private void RewireDes()
        {
            _cspField.SetValue(this, _desHack);
        }

    }

    class DesHack : TripleDES
    {
        TripleDESCryptoServiceProvider _backing = new TripleDESCryptoServiceProvider();

        static MethodInfo _newEncryptor;
        static object _encrypt;
        static object _decrypt;

        public override int BlockSize
        {
            get
            {
                return _backing.BlockSize;
            }
            set
            {
                _backing.BlockSize = value;
            }
        }

        public override int FeedbackSize
        {
            get
            {
                return _backing.FeedbackSize;
            }
            set
            {
                _backing.FeedbackSize = value;
            }
        }

        // For these two we ALSO need to avoid
        // the base class - it also checks
        // for weak keys.
        private byte[] _iv;
        public override byte[] IV
        {
            get
            {
                return _iv;
            }
            set
            {
                _iv = value;
            }
        }

        private byte[] _key;
        public override byte[] Key
        {
            get
            {
                return _key;
            }
            set
            {
                _key = value;
            }
        }

        public override int KeySize
        {
            get
            {
                return _backing.KeySize;
            }
            set
            {
                _backing.KeySize = value;
            }
        }

        public override KeySizes[] LegalBlockSizes
        {
            get
            {
                return _backing.LegalBlockSizes;
            }
        }

        public override KeySizes[] LegalKeySizes
        {
            get
            {
                return _backing.LegalKeySizes;
            }
        }

        public override CipherMode Mode
        {
            get
            {
                return _backing.Mode;
            }
            set
            {
                _backing.Mode = value;
            }
        }

        public override PaddingMode Padding
        {
            get
            {
                return _backing.Padding;
            }
            set
            {
                _backing.Padding = value;
            }
        }


        static DesHack()
        {
            _encrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Encrypt").GetValue(null);
            _decrypt = typeof(object).Assembly.GetType("System.Security.Cryptography.CryptoAPITransformMode").GetField("Decrypt").GetValue(null);
            _newEncryptor = typeof(TripleDESCryptoServiceProvider).GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
        }

        public DesHack()
        {            
        }

        public override ICryptoTransform CreateDecryptor()
        {
            return CreateDecryptor(_key, _iv);
        }

        public override ICryptoTransform CreateEncryptor()
        {
            return CreateEncryptor(_key, _iv);
        }

        public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
        {
            // return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Decrypt);
            return (ICryptoTransform) _newEncryptor.Invoke(_backing,
                new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _decrypt });
        }

        public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
        {
            // return this._NewEncryptor(rgbKey, base.ModeValue, rgbIV, base.FeedbackSizeValue, CryptoAPITransformMode.Encrypt);
            return (ICryptoTransform) _newEncryptor.Invoke(_backing,
                new object[] { rgbKey, ModeValue, rgbIV, FeedbackSizeValue, _encrypt });
        }

        public override void GenerateIV()
        {
            _backing.GenerateIV();
        }

        public override void GenerateKey()
        {
            _backing.GenerateKey();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                ((IDisposable) _backing).Dispose();
            base.Dispose(disposing);
        }
    }
}

【讨论】:

    【解决方案3】:

    您可以在DESCryptoServiceProvider 之上自己实现CBC-MAC,而不是使用重复DES 密钥的MACTripleDES 来伪造单个DES CBC-MAC。

    不是弱 DES 密钥。

    这将计算一个 DES CBC-MAC:

    public static byte[] CalcDesMac(byte[] key, byte[] data){
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            des.Key = key;
            des.IV = new byte[8];
            des.Padding = PaddingMode.Zeros;
            MemoryStream ms = new MemoryStream();
            using(CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write)){
              cs.Write(data, 0, data.Length);
            }
            byte[] encryption = ms.ToArray();
            byte[] mac = new byte[8];
            Array.Copy(encryption, encryption.Length-8, mac, 0, 8);
            PrintByteArray(encryption);
            return mac;
        }
    

    【讨论】:

    • 这很好用。我想我只是专注于 MACTripleDES 类,如果我仔细考虑的话,它实际上只用于 Triple DES 而不是单个 DES。我希望我没有花一年的时间来阅读您的回复!我最终手动实现了 CBC-MAC。
    【解决方案4】:

    很遗憾,该行为无法被覆盖。

    【讨论】:

      【解决方案5】:

      在 MSDN 论坛中有一个很好的建议使用 reflection

      【讨论】:

        【解决方案6】:

        我不是安全专家,但用另一个值异或你的密钥是否足以满足完整性检查?您可以为您的调试版本执行此操作(使用适当的 IFDEF),以便您可以为密钥足够强大的发布或生产版本进行适当的检查并删除它。

        【讨论】:

        • 这确实可以满足完整性检查,但他正在使用的硬件设备仍将使用原始密钥。
        • 是的。我就是这么想的。我只是从代码的角度考虑它,甚至没有考虑硬件。我的坏:)
        【解决方案7】:

        基于反射的解决方案可以帮助您解决问题,但它们既肮脏又邪恶。还没有人提到一个非常有用的方法:TripleDES.IsWeakKey

        我遇到了这个问题,并通过一个非常简单的实用程序解决了这个问题,我在我的 CryptoServiceProvider 上设置密钥之前立即使用该实用程序:

        private void MakeSecureKey(byte[] key)
        {
            while(TripleDES.IsWeakKey(key))
            {
                var sha = SHA256Managed.Create().ComputeHash(key);
                Array.Copy(sha,key,key.Length);
            }
        }
        

        如果您在制作加密器或解密器时调用它,它应该可以防止崩溃并始终为您提供安全密钥。

        【讨论】:

        • 如果必须与需要特定密钥的设备进行互操作,这并没有真正的帮助。
        • 很公平。只是想我会在这里添加它,因为它是谷歌针对该错误命中的第一名,它可能对处于稍微不同情况的人有用。
        【解决方案8】:

        很简单(从 GitHub 上查看代码后)

        静态布尔 TripleDES.IsWeakKey(Byte[] rgbKey)

        由于它是静态的 - 很容易针对它测试您的密钥

        1. 大小必须是 16 或 24 字节 (???) 为什么他们不能把它放在文档中
        2. 代码检查几个简单的重复 只需创建随机足够的值

        查看代码:https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Security.Cryptography/TripleDES.cs

        德克尔

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-09
          相关资源
          最近更新 更多