如何在Ruby中解密由Node弃用的createCipher加密的数据? [英] How can I decrypt data encrypted by Node's deprecated createCipher, in Ruby?

查看:167
本文介绍了如何在Ruby中解密由Node弃用的createCipher加密的数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些用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'并返回输入值'虚拟字符串'

... 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, unused
  • const unsigned char *data // "password"
  • int datal // 8, length of "password",
  • int count // 1, unused
  • unsigned 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)

  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