2

我有一个用 PL/SQL 编写的遗留应用程序,它使用 3DES 加密和解密数据。现在我需要从 ruby​​ 应用程序执行类似的加密。最终,生成的哈希将需要由同一个 PL/SQL 应用程序使用其现有算法进行解密。

问题是我在 PL/SQL 和 Ruby 中获得了不同的加密结果,我不知道为什么。

首先是 PL/SQL 加密的工作原理:

来自 Oracle 关于 DBMS_OBFUSCATION_TOOLKIT 的文档 http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_obtool.htm

“Oracle 的 3DES 实现支持 2 密钥或 3 密钥实现,采用外部密码块链接 (CBC) 模式。”

函数签名:

DBMS_OBFUSCATION_TOOLKIT.DES3Encrypt(
input_string      IN     VARCHAR2,
key_string        IN     VARCHAR2,
encrypted_string  OUT    VARCHAR2,
which             IN     PLS_INTEGER  DEFAULT TwoKeyMode
iv_string         IN     VARCHAR2     DEFAULT NULL);

请注意以下参数:“如果 = 0,(默认),则使用 TwoKeyMode。如果 = 1,则使用 ThreeKeyMode。” 这帮助我选择了 ruby​​ 版本中的密码。

以下是应用程序进行该调用的方式:

set serveroutput on;
declare 
        v_encrypted varchar2(100);
begin  
  dbms_obfuscation_toolkit.des3encrypt(
    input_string => 'abcdefgh',       -- data to encrypt
    key_string => '16_byte_string_k', -- 16 byte = 128 bit key needed by DES3Encrypt
    encrypted_string => v_encrypted,
    iv_string => 'xxxxxxxx');         -- initialization vector
    dbms_output.put_line( lower(utl_raw.cast_to_raw(v_encrypted)) );
    -- prints 23ff779e88e2dbe1
end;

第二个是我在 Ruby 中尝试的:

OpenSSL::Cipher 文档: http ://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html

OpenSSL 文档给我密码名称:来自http://www.openssl.org/docs/apps/enc.html “des-ede-cbc CBC 模式下的两键三重 DES EDE”

require 'openssl'

cipher = OpenSSL::Cipher.new('des-ede-cbc')
cipher.encrypt
input = 'abcdefgh'
cipher.key = '16_byte_string_k'
cipher.iv = 'xxxxxxxx'

# i noticed that cipher.update returns same length hash as PL/SQL
# if called without cipher.final, but you are not supposed to do that
#encrypted = cipher.update(input)
encrypted = cipher.update(input) + cipher.final

hex_representation = encrypted.unpack("H*")

puts hex_representation
# prints a5cfc96485d7203eb929c28ceb9fcd53

如代码所示,ruby 版本计算不同的哈希值。为什么?需要改变什么才能使它们保持一致?

我不确定的点:

  • des-ede-cbc 是否其实和 Oracle 做的一样。
  • utl_raw.cast_to_raw 和 unpack("H*") 是否会对加密的二进制数据做同样的事情。
  • cipher.final 究竟追加了什么,以及是否有任何等效的方法可以在 PL/SQL 中追加该数据。

注意:我知道 DES 是不安全的,并且 AES 已经取代了它。我的用例不需要这些哈希值是牢不可破的。重要的要求是使哈希值保持一致,以便 PL/SQL 应用程序可以解密 ruby​​ 应用程序生成的哈希值。

4

2 回答 2

2

我们去挖吧!

['des-cbc', 'des', 'des-cfb', 'des-ofb', 'des-ecb',
 'des-ede-cbc', 'des-ede', 'des-ede-cfb', 'des-ede-ofb', 
 'des-ede3-cbc', 'des-ede3', 'des3', 'des-ede3-cfb', 
 'des-ede3-ofb', 'desx'].each do |flavour|
  begin
    c = OpenSSL::Cipher.new flavour
    c.encrypt
    c.key = '16_byte_string_k'
    c.iv = 'xxxxxxxx'
    str = 'abcdefgh'
    enc = c.update(str) + c.final
    puts "#{flavour} gives us #{enc.unpack('H*')}"
  rescue => e
    puts "#{flavour} didn't work because #{e.message}"
  end
