如何在 C# 中解密已被 des.exe 加密的文件?
How can I decrypt a file in C# which has been encrypted by des.exe?
我有一个已被 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);
我得到:
�1D���z+�a Sample.y���0F�01
原文:
This is a Sample.
加密:
ñGYjl¦ûg†¼64©‹Bø
é¯Kœ|
令我惊讶的是,您已经正确导出了密钥。那是问题的核心,所以已经解决了那部分的荣誉。当您看到解密中存在部分明文时,密钥是否正确就会变得很清楚 - 如果密钥错误,则不会。
查看源代码和过去的一些文档,我发现可能是全零的 IV 而不是重复使用密钥字节(从密码学的角度来看,这两者都是非常错误的)。
此外,与 SSLeay 一样,ECB 和 CBC 模式使用 PKCS#7 兼容填充,而不是无填充。
最后,如果您关闭流,FlushFinalBlock
将自动调用,例如通过退出 try-with-resources。因此,如果您之后获得数组,那么您应该获得正确的值 - 当然,在您正确取消填充之后。如果你调用 Flush
那么 FlushFinalBlock
已经被调用了,调用它两次会把事情弄得一团糟。
简单地删除刷新调用并检索数组在 CryptoStream
关闭后是可行的方法。
Young 从 MIT 复制的 DES 和密钥推导(des_string_to_key
和 des_string_to_2keys
)都是完全不安全的。使用全零 IV 是错误的。
如果您将其用作传输模式,那么填充预言机将适用,并且攻击者甚至不需要解密。密文不受完整性保护。
如果您使用上述例程来任何东西保密或确保安全,那您就是在自欺欺人。这是 80 年代的技术,我认为当时真正的密码学家也不会发现它是安全的。
基本上如果你的攻击者超过 8 岁,你就有麻烦了。
我有一个已被 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);
我得到:
�1D���z+�a Sample.y���0F�01
原文:
This is a Sample.
加密:
ñGYjl¦ûg†¼64©‹Bø
é¯Kœ|
令我惊讶的是,您已经正确导出了密钥。那是问题的核心,所以已经解决了那部分的荣誉。当您看到解密中存在部分明文时,密钥是否正确就会变得很清楚 - 如果密钥错误,则不会。
查看源代码和过去的一些文档,我发现可能是全零的 IV 而不是重复使用密钥字节(从密码学的角度来看,这两者都是非常错误的)。
此外,与 SSLeay 一样,ECB 和 CBC 模式使用 PKCS#7 兼容填充,而不是无填充。
最后,如果您关闭流,FlushFinalBlock
将自动调用,例如通过退出 try-with-resources。因此,如果您之后获得数组,那么您应该获得正确的值 - 当然,在您正确取消填充之后。如果你调用 Flush
那么 FlushFinalBlock
已经被调用了,调用它两次会把事情弄得一团糟。
简单地删除刷新调用并检索数组在 CryptoStream
关闭后是可行的方法。
Young 从 MIT 复制的 DES 和密钥推导(des_string_to_key
和 des_string_to_2keys
)都是完全不安全的。使用全零 IV 是错误的。
如果您将其用作传输模式,那么填充预言机将适用,并且攻击者甚至不需要解密。密文不受完整性保护。
如果您使用上述例程来任何东西保密或确保安全,那您就是在自欺欺人。这是 80 年代的技术,我认为当时真正的密码学家也不会发现它是安全的。
基本上如果你的攻击者超过 8 岁,你就有麻烦了。