如何在Ruby中解密由Node弃用的createCipher加密的数据? [英] How can I decrypt data encrypted by Node's deprecated createCipher, in Ruby?
问题描述
我有一些用Node加密的旧数据,需要在Ruby中解密。
I have some legacy data, encrypted in Node, which I need to decrypt in Ruby.
问题是,数据已使用现已弃用的方法加密, createCipher
。此方法使用密码来执行加密。它已由 createCipheriv
,它需要一个32字节的密钥和16字节的初始化向量。
The problem is, the data was encrypted with a now deprecated method, createCipher
. This method uses a password to perform encryption. It has been replaced by createCipheriv
, which requires a 32 byte key and a 16 byte initialization vector.
在Node中,我可以使用同样不推荐使用的 createDecipher
,它也接受密码。但是,我不知道Ruby中有任何等效的方法,这很有意义,因为现在已知这些方法不安全且已弃用。
In Node, I can decrypt the string with the similarly deprecated createDecipher
, which also accepts a password. However, I'm not aware of any equivalent method in Ruby, which makes sense, since these methods are now known to be insecure and are deprecated.
Ruby的OpenSSL AES密码正确地需要一个32字节的密钥和16字节的IV,例如较新的 createCipheriv
,但是我没有这两个密码,只有我与 createCipher
。
Ruby's OpenSSL AES Cipher correctly requires a 32 byte key and 16 byte IV like the newer createCipheriv
, but I don't have either of these, only the original password I used with createCipher
.
如何获取 createCipher
/ Ruby中的createDecipher
行为?
How can I get createCipher
/createDecipher
-like behavior in Ruby?
具体来说,鉴于以下JavaScript ...
Specifically, given the following JavaScript...
const crypto = require('crypto');
const cipher = crypto.createCipher("aes-256-cbc", 'secret password');
cipherText = cipher.update('dummy string', "utf8", "hex") + cipher.final("hex");
console.log(cipherText); // f3051259f83c7ca2ac012a396c4c0848
...我如何使用密码的秘密密码'
解密字符串'f3051259f83c7ca2ac012a396c4c0848'
并返回输入值'虚拟字符串'$ c $
... how can I use the password 'secret password'
to decrypt the string 'f3051259f83c7ca2ac012a396c4c0848'
and arrive back at the input value 'dummy string'
in Ruby?
推荐答案
基础算法aes256需要32位密钥和16位初始化向量,而与更高级别的抽象(例如 createCipher
)提供的接口。在后台,它必须产生一个32位密钥和一个16位初始化向量,该向量是从输入密码派生的。
The underlying algorithm, aes256, requires a 32 bit key and 16 bit initialization vector, regardless of the interface provided by higher level abstractions like createCipher
. Under the hood, it must be producing a 32 bit key and 16 bit initialization vector, derived from the input password.
createCipher
通过对输入密码反复进行MD5哈希处理以生成字节数组,从中提取32字节密钥和16字节初始化向量来完成此操作。之所以弃用它,是因为这不是产生随机IV的安全方法。
createCipher
does this by repeatedly MD5 hashing on the input password to produce a byte array, from which it extracts the 32 byte key and a 16 byte initialization vector. It was deprecated because this is not a secure way of producing a random IV.
这发生在Node的C源代码内,位于 / deps / openssl / openssl / crypto / evp / evp_key 。
This happens down inside the C source of Node, in /deps/openssl/openssl/crypto/evp/evp_key.c.
此函数有很多参数,但是用于生成 createCipher('aes-256-cbc','password')的密钥/ iv时
,此方法在此处,并带有以下参数:
This function has quite a few arguments, but when used to generate a key/iv for createCipher('aes-256-cbc', 'password')
, this method is invoked here, with the following arguments:
-
const EVP_CIPHER * type
// aes- 256-cbc'' -
const EVP_MD * md
// EVP_md5() -
const unsigned char * salt
//空,未使用 -
const unsigned char * data
//密码 -
int datal
// 8,密码长度, -
int count
// 1,未使用 -
未签名字符* key
//输出密钥缓冲区, -
unsigned char * iv
//输出iv缓冲区
const EVP_CIPHER *type
// "aes-256-cbc"const EVP_MD *md
// EVP_md5()const unsigned char *salt
// null, unusedconst unsigned char *data
// "password"int datal
// 8, length of "password",int count
// 1, unusedunsigned char *key
// output key buffer,unsigned char *iv
// output iv buffer
此方法必须产生48个字节(对于32个字节,键,并为IV输入16键),方法是先运行 hash = md5(password)
,生成16个字节。
This method must produce 48 total bytes (32 for the key, and 16 for the IV) and it does so by first running hash = md5(password)
, producing 16 bytes.
现在,对于每个随后的16个字节组,它使用密码
连接前16个字节,并再次对其进行哈希处理: hash = md5(hash +密码)
。
Now, for each subsequent set of 16 bytes, it concatenates the previous 16 bytes with the password
, and hashes it again: hash = md5(hash + password)
.
有效字节...
- 0到16通过
md5(密码)
- 通过
md5(md5(password)+ password)$生成的17至32 c $ c>
- 33到48通过
md5(md5(md5(md5(password)+密码)+密码)
生成。 li>
- 0 to 16 generated via
md5(password)
- 17 to 32 generated via
md5(md5(password) + password)
- 33 to 48 genearted via
md5(md5(md5(password) + password) + password)
.
我们可以在Ruby中实现相同的功能以导出正确的密钥,并通过IV解密通过密码给定字符串:
We can implement the same in Ruby to derive the correct key and IV to decrypt a given string via a password:
require 'digest'
require 'openssl'
def decrypt(cipher_text, password)
bytes = [ Digest::MD5.digest(password) ]
bytes << Digest::MD5.digest(bytes.last + password)
bytes << Digest::MD5.digest(bytes.last + password)
bytes = bytes.join
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.decrypt
cipher.key = bytes[0...32]
cipher.iv = bytes[32...48]
# OpenSSL deals in raw bytes, not the hex-encoded representation
# 'f3051259f83c7ca2ac012a396c4c0848' => "\xF3\x05\x12Y\xF8<|\xA2\xAC\x01*9lL\bH"
cipher_text = cipher_text.unpack('a2' * (cipher_text.length / 2)).map(&:hex).pack('c*')
cipher.update(cipher_text) + cipher.final
end
decrypt('f3051259f83c7ca2ac012a396c4c0848', 'secret password') # => 'dummy string'
为完整起见,这是等效的 encrypt
方法,该方法将允许在Node弃用的 createDecipher
中解密Ruby中加密的数据:
For completeness, here is the equivalent encrypt
method, which will allow data encrypted in Ruby to be decrypted in Node's deprecate createDecipher
:
def encrypt(plain_text, password)
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
bytes = [ Digest::MD5.digest(password) ]
bytes << Digest::MD5.digest(bytes.last + password)
bytes << Digest::MD5.digest(bytes.last + password)
bytes = bytes.join
cipher.key = bytes[0...32]
cipher.iv = bytes[32...48]
cipher_text = cipher.update(plain_text) + cipher.final
# Produce a hex representation
# "\xF3\x05\x12Y\xF8<|\xA2\xAC\x01*9lL\bH" => 'f3051259f83c7ca2ac012a396c4c0848'
cipher_text.unpack('C*').map { |byte| byte.to_s(16) }.map { |str| str.rjust(2, "0") }.join
end
encrypt('dummy string', 'secret password') # => "f3051259f83c7ca2ac012a396c4c0848"
这篇关于如何在Ruby中解密由Node弃用的createCipher加密的数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!