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

5

我有一个文件,它被des.exe加密了。

以下是加密和解密文件的命令:

des -E -k "foo" sample.txt sample.txt.enc
des -D -k "foo" sample.txt.enc sample.txt.dec

我尝试使用以下内容进行解密:

public byte[] Decrypt(FileInfo file, string key)
{
  byte[] keyAsBytes = LibDesPasswordConvertor.PasswordToKey(key);
  byte[] initializationVector = keyAsBytes;

  var cryptoProvider = new DESCryptoServiceProvider();  
  cryptoProvider.Mode = CipherMode.CBC;
  cryptoProvider.Padding = PaddingMode.None;  

  using (FileStream fs = file.OpenRead())
  using (var memStream = new MemoryStream())
  using (var decryptor = cryptoProvider.CreateDecryptor(keyAsBytes, initializationVector))
  using (var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write))
  {
    fs.CopyTo(cryptoStream);
    fs.Flush();
    cryptoStream.FlushFinalBlock();

    return memStream.ToArray();
  }
}

public static class LibDesPasswordConvertor
{
  public static byte[] PasswordToKey(string password)
  {
    if (string.IsNullOrWhiteSpace(password)) 
    { 
      throw new ArgumentException("password"); 
    }

    var key = new byte[8];

    for (int i = 0; i < password.Length; i++)
    {
      var c = (int)password[i];
      if ((i % 16) < 8)
      {
        key[i % 8] ^= (byte)(c << 1);
      }
      else
      {
        // reverse bits e.g. 11010010 -> 01001011
        c = (((c << 4) & 0xf0) | ((c >> 4) & 0x0f));
        c = (((c << 2) & 0xcc) | ((c >> 2) & 0x33));
        c = (((c << 1) & 0xaa) | ((c >> 1) & 0x55));
        key[7 - (i % 8)] ^= (byte)c;
      }
    }

    AddOddParity(key);

    var target = new byte[8];
    var passwordBuffer = Encoding.ASCII.GetBytes(password).Concat(new byte[8]).Take(password.Length + (8 - (password.Length % 8)) % 8).ToArray();

    using(var des = DES.Create())
    using(var encryptor = des.CreateEncryptor(key, key))
    {
      for (int x = 0; x < passwordBuffer.Length / 8; ++x)
      {
        encryptor.TransformBlock(passwordBuffer, 8 * x, 8, target, 0);
      }
    }

    AddOddParity(target);

    return target;
  }


  private static void AddOddParity(byte[] buffer)
  {
    for (int i = 0; i < buffer.Length; ++i)
    {
      buffer[i] = _oddParityTable[buffer[i]];
    }
  }

  private static byte[] _oddParityTable = {
          1,  1,  2,  2,  4,  4,  7,  7,  8,  8, 11, 11, 13, 13, 14, 14,
         16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
         32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
         49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
         64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
         81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
         97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
        112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
        128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
        145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
        161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
        176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
        193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
        208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
        224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
        241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254};
}

但是当我执行:

const string KEY = "foo";  
var utf8Bytes = Decrypt(new FileInfo(@"PATH-TO\sample.txt.enc"), KEY);

I get:

�1D���z+�a Sample.y���0F�01

Original text:

This is a Sample.

加密:

ñGYjl¦ûg†¼64©‹Bø
é¯Kœ|
1个回答

4
令我惊讶的是您已经正确推导出密钥。这是问题的核心,因此感谢您已经解决了这部分内容。当您看到明文的一部分出现在解密中时,就会清楚密钥是正确的 - 如果密钥错误,则不会出现。
查看源代码和一些过去的文档,我发现可能的IV全为零,而不是重复使用密钥字节(在密码术术语中,两者都非常错误)。
此外,对于SSLeay,ECB和CBC模式使用PKCS#7兼容填充,而不是没有填充。
最后,如果关闭流(例如通过退出try-with-resources),则将自动调用FlushFinalBlock。因此,如果之后获取数组,则应正确获取值 - 当然,在正确取消填充后。如果调用Flush,则已经调用FlushFinalBlock,并且调用两次将使事情变得混乱。
简单地删除刷新调用并在关闭CryptoStream之后检索数组是正确的方法。
DES以及Young从MIT复制的密钥派生(des_string_to_keydes_string_to_2keys)均不安全。使用全零IV是错误的。
如果将其用作传输模式,则填充咒语将适用,并且攻击者甚至无需解密即可进行攻击。密文未经完整性保护。
如果您使用上述例程来保持任何内容的机密性或安全性,则在愚弄自己。这是80年代技术,我认为真正的密码学家当时也不会发现它是安全的。
基本上,如果您的攻击者超过8岁,那么您就有麻烦了。

非常好,非常感谢您的帮助。不幸的是,我们无法控制传入的文件,否则肯定会避免使用DES。 - MaYaN
不客气。很高兴看到密钥派生实现了,我会记住的 :) 其实,我会把它偷走并稍后在Java中重新实现。 - Maarten Bodewes
@MaYaN 你是如何处理结尾的填充模式的?我在解密时得到了一些额外的字符。看起来 des.exe 没有使用标准的 PaddingMode。 - Anthony Serrano Bianco
我不记得需要做任何特定的事情......但我记得我必须手动刷新流。 - MaYaN
@MaYaN 感谢您的回答。 你是使用了 MaartenBodewes 建议的 PaddingMode.PKCS7,还是使用了 PaddingMode.None?另外,您手动清除的流是加密流,对吗? - Anthony Serrano Bianco
很遗憾,我不记得了,而且我也无法再访问那段代码了 :-( - MaYaN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接