用PHP加密文本并用Python解密

时间:2019-01-14 06:49:14

标签: php python encryption pycrypto php-openssl

我正在使用以下代码段对PHP7中的文本进行加密:

$plaintext = "message to be encrypted";
$cipher = "aes-256-cbc";
$ivlen = openssl_cipher_iv_length($cipher);
$iv = "0123456789012345";
$key = "akshayakshayaksh";
$ciphertext = openssl_encrypt($plaintext, $cipher, $key, $options=0, $iv);
print $ciphertext;
  

输出:cUXDhOEGz19QEo9XDvMzXkGFmg / YQUnXEqKVpfYtUGo =

现在,当我尝试在Python3中对其进行解密时,会出现错误:

from Crypto.Cipher import AES
obj2 = AES.new('akshayakshayaksh', AES.MODE_CBC, '0123456789012345')
ciphertext = "cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
obj2.decrypt(ciphertext)
  

回溯(最近通话最近):
  <模块>中第1行的文件“ ”   
  文件   “ /anaconda3/lib/python3.6/site-packages/Crypto/Cipher/blockalgo.py”,   第295行,在解密中       返回self._cipher.decrypt(ciphertext)ValueError:输入字符串的长度必须为16的倍数

我知道AES是一种分组密码算法。但是,我应该如何修复PHP代码,以便它生成“填充”密码和任何线索?

2 个答案:

答案 0 :(得分:4)

这里的主要问题是您使用的密钥大小不同。 PHP的class AppWindow(QDialog,test3.Ui_Dialog): def __init__(self,parent=None): pg.setConfigOption('background', 'w') #before loading widget super(AppWindow,self).__init__() self.setupUi(self) def update(self): print("icerde") t1=time.clock() points=100 x=np.arange(points) data = np.sin(np.arange(points)/points*3*np.pi+time.time()) C=pg.hsvColor(time.time()/5%1,alpha=.5) pen=pg.mkPen(color=C,width=10) self.graphicsView.plot(x,data,pen=pen,clear=True) QtCore.QCoreApplication.processEvents() self.repeatself.setChecked(True) QtCore.QTimer.singleShot(1,self.update()) if __name__=="__main__": app = QApplication(sys.argv) w = AppWindow() w.show() w.update() app.exec_() print("DONE") 根据加密算法字符串(在这种情况下为openssl_encrypt)确定密钥大小,因此期望使用256位密钥。如果密钥较短,则将其填充零字节,因此"aes-256-cbc"使用的实际密钥为:

openssl_encrypt

Pycryptodome通过密钥的实际大小确定密钥的大小,因此您的Python代码使用AES-128-CBC。另外,正如kelalaka在coments中所提到的,密文是base64编码的("akshayakshayaksh\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 是base64默认对密文编码的-如果我们在{{1中使用{{1} }})。 Pycryptodome无法解码密文,因此我们必须使用openssl_encrypt

OPENSSL_RAW_DATA

末尾的额外$options字符是填充字符-CBC需要填充字符。 Pycryptodome不会自动删除填充,但是它在b64decode()中提供了填充功能。

key = b'akshayakshayaksh\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
obj2 = AES.new(key, AES.MODE_CBC, b'0123456789012345')
ciphertext = b"cUXDhOEGz19QEo9XDvMzXkGFmg/YQUnXEqKVpfYtUGo="
print(obj2.decrypt(b64decode(ciphertext)))
#b'message to be encrypted\t\t\t\t\t\t\t\t\t'

尽管PHP的openssl可以接受任意大小的密钥,但是最好使用算法字符串中指定的密钥大小,以至少避免混淆。此外,密钥字节应尽可能随机。

正如Maarten Bodewes在comments中指出的那样,此密钥使用有限的字节范围,因此非常弱。此外,它是通过重复一个单词创建的,这使其容易受到字典攻击(比暴力攻击要快得多)。

在PHP中,我们可以使用random_bytes()

获得加密安全的随机字节
\t

以及在Python中使用os.urandom()

Crypto.Util.Padding

(您可以使用相同的功能来创建IV;您不应该使用静态IV,IV必须是不可预测的)

您还可以使用KDF从密码中获取密钥。在这种情况下,使用随机盐和足够多的迭代次数很重要。 PHP的hash_pbkdf2函数提供了PBKDF2算法,而hashlib.pbkdf2_hmac函数则提供了Python。

答案 1 :(得分:0)

今天当一位朋友向我指出 Fernet 图书馆时,我正在为此苦苦挣扎。它有一个 python 和 php 版本,使这非常简单和健壮。

PHP 版本:https://github.com/kelvinmo/fernet-php

<?php
require 'vendor/autoload.php';

use Fernet\Fernet;

$key = '[Base64url encoded fernet key]';
$fernet = new Fernet($key);

$token = $fernet->encode('string message');

$message = $fernet->decode('fernet token');
if ($message === null) {
    echo 'Token is not valid';
}

?>

Python:https://pypi.org/project/cryptography/

from cryptography.fernet import Fernet
# Put this somewhere safe!
key = Fernet.generate_key()
f = Fernet(key)
token = f.encrypt(b"A really secret message. Not for prying eyes.")
f.decrypt(token)
print(token)