我有一些用Node加密的旧数据,需要在Ruby中解密。
问题是,数据已使用现已弃用的方法createCipher
加密。此方法使用密码来执行加密。它已被createCipheriv
取代,它需要一个32字节的密钥和一个16字节的初始化向量。
在Node中,我可以使用同样不推荐使用的createDecipher
解密字符串,该字符串也接受密码。但是,我不知道Ruby中有任何等效的方法,这很有意义,因为现在已知这些方法是不安全的并且已弃用。
Ruby的OpenSSL AES密码正确地需要32字节的密钥和16字节的IV,就像较新的createCipheriv
一样,但是我没有这两个,只有createCipher
使用的原始密码。 / p>
如何在Ruby中获得类似createCipher
/ createDecipher
的行为?
具体来说,鉴于以下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
...如何使用密码'secret password'
解密字符串'f3051259f83c7ca2ac012a396c4c0848'
并返回Ruby中的输入值'dummy string'
?
答案 0 :(得分:3)
createCipher
对输入的密码重复执行MD5哈希,以生成32字节的密钥和16字节的初始化向量。之所以弃用它,是因为这不是产生随机IV的安全方法。
这发生在/deps/openssl/openssl/crypto/evp/evp_key.c中Node的C源代码内部。
此函数有很多参数,但是当用于为createCipher('aes-256-cbc', 'password')
生成密钥/ iv时,此方法将使用以下参数调用here:
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,未使用unsigned char *key
//输出密钥缓冲区,unsigned char *iv
//输出iv缓冲区此方法必须产生48个总字节(密钥为32个字节,IV为16个字节),并且首先运行hash = md5(password)
,产生16个字节。
现在,对于每个后续的16个字节组,它将前16个字节与password
连接起来,并再次对其进行哈希处理:hash = md5(hash + password)
。
有效字节...
md5(password)
产生的md5(md5(password) + password)
生成的md5(md5(md5(password) + password) + password)
生成。我们可以在Ruby中实现相同的功能以得出正确的密钥,并可以通过密码IV来解密给定的字符串:
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加密的数据:
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"