如何在 C# 中解密由 des.exe 加密的文件?

MaY*_*YaN 5 .net c# encryption cryptography des

我有一个已由des.exe加密的文件。

\n\n

可以使用以下命令对文件进行加密和解密:

\n\n
des -E -k "foo" sample.txt sample.txt.enc\ndes -D -k "foo" sample.txt.enc sample.txt.dec\n
Run Code Online (Sandbox Code Playgroud)\n\n

我尝试使用以下方法解密:

\n\n
public byte[] Decrypt(FileInfo file, string key)\n{\n  byte[] keyAsBytes = LibDesPasswordConvertor.PasswordToKey(key);\n  byte[] initializationVector = keyAsBytes;\n\n  var cryptoProvider = new DESCryptoServiceProvider();  \n  cryptoProvider.Mode = CipherMode.CBC;\n  cryptoProvider.Padding = PaddingMode.None;  \n\n  using (FileStream fs = file.OpenRead())\n  using (var memStream = new MemoryStream())\n  using (var decryptor = cryptoProvider.CreateDecryptor(keyAsBytes, initializationVector))\n  using (var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write))\n  {\n    fs.CopyTo(cryptoStream);\n    fs.Flush();\n    cryptoStream.FlushFinalBlock();\n\n    return memStream.ToArray();\n  }\n}\n\npublic static class LibDesPasswordConvertor\n{\n  public static byte[] PasswordToKey(string password)\n  {\n    if (string.IsNullOrWhiteSpace(password)) \n    { \n      throw new ArgumentException("password"); \n    }\n\n    var key = new byte[8];\n\n    for (int i = 0; i < password.Length; i++)\n    {\n      var c = (int)password[i];\n      if ((i % 16) < 8)\n      {\n        key[i % 8] ^= (byte)(c << 1);\n      }\n      else\n      {\n        // reverse bits e.g. 11010010 -> 01001011\n        c = (((c << 4) & 0xf0) | ((c >> 4) & 0x0f));\n        c = (((c << 2) & 0xcc) | ((c >> 2) & 0x33));\n        c = (((c << 1) & 0xaa) | ((c >> 1) & 0x55));\n        key[7 - (i % 8)] ^= (byte)c;\n      }\n    }\n\n    AddOddParity(key);\n\n    var target = new byte[8];\n    var passwordBuffer = Encoding.ASCII.GetBytes(password).Concat(new byte[8]).Take(password.Length + (8 - (password.Length % 8)) % 8).ToArray();\n\n    using(var des = DES.Create())\n    using(var encryptor = des.CreateEncryptor(key, key))\n    {\n      for (int x = 0; x < passwordBuffer.Length / 8; ++x)\n      {\n        encryptor.TransformBlock(passwordBuffer, 8 * x, 8, target, 0);\n      }\n    }\n\n    AddOddParity(target);\n\n    return target;\n  }\n\n\n  private static void AddOddParity(byte[] buffer)\n  {\n    for (int i = 0; i < buffer.Length; ++i)\n    {\n      buffer[i] = _oddParityTable[buffer[i]];\n    }\n  }\n\n  private static byte[] _oddParityTable = {\n          1,  1,  2,  2,  4,  4,  7,  7,  8,  8, 11, 11, 13, 13, 14, 14,\n         16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,\n         32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,\n         49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,\n         64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,\n         81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,\n         97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,\n        112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,\n        128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,\n        145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,\n        161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,\n        176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,\n        193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,\n        208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,\n        224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,\n        241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254};\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是当我执行时:

\n\n
const string KEY = "foo";  \nvar utf8Bytes = Decrypt(new FileInfo(@"PATH-TO\\sample.txt.enc"), KEY);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我得到:

\n\n
\xef\xbf\xbd1D\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbdz+\xef\xbf\xbda Sample.y\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd0F\xef\xbf\xbd01\n
Run Code Online (Sandbox Code Playgroud)\n\n

原文:

\n\n
This is a Sample.\n
Run Code Online (Sandbox Code Playgroud)\n\n

加密:

\n\n
\xc3\xb1GYjl\xc2\xa6\xc3\xbbg\xe2\x80\xa0\xc2\xbc64\xc2\xa9\xe2\x80\xb9B\xc3\xb8\n\xc3\xa9\xc2\xafK\xc5\x93|\n
Run Code Online (Sandbox Code Playgroud)\n

Maa*_*wes 4

令我惊讶的是你已经正确地导出了密钥。这就是问题的核心,所以感谢您已经解决了该部分。当您看到解密中存在部分明文时,密钥是正确的就变得很清楚 - 如果密钥错误则不会。


查看源代码和过去的一些文档,我发现 IV 可能全为零,而不是重用密钥字节(从密码学角度来看,两者都是非常错误的)。

此外,与 SSLeay 一样,ECB 和 CBC 模式使用 PKCS#7 兼容填充,而不是无填充。


最后,FlushFinalBlock如果您关闭流,例如通过退出 try-with-resources,将自动调用。因此,如果您之后获得数组,那么您应该获得正确的值 - 当然,在正确取消填充之后。如果你调用的话Flush,那么就FlushFinalBlock已经被调用了,调用两次就会把事情弄得一团糟。

只需删除刷新调用并在关闭后检索数组CryptoStream就是可行的方法。


DES 和Young 从 MIT 复制的密钥派生(des_string_to_key和)都是完全不安全的。des_string_to_2keys使用全零 IV 是错误的。

如果您使用它作为传输模式,那么填充预言机将适用,并且攻击者甚至不需要解密。密文不受完整性保护。

如果您使用上述例程来保密或保护任何内容,那么您就是在欺骗自己。这是 80 年代的技术,我认为当时真正的密码学家也不会发现它是安全的。

基本上,如果攻击者年龄超过 8 岁,你就有麻烦了。