Oracle的MD5函数介绍

2021-09-06 15:30:33 浏览数 (1)

老婆问了个问题,Oracle中可以生成MD5值?

快速解决领导问题是第一位的,搜了一下,Oracle 11g,创建自定义函数,可以得到对应的MD5,

代码语言:javascript复制
SQL> CREATE OR REPLACE FUNCTION MD5(
     passwd IN VARCHAR2)
     RETURN VARCHAR2
     IS
     retval varchar2(32);
     BEGIN 
       retval := utl_raw.cast_to_raw(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT_STRING => passwd)) ;
     RETURN retval;
     END;
     /
Function created.


SQL> select md5(440102199010230759) from dual;
MD5(440102199010230759)
-------------------------------------
43DC9F79656CF8170ECD0CDF0E8D95C4

回过头来,再看下这个函数,核心就是这段,

代码语言:javascript复制
utl_raw.cast_to_raw(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT_STRING => passwd)) ;

1. dbms_obfuscation_toolkit.md5,可以创建数据的MD5哈希值。MD5算法基于给定的数据生成一个128位的散列值,确保数据传输的一致性,其语法如下所示,接收RAW类型和VARCHAR2类型,

代码语言:javascript复制
DBMS_OBFUSCATION_TOOLKIT.MD5(
   input            IN   RAW,
   checksum         OUT  raw_checksum);


DBMS_OBFUSCATION_TOOLKIT.MD5(
   input_string     IN   VARCHAR2,
   checksum_string  OUT  varchar2_checksum);


DBMS_OBFUSCATION_TOOLKIT.MD5(
   input         IN  RAW)
  RETURN raw_checksum;


DBMS_OBFUSCATION_TOOLKIT.MD5(
   input_string  IN  VARCHAR2)
  RETURN varchar2_checksum;

参数解释如下,

2. utl_raw.cast_to_raw函数,是将一个VARCHAR2类型转换成RAW类型,数据本身不做任何改动,只是数据类型,会转换成RAW,语法如下,

代码语言:javascript复制
UTL_RAW.CAST_TO_RAW (
   c  IN VARCHAR2) 
RETURN RAW;

返回值是RAW或者NULL,

我们从官方文档《Database PL/SQL Packages and Types Reference》,可以找到这个存储过程DBMS_OBFUSCATION_TOOLKIT,他允许应用使用DES或者3DES加密数据,同时他指出DBMS_OBFUSCATION_TOOLKIT已经过时了,DBMS_CRYPTO可以替代他,但如果是10g以前的版本,只可以使用DBMS_OBFUSCATION_TOOLKIT包,

DBMS_OBFUSCATION_TOOLKIT enables an application to encrypt data using either the Data Encryption Standard (DES) or the Triple DES algorithms. Note: DBMS_OBFUSCATION_TOOLKIT is deprecated. DBMS_CRYPTO is intended to replace the DBMS_OBFUSCATION_TOOLKIT, providing greater ease of use and support for a range of algorithms to accommodate new and existing systems. See Chapter 39, "DBMS_CRYPTO" for more information.

我们看下DBMS_CRYPTO,功能上和DBMS_OBFUSCATION_TOOLKIT一致,都是提供了数据加密和解密,

DBMS_CRYPTO provides an interface to encrypt and decrypt stored data, and can be used in conjunction with PL/SQL programs running network communications. It provides support for several industry-standard encryption and hashing algorithms, including the Advanced Encryption Standard (AES) encryption algorithm. AES has been approved by the National Institute of Standards and Technology (NIST) to replace the Data Encryption Standard (DES).

但是他支持的加密算法更丰富,

如果是普通用户执行DBMS_CRYPTO,需要SYS授权,

代码语言:javascript复制
SQL> grant execute on dbms_crypto to bisal;
Grant succeeded.

创建自定义函数,和DBMS_OBFUSCATION_TOOLKIT很像,

代码语言:javascript复制
CREATE OR REPLACE FUNCTION NEW_MD5(
  string_in IN VARCHAR2)
  RETURN RAW
     IS
     encrypted_raw RAW(128);
  BEGIN
    encrypted_raw := dbms_crypto.hash(src=>utl_i18n.string_to_raw(string_in,'AL32UTF8'), typ=>dbms_crypto.hash_md5);
  RETURN encrypted_raw;
  END;
/


SQL> select new_md5(440102199010230759) from dual;
NEW_MD5(440102199010230759)
--------------------------------------------------------------------------------
43DC9F79656CF8170ECD0CDF0E8D95C4

了解了创建MD5函数的操作,再学习下什么是MD5?

MD5信息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节,MD5集合的数量是2的128次方)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开。这套算法的程序在RFC 1321标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

MD5计算,对原始消息做有损的压缩计算,无论消息(输入值)的长度字节是多少,是1亿字节、1个字节、还是0个字节,都会生成一个固定长度(128位/16字节)的消息摘要(输出值)。

MD5的原理,摘抄一下,

以512位分组来处理输入的数据信息,且每一分组又被划分为16个32位子分组,经过了一系列算法的处理后,算法的输出由四个32位分组组成,将这四个32位的分组级联后,就可以产生出128位的固定长度散列值,在得到这个MD5值后,如果想在逆向反推输入的数据就基本不可能了。

举个栗子,假设原始消息(如同开始示例的440102199010230759)是,

