网络安全之MD5_SHA1

MD5


首先,我们简单介绍一下MD5(Message Digest Algorithm MD5)。

发展历史

​ 从1989年的MD2->1990年MD4->1991年MD5

应用

  • 一致性校验
  • 数字签名
  • 安全访问认证

一致性验证:unix下有些文件会有一个.md5的文件,它就是对文件的一个签名,用来保证这个文件是否被篡改过。由于不同的内容经过MD5后会产生不同的结果,所以可以保证文件的一致性。利用MD5算法来进行文件的校验被大量应用到软件下载、论坛数据库、系统文件安全等方面。

数字签名:对消息进行加密,消息内容被修改后,重新获取的MD5值就会不一致,如果有第三发进行认证的话,这样就会对文件的消息来源进行保证,防止文件的作者“抵赖”。

安全访问认证:在系统的开发中会经常用到密码的保存。当用户登录时对密码进行MD5加密,然后与数据库的密码进行比对,一致即可完成登录。但是现在通过跑字典的方式可以将MD5加密后的内容还原为加密前的内容。

算法原理

MD5是输入不定长度信息,输出固定的128-bits的算法。经过程序流程,生成四个32位数据,最后连在一起成为一个128-bits散列。基本方式为,求余、取余、调整长度、与链接变量进行循环运算,得到结果。

  1. 处理原文

    首先计算出原文长度(bit)对512求余,如果不等于448,进行填充原文使得原文对515的求余满足等于448。填充的方式是第一位填充1,其余位填0.填完后,信息的长度为512*N+448。

    之后,用剩余的位置(512-448=64位)记录原文的真正长度,把长度的二进制值补在最后。这样处理后的信息长度就是512*(N+1)。

  2. 设置初始值

    MD5的哈希结果长度为128位,按每32位分成一组工4组。这4组的结果是由4个初始值A、B、C、D经过不断演变得到的。MD5的官方实现中,A、B、C、D的初始值如下(16进制)

    1
    2
    3
    4
    A=0x01234567
    B=0x89ABCDEF
    C=0xFEDCBA98
    D=0x76543210
  3. 循环加工

    我们先看下面的这张图

    图中,A、B、C、D就是哈希值的四个分组。每一次循环都会让旧的ABCD产生新的ABCD。循环的次数是由处理后的原文长度决定的。

    假设处理后的原文长度为M

    主循环次数=M/512

    每个主循环中包含512/32 * 4 = 64次子循环。上图为单词子循环的流程。

    每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向左环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。

    现在对图中的元素进行解释

    绿色F

    图中绿色F,代表非线性函数。MD5所用到的函数有四种:

    1
    2
    3
    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))

    在主循环下面64次子循环中,F、G、H、I 交替使用,第一个16次使用F,第二个16次使用G,第三个16次使用H,第四个16次使用I。

    红色田字

    红色的田字代表相加的意思

    Mi

    Mi是第一步处理后的原文。在第一步中,处理后的原文长度为512的整数倍。把原文的没512位在分成16等份,命名为M0~M15,每份长度为32。在64次子循环中,每16次循环,都会交替用到M1~M16之一。

    Ki

    一个常量,在64次子循环中,每一次用到的常量都是不同的。

    黄色的<<

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
第一轮:
FF(a,b,c,d,M0,7,0xd76aa478) s[0]=7, K[0] = 0xd76aa478
  FF(a,b,c,d,M1,12,0xe8c7b756) s[1]=12, K[1] = 0xe8c7b756
  FF(a,b,c,d,M2,17,0x242070db)
  FF(a,b,c,d,M3,22,0xc1bdceee)
  FF(a,b,c,d,M4,7,0xf57c0faf)
  FF(a,b,c,d,M5,12,0x4787c62a)
  FF(a,b,c,d,M6,17,0xa8304613)
  FF(a,b,c,d,M7,22,0xfd469501)
  FF(a,b,c,d,M8,7,0x698098d8)
  FF(a,b,c,d,M9,12,0x8b44f7af)
  FF(a,b,c,d,M10,17,0xffff5bb1)
  FF(a,b,c,d,M11,22,0x895cd7be)
  FF(a,b,c,d,M12,7,0x6b901122)
  FF(a,b,c,d,M13,12,0xfd987193)
  FF(a,b,c,d,M14,17, 0xa679438e)
  FF(a,b,c,d,M15,22,0x49b40821)
