我正在尝试加密Python中的一些内容并在nodejs应用程序中解密它。
我很难让两个AES实现一起工作。我就在这里。
在节点中:
var crypto = require('crypto');
var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// add padding
while (input.length % 16 !== 0) {
input += ' ';
}
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
var decrypt = function (input, password, callback) {
// Convert urlsafe base64 to normal base64
var input = input.replace('-', '+').replace('/', '_');
// Convert from base64 to binary string
var edata = new Buffer(input, 'base64').toString('binary')
// Create key from password
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
// Create iv from password and key
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// Decipher encrypted data
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
var plaintext = new Buffer(decrypted, 'binary').toString('utf8');
callback(plaintext);
};
encrypt(input, password, function (encoded) {
console.log(encoded);
decrypt(encoded, password, function (output) {
console.log(output);
});
});
这会产生输出:
BXSGjDAYKeXlaRXVVJGuREKTPiiXeam8W9e96Nknt3E=
hello world
在python中
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'
def _encrypt(data, nonce, password):
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
# pad to 16 bytes
data = data + " " * (16 - len(data) % 16)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, nonce, password):
edata = base64.urlsafe_b64decode(edata)
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return aes.decrypt(edata)
output = _encrypt(input, "", password)
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)
这会产生输出
BXSGjDAYKeXlaRXVVJGuRA==
hello world
显然它们非常接近,但节点似乎在填充输出。任何想法如何让两者互操作?
答案 0 :(得分:21)
好的,我已经弄明白了,节点使用OpenSSL,它使用PKCS5来填充。 PyCrypto没有处理填充,所以我自己这样做只是在两者中添加'。
如果我在python代码中添加PKCS5填充并删除节点代码中的填充,则可以正常工作。
如此更新的工作代码。 节点:
var crypto = require('crypto');
var password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var input = 'hello world';
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var encrypted;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
encrypted = cipher.update(data, 'binary') + cipher.final('binary');
} else {
encrypted = cipher.update(data, 'utf8', 'binary') + cipher.final('binary');
}
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
var decrypt = function (input, password, callback) {
// Convert urlsafe base64 to normal base64
var input = input.replace(/\-/g, '+').replace(/_/g, '/');
// Convert from base64 to binary string
var edata = new Buffer(input, 'base64').toString('binary')
// Create key from password
var m = crypto.createHash('md5');
m.update(password)
var key = m.digest('hex');
// Create iv from password and key
m = crypto.createHash('md5');
m.update(password + key)
var iv = m.digest('hex');
// Decipher encrypted data
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
// UPDATE: crypto changed in v0.10
// https://github.com/joyent/node/wiki/Api-changes-between-v0.8-and-v0.10
var nodev = process.version.match(/^v(\d+)\.(\d+)/);
var decrypted, plaintext;
if( nodev[1] === '0' && parseInt(nodev[2]) < 10) {
decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
plaintext = new Buffer(decrypted, 'binary').toString('utf8');
} else {
plaintext = (decipher.update(edata, 'binary', 'utf8') + decipher.final('utf8'));
}
callback(plaintext);
};
encrypt(input, password, function (encoded) {
console.log(encoded);
decrypt(encoded, password, function (output) {
console.log(output);
});
});
Python:
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
input = 'hello world'
BLOCK_SIZE = 16
def pad (data):
pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
return data + pad * chr(pad)
def unpad (padded):
pad = ord(padded[-1])
return padded[:-pad]
def _encrypt(data, nonce, password):
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
data = pad(data)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data)
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, nonce, password):
edata = base64.urlsafe_b64decode(edata)
m = md5()
m.update(password)
key = m.hexdigest()
m = md5()
m.update(password + key)
iv = m.hexdigest()
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return unpad(aes.decrypt(edata))
output = _encrypt(input, "", password)
print(output)
plaintext = _decrypt(output, "", password)
print("'" + plaintext + "'")
答案 1 :(得分:0)
对于与我相似的任何人,他都在寻找一种简单的方法来对python中的AES进行加密和解密,而这在node.js中也做同样的事情。这里的类支持AES的不同位以及十六进制和base64编码,它们在node.js中产生相同的结果。
还请注意,如果您缺少Crypto软件包,则只需通过以下方式安装
pip install pycrypto
python的代码如下:
import base64
import hashlib
from Crypto.Cipher import AES
class AESCrypto(object):
def __init__(self, algorithm, password):
self.algorithm = filter(lambda x: not x.isdigit(), algorithm).lower()
self.bits = int(filter(str.isdigit, algorithm))
self.bs = 16
if not self.algorithm == 'aes':
raise Exception('Only AES crypto is supported')
if not self.bits % 8 == 0:
raise Exception('Bits of crypto must be a multiply of 8.')
self.bytes = self.bits / 8
self.password = password
self.generateKeyAndIv()
def generateKeyAndIv(self):
last = ''
allBytes = ''
maxBytes = self.bytes + self.bs
while len(allBytes) < maxBytes:
last = hashlib.md5(last + self.password).digest()
allBytes += last
self.key = allBytes[:self.bytes]
self.iv = allBytes[self.bytes:maxBytes]
def encrypt(self, raw, outputEncoding):
outputEncoding = outputEncoding.lower()
raw = self._pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
encrypted = cipher.encrypt(raw)
if outputEncoding == 'hex':
return encrypted.encode('hex')
elif outputEncoding == 'base64':
return base64.b64encode(encrypted)
else:
raise Exception('Encoding is not supported.')
def decrypt(self, data, inputEncoding):
inputEncoding = inputEncoding.lower()
if inputEncoding == 'hex':
data = ''.join(map(chr, bytearray.fromhex(data)))
elif inputEncoding == 'base64':
data = base64.b64decode(data)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return self._unpad(cipher.decrypt(data))
def _pad(self, data):
padding = self.bs - len(data) % self.bs
return data + padding * chr(padding)
@staticmethod
def _unpad(data):
return data[0:-ord(data[-1])]
以下是使用该类的示例:
加密示例:
password = 'some_random_password'
content = 'content_to_be_encrypted'
cipher = AESCrypto('aes192', password)
encrypted = cipher.encrypt(content, 'hex')
解密示例:
password = 'some_random_password'
content = 'encrypted_content'
cipher = AESCrypto('aes192', password)
decrypted = cipher.decrypt(content, 'hex')
答案 2 :(得分:0)
尝试使用Python 3.8运行Python脚本时,遇到以下错误:
m.update(password)
TypeError: Unicode-objects must be encoded before hashing
密码应为:
password = b'abcd'
我也遇到以下错误:
m.update(password + key)
TypeError: can't concat str to bytes
我可以通过在密钥后面添加以下行来修复它:
key = bytes.fromhex(key_)
python脚本应以这种方式工作:
from Crypto.Cipher import AES
from hashlib import md5
import base64
password = b'abcd'
input = 'hello world'
BLOCK_SIZE = 16
def pad (data):
pad = BLOCK_SIZE - len(data) % BLOCK_SIZE
return data + pad * chr(pad)
def unpad (padded):
pad = ord(chr(padded[-1]))
return padded[:-pad]
def _encrypt(data, nonce, password):
m = md5()
m.update(password)
key_ = m.hexdigest()
key = bytes.fromhex(key_)
m = md5()
m.update(password + key)
iv = m.hexdigest()
iv = bytes.fromhex(iv)
data = pad(data)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
encrypted = aes.encrypt(data.encode('utf-8'))
return base64.urlsafe_b64encode(encrypted)
def _decrypt(edata, nonce, password):
edata = base64.urlsafe_b64decode(edata)
m = md5()
m.update(password)
key = m.hexdigest()
key = bytes.fromhex(key)
m = md5()
m.update(password + key)
iv = m.hexdigest()
iv = bytes.fromhex(iv)
aes = AES.new(key, AES.MODE_CBC, iv[:16])
return unpad(aes.decrypt(edata))
output = _encrypt(input, "", password)
print(output)
plaintext = _decrypt(output, "", password)
print(plaintext)