将RijndaelManaged解密从C重写为Python

2024-10-04 11:25:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在研究将这个“解密”函数从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填充物而挣扎,但在这一点上我完全迷失了方向。非常感谢最后一部分的帮助。:)

^{pr2}$

Tags: ofthenewgetstringbybytesvar
1条回答
网友
1楼 · 发布于 2024-10-04 11:25:52

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

pprp只实现Rijndael-block cipher本身。只允许单个块加密。padding和mode of operation基本上都没有实现,也就是说,它们仍然必须由用户实现。操作模式定义了如何基于块密码对多个块进行加密。”很大程度上”意味着已经有一个适配器实现了(不安全的)ECB模式和PKCS7填充。在这种情况下,这没有帮助,因为C代码使用CBC模式和PKCS7填充。为此,必须首先实现适当的适配器。欧洲中央银行模式的现有实施(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不同,而是一个特殊的variant或{a5}。例如,AES的固定块大小为16字节,而Rijndael允许16、20、24、28或32字节。在C代码中,使用了块大小为32字节的Rijndael,因此长度必须与块大小相对应的IV也有32个字节大。如果您在AES实现中使用这个IV,您将不可避免地收到错误消息,如“错误的IV大小”或类似的消息。此外,pprp支持16、24和32字节的块大小。默认情况下,使用16字节的块大小。在

相关问题 更多 >