我正在使用以下代码段对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代码,以便它生成“填充”密码和任何线索?
答案 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)