第二轮:
  GG(a,b,c,d,M1,5,0xf61e2562)
  GG(a,b,c,d,M6,9,0xc040b340)
  GG(a,b,c,d,M11,14,0x265e5a51)
  GG(a,b,c,d,M0,20,0xe9b6c7aa)
  GG(a,b,c,d,M5,5,0xd62f105d)
  GG(a,b,c,d,M10,9,0x02441453)
  GG(a,b,c,d,M15,14,0xd8a1e681)
  GG(a,b,c,d,M4,20,0xe7d3fbc8)
  GG(a,b,c,d,M9,5,0x21e1cde6)
  GG(a,b,c,d,M14,9,0xc33707d6)
  GG(a,b,c,d,M3,14,0xf4d50d87)
  GG(a,b,c,d,M8,20,0x455a14ed)
  GG(a,b,c,d,M13,5,0xa9e3e905)
  GG(a,b,c,d,M2,9,0xfcefa3f8)
  GG(a,b,c,d,M7,14,0x676f02d9)
  GG(a,b,c,d,M12,20,0x8d2a4c8a)
第三轮:
  HH(a,b,c,d,M5,4,0xfffa3942)
  HH(a,b,c,d,M8,11,0x8771f681)
  HH(a,b,c,d,M11,16,0x6d9d6122)
  HH(a,b,c,d,M14,23,0xfde5380c)
  HH(a,b,c,d,M1,4,0xa4beea44)
  HH(a,b,c,d,M4,11,0x4bdecfa9)
  HH(a,b,c,d,M7,16,0xf6bb4b60)
  HH(a,b,c,d,M10,23,0xbebfbc70)
  HH(a,b,c,d,M13,4,0x289b7ec6)
  HH(a,b,c,d,M0,11,0xeaa127fa)
  HH(a,b,c,d,M3,16,0xd4ef3085)
  HH(a,b,c,d,M6,23,0x04881d05)
  HH(a,b,c,d,M9,4,0xd9d4d039)
  HH(a,b,c,d,M12,11,0xe6db99e5)
  HH(a,b,c,d,M15,16,0x1fa27cf8)
  HH(a,b,c,d,M2,23,0xc4ac5665)
第四轮:
  Ⅱ(a,b,c,d,M0,6,0xf4292244)
  Ⅱ(a,b,c,d,M7,10,0x432aff97)
  Ⅱ(a,b,c,d,M14,15,0xab9423a7)
  Ⅱ(a,b,c,d,M5,21,0xfc93a039)
  Ⅱ(a,b,c,d,M12,6,0x655b59c3)
  Ⅱ(a,b,c,d,M3,10,0x8f0ccc92)
  Ⅱ(a,b,c,d,M10,15,0xffeff47d)
  Ⅱ(a,b,c,d,M1,21,0x85845dd1)
  Ⅱ(a,b,c,d,M8,6,0x6fa87e4f)
  Ⅱ(a,b,c,d,M15,10,0xfe2ce6e0)
  Ⅱ(a,b,c,d,M6,15,0xa3014314)
  Ⅱ(a,b,c,d,M13,21,0x4e0811a1)
  Ⅱ(a,b,c,d,M4,6,0xf7537e82)
  Ⅱ(a,b,c,d,M11,10,0xbd3af235)
  Ⅱ(a,b,c,d,M2,15,0x2ad7d2bb)
  Ⅱ(a,b,c,d,M9,21,0xeb86d391)
  1. 拼接结果

    循环加工最终产生的A,B,C,D四个值拼接在一起,转换成字符串即可

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
162
163
164
165
166
package top.wsylp.demo;


/**
* @Description:
* @Author: wsylp
* @Date: 2019/6/13 6:29
*/
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("a");
System.out.println(str);
}
}

MD5使用很简单:

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
package top.wsylp.demo;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* @Description:
* @Author: wsylp
* @Date: 2019/6/14 22:47
*/
public class MD5Util {
public static void MD5Encrypt(String data) throws NoSuchAlgorithmException {
StringBuffer ensryptStr = new StringBuffer();
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(data.getBytes());
byte[] cipher = digest.digest();
for (byte b : cipher) {
//转换为16进制
String hexStr = Integer.toHexString(b & 0xff);
ensryptStr.append(hexStr.length() == 1 ? "0" + hexStr : hexStr);
}
System.out.println(ensryptStr.toString());

}


public static void main(String[] args) throws NoSuchAlgorithmException {
MD5Encrypt("a");

}
}

SHA1

SHA算法,即安全散列算法(Secure Hash Algorithm)是一种与MD5同源的数据加密算法,该算法经过加密专家多年来的发展和改进已日益完善,现在已成为公认的最安全的散列算法之一,并被广泛使用。

概述

