全称: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 MD5H2 #define MD5H3 #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位的Uint64     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填充197     ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函数为FillMemory98     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加密算法原理及实现相关推荐

  1. md5加密算法原理及其GO语言实现

    md5加密算法原理及其GO语言实现 MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值 ...

  2. MD5加密算法原理及一些其他的加密算法

    Md5加密算法: 概述:md5算法也可以称为消息摘要算法,属于hash算法的一种,md5算法对输入的任意长度的消息进行运行,然后产生一个128位的消息摘要 特点: #不可逆性 唯一性:相同数据的md5 ...

  3. 了解MD5加密算法原理及其应用

    了解MD5加密算法原理及其应用 前言 一.普通强度MD5破解方法 二.Md5算法应用 (1)MD5加密原理 (2)MD5的安全性 (3)MD5加密算法的应用 三.改进后的加密方法 (1)目前MD5加密 ...

  4. 简要分析用MD5加密算法加密信息(如有疑问,敬请留言)

    一.引言 最近看了媒体的一篇关于"网络上公开叫卖个人隐私信息"报导,不法分子通过非法手段获得的个人隐私信息,其详细.准确程度简直令人瞠口结舌.在互联网飞速发展的现在,我们不难想到, ...

  5. php md5加密算法源码,MD5加密和哈希算法

    MD5加密算法为现在应用最广泛的哈希算法之一,该算法广泛应用于互联网网站的用户文件加密,能够将用户密码加密为128位的长整数.数据库并不明文存储用户密码,而是在用户登录时将输入密码字符串进行MD5加密 ...

  6. Md5加密算法的原理及应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

  7. 关于Md5加密算法的原理及应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

  8. MD5加密算法的原理和应用

    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆:所以要解密MD5没有现成的算法,只能用穷举法,把可能出现的明文,用MD5算法散列之后 ...

  9. HTTPS 加密算法原理详解

    本文讲的是HTTPS 加密算法原理详解, 前言 HTTPS(全称:HyperText Transfer Protocol over Secure Socket Layer),其实 HTTPS 并不是一 ...

  10. MD5算法原理简要介绍并采用C#应用在桌面应用系统的用户登录与注册中

    MD5算法原理简要介绍并采用C#应用在桌面应用系统的用户登录与注册中 接上文,本文简要介绍一下MD5加密算法的原理,并采用C#实现MD5算法的加密与解密过程,将这一实现过程应用在我自己开发的桌面办公应 ...

最新文章

  1. 分布式一致Hash算法
  2. 她说要介绍10000个开源项目?来!我们一起监督!
  3. 2014 年第六届全国大学生数学竞赛预赛数学类最后一题参考解答
  4. 如何使用 sklearn 优雅地进行数据挖掘?
  5. Java编程技巧:如何实现参数的输入输出?
  6. 请求的站点不可用或找不到_80%的500强用企业微信,企业越来越找不到拒绝用它的理由...
  7. resource android:attr/dialogCornerRadius not found
  8. C#中用ILMerge合并DLL和exe文件成一个exe文件或者DLL
  9. mac下8080端口到80端口的转发
  10. linux 心脏滴血漏洞,漏洞bash近日“破壳”,当心再次“心脏出血”
  11. 转:用AutoCAD 系统变量编程
  12. 笔记本(win10、win7)开机在LOGO过后出现闪屏几下才进入系统成功解决问题步骤分享
  13. Hijacking tons of Instapage expired users Domains Subdomains
  14. Ubuntu 20.04 源码编译Paddle2.2.2
  15. Python画玫瑰花源代码
  16. 跨专业考计算机研究生有专业限制吗,跨专业考计算机研究生难不难
  17. 可等待计时器与用户计时器
  18. IE和Firefox浏览器CSS网页布局不同点
  19. Java 入门之3:JDK 8 版本的目录结构及bin目录中工具命令的作用概览
  20. QT+PCL+VS制作点云显示界面(彩色显示xyz点云)

热门文章

  1. Java文件上传实例并解决跨域问题
  2. docker部署案例
  3. 使用Pano2VR实现客厅VR效果
  4. android bluez 编译,[android源码分析]bluez起动过程中的各种plugin的初始化(一)-bluetooth_builtin数组所耍的花样...
  5. Matconvnet学习笔记
  6. 一款全面超越ps3的国产游戏机
  7. sht20中写用户寄存器_SHT20 中文技术手册
  8. 获得android手机root权限,安卓手机root助手教你一键获取手机root权限
  9. adb安装apk python小工具
  10. 卡诺模型案例分析_卡诺模型及使用