MD5 in Oracle (DBMS_OBFUSCATION_TOOLKIT.MD5)

2019-05-06 18:31发布

I'm trying to compose a function to obtain MD5 hashes from bits I've gathered here and there. I want to obtain the lower-case hexadecimal representation of the hash. I have this so far:

CREATE OR REPLACE FUNCTION MD5 (
    CADENA IN VARCHAR2
) RETURN DBMS_OBFUSCATION_TOOLKIT.VARCHAR2_CHECKSUM
AS
BEGIN
    RETURN LOWER(
        RAWTOHEX(
            UTL_RAW.CAST_TO_RAW(
                DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT_STRING => CADENA)
            )
        )
    );
END;

I'm not sure about the return type of the function. DBMS_OBFUSCATION_TOOLKIT.VARCHAR2_CHECKSUM looks like the appropriate choice and as far as I can tell it works as expected but the package definition for dbms_obfuscation_toolkit as displayed by SQL Developer shows this:

SUBTYPE varchar2_checksum IS VARCHAR2(16);

The output has 32 characters so I must be doing something wrong. My questions:

  • What's the correct type for the RETURN statement?
  • Am I doing unnecessary conversions to calculate the hash?

2条回答
贪生不怕死
2楼-- · 2019-05-06 19:00

It's a peculiarity of Oracle PL/SQL that stored procedure parameters and function return types cannot be limited. That is, we cannot have a procedure with a signature like this:

SQL> create or replace procedure my_proc (p1 in varchar2(30))
  2  is
  3  begin
  4      null;
  5  end;
  6  /

Warning: Procedure created with compilation errors.

SQL> show error
Errors for PROCEDURE MY_PROC:

LINE/COL ERROR
-------- -----------------------------------------------------------------
1/34     PLS-00103: Encountered the symbol "(" when expecting one of the
         following:
         := . ) , @ % default character
         The symbol ":=" was substituted for "(" to continue.

SQL> create or replace procedure my_proc (p1 in varchar2)
  2  is
  3  begin
  4      null;
  5  end;
  6  /

Procedure created.

SQL>

Sure we can define the procedure's parameter using a SUBTYPE but Oracle will ignore it. Same goes for function return types...

SQL> create or replace package my_subtypes as
  2      subtype ltd_string is varchar2(30);
  3  end;
  4  /

Package created.

SQL> create or replace function my_func return my_subtypes.ltd_string
  2  is
  3  begin
  4      return lpad('a', 4000, 'a');
  5  end;
  6  /

Function created.

SQL> select length(my_func) from dual
  2  /

LENGTH(MY_FUNC)
---------------
           4000

SQL>

The only way of limiting parameters and return types is to declare variables using subtypes within the stored procedure. Use the variables within the package, and assign them to the OUT paramters (or RETURN the variable for functions).

Which is a long-winded way of saying, you can use DBMS_OBFUSCATION_TOOLKIT.VARCHAR2_CHECKSUM in your code confident that it won't prevent your function returning 32 characters.

However, it will confuse developers who will lookup the SUBTYPE declaration. In the worst case these people will use the subtype to declare their own working variables with the following tragic result:

SQL> declare
  2      v my_subtypes.ltd_string;
  3  begin
  4      v := my_func;
  5  end;
  6  /
declare
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 4


SQL>

So, it is better not to use an inappropriate subtype. Instead declare your own.

查看更多
SAY GOODBYE
3楼-- · 2019-05-06 19:15

Here you go:

create or replace function getMD5(
  in_string in varchar2)
return varchar2
as
  cln_md5raw raw(2000);
  out_raw raw(16);
begin
  cln_md5raw := utl_raw.cast_to_raw(in_string);
  dbms_obfuscation_toolkit.md5(input=>cln_md5raw,checksum=>out_raw);
  -- return hex version (32 length)
  return rawtohex(out_raw);
end;

The 32 length is because it is a hex representation of the raw(16) value. Or, modify above to output the raw version and store the raw in a RAW column (less space used, but you'll be doing future rawtohex and hextoraw conversions, believe me).

Cheers

查看更多
登录 后发表回答