代码语言:javascript复制
8wewef390f0uhrnffeeewer2r1398sdsuhdqwwue2342ifwssqwwy88hd893hsxadqwd3kd0u31rnde2eqhwhqior29e9e7asd`e2

经过MD5计算,得到(消息摘要),

(如同开始示例的43DC9F79656CF8170ECD0CDF0E8D95C4)

代码语言:javascript复制
06f636b7a1f0b8b685540ab5434d0cc5

他有几个特征,

1. 不可逆

在不知道原始消息的前提下,是无法凭借16个字节的消息摘要(Message Digest),还原出原始的消息的。看到知乎上有个介绍MD5的文章很形象,

一头耗牛被加工成一包牛肉干,这个就是一次MD5的操作,在加工过程中,N多身体部位有损失,因此不能通过牛肉干复原出一头耗牛,即MD5不可逆。

2. 单向性

如果告诉我们原始消息,算法是MD5,迭代次数=1,我们都可以得到一模一样的消息摘要(Message Digest)。

3. 恒定性

如果各位计算出的消息摘要,和我计算的不一样,那肯定是使用一个假的MD5工具。因为他无法满足,当原始消息恒定时,每次运行MD5产生的消息摘要都是恒定不变的。正常来讲,无论是你、我、他来计算,结果都应该是一模一样的。

4. 不可预测性

将原始消息的最后一个数字'2',修改成'1',

8wewef390f0uhrnffeeewer2r1398sdsuhdqwwue2342ifwssqwwy88hd893hsxadqwd3kd0u31rnde2eqhwhqior29e9e7asd`e1

产生的消息摘要,是不是和'06f636b7a1f0b8b685540ab5434d0cc5'很相似?但是,产生的消息摘要没有一丝一毫的关联性,新的消息摘要如下所示:'e7cdaf219b3bbaa49db252677f95dec6'。

MD5的作用,可以用于数据完整性校验、数字签名、文件完整性检查、密码保存等,以密码保存为例,一个明文密码,通过MD5加密,可以生成一串看着很复杂的字符串,一般情况下,对于非黑客,还是能提升安全等级的,

看起来是很复杂,意会一下,

各种编程语言都会提供MD5的实现,如下是找到一段Java版本的MD5,和开始的示例是一样的,根据“单向性”,输入440102199010230759,得到的MD5值是43DC9F79656CF8170ECD0CDF0E8D95C4,

代码语言:javascript复制
public class MD5{
    /*
    *四个链接变量
    */
    private final int A=0x67452301;
    private final int B=0xefcdab89;
    private final int C=0x98badcfe;
    private final int D=0x10325476;
    /*
    *ABCD的临时变量
    */
    private int Atemp,Btemp,Ctemp,Dtemp;
     
    /*
    *常量ti
    *公式:floor(abs(sin(i 1))×(2pow32)
    */
    private final int K[]={
        0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
        0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
        0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
        0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
        0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
        0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
        0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
        0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
        0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
        0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
        0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
        0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
        0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
    /*
    *向左位移数,计算方法未知
    */
    private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
        12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
        4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
        15,21,6,10,15,21,6,10,15,21,6,10,15,21};
         
    /*
    *初始化函数
    */
    private void init(){
        Atemp=A;
        Btemp=B;
        Ctemp=C;
        Dtemp=D;
    }
    /*
    *移动一定位数
    */
    private    int    shift(int a,int s){
        return(a<>>(32-s));//右移的时候,高位一定要补零,而不是补充符号位
    }
    /*
    *主循环
    */
    private void MainLoop(int M[]){
        int F,g;
        int a=Atemp;
        int b=Btemp;
        int c=Ctemp;
        int d=Dtemp;
        for(int i = 0; i < 64; i   ){
            if(i<16){
                F=(b&c)|((~b)&d);
                g=i;
            }else if(i<32){
                F=(d&b)|((~d)&c);
                g=(5*i 1);
            }else if(i<48){
                F=b^c^d;
                g=(3*i 5);
            }else{
                F=c^(b|(~d));
                g=(7*i);
            }
            int tmp=d;
            d=c;
            c=b;
            b=b shift(a F K[i] M[g],s[i]);
            a=tmp;
        }
        Atemp=a Atemp;
        Btemp=b Btemp;
        Ctemp=c Ctemp;
        Dtemp=d Dtemp;   
    }
    /*
    *填充函数
    *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
    *填充方式为先加一个0,其它位补零
    *最后加上64位的原来长度
    */
    private int[] add(String str){
        int num=((str.length() 8)/64) 1;//以512位,64个字节为一组
        int strByte[]=new int[num*16];//64/4=16,所以有16个整数
        for(int i=0;i>2]|=str.charAt(i)<<((i%4)*8);//一个整数存储四个字节,小端序
        }
        strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
        /*
        *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位
        */
        strByte[num*16-2]=str.length()*8;
            return strByte;
    }
    /*
    *调用函数
    */
    public String getMD5(String source){
        init();
        int strByte[]=add(source);
        for(int i=0;i>i*8)%(1<<8))&0xff)).replace(' ', '0');
 
        }
        return str;
    }
    /*
    *单例
    */
    private static MD5 instance;
    public static MD5 getInstance(){
        if(instance==null){
            instance=new MD5();
        }
        return instance;
    }
     
    private MD5(){};
     
    public static void main(String[] args){
        String str=MD5.getInstance().getMD5("440102199010230759");
        System.out.println(str); //打印输出43DC9F79656CF8170ECD0CDF0E8D95C4
    }
}

参考,

https://baike.baidu.com/item/MD5/212708?fr=aladdin

https://www.zhihu.com/question/22651987

0 人点赞