I have a legacy application written in PL/SQL that encrypts and decrypts data using 3DES. Now I need to perform similar encryption from a ruby app. Eventually the resulting hash will need to be decrypted by the same PL/SQL application using its existing algorithm.
The problem is that I'm obtaining different encrypted results in PL/SQL and Ruby and I don't know why.
First here is exactly how the PL/SQL encryption works:
From Oracle's docs about DBMS_OBFUSCATION_TOOLKIT http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_obtool.htm
"Oracle's implementation of 3DES supports either a 2-key or 3-key implementation, in outer cipher-block-chaining (CBC) mode."
Function signature:
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);
Note about the parameter which: "If = 0, (default), then TwoKeyMode is used. If = 1, then ThreeKeyMode is used." This helped me choose the cipher in the ruby version.
Here is how the application makes that call:
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;
Second here is what I'm trying in Ruby:
OpenSSL::Cipher docs: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html
OpenSSL docs to give me the cipher name: From http://www.openssl.org/docs/apps/enc.html "des-ede-cbc Two key triple DES EDE in CBC mode"
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
As shown in the code the ruby version computes a different hash value. Why? What needs to change to make them consistent?
Points I'm unsure about:
- Whether des-ede-cbc is in fact the same as what Oracle does.
- Whether utl_raw.cast_to_raw and unpack("H*") will do the same thing to the encrypted binary data.
- What exactly cipher.final appends and if there's any equivalent way to append that data in PL/SQL.
Note: I am aware that DES is insecure and that AES has superseded it. My use case does not require these hashes to be unbreakable. The important requirement is to make the hashes consistent so that the PL/SQL app can decrypt hashes generated by the ruby app.