【问题标题】:Rewriting RijndaelManaged decryption from C# to Python将 RijndaelManaged 解密从 C# 重写为 Python
【发布时间】:2019-10-03 20:58:54
【问题描述】:

我正在考虑将这个“解密”函数从 C# 移植到 Python(来源:https://stackoverflow.com/a/10177020

public static string Decrypt(string cipherText, string passPhrase)
        {
            // Get the complete stream of bytes that represent:
            // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
            // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                var plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }

我已经在 C# 中加密了“Hello World”,并设法达到了在 Python 中一直到(包括)keyBytes 的正确值。我尝试使用 pprp (https://pypi.org/project/pprp/) 和其他库来获取解密的文本,但无论我尝试什么,我只会得到“错误的 IV 大小”或垃圾数据。我假设我正在为 PKCS7 填充而苦苦挣扎,但此时我完全迷失了。非常感谢最后一部分的任何帮助。 :)

import pprp
import base64
from pkcs7 import PKCS7Encoder      


cipherText = "JKjzaiOSreH+l0GSzsatS8nmohRisvINOwrEjHOwIqIXo88CQT0/al7V7vXOmuamfTeJ235O1SZ0Yd2BZk2e2V4MznT7hyzqzu5J326JIReXPeH6EdtPFrxhdTPsfb8Q"
passphrase = "a78356254f093b00e45434828110c7b5"

# This constant is used to determine the keysize of the encryption algorithm in bits.
# We divide this by 8 within the code below to get the equivalent number of bytes.
keysize = 256

# This constant determines the number of iterations for the password bytes generation function.
derivationIterations = 1000

# Get the complete stream of bytes that represent:
# [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
cipherTextBytesWithSaltAndIv = base64.b64decode(cipherText)
cipherTextBytesWithSaltAndIv=list(bytearray(cipherTextBytesWithSaltAndIv))

# Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
saltStringBytes = cipherTextBytesWithSaltAndIv[:int(keysize / 8)]

# Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
ivStringBytes = cipherTextBytesWithSaltAndIv[int(keysize / 8):int((keysize / 8) * 2)]

# Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.    
cipherTextBytes = cipherTextBytesWithSaltAndIv[int((keysize / 8) * 2):]

keyBytes = pprp.pbkdf2(passphrase.encode('utf-8'), bytes(saltStringBytes), int(keysize / 8))

print(base64.b64encode(keyBytes))

【问题讨论】:

    标签: c# python encryption


    【解决方案1】:

    如果您发布了完整的代码,包括 Rijndael 加密(您已根据您的描述实现),那将是有意义的。无论如何,现有的 Python 代码(包括使用 PBKDF2 生成密钥)生成的数据与 C# 代码相同。

    pprp 仅实现 Rijndael-block cipher 本身。这仅允许对单个完整块进行加密。 padding 和mode of operation 基本上都没有实现,即它们仍然必须由用户实现。操作模式定义了如何基于块密码对多个块进行加密。 “很大程度上”意味着已经有一个适配器实现了(不安全的)ECB 模式和 PKCS7 填充。这在这种情况下没有帮助,因为 C# 代码使用 CBC-mode 和 PKCS7-padding。为此,必须首先实施适当的适配器。 ECB 模式的现有实现 (adapters.py) 可以用作蓝图。一个可能的实现(用于解密)是:

    import pprp
    import base64
    
    # CBC-Adapter
    def rjindael_decrypt_gen_CBC(key, s, iv, block_size=pprp.config.DEFAULT_BLOCK_SIZE_B):
        r = pprp.crypto_3.rijndael(key, block_size=block_size) # for Python 2 use crypto_2 instead of crypto_3
    
        i = 0
        for block in s:
    
            decrypted = r.decrypt(block)
            decrypted = xor(decrypted, iv)  
            iv = block
    
            yield decrypted
            i += 1
    
    def xor(block, iv):
        resultList = [ (a ^ b) for (a,b) in zip(block, iv) ]
        return bytes(resultList)
    
    #
    # Posted code goes here...
    #
    
    # Decryption
    blocksize = 32
    sg = pprp.data_source_gen(cipherTextBytes, blocksize)
    dg = rjindael_decrypt_gen_CBC(keyBytes, sg, ivStringBytes, blocksize);
    decrypted = pprp.decrypt_sink(dg, blocksize)
    print("Decrypted data: " + str(decrypted))
    

    关于“错误的 IV 大小”错误消息,我怀疑您尝试的其他实现实际上是 AES 实现。 AES 与 Rijndael 不同,而是 Rijndael 的特殊 variant。例如,AES 具有 16 字节的固定块大小,而 Rijndael 允许 16、20、24、28 或 32 字节。在 C# 代码中,使用了块大小为 32 字节的 Rijndael,因此长度必须与块大小相对应的 IV 也是 32 字节大。如果您在 AES 实现中使用此 IV,您将不可避免地收到一条错误消息,如“IV 大小错误”或类似内容。此外,pprp 支持 16、24 和 32 字节的块大小。默认情况下使用 16 字节的块大小。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 2017-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多