Ruby OpenSSL::Cipher在CBC模式下给出不同结果的三重DES
我需要在CBC模式下使用双倍长度三重DES(TDEA)对称密钥加密数据。我正在使用Ruby OpenSSl::Cipher。但与BP工具密码计算器和EMV DES计算器相比,它给出了不同的结果。下面是我的代码 有人能帮我解释为什么我的结果不同吗?我有在线检查工具来验证我的结果 它给出了与BP工具相同的结果Ruby OpenSSL::Cipher在CBC模式下给出不同结果的三重DES,ruby,sinatra,Ruby,Sinatra,我需要在CBC模式下使用双倍长度三重DES(TDEA)对称密钥加密数据。我正在使用Ruby OpenSSl::Cipher。但与BP工具密码计算器和EMV DES计算器相比,它给出了不同的结果。下面是我的代码 有人能帮我解释为什么我的结果不同吗?我有在线检查工具来验证我的结果 它给出了与BP工具相同的结果 flavour = 'des-ede3-cbc' # key and input text are in hexadecimal key = "2315208C9110AD4023152
flavour = 'des-ede3-cbc'
# key and input text are in hexadecimal
key = "2315208C9110AD402315208C9110AD40"
iv = "0000000000000000"
input_text = "2020205a4f534135366461746574696d653d32303138313032343130303332333b6578706972793d313232323b70616e3d343233363031373839303132333435362121212121212121"
begin
c = OpenSSL::Cipher.new flavour
c.encrypt
c.key = key
c.iv = iv
str = input_text
enc = c.update(str) + c.final
puts "#{flavour} gives us #{enc.unpack('H*').first.upcase}"
rescue => e
puts "#{flavour} didn't work because #{e.message}"
end
为了获得预期的结果,您需要调整代码,以便OpenSSL实际知道您正在使用十六进制编码的数据。由于OpenSSL处理普通字节,因此需要首先将十六进制字符串编码为原始字节:
key = ["2315208C9110AD402315208C9110AD40"].pack('H*')
iv = ["0000000000000000"].pack('H*')
input_text = ["2020205a4f534135366461746574696d653d32303138313032343130303332333b6578706972793d313232323b70616e3d343233363031373839303132333435362121212121212121"].pack('H*')
之后,您需要选择正确的加密算法。3DE有多种不同的口味,其中(除其他细节外)在预期的键长上有所不同。由于您正在处理一个16字节的密钥,因此您似乎正在使用OpenSSL调用的des ede cbc
flavour = 'des-ede-cbc'
这些调整通常足以导致与您在链接的网站上看到的结果相同的结果。但是,由于我不知道的原因,他们在实际加密输入数据之前会默默地更改输入数据
我不知道他们使用的具体规则,但要获得与网站相同的密文输出,您需要删除任何尾随感叹号,并从输入字符串中删除前导空格字符
input_text # packed as you provided it originally
# => " ZOSA56datetime=20181024100323;expiry=1222;pan=4236017890123456!!!!!!!!"
# remove trailing exclamation marks
input_text = input_text.sub(/!*$/, '')
# remove the first character
input_text = input_text[1..-1]
最后,您现在可以对“改进的”输入文本进行加密:
begin
c = OpenSSL::Cipher.new(flavour)
c.encrypt
c.key = key
c.iv = iv
enc = c.update(input_text) + c.final
puts "#{flavour} gives us #{enc.unpack('H*').first.upcase}"
rescue => e
puts "#{flavour} didn't work because #{e.message}"
end
这将产生几乎预期的密码文本
不过,在最后一个8字节的块中,它只是略有不同。我假设该网站使用了一些奇怪的非标准填充(这是确保输入文本可以划分为完整的8字节块所必需的),这导致最终的块不同
在任何情况下,您都可以使用以下代码解密结果(以及网站上的“预期”密文):
flavour = 'des-ede-cbc'
key = ["2315208C9110AD402315208C9110AD40"].pack('H*')
iv = ["0000000000000000"].pack('H*')
encrypted = ["4A9E9B245BBDC16D76998143CB6FC1C2B8780539C1C9A100AEC3D745B8BF00DF43A4B51A29A6205845E510E18E26AB940152F90F12E86543A9E5239B30DFDBCD8D3FCDB65F603979"].pack('H*')
c = OpenSSL::Cipher.new(flavour)
c.decrypt
c.key = key
c.iv = iv
decrypted = c.update(encrypted)
如何获得正确的填充以匹配来自网站的结果将留给读者作为练习:)不幸的是,我没有找到任何文档或他们工具的源代码
在任何情况下,请注意3DES是一种非常过时的加密算法,不再被认为是安全的。除非您必须实际使用3DES,否则您应该使用更安全的算法
libnaude项目提供了加密和签名安全算法的强化实现,可以为您所需的协议提供有效和安全的基础。gem提供了Ruby绑定。c.key=key
返回ArgumentError:key必须是24个字节
。请提供一个有效的例子。
flavour = 'des-ede-cbc'
key = ["2315208C9110AD402315208C9110AD40"].pack('H*')
iv = ["0000000000000000"].pack('H*')
encrypted = ["4A9E9B245BBDC16D76998143CB6FC1C2B8780539C1C9A100AEC3D745B8BF00DF43A4B51A29A6205845E510E18E26AB940152F90F12E86543A9E5239B30DFDBCD8D3FCDB65F603979"].pack('H*')
c = OpenSSL::Cipher.new(flavour)
c.decrypt
c.key = key
c.iv = iv
decrypted = c.update(encrypted)