SHA算法能计算出一个数位信息所对应到的,长度固定的字串,又称信息摘要。而且如果输入信息有任何的不同,输出的对应摘要不同的机率非常高。因此SHA算法也是FIPS所认证的五种安全杂凑算法之一。原因有两点:一是由信息摘要反推原输入信息,从计算理论上来说是极为困难的;二是,想要找到两组不同的输入信息发生信息摘要碰撞的几率,从计算理论上来说是非常小的。任何对输入信息的变动,都有很高的几率导致的信息摘要大相径庭。

SHA实际上是一系列算法的统称,分别包括:SHA-1、SHA-224、SHA-256、SHA-384以及SHA-512。后面4中统称为SHA-2,事实上SHA-224是SHA-256的缩减版,SHA-384是SHA-512的缩减版。各中SHA算法的数据比较如下表,其中的长度单位均为位:

类别SHA-1SHA-224SHA-256SHA-384SHA-512
消息摘要长度160224256384512
消息长度小于(2的64幂)位小于(2的64幂)位小于(2的64幂)位小于(2的128幂)位小于(2的128幂)位
分组长度51251251210241024
计算字长度3232326464
计算步骤数8064648080

SHA-1在许多安全协定中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5的后继者。SHA1主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于264位的消息,SHA1会产生一个160位的消息摘要。

基本原理

我们分析SHA-1的算法原理。SHA-1是一种数字加密算法,该算法的思想是接受一段明文,然后以一种不可逆的方式将它转换为一段密文,将不定长读的信息加密为固定长度的散列值,也称之为信息摘要或信息认证过程。

SHA-1算法输入报文的最大长度不超过264位,产生的输出是一个160位的报文摘要。输入是按512 位的分组进行处理的。SHA-1是不可逆的、防冲突,并具有良好的雪崩效应。

  1. 对输入信息进行处理

    ​ 对信息处理的方式与MD5的有些类似,对输入信息按512位进行分组并进行填充。填充后的长度与512进行相除,得到的余数为448位。填充的内容是什么?现在报文的后面加一个1,在加多个0,直到长度满足对512取余的模为448。至于为什么是448位?与MD5类似,会在后面补充64的长度信息。

    在上面的报表中,消息长度小于2的64幂,是因为长度信息的位数为64位。

  2. 信息分组处理

    对输入信息进行填充后,长度为512的倍数,然后按照512的长度进行分组,可以得到一定数量的明文分组,这里用A、B、C 、 D等明文分组。对于每一个明文分组,都要重复反复的处理,这些与MD5都是相同的。

    而对于每个512位的明文分组,SHA1将其再分成16份更小的明文分组,称为子明文分组,每个子明文分组为32位,我们且使用M[t](t= 0, 1,……15)来表示这16个子明文分组。然后需要将这16个子明文分组扩充到80个子明文分组,我们将其记为W[t](t= 0, 1,……79),扩充的具体方法是:当0≤t≤15时,Wt = Mt;当16≤t≤79时,Wt = ( Wt-3 ⊕ Wt-8⊕ Wt-14⊕ Wt-16) <<< 1,从而得到80个子明文分组。

  3. 初始化缓存

    所谓初始化缓存就是为链接变量赋初值。前面我们实现MD5算法时,说过由于摘要是128位,以32位为计算单位,所以需要4个链接变量。同样SHA-1采用160位的信息摘要,也以32位为计算长度,就需要5个链接变量。我们记为A、B、C、D、E。其初始赋值分别为:A = 0x67452301、B = 0xEFCDAB89、C = 0x98BADCFE、D = 0x10325476、E = 0xC3D2E1F0。

    如果我们对比前面说过的MD5算法就会发现,前4个链接变量的初始值是一样的,因为它们本来就是同源的。

  4. 计算信息摘要

    A)、将A左移5为与 函数的结果求和,再与对应的子明文分组、E以及计算常数求和后的结果赋予H0。

    (B)、将A的值赋予H1。

    (C)、将B左移30位,并赋予H2。

    (D)、将C的值赋予H3。

    (E)、将D的值赋予H4。

    (F)、最后将H0、H1、H2、H3、H4的值分别赋予A、B、C、D

    这个过程表示如下:

1
2
3
4
5
6
7
8
9
10
11
12
public static void sha1Encrypt(String str) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(str.getBytes());
byte[] cipher = messageDigest.digest();
String a = "";
for (byte b : cipher) {
String hex = Integer.toHexString(b & 0xff);
a += (hex.length() == 1 ? "0" + hex : hex);
}
System.out.println(a);

}

参考内容:

本文标题:网络安全之MD5_SHA1

文章作者:wsylp

发布时间:2018年06月01日 - 07:06

最后更新:2020年01月02日 - 10:01

原始链接:http://wsylp.top/2018/06/01/网络安全之MD5_SHA1/

许可协议: 本文为 wsylp 版权所有 转载请保留原文链接及作者。

-------------本文结束感谢阅读-------------