md5加密 订阅
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。 展开全文
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
信息
别    称
摘要算法
提出者
罗纳德·李维斯特
应用学科
信息技术,计算机科学
中文名
消息摘要算法
外文名
Message Digest Algorithm MD5
提出时间
1992年
MD5发展历史
1992年8月,罗纳德·李维斯特向互联网工程任务组(IETF)提交了一份重要文件,描述了这种算法的原理。由于这种算法的公开性和安全性,在90年代被广泛使用在各种程序语言中,用以确保资料传递无误等 [1]  。MD5由MD4、MD3、MD2改进而来,主要增强算法复杂度和不可逆性。MD5算法因其普遍、稳定、快速的特点,仍广泛应用于普通数据的加密保护领域 [2]  。Rivest在1989年开发出MD2算法 [3]  。在这个算法中,首先对信息进行数据补位,使信息的字节长度是16的倍数。然后,以一个16位的校验和追加到信息末尾,并且根据这个新产生的信息计算出散列值。后来,Rogier和Chauvaud发现如果忽略了校验和MD2将产生冲突。MD2算法加密后结果是唯一的(即不同信息加密后的结果不同) [4]  。为了加强算法的安全性,Rivest在1990年又开发出MD4算法 [3]  。MD4算法同样需要填补信息以确保信息的比特位长度减去448后能被512整除(信息比特位长度mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damgard/merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。Den boer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到MD4完整版本中的冲突(这个冲突实际上是一种漏洞,它将导致对不同的内容进行加密却可能得到相同的加密后结果) [5]  。1991年,Rivest开发出技术上更为趋近成熟的MD5算法。它在MD4的基础上增加了"安全带"(safety-belts)的概念。虽然MD5比MD4复杂度大一些,但却更为安全。这个算法很明显的由四个和MD4设计有少许不同的步骤组成。在MD5算法中,信息-摘要的大小和填充的必要条件与MD4完全相同。Den boer和Bosselaers曾发现MD5算法中的假冲突(pseudo-collisions),但除此之外就没有其他被发现的加密后结果了 [3]  。
收起全文
精华内容
参与话题
问答
  • Java实现MD5加密的方式

    万次阅读 多人点赞 2018-07-02 00:08:15
     MD5加密全程是Message-Digest Algoorithm 5(信息-摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串。  例如我们要加密一篇文章,那么我们会随机从每段话或者每行中获取...

      MD5加密是一种常见的加密方式,我们经常用在保存用户密码和关键信息上。那么它到底有什么,又什么好处呢,会被这么广泛的运用在应用开发中。
      1、什么是MD5
      MD5加密全程是Message-Digest Algoorithm 5(信息-摘要算法),它对信息进行摘要采集,再通过一定的位运算,最终获取加密后的MD5字符串。
      例如我们要加密一篇文章,那么我们会随机从每段话或者每行中获取一个字,把这些字统计出来后,再通过一定的运算获得一个固定长度的MD5加密后信息。因此,其很难被逆向破解。
      MD5加密流程图
      2、MD5有哪些特点
      MD5加密的特点主要有以下几点:
      1、针对不同长度待加密的数据、字符串等等,其都可以返回一个固定长度的MD5加密字符串。(通常32位的16进制字符串);
      2、其加密过程几乎不可逆,除非维护一个庞大的Key-Value数据库来进行碰撞破解,否则几乎无法解开。
      3、运算简便,且可实现方式多样,通过一定的处理方式也可以避免碰撞算法的破解。
      4、对于一个固定的字符串。数字等等,MD5加密后的字符串是固定的,也就是说不管MD5加密多少次,都是同样的结果。
      3、Java中MD5加密的实现方式
      具体废话不多说,直接上代码:
      

    public static String getMD5String(String str) {
            try {
                // 生成一个MD5加密计算摘要
                MessageDigest md = MessageDigest.getInstance("MD5");
                // 计算md5函数
                md.update(str.getBytes());
                // digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
                // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
                //一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)
                return new BigInteger(1, md.digest()).toString(16);
            } catch (Exception e) {
               e.printStackTrace();
               return null;
            }
        }
    

     上述的是利用Java自带的MessageDigest类实现的最简单的MD5加密方法。对加密过程没有做任何处理。下面这个相对来说更加复杂。

    package com.mmall.util;
    
    import java.security.MessageDigest;
    
    /**
     * Created by dt
     */
    public class MD5Util {
    
      //这里主要是遍历8个byte,转化为16位进制的字符,即0-F
        private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();
            for (int i = 0; i < b.length; i++)
                resultSb.append(byteToHexString(b[i]));
    
            return resultSb.toString();
        }
      //这里是针对单个byte,256的byte通过16拆分为d1和d2
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0)
                n += 256;
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
    
        /**
         * 返回大写MD5
         *
         * @param origin
         * @param charsetname
         * @return
         */
        private static String MD5Encode(String origin, String charsetname) {
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("MD5");
                if (charsetname == null || "".equals(charsetname))
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
                else
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
            } catch (Exception exception) {
            }
            return resultString.toUpperCase();
        }
    
        public static String MD5EncodeUtf8(String origin) {
            origin = origin + PropertiesUtil.getProperty("password.salt", "");
            return MD5Encode(origin, "utf-8");
        }
    
    
        private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
                "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
    
    }
    
    //在这段代码里面还引入了PropertiesUtil.getProperty("password.salt","")来获取一个额外的字符串,以保证碰撞算法的命中率更低,且在第三方MD5平台上也很难被搜索到。
    password.salt = 23543dfggeelysdafaqj23ou89ZXcj@#$@#$#@KJdjklj;D../dSF.,

    还有个更详细的版本,妇幼更详细的注释:

    public static String stringMD5(String input) {
    
      try {
    
         // 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”)
    
         MessageDigest messageDigest =MessageDigest.getInstance("MD5");
    
    
    
         // 输入的字符串转换成字节数组
    
         byte[] inputByteArray = input.getBytes();
    
    
    
         // inputByteArray是输入字符串转换得到的字节数组
    
         messageDigest.update(inputByteArray);
    
    
    
         // 转换并返回结果,也是字节数组,包含16个元素
    
         byte[] resultByteArray = messageDigest.digest();
    
    
    
         // 字符数组转换成字符串返回
    
         return byteArrayToHex(resultByteArray);
    
    
    
      } catch (NoSuchAlgorithmException e) {
    
         return null;
    
      }
    }
    //下面这个函数用于将字节数组换成成16进制的字符串
    
    public static String byteArrayToHex(byte[] byteArray) {
    
      // 首先初始化一个字符数组,用来存放每个16进制字符
    
      char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
    
    
    
      // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
    
      char[] resultCharArray =new char[byteArray.length * 2];
    
    
    
      // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
    
      int index = 0;
    
      for (byte b : byteArray) {
    
         resultCharArray[index++] = hexDigits[b>>> 4 & 0xf];
    
         resultCharArray[index++] = hexDigits[b& 0xf];
    
      }
    
    
    
      // 字符数组组合成字符串返回
    
      return new String(resultCharArray);
    }

    这两处代码出自https://www.cnblogs.com/hihtml5/p/6064999.html

    展开全文
  • MD5加密

    2019-04-27 20:15:47
    MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特...

        MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。

       

    发展历史

    编辑

    MD2

    Rivest在1989年开发出MD2算法。在这个算法中,首先对信息进行数据补位,使信息的字节长度是16的倍数。然后,以一个16位的检验和追加到信息末尾,并且根据这个新产生的信息计算出散列值。后来,Rogier和Chauvaud发现如果忽略了检验和MD2将产生冲突。MD2算法加密后结果是唯一的(即不同信息加密后的结果不同)。

    MD4

    为了加强算法的安全性,Rivest在1990年又开发出MD4算法。MD4算法同样需要填补信息以确保信息的比特位长度减去448后能被512整除(信息比特位长度mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damg?rd/merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。Den boer和Bosselaers以及其他人很快的发现了攻击MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到MD4完整版本中的冲突(这个冲突实际上是一种漏洞,它将导致对不同的内容进行加密却可能得到相同的加密后结果)。毫无疑问,MD4就此被淘汰掉了。

    尽管MD4算法在安全上有个这么大的漏洞,但它对在其后才被开发出来的好几种信息安全加密算法的出现却有着不可忽视的引导作用。

    MD5

    1991年,Rivest开发出技术上更为趋近成熟的md5算法。它在MD4的基础上增加了"安全-带子"(safety-belts)的概念。虽然MD5比MD4复杂度大一些,但却更为安全。这个算法很明显的由四个和MD4设计有少许不同的步骤组成。在MD5算法中,信息-摘要的大小和填充的必要条件与MD4完全相同。Den boer和Bosselaers曾发现MD5算法中的假冲突(pseudo-collisions),但除此之外就没有其他被发现的加密后结果了。

    MD5应用

    编辑

    一致性验证

    MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。比如,在Unix下有很多软件在下载的时候都有一个文件名相同,文件扩展名为.md5的文件,在这个文件中通常只有一行文本,大致结构如: [1] 

    MD5 (tanajiya.tar.gz) = 38b8c2c1093dd0fec383a9d9ac940515

    这就是tanajiya.tar.gz文件的数字签名。MD5将整个文件当作一个大文本信息,通过其不可逆的字符串变换算法,产生了这个唯一的MD5信息摘要。为了让读者朋友对MD5的应用有个直观的认识,笔者以一个比方和一个实例来简要描述一下其工作过程:

    大家都知道,地球上任何人都有自己独一无二的指纹,这常常成为司法机关鉴别罪犯身份最值得信赖的方法;与之类似,MD5就可以为任何文件(不管其大小、格式、数量)产生一个同样独一无二的“数字指纹”,如果任何人对文件做了任何改动,其MD5值也就是对应的“数字指纹”都会发生变化。

    我们常常在某些软件下载站点的某软件信息中看到其MD5值,它的作用就在于我们可以在下载该软件后,对下载回来的文件用专门的软件(如Windows MD5 Check等)做一次MD5校验,以确保我们获得的文件与该站点提供的文件为同一文件。

    具体来说文件的MD5值就像是这个文件的“数字指纹”。每个文件的MD5值是不同的,如果任何人对文件做了任何改动,其MD5值也就是对应的“数字指纹”就会发生变化。比如下载服务器针对一个文件预先提供一个MD5值,用户下载完该文件后,用我这个算法重新计算下载文件的MD5值,通过比较这两个值是否相同,就能判断下载的文件是否出错,或者说下载的文件是否被篡改了。MD5实际上一种有损压缩技术,压缩前文件一样MD5值一定一样,反之MD5值一样并不能保证压缩前的数据是一样的。在密码学上发生这样的概率是很小的,所以MD5在密码加密领域有一席之地。但是专业的黑客甚至普通黑客也可以利用MD5值实际是有损压缩技术这一原理,将MD5的逆运算的值作为一张表俗称彩虹表的散列表来破解密码。

    利用MD5算法来进行文件校验的方案被大量应用到软件下载站、论坛数据库、系统文件安全等方面。

    数字签名

    MD5的典型应用是对一段Message(字节串)产生fingerprint(指纹),以防止被“篡改”。举个例子,你将一段话写在一个叫 readme.txt文件中,并对这个readme.txt产生一个MD5的值并记录在案,然后你可以传播这个文件给别人,别人如果修改了文件中的任何内容,你对这个文件重新计算MD5时就会发现(两个MD5值不相同)。如果再有一个第三方的认证机构,用MD5还可以防止文件作者的“抵赖”,这就是所谓的数字签名应用。

    安全访问认证

    MD5还广泛用于操作系统的登陆认证上,如Unix、各类BSD系统登录密码、数字签名等诸多方面。如在Unix系统中用户的密码是以MD5(或其它类似的算法)经Hash运算后存储在文件系统中。当用户登录的时候,系统把用户输入的密码进行MD5 Hash运算,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这可以避免用户的密码被具有系统管理员权限的用户知道。MD5将任意长度的“字节串”映射为一个128bit的大整数,并且是通过该128bit反推原始字符串是困难的,换句话说就是,即使你看到源程序和算法描述,也无法将一个MD5的值变换回原始的字符串,从数学原理上说,是因为原始的字符串有无穷多个,这有点象不存在反函数的数学函数。所以,要遇到了md5密码的问题,比较好的办法是:你可以用这个系统中的md5()函数重新设一个密码,如admin,把生成的一串密码的Hash值覆盖原来的Hash值就行了。

    正是因为这个原因,现在被黑客使用最多的一种破译密码的方法就是一种被称为"跑字典"的方法。有两种方法得到字典,一种是日常搜集的用做密码的字符串表,另一种是用排列组合方法生成的,先用MD5程序计算出这些字典项的MD5值,然后再用目标的MD5值在这个字典中检索。我们假设密码的最大长度为8位字节(8 Bytes),同时密码只能是字母和数字,共26+26+10=62个字节,排列组合出的字典的项数则是P(62,1)+P(62,2)….+P(62,8),那也已经是一个很天文的数字了,存储这个字典就需要TB级的磁盘阵列,而且这种方法还有一个前提,就是能获得目标账户的密码MD5值的情况下才可以。这种加密技术被广泛的应用于Unix系统中,这也是为什么Unix系统比一般操作系统更为坚固一个重要原因。

    算法原理

    编辑

    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。 [2] 

    总体流程如下图所示, 表示第i个分组,每次的运算都由前一轮的128位结果值和第i块512bit值进行运算。

    图1.MD5算法的整体流程图图1.MD5算法的整体流程图

    代码

    编辑

    C++实现

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    #include<iostream>

    #include<string>

    using namespace std;

    #define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的时候,高位一定要补零,而不是补充符号位

    #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))    

    #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))

    #define H(x, y, z) ((x) ^ (y) ^ (z))

    #define I(x, y, z) ((y) ^ ((x) | (~z)))

    #define A 0x67452301

    #define B 0xefcdab89

    #define C 0x98badcfe

    #define D 0x10325476

    //strBaye的长度

    unsigned int strlength;

    //A,B,C,D的临时变量

    unsigned int atemp;

    unsigned int btemp;

    unsigned int ctemp;

    unsigned int dtemp;

    //常量ti unsigned int(abs(sin(i+1))*(2pow32))

    const unsigned 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};

    //向左位移数

    const unsigned 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};

    const char str16[]="0123456789abcdef";

    void mainLoop(unsigned int M[])

    {

        unsigned int f,g;

        unsigned int a=atemp;

        unsigned int b=btemp;

        unsigned int c=ctemp;

        unsigned int d=dtemp;

        for (unsigned int i = 0; i < 64; i++)

        {

            if(i<16){

                f=F(b,c,d);

                g=i;

            }else if (i<32)

            {

                f=G(b,c,d);

                g=(5*i+1)%16;

            }else if(i<48){

                f=H(b,c,d);

                g=(3*i+5)%16;

            }else{

                f=I(b,c,d);

                g=(7*i)%16;

            }

            unsigned 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)

    *填充方式为先加一个1,其它位补零

    *最后加上64位的原来长度

    */

    unsigned int* add(string str)

    {

        unsigned int num=((str.length()+8)/64)+1;//以512位,64个字节为一组

        unsigned int *strByte=new unsigned int[num*16];    //64/4=16,所以有16个整数

        strlength=num*16;

        for (unsigned int i = 0; i < num*16; i++)

            strByte[i]=0;

        for (unsigned int i=0; i <str.length(); i++)

        {

            strByte[i>>2]|=(str[i])<<((i%4)*8);//一个整数存储四个字节,i>>2表示i/4 一个unsigned int对应4个字节,保存4个字符信息

        }

        strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1 一个unsigned int保存4个字符信息,所以用128左移

        /*

        *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位

        */

        strByte[num*16-2]=str.length()*8;

        return strByte;

    }

    string changeHex(int a)

    {

        int b;

        string str1;

        string str="";

        for(int i=0;i<4;i++)

        {

            str1="";

            b=((a>>i*8)%(1<<8))&0xff;   //逆序处理每个字节

            for (int j = 0; j < 2; j++)

            {

                str1.insert(0,1,str16[b%16]);

                b=b/16;

            }

            str+=str1;

        }

        return str;

    }

    string getMD5(string source)

    {

        atemp=A;    //初始化

        btemp=B;

        ctemp=C;

        dtemp=D;

        unsigned int *strByte=add(source);

        for(unsigned int i=0;i<strlength/16;i++)

        {

            unsigned int num[16];

            for(unsigned int j=0;j<16;j++)

                num[j]=strByte[i*16+j];

            mainLoop(num);

        }

        return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));

    }

    unsigned int main()

    {

        string ss;

    //    cin>>ss;

        string s=getMD5("abc");

        cout<<s;

        return 0;

    }

    JAVA实现

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    141

    142

    143

    144

    145

    146

    147

    148

    149

    150

    151

    152

    153

    154

    155

    156

    157

    158

    159

    160

    161

    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<<s)|(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)%16;

                }else if(i<48){

                    F=b^c^d;

                    g=(3*i+5)%16;

                }else{

                    F=c^(b|(~d));

                    g=(7*i)%16;

                }

                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<num*16;i++){//全部初始化0

                strByte[i]=0;

            }

            int    i;

            for(i=0;i<str.length();i++){

                strByte[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<strByte.length/16;i++){

            int num[]=new int[16];

            for(int j=0;j<16;j++){

                num[j]=strByte[i*16+j];

            }

            MainLoop(num);

            }

            return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);

         

        }

        /*

        *整数变成16进制字符串

        */

        private String changeHex(int a){

            String str="";

            for(int i=0;i<4;i++){

                str+=String.format("%2s", Integer.toHexString(((a>>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("");

            System.out.println(str);

        }

    }

     

     

    结果错误

    1

     

    VB2010实现

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    Imports System

     

    Imports System.Security.Cryptography

     

    Imports System.Text

     

     

    Module Example

        '哈希输入字符串并返回一个 32 字符的十六进制字符串哈希。

     

        Function GetMd5Hash(ByVal input As StringAs String

     

            '创建新的一个 MD5CryptoServiceProvider 对象的实例。

     

            Dim md5Hasher As New MD5CryptoServiceProvider()

     

            '输入的字符串转换为字节数组,并计算哈希。

     

            Dim data As Byte() = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input))

     

            '创建一个新的 StringBuilder 收集的字节,并创建一个字符串。

     

            Dim sBuilder As New StringBuilder()

     

            '通过每个字节的哈希数据和格式为十六进制字符串的每一个循环。

     

            For As Integer = 0 To data.Length - 1

     

                sBuilder.Append(data(i).ToString("x2"))

     

            Next

     

            '返回十六进制字符串。

     

            Return sBuilder.ToString()

     

        End Function

     

     

        '验证对一个字符串的哈希值。

     

        Function VerifyMd5Hash(ByVal input As StringByVal hash As StringAs Boolean

     

            '哈希的输入。

     

            Dim hashOfInput As String = GetMd5Hash(input)

     

            '创建 StringComparer 的哈希进行比较。

     

            Dim comparer As StringComparer = StringComparer.OrdinalIgnoreCase

     

            Return comparer.Compare(hashOfInput, hash) = 0

     

        End Function

     

     

        Sub Main()

     

            Dim source As String "Hello World!"

     

            Dim hash As String = GetMd5Hash(source)

     

            Console.WriteLine($"进行MD5加密的字符串为:{source},加密的结果是:{hash}。")

     

            Console.WriteLine("正在验证哈希……")

     

            If VerifyMd5Hash(source, hash) Then

     

                Console.WriteLine("哈希值是

    相同的。")

     

    Else

     

                Console.WriteLine("哈希值是不相同的。")

     

    EndIf

     

        EndSub

     

    EndModule

     

     

    '此代码示例产生下面的输出:

     

     

    '进行MD5加密的字符串为:Hello World!,加密的结果是:ed076287532e86365e841e92bfc50d8c。

     

    '正在验证哈希……

     

    '哈希值是相同的。

    JavaScript实现

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    141

    142

    143

    144

    145

    146

    147

    148

    149

    150

    151

    152

    153

    154

    155

    156

    157

    158

    159

    160

    161

    162

    163

    164

    165

    166

    167

    168

    169

    170

    171

    172

    173

    174

    175

    176

    177

    178

    179

    180

    181

    182

    183

    184

    185

    186

    187

    188

    189

    190

    191

    192

    193

    194

    195

    196

    197

    198

    199

    200

    201

    202

    203

    function md5(string) {

        function md5_RotateLeft(lValue, iShiftBits) {

            return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));

        }

        function md5_AddUnsigned(lX, lY) {

            var lX4, lY4, lX8, lY8, lResult;

            lX8 = (lX & 0x80000000);

            lY8 = (lY & 0x80000000);

            lX4 = (lX & 0x40000000);

            lY4 = (lY & 0x40000000);

            lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);

            if (lX4 & lY4) {

                return (lResult ^ 0x80000000 ^ lX8 ^ lY8);

            }

            if (lX4 | lY4) {

                if (lResult & 0x40000000) {

                    return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);

                else {

                    return (lResult ^ 0x40000000 ^ lX8 ^ lY8);

                }

            else {

                return (lResult ^ lX8 ^ lY8);

            }

        }

        function md5_F(x, y, z) {

            return (x & y) | ((~x) & z);

        }

        function md5_G(x, y, z) {

            return (x & z) | (y & (~z));

        }

        function md5_H(x, y, z) {

            return (x ^ y ^ z);

        }

        function md5_I(x, y, z) {

            return (y ^ (x | (~z)));

        }

        function md5_FF(a, b, c, d, x, s, ac) {

            a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));

            return md5_AddUnsigned(md5_RotateLeft(a, s), b);

        };

        function md5_GG(a, b, c, d, x, s, ac) {

            a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));

            return md5_AddUnsigned(md5_RotateLeft(a, s), b);

        };

        function md5_HH(a, b, c, d, x, s, ac) {

            a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));

            return md5_AddUnsigned(md5_RotateLeft(a, s), b);

        };

        function md5_II(a, b, c, d, x, s, ac) {

            a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));

            return md5_AddUnsigned(md5_RotateLeft(a, s), b);

        };

        function md5_ConvertToWordArray(string) {

            var lWordCount;

            var lMessageLength = string.length;

            var lNumberOfWords_temp1 = lMessageLength + 8;

            var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;

            var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;

            var lWordArray = Array(lNumberOfWords - 1);

            var lBytePosition = 0;

            var lByteCount = 0;

            while (lByteCount < lMessageLength) {

                lWordCount = (lByteCount - (lByteCount % 4)) / 4;

                lBytePosition = (lByteCount % 4) * 8;

                lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));

                lByteCount++;

            }

            lWordCount = (lByteCount - (lByteCount % 4)) / 4;

            lBytePosition = (lByteCount % 4) * 8;

            lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);

            lWordArray[lNumberOfWords - 2] = lMessageLength << 3;

            lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;

            return lWordArray;

        };

        function md5_WordToHex(lValue) {

            var WordToHexValue = "",

            WordToHexValue_temp = "",

            lByte, lCount;

            for (lCount = 0; lCount <= 3; lCount++) {

                lByte = (lValue >>> (lCount * 8)) & 255;

                WordToHexValue_temp = "0" + lByte.toString(16);

                WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);

            }

            return WordToHexValue;

        };

        function md5_Utf8Encode(string) {

            string = string.replace(/\r\n/g, "\n");

            var utftext = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {

                    utftext += String.fromCharCode(c);

                else if ((c > 127) && (c < 2048)) {

                    utftext += String.fromCharCode((c >> 6) | 192);

                    utftext += String.fromCharCode((c & 63) | 128);

                else {

                    utftext += String.fromCharCode((c >> 12) | 224);

                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);

                    utftext += String.fromCharCode((c & 63) | 128);

                }

            }

            return utftext;

        };

        var x = Array();

        var k, AA, BB, CC, DD, a, b, c, d;

        var S11 = 7,

        S12 = 12,

        S13 = 17,

        S14 = 22;

        var S21 = 5,

        S22 = 9,

        S23 = 14,

        S24 = 20;

        var S31 = 4,

        S32 = 11,

        S33 = 16,

        S34 = 23;

        var S41 = 6,

        S42 = 10,

        S43 = 15,

        S44 = 21;

        string = md5_Utf8Encode(string);

        x = md5_ConvertToWordArray(string);

        a = 0x67452301;

        b = 0xEFCDAB89;

        c = 0x98BADCFE;

        d = 0x10325476;

        for (k = 0; k < x.length; k += 16) {

            AA = a;

            BB = b;

            CC = c;

            DD = d;

            a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);

            d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);

            c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB);

            b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);

            a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);

            d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);

            c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613);

            b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501);

            a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8);

            d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);

            c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);

            b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);

            a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122);

            d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193);

            c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E);

            b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821);

            a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);

            d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340);

            c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);

            b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);

            a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);

            d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453);

            c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);

            b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);

            a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);

            d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);

            c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);

            b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);

            a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);

            d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);

            c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);

            b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);

            a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);

            d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681);

            c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);

            b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);

            a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);

            d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);

            c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);

            b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);

            a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);

            d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);

            c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);

            b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05);

            a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);

            d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);

            c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);

            b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);

            a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244);

            d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97);

            c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);

            b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039);

            a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3);

            d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);

            c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);

            b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1);

            a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);

            d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);

            c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314);

            b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);

            a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82);

            d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);

            c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);

            b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391);

            a = md5_AddUnsigned(a, AA);

            b = md5_AddUnsigned(b, BB);

            c = md5_AddUnsigned(c, CC);

            d = md5_AddUnsigned(d, DD);

        }

        return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase();

    }

     

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆;所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后,把得到的散列值和原始的数据形成一个一对一的映射表,通过比在表中比破解密码的MD5算法散列值,通过匹配从映射表中找出破解密码所对应的原始明文。

    对信息系统或者网站系统来说,MD5算法主要用在用户注册口令的加密,对于普通强度的口令加密,可以通过以下三种方式进行破解:

    (1)在线查询密码。一些在线的MD5值查询网站提供MD5密码值的查询,输入MD5密码值后,如果在数据库中存在,那么可以很快获取其密码值。

    (2)使用MD5破解工具。网络上有许多针对MD5破解的专用软件,通过设置字典来进行破解。

    (3)通过社会工程学来获取或者重新设置用户的口令。

    展开全文
  • MD5加密概述,原理及实现

    千次阅读 2019-05-21 20:53:01
    MD5概述: MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要(32位的数字字母混合码)。 MD5主要特点: 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样 ...

    MD5概述:

    MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要(32位的数字字母混合码)。

    MD5主要特点:

    不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样

    (一个MD5理论上的确是可能对应无数多个原文的,因为MD5是有限多个的而原文可以是无数多个。比如主流使用的MD5将任意长度的“字节串映射为一个128bit的大整数。也就是一共有2^128种可能,大概是3.4*10^38,这个数字是有限多个的,而但是世界上可以被用来加密的原文则会有无数的可能性)

    MD5的性质:

    1、压缩性:任意长度的数据,算出的MD5值长度都是固定的(相当于超损压缩)。

    2、容易计算:从原数据计算出MD5值很容易。

    3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。

    4、弱抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

    5、强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。

    虽说MD5有不可逆的特点

    但是由于某些MD5破解网站,专门用来查询MD5码,其通过

    把常用的密码先MD5处理,并将数据存储起来,然后跟需要查询的MD5结果匹配,这时就有可能通过匹配的MD5得到明文,所以有些简单的MD5码是反查到加密前原文的。

    为了让MD5码更加安全,涌现了很多其他方法,如加盐。 盐要足够长足够乱 得到的MD5码就很难查到。

    MD5用途:

    1.防止被篡改:

    1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果ab一样就代表中途未被篡改。

    2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。

    3SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

    2.防止直接看到明文:

    现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

    3.防止抵赖(数字签名):

    这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的数字签名

    MD5加密算法原理及实现:

    MD5算法原理:

    1、数据填充

    对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。

    填充方法:在消息后面进行填充,填充第一位为1,其余为0。

    (此时消息长度为N*512+448)

    2、添加消息长度

    在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。

    在此步骤进行完毕后,最终消息长度就是512的整数倍。

    (此时消息长度为(N+1)*512 )

    3、数据处理

    首先需要用到4个常数:

    四个32位变量初始化(经过研究所得,为固定值)

    A = 0×01234567 

    B = 0×89ABCDEF 

    C = 0xFEDCBA98 

    D = 0×76543210  

    它们称为链接变量(chaining variable)

     

    以上为标准的幻数的(物理顺序)

    如果在程序中(小端模式)定义应该是: 

    A = 0x67452301

    B = 0xEFCDAB89

    C = 0x98BADCFE

    D = 0x10325476

     

    然后需要用到4个非线性函数

    F(X,Y,Z)=(X & Y) | ((~X) & Z);

    G(X,Y,Z)=(X & Z) | (Y & (~Z));

     H(X,Y,Z)=X ^ Y ^ Z;

    I(X,Y,Z)=Y ^ (X | (~Z));

    (&是与, |是或, ~是非, ^是异或)

     

    然后是4种操作

    FF(a,b,c,d,Mi,s,tj)  表示a=b+((a+(F(b,c,d)+Mi+tj)<<< s)

    GG(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(G(b,c,d)+Mi+tj)<<< s) 

    HH(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(H(b,c,d)+Mi+tj)<<< s) 

    II(a,b,c,d,Mi,s,tj)   表示a=b+((a+(I(b,c,d)+Mi+tj)<<< s) 

    其中Mi表示消息的第i个子分组(从0到15,共16个),<<< s表示循环左移s位

    常数tj为: 

    在第j步中,tj是4294967296*abs(sin(j))的整数部分,i的单位是弧度。

    (4294967296是2的32次方)

    亦可用 0x100000000UL * abs(sin((double)j)) 计算

     

    x循环左移s位:

    ( s << x ) | ( s >> (32 - x) )

     

    4.MD5运算:

    由类似的64次循环构成,分成4轮,每轮16次。每轮使用FF,GG,HH,II中的一种操作;

    一轮中,a,b,c,d的使用顺序轮转;

    例如第一轮:

    第一次计算 FF(a,b,c,d,M0,s,t1)

                       a = a+(F(b,c,d)+M0+t1);

                       a = ( s <<a ) | ( s >> (32 - a) );

                       a = a + b;

    第二次计算 FF(b,c,d,a,M1,s,t2)

                       b = b+(F(c,d,a)+M1+t2);

                       b = ( s <<b ) | ( s >> (32 - b) );

                       b = b + c;

     

    这4轮共64步是: 

    (初次使用的a,b,c,d为A,B,C,D的值,而Mi,s,tj根据下面的数值进行使用,可认为是常量,)

    第一轮 

    FF(a,b,c,d,M0,7,0xd76aa478) 

    FF(d,a,b,c,M1,12,0xe8c7b756) 

    FF(c,d,a,b,M2,17,0×242070db) 

    FF(b,c,d,a,M3,22,0xc1bdceee) 

    FF(a,b,c,d,M4,7,0xf57c0faf) 

    FF(d,a,b,c,M5,12,0×4787c62a) 

    FF(c,d,a,b,M6,17,0xa8304613) 

    FF(b,c,d,a,M7,22,0xfd469501) 

    FF(a,b,c,d,M8,7,0×698098d8) 

    FF(d,a,b,c,M9,12,0×8b44f7af) 

    FF(c,d,a,b,M10,17,0xffff5bb1) 

    FF(b,c,d,a,M11,22,0×895cd7be) 

    FF(a,b,c,d,M12,7,0×6b901122) 

    FF(d,a,b,c,M13,12,0xfd987193) 

    FF(c,d,a,b,M14,17,0xa679438e) 

    FF(b,c,d,a,M15,22,0×49b40821) 

     

    第二轮 

    GG(a,b,c,d,M1,5,0xf61e2562) 

    GG(d,a,b,c,M6,9,0xc040b340) 

    GG(c,d,a,b,M11,14,0×265e5a51) 

    GG(b,c,d,a,M0,20,0xe9b6c7aa) 

    GG(a,b,c,d,M5,5,0xd62f105d) 

    GG(d,a,b,c,M10,9,0×02441453) 

    GG(c,d,a,b,M15,14,0xd8a1e681) 

    GG(b,c,d,a,M4,20,0xe7d3fbc8) 

    GG(a,b,c,d,M9,5,0×21e1cde6) 

    GG(d,a,b,c,M14,9,0xc33707d6) 

    GG(c,d,a,b,M3,14,0xf4d50d87) 

    GG(b,c,d,a,M8,20,0×455a14ed) 

    GG(a,b,c,d,M13,5,0xa9e3e905) 

    GG(d,a,b,c,M2,9,0xfcefa3f8) 

    GG(c,d,a,b,M7,14,0×676f02d9) 

    GG(b,c,d,a,M12,20,0×8d2a4c8a) 

     

    第三轮 

    HH(a,b,c,d,M5,4,0xfffa3942) 

    HH(d,a,b,c,M8,11,0×8771f681) 

    HH(c,d,a,b,M11,16,0×6d9d6122) 

    HH(b,c,d,a,M14,23,0xfde5380c) 

    HH(a,b,c,d,M1,4,0xa4beea44) 

    HH(d,a,b,c,M4,11,0×4bdecfa9) 

    HH(c,d,a,b,M7,16,0xf6bb4b60) 

    HH(b,c,d,a,M10,23,0xbebfbc70) 

    HH(a,b,c,d,M13,4,0×289b7ec6) 

    HH(d,a,b,c,M0,11,0xeaa127fa) 

    HH(c,d,a,b,M3,16,0xd4ef3085) 

    HH(b,c,d,a,M6,23,0×04881d05) 

    HH(a,b,c,d,M9,4,0xd9d4d039) 

    HH(d,a,b,c,M12,11,0xe6db99e5) 

    HH(c,d,a,b,M15,16,0×1fa27cf8) 

    HH(b,c,d,a,M2,23,0xc4ac5665) 

     

    第四轮 

    II(a,b,c,d,M0,6,0xf4292244) 

    II(d,a,b,c,M7,10,0×432aff97) 

    II(c,d,a,b,M14,15,0xab9423a7) 

    II(b,c,d,a,M5,21,0xfc93a039) 

    II(a,b,c,d,M12,6,0×655b59c3) 

    II(d,a,b,c,M3,10,0×8f0ccc92) 

    II(c,d,a,b,M10,15,0xffeff47d) 

    II(b,c,d,a,M1,21,0×85845dd1) 

    II(a,b,c,d,M8,6,0×6fa87e4f) 

    II(d,a,b,c,M15,10,0xfe2ce6e0) 

    II(c,d,a,b,M6,15,0xa3014314) 

    II(b,c,d,a,M13,21,0×4e0811a1) 

    II(a,b,c,d,M4,6,0xf7537e82) 

    II(d,a,b,c,M11,10,0xbd3af235) 

    II(c,d,a,b,M2,15,0×2ad7d2bb) 

    II(b,c,d,a,M9,21,0xeb86d391) 

     

    消息分以512位为一分组进行处理,每一个分组进行上述4轮共64次计算后,将A,B,C,D分别加上计算得到的a,b,c,d。当做新的A,B,C,D,并将这4个变量赋值给a,b,c,d再进行下一分组的运算。由于填充后的消息长度为(N+1)*512,则共需计算N+1个分组。计算所有数据分组后,这4个变量为最后的结果,即MD5值。

     

    MD5算法实现:

     

    实现一:

    MD5.h

    #ifndef _MD5H_
    #define _MD5H_
    #include <math.h>
    #include <Windows.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    
    void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
    void ltob(unsigned int &i); //B\L互转,接受UINT类型
    unsigned int* MD5(const char* mStr); //MD5加密函数,并执行数据填充
    unsigned int* MD5_2(const char* mStr); //MD5加密函数,并执行数据填充,更优
    
    #endif

     MD5.cpp

    #include "MD5.h"
    
    //4组计算函数
    inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
    {
        return (X & Y) | ((~X) & Z);
    }
    inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
    {
        return (X & Z) | (Y & (~Z));
    }
    inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
    {
        return X ^ Y ^ Z;
    }
    inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
    {
        return Y ^ (X | (~Z));
    }
    
    //32位数循环左移(或称右移)实现函数
    void ROL(unsigned int &s, unsigned short cx)
    {
        if (cx > 32)cx %= 32;
        s = (s << cx) | (s >> (32 - cx));
        return;
    }
    
    //B\L互转,接收UINT类型
    void ltob(unsigned int &i)
    {
        unsigned int tmp = i;//保存副本
        byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
        pdes += 3;//调整指针,准备左右调转
        for (short j = 3; j >= 0; --j)
        {
            CopyMemory(pdes - j, psour + j, 1);
        }
        return;
    }
    
    //MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
    void AccLoop(unsigned short label, unsigned int *lGroup, void *M)
    {
        unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
        typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
    
        //循环左移-位数表
        const unsigned int rolarray[4][4] = {
            { 7, 12, 17, 22 },
            { 5, 9, 14, 20 },
            { 4, 11, 16, 23 },
            { 6, 10, 15, 21 }
        };
        //数据坐标表
        const unsigned short mN[4][16] = {
            { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
            { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
            { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
            { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
        };
        const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
        TAcc = ((label - 1) * 16) + 1;
        clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组
    
        //一轮循环开始(16组->16次)
        for (short i = 0; i < 16; ++i)
        {
            /*进行指针自变换*/
            i1 = lGroup + ((0 + i) % 4);
            i2 = lGroup + ((1 + i) % 4);
            i3 = lGroup + ((2 + i) % 4);
            i4 = lGroup + ((3 + i) % 4);
    
            if(0 == i%2)
            {
                //计算开始: A+F(B,C,D)+M[i]+T[i+1]
                tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
                //循环左移
                ROL(tmpi, rolarray[label - 1][i % 4]);
                //相加并赋值到下一个种子
                *i1 = *i2 + tmpi;
            }
            else
            {
                tmpi = (*i3 + clacArr[label - 1](*i4, *i1, *i2) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
                //循环左移
                ROL(tmpi, rolarray[label - 1][i % 4]);
                //相加并赋值到下一个种子
                *i3 = *i4 + tmpi;
            }
        }
        return;
    }
    
    //加密函数
    unsigned int* MD5(const char* mStr)
    {
        //计算缓冲区长度,并进行数据填充
        unsigned int mLen = strlen(mStr);
        if (mLen <= 0) 
        {
            return 0;
        }
        unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
        unsigned int FSbyte = FillSize / 8;               //以字节表示的填充数
        //预留512-448=64bit,填充原消息的长度
        unsigned int BuffLen = mLen + 8 + FSbyte;         //缓冲区长度
        unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
        CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区
    
        //数据填充
        md5Buff[mLen] = 0x80; //第一个bit填充1
        ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0
        unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充后64bit
        CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8);
    
        //数据运算
        unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
        unsigned int A = 0x67452301, B = 0xEFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
        unsigned int *lGroup = new unsigned int[4];
        lGroup[0] = A;
        lGroup[1] = B;
        lGroup[2] = C;
        lGroup[3] = D;
    
        for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
        {
            //进入4次计算的小循环,共4*16次
            for (unsigned short Lcount = 0; Lcount < 4;)
            {
                AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);
            }
            //数据相加作为下一轮的种子或者最终输出
            A = (lGroup[0] += A);
            B = (lGroup[1] += B);
            C = (lGroup[2] += C);
            D = (lGroup[3] += D);
    
        }
        //转换内存中的布局后才能正常显示
        ltob(lGroup[0]);
        ltob(lGroup[1]);
        ltob(lGroup[2]);
        ltob(lGroup[3]);
        delete[] md5Buff;
        return lGroup;
    }
    
    /*
    MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
    种子数组排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B;
    */
    void AccLoop_2(unsigned short label, unsigned int *lGroup, void *M)
    {
        unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
        typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
        const unsigned int rolarray[4][4] = {
            { 7, 12, 17, 22 },
            { 5, 9, 14, 20 },
            { 4, 11, 16, 23 },
            { 6, 10, 15, 21 }
        };//循环左移-位数表
        const unsigned short mN[4][16] = {
            { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
            { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
            { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
            { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
        };//数据坐标表
        const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
        TAcc = ((label - 1) * 16) + 1; //根据第几轮循环初始化T表累加器
        clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组
    
        /*一轮循环开始(16组->16次)*/
        for (short i = 0; i < 16; ++i)
        {
            /*进行指针自变换*/
            i1 = lGroup + ((0 + i) % 4);
            i2 = lGroup + ((3 + i) % 4);
            i3 = lGroup + ((2 + i) % 4);
            i4 = lGroup + ((1 + i) % 4);
    
            /*第一步计算开始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接计算T表*/
            tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
            ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循环左移
            *i1 = *i2 + tmpi;//第三步:相加并赋值到种子
        }
        return;
    }
    
    /*接口函数,并执行数据填充*/
    unsigned int* MD5_2(const char* mStr)
    {
        unsigned int mLen = strlen(mStr); //计算字符串长度
        if (mLen < 0) return 0;
        unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
        unsigned int FSbyte = FillSize / 8; //以字节表示的填充数
        unsigned int BuffLen = mLen + 8 + FSbyte; //缓冲区长度或者说填充后的长度
        unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
        CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区
    
        /*数据填充开始*/
        md5Buff[mLen] = 0x80; //第一个bit填充1
        ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory
        unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充
        CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充长度
        /*数据填充结束*/
    
        /*运算开始*/
        unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
        unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
        //unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //种子副本数组,并作为返回值返回
        unsigned int *lGroup = new unsigned int[4];
        lGroup[0] = A;
        lGroup[1] = D;
        lGroup[2] = C;
        lGroup[3] = B;
    
        for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
        {
            /*进入4次计算的小循环*/
            for (unsigned short Lcount = 0; Lcount < 4;)
            {
                AccLoop_2(++Lcount, lGroup, &md5Buff[Bcount * 64]);
            }
            /*数据相加作为下一轮的种子或者最终输出*/
            A = (lGroup[0] += A);
            B = (lGroup[3] += B);
            C = (lGroup[2] += C);
            D = (lGroup[1] += D);
        }
        /*转换内存中的布局后才能正常显示*/
        ltob(lGroup[0]);
        ltob(lGroup[1]);
        ltob(lGroup[2]);
        ltob(lGroup[3]);
        delete[] md5Buff; //清除内存并返回
        return lGroup;
    }

     

    main.cpp

    #include "MD5.h"
    
    int main()
    {
        char tmpstr[256], buf[4][10];
        std::cout << "请输入要加密的字符串:";
        std::cin >> tmpstr;
    
        //char buf[4][10];
        //char tmpstr[] ="admin";
        //MD5值:21232F297A57A5A743894A0E4A801FC3
    
        unsigned int* tmpGroup = MD5(tmpstr);
        sprintf_s(buf[0], "%8X", tmpGroup[0]);  //A
        sprintf_s(buf[1], "%8X", tmpGroup[1]);  //B
        sprintf_s(buf[2], "%8X", tmpGroup[2]);  //C
        sprintf_s(buf[3], "%8X", tmpGroup[3]);  //D
        std::cout <<"1-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;
    
        tmpGroup = MD5_2(tmpstr);
        sprintf_s(buf[0], "%8X", tmpGroup[0]);  //A
        sprintf_s(buf[1], "%8X", tmpGroup[3]);  //B
        sprintf_s(buf[2], "%8X", tmpGroup[2]);  //C
        sprintf_s(buf[3], "%8X", tmpGroup[1]);  //D
        std::cout <<"2-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;
    
        delete[] tmpGroup;
    
        return 0;
    }

    实现二:

    #include <string.h>
    #include <math.h>
    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    
    
    /***********************************
    * 非线性函数
    * (&是与,|是或,~是非,^是异或) 
    * 
    * 这些函数是这样设计的:
    *   如果X、Y和Z的对应位是独立和均匀的,
    *   那么结果的每一位也应是独立和均匀的。 
    * 
    * 函数F是按逐位方式操作:如果X,那么Y,否则Z。
    * 函数H是逐位奇偶操作符
    **********************************/
    #define F(x,y,z) ((x & y) | (~x & z))  
    #define G(x,y,z) ((x & z) | (y & ~z))  
    #define H(x,y,z) (x^y^z)  
    #define I(x,y,z) (y ^ (x | ~z))  
    
    
    /**************************************
    *向左换移(右环移)n个单位
    * ************************************/
    #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))  
    
    
    /****************************************************
    * 每次操作对a,b,c和d中的其中三个作一次非线性函数运算
    *  F(b,c,d)   G(b,c,d)   H(b,c,d)   I(b,c,d)
    *
    * 然后将所得结果加上 第四个变量(a),
    * F(b,c,d)+a
    *
    * 文本的一个子分组(x)
    * F(b,c,d)+a+x
    * 
    * 和一个常数(ac)。
    * F(b,c,d)+a+x+ac
    *
    * 再将所得结果向右环移一个不定的数(s),
    * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )
    * 
    * 并加上a,b,c或d中之一(b)。
    * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
    * 
    * 最后用该结果取代a,b,c或d中之一(a)。
    * a=ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b
    * 
    * ***************************************************/
    #define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
    #define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
    #define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
    #define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac;  a = ROTATE_LEFT(a,s); a += b; }
    
    
    //储存一个MD5 text信息 
    typedef struct  
    {  
        unsigned int count[2];    
        //记录当前状态,其数据位数   
    
        unsigned int state[4];    
        //4个数,一共32位 记录用于保存对512bits信息加密的中间结果或者最终结果  
    
        unsigned char buffer[64];
        //一共64字节,512位      
    }MD5_CTX;  
    
    
    //第一位1 其后若干个0,用于MD5Final函数时的补足
    unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  
    
    
    //函数声明
    void MD5Init(MD5_CTX *context);  
    void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);  
    void MD5Final(MD5_CTX *context,unsigned char digest[16]);  
    void MD5Transform(unsigned int state[4],unsigned char block[64]);  
    void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);  
    void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);  
    
    
    /************************
    * 函数功能:初始化一个MD5 text
    * 函数参数:MD5 text 指针
    * ***********************/
    //初始化
    void MD5Init(MD5_CTX *context)  
    {  
        context->count[0] = 0;  
        context->count[1] = 0;   
        //分别赋固定值  
        context->state[0] = 0x67452301;
        context->state[1] = 0xEFCDAB89;  
        context->state[2] = 0x98BADCFE;  
        context->state[3] = 0x10325476;  
    }  
    
    
    /************************************************
    * 函数功能:对一个MD5 text,把输入的数据进行分组,并进行加密
    * 未用到的数据把其储存在MD5 text中。
    *
    * 参数分析:
    * MD5_CTX *context       :一个MD5 text   
    * unsigned char *input   :新添加的数据  
    * unsigned int inputlen  :新添加数据的长度(字节)
    *
    ***********************************************/
    
    void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen)  
    {  
        unsigned int i = 0,index = 0,partlen = 0;  
    
        //index:当前状态的位数对64取余,其单位是字节
        //也可以写作:  index=(context->count[0]/8)%64
        index = (context->count[0] >> 3) & 0x3F;  
    
        //partlen:可以补齐64字节的字节数
        partlen = 64 - index;
    
        //下面代码是解决一个unsignde int 无法储存极大数据导致溢出的问题
        //当前位数加上新添加的位数,由于inputlen是以字节为单位,所以其转换为位数
        //相当于context->count[0] += inputlen*8;  
        context->count[0] += inputlen << 3;  
    
        //当其出现溢出的情况时,通过以下操作把两个16位的数连在一块,生成一个
        //32位的二进制数串,从而扩大其储存范围
        if(context->count[0] < (inputlen << 3))
        {
            context->count[1]++;
        }
    
        //该语句可替换为 context->count[1]+=(inputlen<<3)>>32;
        //便于理解
        context->count[1] += inputlen >> 29;  
    
        //当其输入字节数的大于其可以补足64字节的字节数,进行补足
        if(inputlen >= partlen)  
        {  
            //向buffer中补足partlen个字节,使其到达64字节
            memcpy(&context->buffer[index], input, partlen);
    
            //buffer达到64字节512位,则把其作为一组进行运算
            MD5Transform(context->state, context->buffer);  
    
            //如果输入的数据还可以组成多个64字节,则把其可以组成
            //的作为若干组进行运算
            for(i = partlen;i+64 <= inputlen;i+=64)  
                MD5Transform(context->state, &input[i]);  
    
            //恢复0值,照应 下面 把输入 剩余字节(不能组成64字节组) 储存的操作
            index = 0;          
        }   
        //否则,把输入的数据按顺序放在原来数据后面
        else  
        {  
            i = 0;  
        }  
    
        //放置剩余数据
        memcpy(&context->buffer[index],&input[i],inputlen-i);  
    }  
    
    
    /*************************************************
    * 函数功能:对数据进行补足,并加入数据位数信息,并进一步加密
    * 
    * 参数分析:
    * MD5_CTX *context          :一个MD5 text
    * unsigned char digest[16]  :储存加密结果的数组
    *************************************************/
    
    void MD5Final(MD5_CTX *context,unsigned char digest[16])  
    {  
        unsigned int index = 0,padlen = 0;  
    
        //bits: 8个字节,64位
        unsigned char bits[8];  
    
        //index:对64取余结果
        index = (context->count[0] >> 3) & 0x3F;  
        //因为要填充满足使其位长对512求余的结果等于448(56位)
        //所以当其所剩余的数小于56字节,则填充56-index字节,
        //否则填充120-index字节
        //这里padlen代表其所需填充的字节
        padlen = (index < 56)?(56-index):(120-index);  
    
        //然后,在这个结果后面附加一个以64位二进制表示的填充前数据长度。
        //把填充前数据数据长度转换后放到bit字符数组中
        MD5Encode(bits,context->count,8);
    
        //根据已经存储好的数组PADDING,在信息的后面填充一个1和无数个0,
        //直到满足上面的条件时才停止用0对信息的填充
        //其填充后进行了一系列的加密操作,其定剩余48个字节
        MD5Update(context,PADDING,padlen);  
    
        //在最后添加进8个字节的数据长度信息,最后凑成一组,进行一次加密处理
        MD5Update(context,bits,8);  
    
        //把最终得到的加密信息变成字符输出,共16字节
        MD5Encode(digest,context->state,16);  
    }  
    
    
    /**********************************************************
    * 函数功能:利用位操作,按1->4方式把数字分解成字符
    *
    * 参数分析:
    * unsigned char  *output :输出的字符的数组
    * unsigned int   *input  :输入数字的数组
    * unsigned int   len     : 输入数字数组的长度(单位:位) 
    * *********************************************************/
    
    void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)  
    {  
        unsigned int i = 0,j = 0;  
        while(j < len)  
        {  
            //这里& 0xFF为取后8位
            //i代表数字数组下标
            //j代表字符数组下标
            //把数字的8、8-16、16-24、24-32分别赋值给字符
            output[j] = input[i] & 0xFF;    
            output[j+1] = (input[i] >> 8) & 0xFF;  
            output[j+2] = (input[i] >> 16) & 0xFF;  
            output[j+3] = (input[i] >> 24) & 0xFF;  
            i++;  
            j+=4;  
        }  
    }  
    
    
    /**********************************************************
    * 函数功能:利用位操作,按4->1方式把字符合成数字
    *
    * 参数分析:
    * unsigned int  *output :输出的数字的数组
    * unsigned char *input  :输入字符的数组
    * unsigned int  len     : 输入字符的长度 (单位:位)
    * *********************************************************/
    
    void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)  
    {  
        unsigned int i = 0,j = 0;  
        while(j < len)  
        {  
            //利用位操作,把四个单位为1字节的字符,合成一个单位为4字节的数字
            //因为FF GG HH II和非线性函数都只能对数字进行处理
            //第一个字符占前8位,第二个占8-16位,第三个占16-24位,第四个占
            //24-32位。
            //i代表数字数组下标
            //j代表字符数组下标
            output[i] = (input[j]) |  
                (input[j+1] << 8) |  
                (input[j+2] << 16) |  
                (input[j+3] << 24);  
            i++;  
            j+=4;   
        }  
    }  
    
    
    /**************************************************************
    * 函数功能:对512位的block数据进行加密,并把加密结果存入state数组中
    * 对512位信息(即block字符数组)进行一次处理,每次处理包括四轮
    *state[4]:md5结构中的state[4],用于保存对512bits信息加密的中间结果或者最终结果
    * block[64]:欲加密的512bits信息或其中间数据
    ***************************************************************/
    void MD5Transform(unsigned int state[4], unsigned char block[64])  
    {  
        //a b c d继承上一个加密的结果,所以其具有继承性
        unsigned int a = state[0];  
        unsigned int b = state[1];  
        unsigned int c = state[2];  
        unsigned int d = state[3];  
    
        //这里只需用到16个,我把原来的unsiged int x[64]  改为了 x[16]
        unsigned int x[16];  
    
        //把字符转化成数字,便于运算
        MD5Decode(x,block,64);  
    
    
        //具体函数方式固定,不再赘述
    
        /*************第一轮******************/
        FF(a, b, c, d, x[ 0], 7, 0xd76aa478);   
        FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);   
        FF(c, d, a, b, x[ 2], 17, 0x242070db);   
        FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);   
    
        FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);   
        FF(d, a, b, c, x[ 5], 12, 0x4787c62a);   
        FF(c, d, a, b, x[ 6], 17, 0xa8304613);   
        FF(b, c, d, a, x[ 7], 22, 0xfd469501);   
    
        FF(a, b, c, d, x[ 8], 7, 0x698098d8);   
        FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);   
        FF(c, d, a, b, x[10], 17, 0xffff5bb1);   
        FF(b, c, d, a, x[11], 22, 0x895cd7be);   
    
        FF(a, b, c, d, x[12], 7, 0x6b901122);   
        FF(d, a, b, c, x[13], 12, 0xfd987193);   
        FF(c, d, a, b, x[14], 17, 0xa679438e);   
        FF(b, c, d, a, x[15], 22, 0x49b40821);   
    
    
        /*************第二轮*****************/
        GG(a, b, c, d, x[ 1], 5, 0xf61e2562);   
        GG(d, a, b, c, x[ 6], 9, 0xc040b340);   
        GG(c, d, a, b, x[11], 14, 0x265e5a51);   
        GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);   
    
        GG(a, b, c, d, x[ 5], 5, 0xd62f105d);   
        GG(d, a, b, c, x[10], 9,  0x2441453);   
        GG(c, d, a, b, x[15], 14, 0xd8a1e681);   
        GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);   
    
        GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);   
        GG(d, a, b, c, x[14], 9, 0xc33707d6);   
        GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);   
        GG(b, c, d, a, x[ 8], 20, 0x455a14ed);   
    
        GG(a, b, c, d, x[13], 5, 0xa9e3e905);   
        GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);   
        GG(c, d, a, b, x[ 7], 14, 0x676f02d9);   
        GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);   
    
    
        /*************第三轮*****************/
        HH(a, b, c, d, x[ 5], 4, 0xfffa3942);   
        HH(d, a, b, c, x[ 8], 11, 0x8771f681);   
        HH(c, d, a, b, x[11], 16, 0x6d9d6122);   
        HH(b, c, d, a, x[14], 23, 0xfde5380c);   
    
        HH(a, b, c, d, x[ 1], 4, 0xa4beea44);   
        HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);   
        HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);   
        HH(b, c, d, a, x[10], 23, 0xbebfbc70);   
    
        HH(a, b, c, d, x[13], 4, 0x289b7ec6);   
        HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);   
        HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);   
        HH(b, c, d, a, x[ 6], 23,  0x4881d05);   
    
        HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);   
        HH(d, a, b, c, x[12], 11, 0xe6db99e5);   
        HH(c, d, a, b, x[15], 16, 0x1fa27cf8);   
        HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);   
    
    
    
        /*************第四轮******************/
        II(a, b, c, d, x[ 0], 6, 0xf4292244);   
        II(d, a, b, c, x[ 7], 10, 0x432aff97);   
        II(c, d, a, b, x[14], 15, 0xab9423a7);   
        II(b, c, d, a, x[ 5], 21, 0xfc93a039);   
    
        II(a, b, c, d, x[12], 6, 0x655b59c3);   
        II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);   
        II(c, d, a, b, x[10], 15, 0xffeff47d);   
        II(b, c, d, a, x[ 1], 21, 0x85845dd1);   
    
        II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);   
        II(d, a, b, c, x[15], 10, 0xfe2ce6e0);   
        II(c, d, a, b, x[ 6], 15, 0xa3014314);   
        II(b, c, d, a, x[13], 21, 0x4e0811a1);   
    
        II(a, b, c, d, x[ 4], 6, 0xf7537e82);   
        II(d, a, b, c, x[11], 10, 0xbd3af235);   
        II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);   
        II(b, c, d, a, x[ 9], 21, 0xeb86d391);   
    
    
        //更换原来的结果
        state[0] += a;  
        state[1] += b;  
        state[2] += c;  
        state[3] += d;  
    }
    
    
    int main(int argc, char *argv[])  
    {  
        MD5_CTX md5;  //定义一个MD5 text
        MD5Init(&md5);//初始化
        int i;
        //unsigned char encrypt[] ="admin";//要加密内容
        //加密结果:21232f297a57a5a743894a0e4a801fc3
    
        unsigned char encrypt[1000];//要加密内容
        printf("请输入要加密的字符串:");
        gets((char *)encrypt);
    
        unsigned char decrypt[16]; //加密结果
        MD5Update(&md5, encrypt, strlen((char *)encrypt));//进行初步分组加密
    
        MD5Final(&md5,decrypt);   //进行后序的补足,并加密 
    
        printf("加密前:%s\n加密后16位:",encrypt);  
        for(i=4;i<12;i++)  
        {  
            printf("%02x",decrypt[i]);
        }  
    
        printf("\n加密前:%s\n加密后32位:",encrypt);  
        for(i=0;i<16;i++)  
        {  
            printf("%02x",decrypt[i]);
        }
        printf("\n");
    
        return 0;  
    }  

     

     

    展开全文
  • MD5加密算法原理及实现

    万次阅读 2018-06-05 08:19:39
    全称:message-digest algorithm 5翻译过来就是:信息 摘要 算法 51.特点1.长度固定:不管多长的字符串,加密后长度都是一样长作用:方便平时信息的统计和管理2.易计算:字符串和文件加密的过程是容易的.作用:开发者很容易...

    全称:message-digest algorithm 5 
    翻译过来就是:信息 摘要 算法 5

    1.特点

    • 1.长度固定:

      不管多长的字符串,加密后长度都是一样长 
      作用:方便平时信息的统计和管理

    • 2.易计算:

      字符串和文件加密的过程是容易的. 
      作用: 开发者很容易理解和做出加密工具

    • 3.细微性

      一个文件,不管多大,小到几k,大到几G,你只要改变里面某个字符,那么都会导致MD5值改变. 
      作用:很多软件和应用在网站提供下载资源,其中包含了对文件的MD5码,用户下载后只需要用工具测一下下载好的文件,通过对比就知道该文件是否有过更改变动.

    • 4.不可逆性

      你明明知道密文和加密方式,你却无法反向计算出原密码. 
      作用:基于这个特点,很多安全的加密方式都是用到.大大提高了数据的安全性


    2.后续讲解

    • 关于撞库破解:

      这是概率极低的破解方法,原理就是:

      1.建立一个大型的数据库,把日常的各个语句,通过MD5加密成为密文,不断的积累大量的句子,放在一个庞大的数据库里.

      2.比如一个人拿到了别人的密文,想去查询真实的密码,就需要那这个密文去到提供这个数据库的公司网站去查询.

      这就是撞库的概念.


    3.关于MD5加盐:

    比如我的银行密码是”12345”

    1.得到的MD5是:827ccb0eea8a706c4c34a16891f84e7b

    2.一个人截取到这个密文,那么通过撞库肯定容易撞出12345.

    3.我们要做的就是加盐,银行密码还是”12345”,然后我把银行密码加上我特定的字符串才计算MD5 
    所以密码还是那个密码,但是变成求”12345密码加密987”的MD5值,然后再得到MD5,那么这个MD5起码可以确认那个数据库不会有.


    说了那么多我们开始我们的MD5工具的制作

    我们一般加密都是加密字符串或者文件,所以我们的工具就有加密字符串和文件的两种方法,两个方法同名,通过重载完成

    1.加密字符串

    逻辑思维:

    • 1.获取信息摘要对象:md5

      通过信息摘要单例的构造函数获取:

      MessageDigest md5 = MessageDigest.getInstance("MD5");
      
    • 2.信息摘要对象是对字节数组进行摘要的,所以先获取字符串的字节数组.

      byte[] bytes = str.getBytes();
      
    • 3.信息摘要对象对字节数组进行摘要,得到摘要字节数组:

      byte[] digest = md5.digest(bytes);
      
    • 4.把摘要数组中的每一个字节转换成16进制,并拼在一起就得到了MD5值. 
      (PS,有些转换过来得到的是前面有6个f的情况,如:ffffff82,这是因为前面有6组4个1,所以提前把这6组1111先变成0就好了,然后再转16进制就没有f了) 
      (其实也可以在后面续把f去掉)


    2.加密文件

    方法传入的是文件对象 : file

    • 1.因为是文件不是方法,所以不是像刚才那样通过摘要获取字符串.

    • 2.使用到另一个方法即可:就是信息摘要对象更新:md5.update(byte[] input)方法,用法是通过读取流,不断的更新从流中读到的”信息数组”.

    • 3.然后通过”信息摘要对象”获取摘要,不用参数:md5.digest(),此时返回的数组就已经是包含内容的摘要数组


    以下是详细代码:

    public class MD5Tool {
        public static void main(String[] args) throws Exception {
            /*--------------字符串--------------*/
            String str = "12345";
            String md1 = getMD5(str);
            System.out.println(md1);//827ccb0eea8a706c4c34a16891f84e7b
    
            /*--------------文件--------------*/
            File file = new File("D:\\1.mp3");
            String md2 = getMD5(file);
            System.out.println(md2);//9068aaead9a5b75e6a54395d8183ec9
        }
        /**
         * 逻辑:
         *
         * 1.获取md5对象,通过"信息摘要"获取实例构造("MD5").
         * 2.md5对象对("字符串的"字节形式"-得到的数组)进行摘要",那么会返回一个"摘要的字节数组"
         * 3.摘要字节数组中的"每个二进制值"字节形式,"转成十六进制形式",然后再把这些值给拼接起来,就是MD5值了
         *      (PS:为了便于阅读,把多余的fff去掉,并且单个字符前加个0)
         *
         */
        public static String getMD5(String str) throws Exception {
    
            String MD5 = "";
    
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = str.getBytes();
            byte[] digest = md5.digest(bytes);
    
            for (int i = 0; i < digest.length; i++) {
                //摘要字节数组中各个字节的"十六进制"形式.
                int j = digest[i];
                 j = j & 0x000000ff;
                String s1 = Integer.toHexString(j);
    
                if (s1.length() == 1) {
                    s1 = "0" + s1;
                }
                MD5 += s1;
            }
            return MD5;
        }
        //重载,所以用户传入"字符串"或者"文件"都可以解决.
    
        /**
         * 处理逻辑:
         * 1.现在传入的是"文件",不是字符串
         * 2.所以信息摘要对象.进行摘要得到数组不能像上面获得:md5.digest(bytes),因为不是str.getBytes得到bytes
         * 3.其实还是通过mdt.digest();获取到字节数组,但是前期必须要有一个方法必须是md5.update(),即"信息摘要对象"要先更新
         * 4."信息摘要更新"里面有(byte[] input),说明是读取流获取到的数组,所以我们就用这个方法.
         * 5.所以最终的逻辑就是:
         *
         *      1.获取文件的读取流
         *      2.不停的读取流中的"内容"放入字符串,放一部分就"更新"一部分.直到全部完毕
         *      3.然后调用md5.digest();就会得到有内容的字节数组,剩下的就和上边一样了.
         */
        public static String getMD5(File file) throws Exception {
            String MD5 = "";
    
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            FileInputStream fis = new FileInputStream(file);
    
            byte[] bytes = new byte[1024 * 5];
    
            int len = -1;
            while ((len=fis.read(bytes))!=-1) {
                //一部分一部分更新
                md5.update(bytes, 0, len);
            }
            byte[] digest = md5.digest();
            for (int i = 0; i <digest.length; i++) {
                int n = digest[i] & 0x000000ff;
                String s = Integer.toHexString(n);
    
                MD5 += s;
            }
            return MD5;
        }
    }

    MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。

    以下所描述的消息长度、填充数据都以位(Bit)为单位,字节序为小端字节。

    算法原理

    1、数据填充

    对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。

    填充方法:在消息后面进行填充,填充第一位为1,其余为0。

    2、添加消息长度

    在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。

    在此步骤进行完毕后,最终消息长度就是512的整数倍。

    3、数据处理

    准备需要用到的数据:

    • 4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
    • 4个函数:F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z));  H(X,Y,Z)=X ^ Y ^ Z; I(X,Y,Z)=Y ^ (X | (~Z));

    把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。

    具体计算的实现较为复杂,建议查阅相关书籍,下面给出在C++上的实现代码。

    代码实现

    #MD5.h

     
     1 #ifndef MD5H
     2 #define MD5H
     3 #include <math.h>
     4 #include <Windows.h>
     5 
     6 void ROL(unsigned int &s, unsigned short cx); //32位数循环左移实现函数
     7 void ltob(unsigned int &i); //B\L互转,接受UINT类型
     8 unsigned int* MD5(const char* mStr); //接口函数,并执行数据填充,计算MD5时调用此函数
     9 
    10 #endif
     

    #MD5.cpp

     
      1 #include "MD5.h"
      2 
      3 /*4组计算函数*/
      4 inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z)
      5 {
      6     return (X & Y) | ((~X) & Z);
      7 }
      8 inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z)
      9 {
     10     return (X & Z) | (Y & (~Z));
     11 }
     12 inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z)
     13 {
     14     return X ^ Y ^ Z;
     15 }
     16 inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z)
     17 {
     18     return Y ^ (X | (~Z));
     19 }
     20 /*4组计算函数结束*/
     21 
     22 /*32位数循环左移实现函数*/
     23 void ROL(unsigned int &s, unsigned short cx)
     24 {
     25     if (cx > 32)cx %= 32;
     26     s = (s << cx) | (s >> (32 - cx));
     27     return;
     28 }
     29 
     30 /*B\L互转,接收UINT类型*/
     31 void ltob(unsigned int &i)
     32 {
     33     unsigned int tmp = i;//保存副本
     34     byte *psour = (byte*)&tmp, *pdes = (byte*)&i;
     35     pdes += 3;//调整指针,准备左右调转
     36     for (short i = 3; i >= 0; --i)
     37     {
     38         CopyMemory(pdes - i, psour + i, 1);
     39     }
     40     return;
     41 }
     42 
     43 /*
     44 MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)
     45 种子数组排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B;
     46 */
     47 void AccLoop(unsigned short label, unsigned int *lGroup, void *M)
     48 {
     49     unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定义:4个指针; T表累加器; 局部变量
     50     typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定义函数类型
     51     const unsigned int rolarray[4][4] = {
     52         { 7, 12, 17, 22 },
     53         { 5, 9, 14, 20 },
     54         { 4, 11, 16, 23 },
     55         { 6, 10, 15, 21 }
     56     };//循环左移-位数表
     57     const unsigned short mN[4][16] = {
     58         { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
     59         { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },
     60         { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },
     61         { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }
     62     };//数据坐标表
     63     const unsigned int *pM = static_cast<unsigned int*>(M);//转换类型为32位的Uint
     64     TAcc = ((label - 1) * 16) + 1; //根据第几轮循环初始化T表累加器
     65     clac clacArr[4] = { F, G, H, I }; //定义并初始化计算函数指针数组
     66 
     67     /*一轮循环开始(16组->16次)*/
     68     for (short i = 0; i < 16; ++i)
     69     {
     70         /*进行指针自变换*/
     71         i1 = lGroup + ((0 + i) % 4);
     72         i2 = lGroup + ((3 + i) % 4);
     73         i3 = lGroup + ((2 + i) % 4);
     74         i4 = lGroup + ((1 + i) % 4);
     75 
     76         /*第一步计算开始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接计算T表*/
     77         tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));
     78         ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循环左移
     79         *i1 = *i2 + tmpi;//第三步:相加并赋值到种子
     80     }
     81     return;
     82 }
     83 
     84 /*接口函数,并执行数据填充*/
     85 unsigned int* MD5(const char* mStr)
     86 {
     87     unsigned int mLen = strlen(mStr); //计算字符串长度
     88     if (mLen < 0) return 0;
     89     unsigned int FillSize = 448 - ((mLen * 8) % 512); //计算需填充的bit数
     90     unsigned int FSbyte = FillSize / 8; //以字节表示的填充数
     91     unsigned int BuffLen = mLen + 8 + FSbyte; //缓冲区长度或者说填充后的长度
     92     unsigned char *md5Buff = new unsigned char[BuffLen]; //分配缓冲区
     93     CopyMemory(md5Buff, mStr, mLen); //复制字符串到缓冲区
     94 
     95     /*数据填充开始*/
     96     md5Buff[mLen] = 0x80; //第一个bit填充1
     97     ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory
     98     unsigned long long lenBit = mLen * 8ULL; //计算字符串长度,准备填充
     99     CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充长度
    100     /*数据填充结束*/
    101 
    102     /*运算开始*/
    103     unsigned int LoopNumber = BuffLen / 64; //以16个字为一分组,计算分组数量
    104     unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4个种子,小端类型
    105     unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //种子副本数组,并作为返回值返回
    106     for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分组大循环开始
    107     {
    108         /*进入4次计算的小循环*/
    109         for (unsigned short Lcount = 0; Lcount < 4;)
    110         {
    111             AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);
    112         }
    113         /*数据相加作为下一轮的种子或者最终输出*/
    114         A = (lGroup[0] += A);
    115         B = (lGroup[3] += B);
    116         C = (lGroup[2] += C);
    117         D = (lGroup[1] += D);
    118     }
    119     /*转换内存中的布局后才能正常显示*/
    120     ltob(lGroup[0]);
    121     ltob(lGroup[1]);
    122     ltob(lGroup[2]);
    123     ltob(lGroup[3]);
    124     delete[] md5Buff; //清除内存并返回
    125     return lGroup;
    126 }
     

     

    再给出调用实例(以win32控制台应用程序为例):

    #main.cpp

     
     1 #include <iostream>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include "MD5.h"
     5 
     6 int main(int argc, char **argv)
     7 {
     8     char tmpstr[256], buf[4][10];
     9     std::cout << "请输入要加密的字符串:";
    10     std::cin >> tmpstr;
    11     unsigned int* tmpGroup = MD5(tmpstr);
    12     sprintf_s(buf[0], "%8X", tmpGroup[0]);
    13     sprintf_s(buf[1], "%8X", tmpGroup[3]);
    14     sprintf_s(buf[2], "%8X", tmpGroup[2]);
    15     sprintf_s(buf[3], "%8X", tmpGroup[1]);
    16     std::cout <<"MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;
    17     
    18     delete[] tmpGroup;
    19     return 0; //在此下断点才能看到输出的值
    20 }

     

    拓展

    0xfffffff代表的含义:

    • 0x:代表16进制;

    • 一个f代表:4个1,即(1111);

    • 所以0xffffffff代表:8组4个1

      1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 1111
      
      • 1
      • 2
    • 所以刚才的0xffffff82就是前面6组都是1,后面两组是

      1111 - 1111 - 1111 - 1111 - 1111 - 1111 - 0111 - 0010
      
      • 1
      • 2
    • 所以先与上0x000000ff,即

      0000 - 0000 - 0000 - 0000 - 0000 - 0000 - 1111 - 1111
      
      • 1
      • 2
    • 就得到了82了

    上面的方法也可以写写成:

            for (int i = 0; i < digest.length; i++) {
                //摘要字节数组中各个字节的"十六进制"形式.
                String s1 = Integer.toHexString( digest[i]);
    
                //如果是8个长度的,把前面的6个f去掉,只获取后面的
                if (s1.length() == 8) {
                    s1 = s1.substring(6);
                }
                if (s1.length() == 1) {
                    s1 = "0" + s1;
                }
                MD5 += s1;
            }
    
    
    展开全文
  • MD5多种加密方式

    千次阅读 2018-08-27 16:43:59
    加密  using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text...
  • MD5加密算法

    千次阅读 2017-09-20 16:43:01
    MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest开发出来,经MD2、MD3和MD4发展而来。 MD5加密算法:...
  • 前端参数用MD5加密

    万次阅读 2018-12-09 13:03:54
    今天来讲一下如何用这个MD5加密 第一步: 两种导入MD5.js 一种你可以到以下这个地址去下载MD5的js路径:https://www.bootcdn.cn/blueimp-md5/ 另一种下面是md5的js,直接复制下去放到js文件里。亲测可用 var ...
  • MD5加密(转)

    千次阅读 2019-05-31 21:01:47
    MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特...
  • MD5加密原理解析及OC版原理实现

    千次阅读 2019-01-28 22:33:31
    MD5加密原理解析及OC版原理实现  一、MD5算法基础概念  MD5算法是Hash算法的一种,叫做讯息摘要演算法。所谓摘要,从字面意思理解,是指内容的大概。在MD5算法中,这个摘要是指将任意数据映射成一个128位长的...
  • MD5原理及加密字符串和文件

    千次阅读 2018-01-19 09:33:54
    MD5,全名Message Digest Algorithm 5 ,中文名为消息摘要算法第五版,为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。 主要是通过特定的hash散列方法将文本信息转换成简短的信息摘要,压缩+...
  • java将字符串进行MD5加密

    万次阅读 热门讨论 2017-12-09 15:06:14
    对于一个明文,为了安全,有时我们需要对其进行MD5加密,下面提供2个java工具方法,直接调用其中一个即可。 方法1: /** * MD5加密 */ public class MD5Util { /** * Encodes a string 2 MD5 * * @param ...
  • java与MD5加密以及两次MD5加密

    千次阅读 2019-06-01 15:12:55
    java中实现MD5加密通常有两种方法 方法一:通过java自带的java.security.MessageDigest实现加密 package com.wantao.md5; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; ...
  • JAVA核心知识点--MD5加密

    万次阅读 2017-08-09 16:27:52
    MD5即Message-Digest Algorithm 5(信息-摘要算法第五版),是一种在计算机安全领域被广泛使用的散列函数(又译杂凑算法、摘要算法、哈希算法),用于确保所加密的数据的信息完整性和一致性。将数据(如文本、压缩包...
  • 浅谈md5加密

    万次阅读 2017-06-04 17:04:15
    md5加密是我们生活中十分常见的加密算法。 我是最近在写一个H5 的项目时接触到的这个算法,这个算法极大的引起了我的好奇心,是登陆界面,要求是将用户输入的密码使用md5加密之后,再传回服务器,当时我十分不理解...
  • MD5加密c语言实现

    热门讨论 2011-05-12 10:22:53
    MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现MD5加密c语言实现
  • 自制工具: 用Flash做的一个MD5加密工具 可加密为32位的md5加密码 简单使用方法,下载后把文件拖到IE打开便可.
  • MD5 加密算法详细介绍

    千次阅读 2018-04-07 18:59:37
    大家好,我们现在来讲解关于加密方面的知识,说到加密我认为不得不提MD5,因为这是一种特殊的加密方式,它到底特殊在哪,现在我们就开始学习它 全称:message-digest algorithm 5 翻译过来就是:信息 摘要 算法 5 加密和...
  • c# MD5加密

    千次阅读 2018-12-17 11:49:37
    using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks;.../*********************... * 概要:MD5加密  * 设...
  • python3中的md5加密

    万次阅读 多人点赞 2017-12-19 01:48:38
    在python3的标准库中,已经移除了md5,而关于hash加密算法都放在hashlib这个标准库中,如SHA1、SHA224、SHA256、SHA384、SHA512和MD5算法等。另:在网上找关于python的md5加密,发现要不是比较旧的不适用当前py版本...
  • js md5加密和java md5加密不一致

    千次阅读 2017-08-18 11:10:39
    页面请求后台数据时会有一个验签过程,前台js将数据md5加密后传到后台,后台根据接收的请求数据根据约定的规则同样生成md5,数字和英文没有问题,但是要加密的字符串中包含中文则两者结果对比不一致后来替换md5 js...

空空如也

1 2 3 4 5 ... 20
收藏数 165,078
精华内容 66,031
关键字:

md5加密