问题描述
我正在尝试创建一种多平台的加密-解密机制,到目前为止,我已经能够使用python进行加密并使用C进行解密,反之亦然,现在我正在尝试使用python脚本和一个节点来进行相同的操作js脚本。我能够在节点js中加密字符串并在python中解密,但是使用Python的加密消息在Node中解密却没有发生 这是python代码:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto import Random
from base64 import b64decode
from base64 import b64encode
import json
import random
#iv= get_random_bytes(16)
key=b"aaaaaaaaaaaaaaaa"
iv= b"aaaaaaaaaaaaaaaa"
value = "Hello World"
strValue= str.encode(value)
data =strValue
#Encryption
data = b64encode(data)
pad =data + b"\0" * (AES.block_size - len(data) % AES.block_size)
cipher = AES.new(key,AES.MODE_CBC,iv)
ciphertext= cipher.encrypt(pad)
print (type(ciphertext))
print(b64encode(ciphertext).decode("utf-8"))
# Decryption
cipher = AES.new(key,iv)
data = cipher.decrypt(ciphertext)
print(b64decode(data))
这是Nodejs代码:
const crypto = require('crypto');
var iv = Buffer.from('aaaaaaaaaaaaaaaa')
var key = Buffer.from('aaaaaaaaaaaaaaaa')
var cipher = crypto.createCipheriv('aes-128-cbc',key,iv);
let enc= cipher.update( "Hello World");
console.log(typeof (enc))
enc += cipher.final('base64');
console.log("enc is :",enc)
var decipher = crypto.createDecipheriv('aes-128-cbc',iv);
let decrypted = decipher.update(enc,'base64');
decrypted += decipher.final('utf8');
console.log("plain text is :",decrypted)
我从中取出了节点部分:
AES - Encryption with Crypto (node-js) / decryption with Pycrypto (python)
我收到错误消息:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
任何帮助将不胜感激,谢谢!
如果有更好的Node js实现方法,请告诉我们。
解决方法
在Python代码中,明文在加密之前是经过Base64编码的,而在NodeJS代码中则不是。此外,Python代码使用自定义的零填充,NodeJS代码使用默认的PKCS7填充。
要使NodeJS代码提供与Python代码相同的密文,必须在加密之前对明文进行Base64编码。此外,必须禁用默认的PKCS7填充,并且必须应用Python代码的“零填充”变体。由于PyCryptodome不支持零填充,因此需要自定义实现。
可能的NodeJS实现可能是:
const crypto = require('crypto')
const buffertrim = require('buffertrim')
function toB64padded(plaintext,blocksize){
var bufPlaintext = Buffer.from(plaintext,'utf8')
var bufPlaintextB64 = Buffer.from(bufPlaintext.toString('base64'),'utf8') // Base64 encoding
var bufPadding = Buffer.alloc(blocksize - bufPlaintextB64.length % blocksize)
return Buffer.concat([bufPlaintextB64,bufPadding]) // Zero padding
}
var iv = Buffer.from('aaaaaaaaaaaaaaaa') // Static IV only for testing purposes
var key = Buffer.from('aaaaaaaaaaaaaaaa')
// Encryption
var plaintext = "The quick brown fox jumps over the lazy dog"
var bufPlaintextB64padded = toB64padded(plaintext,16) // Base64 encoding and Zero padding
var cipher = crypto.createCipheriv('aes-128-cbc',key,iv)
cipher.setAutoPadding(false) // Disable PKCS7 padding
var ciphertextB64 = cipher.update(bufPlaintextB64padded,'','base64') // Encryption,Base64 encoding of ciphertext
ciphertextB64 += cipher.final('base64')
console.log("ciphertext is:",ciphertextB64)
// Decryption
var decipher = crypto.createDecipheriv('aes-128-cbc',iv)
decipher.setAutoPadding(false) // Disable PKCS7 (un-) padding
var bufPlaintextB64padded = Buffer.concat([ // Base64 decoding of ciphertext,decryption
decipher.update(ciphertextB64,'base64'),decipher.final()
]);
var bufPlaintextB64 = buffertrim.trimEnd(bufPlaintextB64padded) // Unpadding (unreliable)
var bufPlaintext = Buffer.from(bufPlaintextB64.toString('utf8'),'base64') // Base64 decoding
console.log("plaintext is:",bufPlaintext.toString('utf8'))
作为示例,纯文本快速的棕狐跳过懒狗已使用上面的Python代码和NodeJS代码进行了加密。两种代码都使用发布的密钥和IV产生相同的密文,即:
IETFUbiTGsKFZWLgjjP5RrKPX+GeVon1Kuy38bPdKXwqUJWUGWMJ9MOL9gEAsF+1U/N0Juwzu24Dju4UMwdZaA==
这意味着可以使用一种代码进行加密,而使用另一种代码进行解密。
请注意,在加密之前,无需对Base64编码明文 。同样,零填充是一种不可靠填充,最好用PKCS7填充代替。 PyCryptodome通过Crypto.Util.Padding
模块支持PKCS7填充,因此填充不需要自定义实现。并且如上所述,NodeJS的加密模块默认使用PKCS7填充。
出于安全原因,每次加密都必须使用 random IV。 IV(不是秘密的)通常与密文连接在一起。在Python代码中,实现了IV的随机生成(但已注释掉,可能出于测试目的)。但是,缺少串联部分。为了与Python代码兼容,在NodeJS代码中都省略了这两个代码。