end

结果:

des-cbc gives us ["a5cfc96485d7203eb929c28ceb9fcd53"]
des gives us ["a5cfc96485d7203eb929c28ceb9fcd53"]
des-cfb gives us ["d898369e91589ae8"]
des-ofb gives us ["d898369e91589ae8"]
des-ecb gives us ["de8579b342a528b6143594946045d91a"]
des-ede-cbc gives us ["23ff779e88e2dbe1c009dc3105d8ff88"]
des-ede gives us ["0e589e3d85ac83efbb271a2e4a77cf4e"]
des-ede-cfb gives us ["1618988004b6a948"]
des-ede-ofb gives us ["1618988004b6a948"]
des-ede3-cbc didn't work because key length too short
des-ede3 didn't work because key length too short
des3 didn't work because key length too short
des-ede3-cfb didn't work because key length too short
des-ede3-ofb didn't work because key length too short
desx didn't work because key length too short

des-ede-cbc给你一个匹配——至少第一部分匹配。问题是,为什么加密的正文更长?我敢打赌这是正确的内容,而且 PL/SQL 版本会以某种方式被截断——我会看看我是否能弄清楚。

编辑:不,这是 padding。当您在密码上将填充设置为 0 时,您将获得与 PL/SQL 版本相同的结果,例如

['des-cbc', 'des', 'des-cfb', 'des-ofb', 'des-ecb',
 'des-ede-cbc', 'des-ede', 'des-ede-cfb', 'des-ede-ofb', 
 'des-ede3-cbc', 'des-ede3', 'des3', 'des-ede3-cfb', 
 'des-ede3-ofb', 'desx'].each do |flavour|
  begin
    c = OpenSSL::Cipher.new flavour
    c.encrypt
    c.key = '16_byte_string_k'
    c.iv = 'xxxxxxxx'
    c.padding = 0 # This is the important part!
    str = 'abcdefgh'
    enc = c.update(str) + c.final
    puts "#{flavour} gives us #{enc.unpack('H*')}"
  rescue => e
    puts "#{flavour} didn't work because #{e.message}"
  end
end

...
des-ede-cbc gives us ["23ff779e88e2dbe1"]
...

您现在需要将两种算法与不同长度的输入字符串进行比较。在此处查看填充方法的文档:http ://www.ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html

于 2013-10-31T23:40:03.047 回答
0

我们遇到了同样的问题,但有一个重要区别:我们的数据库过程在加密密码时没有指定初始化向量 (IV)。在 ruby​​ 中省略 IV 并不会导致与在 Oracle 过程调用中省略它相同的结果,因此两者似乎都使用了不同的“默认”IV。

默认的 Oracle IV 是“0123456789abcdef”十六进制解码,正如一些人在这里发现的那样:https ://community.oracle.com/thread/1528090

在 Ruby 中,你可以这样设置:

['des-cbc', 'des', 'des-cfb', 'des-ofb', 'des-ecb',
 'des-ede-cbc', 'des-ede', 'des-ede-cfb', 'des-ede-ofb', 
 'des-ede3-cbc', 'des-ede3', 'des3', 'des-ede3-cfb', 
 'des-ede3-ofb', 'desx'].each do |flavour|
  begin
    c = OpenSSL::Cipher.new flavour
    c.encrypt
    c.key = '16_byte_string_k'
    c.iv = ['0123456789abcdef'].pack('H*') # Required if no IV is set in Oracle!
    c.padding = 0
    str = 'abcdefgh'
    enc = c.update(str) + c.final
    puts "#{flavour} gives us #{enc.unpack('H*')}"
  rescue => e
    puts "#{flavour} didn't work because #{e.message}"
  end
end
于 2016-04-26T08:32:44.253 回答