python-3.xencryptiontripledes

How do I do TripleDES encryption in python3 using a pre-defined key and IV?


Because of legacy C#.NET code, I cannot change the key and the IV. They simply are what they are. But I cannot seem to find a way to do what I need to do. I tried this...

strToEncrypt = "Please encrypt me."
ivStr = "AStringOfIV="
keyStr = "aKeyOfSomeKindThat+IWillNotShare"

cipher = DES3.new(keyStr, DES3.MODE_CFB, IV=ivStr)
encryptedStr = cipher.iv + cipher.encrypt(strToEncrypt)

This told me "IV must be 8 bytes long."

Mine isn't and it can't be. So apparently this won't work. I also tried this:

m=hashlib.md5()
print(sys.getsizeof(iv))
m.update(bytes(keyStr, 'UTF-8'))
k = pyDes.triple_des(m.digest(),pyDes.CBC,IV=iv,padmode=pyDes.PAD_PKCS5)
encryptedStr = k.encrypt(strToEncrypt)

This told me "Invalid Initial Value (IV), must be a multiple of 8 bytes".

I tweaked the ivStr variable, checking it with getsizeof() as shown above, just to see what would happen:

ivStr = "AStringOfIV===="

I got the same message, "Invalid Initial Value (IV), must be a multiple of 8 bytes". But... it is. Now what?

I tried passing it in as bytes directly, both with the original ivStr and with the one that was 64 bytes, with extra equals signs.

k = pyDes.triple_des(
    keyStr,
    mode=pyDes.CBC,
    IV=bytes(ivStr, 'UTF-8'),
    pad=None,
    padmode=pyDes.PAD_PKCS5
)
encryptedStr = k.encrypt(strToEncrypt)

I got the same message, "Invalid Initial Value (IV), must be a multiple of 8 bytes". It is I tell you!

I tried pyDes.CBC and pyDes.ECB, wondering if somehow that would make a difference. None I could see.


Per a suggestion from @ShadowRanger in the comments below, I switched my initial code to add b64decode and b64encode like this:

pyDes.triple_des(m.digest(),pyDes.CBC,IV=base64.b64decode(ivStr),padmode=pyDes.PAD_PKCS5)
encryptedStr = base64.b64encode(k.encrypt(strToEncrypt))

That did remove the 8-bytes error I was getting and produced a string result, however, the string result does not match the result the C# program produces, so I am still not at the finish line yet.


For reference, if it helps, the C#.NET decrypt code that will be used against my encrypted string (and the old C#.NET code to encrypt it in the first place) is here:

using System;
using System.Security.Cryptography;
using System.Text;

namespace encryption
{
    class Program
    {
        const string IV = "AStringOfIV=";
        const string Key = "aKeyOfSomeKindThat+IWillNotShare";

        public static string Encrypt(string data)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(data);
            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            ICryptoTransform transform = tdes.CreateEncryptor(Convert.FromBase64String(Key), Convert.FromBase64String(IV));
            byte[] result = transform.TransformFinalBlock(buffer, 0, buffer.Length);
            string encrypted = BitConverter.ToString(result).Replace("-", "");
            return encrypted;
        }

        public static string Decrypt(string data)
        {
            byte[] result = StringToByteArray(data);
            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            ICryptoTransform transform = tdes.CreateDecryptor(Convert.FromBase64String(Key), Convert.FromBase64String(IV));
            byte[] originalBytes = transform.TransformFinalBlock(result, 0, result.Length);
            string original = Encoding.UTF8.GetString(originalBytes);
            return original;
        }

        public static byte[] StringToByteArray(String hex)
        {
            int NumberChars = hex.Length / 2;
            byte[] bytes = new byte[NumberChars];
            using (var sr = new System.IO.StringReader(hex))
            {
                for (int i = 0; i < NumberChars; i++)
                {
                    string thisByte = new string(new char[2] { (char)sr.Read(), (char)sr.Read() });
                    bytes[i] = Convert.ToByte(thisByte, 16);
                }
            }
            return bytes;
        }

        static void Main(string[] args)
        {
            Console.WriteLine(Encrypt("397854"));
            Console.WriteLine(Encrypt("397786"));
            Console.WriteLine(Encrypt("70001948"));
            Console.WriteLine(Encrypt("70001890"));

            Console.WriteLine("Press enter to close...");
            Console.ReadLine();
        }
    }
}

The code above, when run as a console application, produces the following results, which I need to duplicate in python3:

4B7389BB6DFE3AC7
39C739D51AC4FD13
BEB55BBC9430B83583E3E5E4E6C3799E
FD66F55347E715560C963B0EA181FB50

So, how do I do TripleDES encryption in python3 using a pre-defined key and IV?

If you can offer me any useful assistance, it would be very much appreciated!


Solution

  • Based on the equals padding, I strongly suspect your IV is base64 encoded. Perhaps the original API expected to receive it base64 encoded? If you do base64.b64decode("AStringOfIV=") it produces a length 8 IV, b'\x01+k\x8ax\x0e|\x85', which may get the behavior you want.