如何使用预定义的密钥和 IV 在 python3 中进行 TripleDES 加密?

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

由于遗留 C#.NET 代码,我无法更改密钥和 IV。他们就是他们本来的样子。但我似乎无法找到一种方法来做我需要做的事情。我试过这个...

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

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

这告诉我“IV 必须是 8 个字节长。”

我的不是,也不可能。所以显然这行不通。我也试过这个:

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)

这告诉我“初始值 (IV) 无效,必须是 8 字节的倍数”。

我调整了 ivStr 变量,如上所示使用 getsizeof() 检查它,看看会发生什么:

ivStr = "AStringOfIV===="

我收到相同的消息,“初始值 (IV) 无效,必须是 8 字节的倍数”。但它是。现在怎么办?

我尝试直接将它作为字节传递,原始的 ivStr 和 64 字节的都带有额外的等号。

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

我收到相同的消息,“初始值 (IV) 无效,必须是 8 字节的倍数”。就是我告诉你!

我尝试了 pyDes.CBC 和 pyDes.ECB,想知道这是否会有所不同。 None我看到了。


根据@ShadowRanger 在下面评论中的建议,我将初始代码更改为添加 b64decodeb64encode,如下所示:

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

这确实消除了我得到的 8 字节错误并生成了一个字符串结果,但是,该字符串结果与 C# 程序生成的结果不匹配,所以我还没有到达终点。


作为参考,如果有帮助,将用于我的加密字符串的 C#.NET 解密代码(以及首先用于加密它的旧 C#.NET 代码)位于此处:

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();
        }
    }
}

上面的代码,当 运行 作为控制台应用程序时,会产生以下结果,我需要将其复制到 python3:

4B7389BB6DFE3AC7
39C739D51AC4FD13
BEB55BBC9430B83583E3E5E4E6C3799E
FD66F55347E715560C963B0EA181FB50

那么,如何使用预定义密钥和 IV 在 python3 中进行 TripleDES 加密?

如果您能为我提供任何有用的帮助,将不胜感激!

根据等号填充,我强烈怀疑您的 IV 是 base64 编码的。也许原始 API 期望收到它的 base64 编码?如果你这样做 base64.b64decode("AStringOfIV=") 它会产生一个长度为 8 的 IV,b'\x01+k\x8ax\x0e|\x85',这可能会得到你想要的行为。

确保您对每个值使用正确的编码 (Base64) 并使用正确的操作模式 (CBC)。

这是对我有用的 python 代码:

import pyDes
import base64

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

k = pyDes.triple_des(
    base64.b64decode(keyStr),
    mode=pyDes.CBC,
    IV=base64.b64decode(ivStr),
    padmode=pyDes.PAD_PKCS5
)
encryptedStr = k.encrypt(strToEncrypt)

k = pyDes.triple_des(
    base64.b64decode(keyStr),
    mode=pyDes.CBC,
    IV=base64.b64decode(ivStr),
    padmode=pyDes.PAD_PKCS5
)
decryptedStr = k.decrypt(encryptedStr)

请注意,由于存在许多问题,此代码并不安全:

  • IV 是静态的,但对于 CBC 模式必须是不可预测的。 IV 不应该是秘密的。我们通常在加密时将其添加到密文中,然后在解密前将其切掉。
  • Triple DES 的块大小较小,您使用的协议可能容易受到 Sweet32 或类似攻击。
  • 您没有使用任何类型的身份验证。这样你就无法检测到密文是否有任何(恶意的)更改。 CBC 模式具有部分可延展性,因此如果您将其用于任何严肃的事情,接收方必须验证解密的消息,以便它完全符合非常严格的结构。相反,我建议使用 AES-GCM 或至少通过 encrypt-then-MAC 方案添加消息验证码,如 HMAC-SHA256。
  • 您的 C# 代码使用默认的操作模式和填充。这很棘手,因为默认值可能会在未来的 .Net 版本中发生变化,这可能会破坏您的代码。始终限定您使用的选项。