MD5加密算法
前言
MD5(Message-Digest Algorithm),是一种信息摘要算法,将明文字符串经hash处理后,返回一个16字节的数组。
MD5严格意义上不算一种加密算法,因为通常我们理解的加密,总是可以通过解密的方式来还原成我们需要的明文,但是经过Hash算法处理的明文,是不可还原的。
所以,一般用MD5做数字签名,以确保信息的完整性、信息来源的可靠性。
源码解析
示例明文:String s = "abcd";
» 编码
使用utf-8将source编码为一个字节数组,byte[] s = [97,98,99,100]; (字符a对应的十进制数是97)
» 填充
填充结果:待加密字节数组长度为 n*64.
填充规则:首先填充1000 0000,即十进制的-128;后面位填充0;最后8字节,需要填充原字符串的位数(4 * 8 = 32bit)
所以,示例的填充结果为:
byte[64] buffer = [97,98,99,100,-128,0,0,0,0...0,32,0,0,0,0,0,0,0];
long var3 = this.bytesProcessed << 3; int var5 = (int)this.bytesProcessed & 63; int var6 = var5 < 56 ? 56 - var5 : 120 - var5; this.engineUpdate(padding, 0, var6); ByteArrayAccess.i2bLittle4((int)var3, this.buffer, 56); ByteArrayAccess.i2bLittle4((int)(var3 >>> 32), this.buffer, 60);
» 分组、分块
ByteArrayAccess.b2iLittle64(var1, var2, this.x);
分组:每64字节为一组;
分块:每4字节为一块;
示例中,分组分块结果为 1组、16块,因为在java中一个int就是4字节,所以我们可以用一个长度为16的int数组来保存分块结果。
int[16] x = [x0,x1,x2,...,x64,x65];
这里有个小细节,就是如何将字节转换为int。
97 对应的二进制表示为 0110 0001
98 -------------- 0110 0010
99 -------------- 0110 0011
100-------------- 0110 0100
在转换为int的时候,要倒序拼接,即转换结果用二进制表示为:0110 0100 0110 0011 0110 0010 0110 0001
» 摘要处理
//A、B、C、D4个int型数值,是用来保存最后的摘要结果的,下面给出的值,是它们的默认值。
void implReset() {
int A = 1732584193;
int B = -271733879;
int C = -1732584194;
int D = 271733878;
}
//进行摘要处理的4个非线性函数
private static int FF(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { var0 += (var1 & var2 | ~var1 & var3) + var4 + var6; return (var0 << var5 | var0 >>> 32 - var5) + var1; } private static int GG(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { var0 += (var1 & var3 | var2 & ~var3) + var4 + var6; return (var0 << var5 | var0 >>> 32 - var5) + var1; } private static int HH(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { var0 += (var1 ^ var2 ^ var3) + var4 + var6; return (var0 << var5 | var0 >>> 32 - var5) + var1; } private static int II(int var0, int var1, int var2, int var3, int var4, int var5, int var6) { var0 += (var2 ^ (var1 | ~var3)) + var4 + var6; return (var0 << var5 | var0 >>> 32 - var5) + var1; }
可以看到,每个函数都有7个入参,通过&、|、 << 、 >>>等位运算符,对这7个参数值进行运算。
var0 ~ var3的取值范围为 A、B、C、D;
var4 取值范围为x;
var5 取值范围为从固定的4个int中,轮流取值,并且每个函数的固定值不同;
var6 貌似是个随机数,每次处理都不一样,在源码中是直接写死的;
接下来,我们就可以看看,是如何运用这4个函数做摘要处理的。可以结合下面给出的流程图,理解源码。
void implCompress(byte[] var1, int var2) { ByteArrayAccess.b2iLittle64(var1, var2, this.x);//可以忽略,功能就是上面说的分块过程 int var3 = this.state[0]; int var4 = this.state[1]; int var5 = this.state[2]; int var6 = this.state[3]; var3 = FF(var3, var4, var5, var6, this.x[0], 7, -680876936); var6 = FF(var6, var3, var4, var5, this.x[1], 12, -389564586); var5 = FF(var5, var6, var3, var4, this.x[2], 17, 606105819); var4 = FF(var4, var5, var6, var3, this.x[3], 22, -1044525330); var3 = FF(var3, var4, var5, var6, this.x[4], 7, -176418897); var6 = FF(var6, var3, var4, var5, this.x[5], 12, 1200080426); var5 = FF(var5, var6, var3, var4, this.x[6], 17, -1473231341); var4 = FF(var4, var5, var6, var3, this.x[7], 22, -45705983); var3 = FF(var3, var4, var5, var6, this.x[8], 7, 1770035416); var6 = FF(var6, var3, var4, var5, this.x[9], 12, -1958414417); var5 = FF(var5, var6, var3, var4, this.x[10], 17, -42063); var4 = FF(var4, var5, var6, var3, this.x[11], 22, -1990404162); var3 = FF(var3, var4, var5, var6, this.x[12], 7, 1804603682); var6 = FF(var6, var3, var4, var5, this.x[13], 12, -40341101); var5 = FF(var5, var6, var3, var4, this.x[14], 17, -1502002290); var4 = FF(var4, var5, var6, var3, this.x[15], 22, 1236535329); var3 = GG(var3, var4, var5, var6, this.x[1], 5, -165796510); var6 = GG(var6, var3, var4, var5, this.x[6], 9, -1069501632); var5 = GG(var5, var6, var3, var4, this.x[11], 14, 643717713); var4 = GG(var4, var5, var6, var3, this.x[0], 20, -373897302); var3 = GG(var3, var4, var5, var6, this.x[5], 5, -701558691); var6 = GG(var6, var3, var4, var5, this.x[10], 9, 38016083); var5 = GG(var5, var6, var3, var4, this.x[15], 14, -660478335); var4 = GG(var4, var5, var6, var3, this.x[4], 20, -405537848); var3 = GG(var3, var4, var5, var6, this.x[9], 5, 568446438); var6 = GG(var6, var3, var4, var5, this.x[14], 9, -1019803690); var5 = GG(var5, var6, var3, var4, this.x[3], 14, -187363961); var4 = GG(var4, var5, var6, var3, this.x[8], 20, 1163531501); var3 = GG(var3, var4, var5, var6, this.x[13], 5, -1444681467); var6 = GG(var6, var3, var4, var5, this.x[2], 9, -51403784); var5 = GG(var5, var6, var3, var4, this.x[7], 14, 1735328473); var4 = GG(var4, var5, var6, var3, this.x[12], 20, -1926607734); var3 = HH(var3, var4, var5, var6, this.x[5], 4, -378558); var6 = HH(var6, var3, var4, var5, this.x[8], 11, -2022574463); var5 = HH(var5, var6, var3, var4, this.x[11], 16, 1839030562); var4 = HH(var4, var5, var6, var3, this.x[14], 23, -35309556); var3 = HH(var3, var4, var5, var6, this.x[1], 4, -1530992060); var6 = HH(var6, var3, var4, var5, this.x[4], 11, 1272893353); var5 = HH(var5, var6, var3, var4, this.x[7], 16, -155497632); var4 = HH(var4, var5, var6, var3, this.x[10], 23, -1094730640); var3 = HH(var3, var4, var5, var6, this.x[13], 4, 681279174); var6 = HH(var6, var3, var4, var5, this.x[0], 11, -358537222); var5 = HH(var5, var6, var3, var4, this.x[3], 16, -722521979); var4 = HH(var4, var5, var6, var3, this.x[6], 23, 76029189); var3 = HH(var3, var4, var5, var6, this.x[9], 4, -640364487); var6 = HH(var6, var3, var4, var5, this.x[12], 11, -421815835); var5 = HH(var5, var6, var3, var4, this.x[15], 16, 530742520); var4 = HH(var4, var5, var6, var3, this.x[2], 23, -995338651); var3 = II(var3, var4, var5, var6, this.x[0], 6, -198630844); var6 = II(var6, var3, var4, var5, this.x[7], 10, 1126891415); var5 = II(var5, var6, var3, var4, this.x[14], 15, -1416354905); var4 = II(var4, var5, var6, var3, this.x[5], 21, -57434055); var3 = II(var3, var4, var5, var6, this.x[12], 6, 1700485571); var6 = II(var6, var3, var4, var5, this.x[3], 10, -1894986606); var5 = II(var5, var6, var3, var4, this.x[10], 15, -1051523); var4 = II(var4, var5, var6, var3, this.x[1], 21, -2054922799); var3 = II(var3, var4, var5, var6, this.x[8], 6, 1873313359); var6 = II(var6, var3, var4, var5, this.x[15], 10, -30611744); var5 = II(var5, var6, var3, var4, this.x[6], 15, -1560198380); var4 = II(var4, var5, var6, var3, this.x[13], 21, 1309151649); var3 = II(var3, var4, var5, var6, this.x[4], 6, -145523070); var6 = II(var6, var3, var4, var5, this.x[11], 10, -1120210379); var5 = II(var5, var6, var3, var4, this.x[2], 15, 718787259); var4 = II(var4, var5, var6, var3, this.x[9], 21, -343485551); this.state[0] += var3; this.state[1] += var4; this.state[2] += var5; this.state[3] += var6;
}
我们把最后的摘要结果记为int[] An,Bn,Cn,Dn,最后将int转换为字节数组,长度为16.此时MD5加密结束。一般我们还需要将字节数组,转换为16进制的字符串。