Ruby 中的 Coldfusion "AES/CBC/PKCS5Padding" 解密

Coldfusion "AES/CBC/PKCS5Padding" decryption in Ruby

我需要解密使用 AES/CBC/PKCS5Padding 方案加密的文本。我得到的密文是用Coldfusion生成的

下面的 CFML 示例:

<table border="1" cellpadding="5" cellspacing="0">
    <tr bgcolor="c0c0c0">
        <th>Decrypted string</th>
    <th>3DESKey</th>
    </tr>
        <cfset variables.algorithm ="AES/CBC/PKCS5Padding">
        <cfset variables.seed ="C610297CE8570750">
        <cfset variables.password = "Vza0O49SHpIe/mR4+4jHXhApmKhEyl5O2nzzDxVNQbo=">
        <cfset variables.decryptedString = Decrypt(variables.password, generate3DesKey("#variables.seed#"), "#variables.algorithm#", "Base64")>
        <cfoutput>
        <tr>
            <td>#variables.decryptedString#</td>
        <td><cfoutput>#generate3DesKey("variables.seed")#</cfoutput></td>
        </tr>
        </cfoutput>
</table>

输出为:

Decrypted String: Name322big563
3DESKey: QzYxMDI5N0NFODU3MDc1MA==

我试过 ruby:

require 'openssl'
require 'base64'

string = "Vza0O49SHpIe/mR4+4jHXhApmKhEyl5O2nzzDxVNQbo="

def decrypt(cpass)
  des = OpenSSL::Cipher::Cipher.new('AES-256-CBC')
  des.decrypt
  des.key = 'C610297CE8570750'
  return des.update(Base64.decode64(cpass)) + des.final
end

decrypted = decrypt(string)

puts "decrypted string: #{decrypted}"

我明白了 key length too short (OpenSSL::Cipher::CipherError) 问题是我不知道密钥,只知道使用的种子 C610297CE8570750,因为 CFML 脚本返回的密钥是 base64,但我需要一个十六进制密钥。 我也尝试过 OpenSSL::Cipher::AES256.new(:CBC) 同样的错误。

require 'openssl'
require 'base64'
# decryption

aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.decrypt
aes.padding = 1 # actually it's on by default
aes.key = "QzYxMDI5N0NFODU3MDc1MA=="
aes.iv = "C610297CE8570750"
aes.update(Base64::decode64("Vza0O49SHpIe/mR4+4jHXhApmKhEyl5O2nzzDxVNQbo="))+aes.final

有什么想法吗?

编辑:

正如@Leigh 所暗示的,需要使用 AES-128-CBC,所以我这样做了:

require 'openssl'
require 'base64'

string = "Vza0O49SHpIe/mR4+4jHXhApmKhEyl5O2nzzDxVNQbo="

def decrypt(cpass)
  des = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
  des.decrypt
  des.key = 'C610297CE8570750'
  return des.update(Base64.decode64(cpass)) + des.final
end

decrypted = decrypt(string)

puts "decrypted string: #{decrypted}"

实际上似乎有点工作(...ish)。

decrypted string: ▒▒.ϥD▒▒       ▒▒▒▒▒Name322big563

知道还有什么问题吗?

除非我大错特错,否则这是我几年前遇到的问题。

PHP Encryption Code Converted to ColdFusion

(根据评论扩展)

but I need a hex key

然后将其从 base64 转换为十六进制。在 CF 中,您可以使用 BinaryEncode() and BinaryDecode functions:

binaryEncode(binaryDecode("QzYxMDI5N0NFODU3MDc1MA==", "base64"), "hex")

看起来还有其他一些问题:

  1. CF代码生成128位密钥,但是ruby代码使用的是AES 256,需要使用AES 128。

  2. CF 代码正在生成 随机 IV。 Ruby 代码使用了完全不同的 iv。使用CBC模式,双方必须使用相同的iv才能得到预期的结果。 "Decrypting with the incorrect IV causes the first block of plaintext to be corrupt ...",这就是您的解密值关闭的原因。要解决此问题,Ruby 代码应使用用于加密的相同 iv


更新:

当 CF 自动生成 IV 时(就像它在这里所做的那样),它 prepends that IV to the encrypted value:

When ColdFusion creates an IV automatically, it generates a secure, random IV and prepends this to the encrypted data. When ColdFusion decrypts the data, this IV is recovered and used. It is cryptologically important that the IV varies between encryptions. This is why the encrypted value changes when you repeatedly encrypt the same string with an algorithm that uses an IV, like DES/CBC/PKCS5Padding. Unlike the encryption key, it is not necessary for the IV to be kept secret.

因此可以通过删除加密二进制文件的第一个 "block" 来提取 IV 值。块大小取决于算法。对于 AES,它是 16。我不知道确切的 Ruby 代码,但在 CF 中你可以像这样提取 IV:

blockSize = 16;
rawBinary = binaryDecode(encryptedString, "base64");
// IV is always the first block 
ivBytes   = arraySlice(rawBinary, 1, blockSize);
// Remaining bytes are the encrypted value
dataBytes = arraySlice(rawBinary, blockSize+1, arrayLen(rawBinary)-blockSize);