I have a file which has been encrypted by des.exe.
A file can be encrypted and decrypted using the following commands:
des -E -k "foo" sample.txt sample.txt.enc
des -D -k "foo" sample.txt.enc sample.txt.dec
I have attempted to decrypt using the following:
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};
}
But when I execute:
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.
Encrypted:
ñGYjl¦ûg†¼64©‹Bø
é¯Kœ|
Decrypt Files From PropertiesRight-click on the encrypted file and select Properties. In the General tab, select Advanced. Now, uncheck the Encrypt contents to secure data radio box and click on OK.
Removing a password from a document is a simple process, but you have to know the original password. Open the document and enter its password. Go to File > Info > Protect Document > Encrypt with Password. Clear the password in the Password box, and then click OK.
To decrypt a single file, run the command cipher /d "full path to the file". To decrypt a folder, type the following command: cipher / d "full path to the folder". To decrypt a folder that contains subfolders and files, type: cipher /d / s: "full path to the folder."
To my surprise you've already derived the key correctly. That was the meat of the problem, so Kudos for solving that part already. That the key is correct becomes clear when you see that part of the plaintext is present in the decryption - it wouldn't if the key was wrong.
Looking into the source and some docs from times past, I found a likely IV of all zeros instead of reusing the key bytes (both are very wrong, in cryptographic terms).
Furthermore, as always for SSLeay, the ECB and CBC modes use PKCS#7 compatible padding, rather than no padding.
Finally, FlushFinalBlock
will be automatically called if you close the stream, e.g. by exiting the try-with-resources. So if you get the array afterwards then you should get the right values - after you unpad correctly, of course. If you call Flush
then FlushFinalBlock
will already be called, and calling it twice will make a mess out of things.
Simply removing the flush calls and retrieving the array after the CryptoStream
is closed is the way to go.
Both DES and the key derivation (des_string_to_key
and des_string_to_2keys
) that Young copied from MIT are completely insecure. Using an all zero IV is wrong.
If you use this as transport mode than padding oracles will apply, and decryption is not even necessary for an attacker. The ciphertext is not integrity protected.
If you use the above routines to keep anything confidential or secure you're fooling yourself. This is 80s technology, and I think that real cryptographers wouldn't find it secure back then either.
Basically if your attacker is over 8 years old, you're in trouble.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With