【问题标题】:Convert C# encryption algorithm to Python 3将 C# 加密算法转换为 Python 3
【发布时间】:2019-09-21 03:52:07
【问题描述】:

我获得了一个现有的加密算法,需要在将密码发送到 API 之前对其进行加密。

我以前没有使用 Python 以外的任何其他语言的经验,因此我无法理解在功能上我需要做什么才能复制它。

我相信 C# 中 AES 的默认模式是 CBC。我想我已经复制了大部分所需的工作,但我需要填充数据,而且我不知道具体发生在哪个阶段,或者在哪里添加了长度。我不明白 C 代码中发生的事情的顺序。我也相信默认的填充方法是 PKCS#7,尽管我很高兴在这方面得到更正。

Original code

public static string EncryptStringToBytes_Aes(string username, string password)
{
    string encrypted = string.Empty;
    byte[] clearBytes = Encoding. UTF8.GetBytes(password);
    using (Aes aesAlg = Aes.Create())
    {
        byte[] k;
        byte[] iv;
          byte[] bytes = Encoding.UTF8.GetBytes(username);
        k = SHA256.Create().ComputeHash(bytes);
        iv = MD5.Create().ComputeHash(bytes);
        aesAlg.Key = k;
        aesAlg.IV = iv;

        ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, 
        aesAlg.IV);

        using (MemoryStream msEncrypt = new MemoryStream())
        {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, 
                encryptor, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(clearBytes, 0, clearBytes.Length);
                }
                encrypted = Convert.ToBase64String(msEncrypt.ToArray());
        }
    }
    return encrypted;
}
python recreation

from Crypto.Cipher import AES
import hashlib
username = "example"
password = "example2"
mode = AES.MODE_CBC
clearbytes = password.encode('utf-8')
bytes = username.encode('utf-8')
key = hashlib.sha256(bytes).digest()
iv = hashlib.md5(bytes).digest()

encryptor = AES.new(key, mode, IV = iv)
length = password + '0' + str(len(clearbytes))
encrypted= encryptor.encrypt(length).encode('base64')

我收到“ValueError:输入字符串的长度必须是 16 的倍数”。

当我尝试了一些填充时,编码状态“AttributeError: 'bytes' object has no attribute 'encode'”

我希望能够阅读此代码的人可以帮助我完成在 Python 中重新创建功能的最后步骤。

【问题讨论】:

  • 您可以在Crypto.Util.Padding 中找到填充函数,或者如果您使用的是 PyCrypto,您可以复制 PyCryptodome 的填充函数 from github。但是,您的 C# 代码看起来不太好:它使用散列作为 kdf、确定性 IV,并且没有身份验证。这些因素可能会降低甚至破坏您的加密方案的安全性。
  • 感谢您的帮助!是的,我知道加密不是很好,它是一个外部公司的 API,所以我们必须根据需要跳过箍。幸运的是,它不是我们特别需要确保安全的东西:我们只需要它工作!

标签: c# python .net encryption aes


【解决方案1】:

查看docs,您没有正确使用密码模块。

在调用AES.new()时,传入的key需要是16个字节的字符串,所以你需要把key属性填充为16个字节的长度。

encrypt() 方法将字节字符串作为输入,而您似乎试图传入一个长度(在您的示例中甚至没有定义)。

您的 cmets 建议您通过了,否则您不会看到 AttributeError 异常。

您看到的AttributeError 是因为Cipher.encrypt() 返回一个字节数组,而一个字节数组没有encode() 方法。您需要先将字节数组转换为字符串。来自here 的以下代码 sn-p 应该会有所帮助:

import array
decoded = array.array('b', your_input).tostring().decode('utf-8') 

您显然应该将'utf-8' 替换为'base64'

【讨论】:

  • 抱歉,我错过了这一行。 length = password + '0' + str(len(clearbytes))
  • 看起来不错,应该可以修复输出,我希望一切都只需要 PKCS#7 填充,但原始代码行 csEncrypt.Write(clearBytes, 0, clearBytes.Length) 让我担心,因为我不确定它到底是什么做!
  • 建议pad the key 不是好建议。应该说为 AES-128 生成一个随机的 16 字节密钥。可能可以通过使用 PBKDF 函数(如 Argon2、Bcrypt 和 PBKDF2)从密码生成。
【解决方案2】:

我相信我已经成功了,在这里结合了答案和 cmets。不需要填充密钥,因为哈希已经是 16 的倍数。(希望)成功的代码在下面供参考。 (不幸的是,在我测试连接之前,我还有另外两段 C# 代码需要翻译!)

Python Conversion

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import hashlib
import base64


username = "example"
password = "example"
mode = AES.MODE_CBC
bytes = username.encode('utf-8')
data_for_padding = password + '0' + str(len(password))
padded = pad(data_for_padding)

key = hashlib.sha256(bytes).digest()
iv = hashlib.md5(bytes).digest()
encryptor = AES.new(key, mode, IV = iv)
clearbytes = padded.encode('utf-8')
ciphertext = encryptor.encrypt(clearbytes)

result = base64.b64encode(ciphertext)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-05
    • 2018-11-10
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多