解密在 PHP 中使用 MCRYPT_RIJNDAEL_256 加密的 Python 字符串

Posted

技术标签:

【中文标题】解密在 PHP 中使用 MCRYPT_RIJNDAEL_256 加密的 Python 字符串【英文标题】:Decrypting strings in Python that were encrypted with MCRYPT_RIJNDAEL_256 in PHP 【发布时间】:2012-01-03 06:34:40 【问题描述】:

我在 php 中有一个函数可以对文本进行如下加密:

function encrypt($text)

    $Key = "MyKey";

    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $Key, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));

如何在 Python 中解密这些值?

【问题讨论】:

没有人能解密这个,你把iv扔掉。 @hop - ECB 模式可以在没有 IV 的情况下解密。在 ECB 模式下,IV 不用于加密或解密。 @birryree:对!我只看到 iv 参数已设置并假设...这说明了为什么您不在一行中编写整个程序。 @dharmesh - 我正在尝试解决您的问题,但是感谢 PHP,MCRYPT_RIJNDAEL_256 算法与 AES 256 不同(在 Rijndael 中完成了更多轮次)。我必须找到一个 Python 实现或纯 Rijndael 256,它看起来不像 PyCrypto 也不支持 M2Crypto。 @dharmesh:你为什么不首先使用 php 端常用的东西? 【参考方案1】:

要解密这种形式的加密,您需要获取 Rijndael 版本。可以找到here。然后您需要模拟 PHP Mcrypt 模块中使用的密钥和文本填充。他们添加'\0' 以填充文本并键入正确的大小。他们使用 256 位块大小,与您提供的密钥一起使用的密钥大小为 128(如果您提供更大的密钥,它可能会增加)。不幸的是,我链接到的 Python 实现一次只编码一个块。我创建了 python 函数来模拟 Python 中的加密(用于测试)和解密

import rijndael
import base64

KEY_SIZE = 16
BLOCK_SIZE = 32

def encrypt(key, plaintext):
    padded_key = key.ljust(KEY_SIZE, '\0')
    padded_text = plaintext + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE) * '\0'

    # could also be one of
    #if len(plaintext) % BLOCK_SIZE != 0:
    #    padded_text = plaintext.ljust((len(plaintext) / BLOCK_SIZE) + 1 * BLOCKSIZE), '\0')
    # -OR-
    #padded_text = plaintext.ljust((len(plaintext) + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE)), '\0')

    r = rijndael.rijndael(padded_key, BLOCK_SIZE)

    ciphertext = ''
    for start in range(0, len(padded_text), BLOCK_SIZE):
        ciphertext += r.encrypt(padded_text[start:start+BLOCK_SIZE])

    encoded = base64.b64encode(ciphertext)

    return encoded


def decrypt(key, encoded):
    padded_key = key.ljust(KEY_SIZE, '\0')

    ciphertext = base64.b64decode(encoded)

    r = rijndael.rijndael(padded_key, BLOCK_SIZE)

    padded_text = ''
    for start in range(0, len(ciphertext), BLOCK_SIZE):
        padded_text += r.decrypt(ciphertext[start:start+BLOCK_SIZE])

    plaintext = padded_text.split('\x00', 1)[0]

    return plaintext

可以这样使用:

key = 'MyKey'
text = 'test'

encoded = encrypt(key, text)
print repr(encoded)
# prints 'I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY='

decoded = decrypt(key, encoded)
print repr(decoded)
# prints 'test'

为了比较,下面是 PHP 的输出,带有相同的文本:

$ php -a
Interactive shell

php > $key = 'MyKey';
php > $text = 'test';
php > $output = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB);
php > $encoded = base64_encode($output);
php > echo $encoded;
I+KlvwIK2e690lPLDQMMUf5kfZmdZRIexYJp1SLWRJY=

【讨论】:

绝对精彩!我能够让您的测试代码运行。然后,我针对来自 PHP 的加密值运行了解密函数(在 Python 中)。奇迹般有效。非常感谢。 除了键 len > 块大小部分你应该使用 string.ljust() 来填充键。 @hop 感谢您对 string.ljust() 的提醒。我已经更新了键填充以使用它。换一种方式,文本填充似乎仍然更好。 @101100 使用“pip install rijndael”安装包,但出现错误。 r = rijndael.rijndael(padded_key, BLOCK_SIZE) AttributeError: 'module' object has no attribute 'rijndael' @101100 我试过这段代码,但它给了我属性错误“AttributeError: 'module' object has no attribute 'rijndael'”【参考方案2】:

如果你愿意在 PHP 端使用 MCRYPT_RIJNDAEL_128 而不是 256,这很简单:

from Crypto.Cipher import AES
import base64
key="MyKey"

def decrypt(text)
    cipher=AES.new(key)
    return cipher.decrypt(base64.b64decode(text))

【讨论】:

谢谢。我认为值得注意的是Crypto模块可以在pycrypto中找到。【参考方案3】:

虽然@101100 的答案在当时是一个不错的答案,但它不再可行。该参考现在是一个断开的链接,并且代码只能在较旧的 Python 上运行 (

相反,pprp project 似乎很好地填补了空白。在 Python 2 或 Python 3 上,只需 pip install pprp,然后:

import pprp
import base64

KEY_SIZE = 16
BLOCK_SIZE = 32


def encrypt(key, plaintext):
    key = key.encode('ascii')
    plaintext = plaintext.encode('utf-8')
    padded_key = key.ljust(KEY_SIZE, b'\0')

    sg = pprp.data_source_gen(plaintext, block_size=BLOCK_SIZE)
    eg = pprp.rjindael_encrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)

    ciphertext = pprp.encrypt_sink(eg)

    encoded = base64.b64encode(ciphertext)

    return encoded.decode('ascii')


def decrypt(key, encoded):
    key = key.encode('ascii')
    padded_key = key.ljust(KEY_SIZE, b'\0')

    ciphertext = base64.b64decode(encoded.encode('ascii'))

    sg = pprp.data_source_gen(ciphertext, block_size=BLOCK_SIZE)
    dg = pprp.rjindael_decrypt_gen(padded_key, sg, block_size=BLOCK_SIZE)

    return pprp.decrypt_sink(dg).decode('utf-8')


key = 'MyKey'
text = 'test'

encoded = encrypt(key, text)
print(repr(encoded))
# prints 'ju0pt5Y63Vj4qiViL4VL83Wjgirq4QsGDkj+tDcNcrw='

decoded = decrypt(key, encoded)
print(repr(decoded))
# prints 'test'

我有点沮丧,密文与您在 101100 的答案中看到的不同。但是,我已经使用这种技术成功解密了 OP 中描述的用 PHP 加密的数据。

【讨论】:

以上是关于解密在 PHP 中使用 MCRYPT_RIJNDAEL_256 加密的 Python 字符串的主要内容,如果未能解决你的问题,请参考以下文章

在 php 中加密,然后在 javascript 中使用相同的密钥解密

在php中解密河豚

CryptoJS 中 CFB 模式中的 mcrypt_encrypt 函数

如何在php中加密/解密数据?

在 PHP 中加密和解密许可证密钥

在PHP中解密密码[关闭]