MD5:Message Digest Algorithm 5
信息摘要算法 第五版
由MD2、MD3、MD4发展而来。
任意长度的字符串或字节流,经过MD5的编码后,生成一个128bit的二进制串。
通常表示为32个十六进制数字连成的字符串。
例:
MD5("")=d41d8cd98f00b204e9800998ecf8427e
MD5("hello world!")=fc3ff98e8c6ad3087d515c0473f8677
由【任意长度 -> 128bit】可以看出,MD5算法是不可逆的,否则,这将会是极其极其极其.......高效的压缩算法。
通常应用:
- 加密密码。
- 数据校验。例:下载文件时保证文件的完整性。
这里不仔细讨论MD5算法的详情了,仅给出维基百科上给出的MD5伪代码的Java实现。
public class MD5 {
// r specifies the per-round shift amounts
private static int[] r = { 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 static long[] k = new long[64];
static {
// Use binary integer part of the sines of integers as constants:
for (int i = 0; i < 64; i++) {
k[i] = (long) Math.floor(Math.abs(Math.sin(i + 1))
* Math.pow(2, 32));
}
}
// Initialize variables:
private static long h0 = 0x67452301;
private static long h1 = 0xEFCDAB89;
private static long h2 = 0x98BADCFE;
private static long h3 = 0x10325476;
public static String md5(String str) {
// Pre-processing:
// append "1" bit to message
// append "0" bits until message length in bits ≡ 448 (mod 512)
// append bit length of message as 64-bit little-endian integer to message
byte[] bs = str.getBytes();
int len = bs.length;
int n = ((len * 8 + 1) / 512);
int m = ((len * 8 + 1) % 512);
if (m > 448) {
n += 2;
} else {
n += 1;
}
int nBytes = n * 512 / 8;
byte[] data = new byte[nBytes];
System.arraycopy(bs, 0, data, 0, len);
// 字节起始处加个二进制1,相当于把字节设置为0x80
data[len] = (byte) 0x80;
// len + 1 ~ nBytes - 9 : 用二进制0填充
for (int i = len + 1; i < nBytes - 8; i++) {
data[i] = 0;
}
// nBytes - 8 ~ nBytes - 5 : little endian的二进制位数
for (int i = 4;i>0;i--) {
data[nBytes - 4 - i] = (byte) (len * 8 >> (8 * (4 - i)) & 0xFF);;
}
// nBytes - 4 ~ nBytes - 1 : 因为Java中数组长度只能为int,所以后四个字节填充为0
for (int i = nBytes - 4; i < nBytes; i++) {
data[i] = 0;
}
// Process the message in successive 512-bit chunks:
long[] W = new long[16];
for (int i = 0; i < n; i++) {
for (int j = 0; j < 64; j += 4) {
// W[ j ] = transform( s.substr( 64 * k + 4 * i, 4 ) );
W[j / 4] = (ub(data[i * 512 / 8 + j+3]) << 24)
| (ub(data[i * 512 / 8 + j + 2]) << 16)
| (ub(data[i * 512 / 8 + j + 1]) << 8)
| (ub(data[i * 512 / 8 + j + 0])) ;
}
long a = h0;
long b = h1;
long c = h2;
long d = h3;
long f, g, tmp;
for (int j = 0; j < 64; j++) {
if (j >= 0 && j <= 15) {
f = (b & c) | ((~b) & d);
g = j;
} else if (j >= 16 && j <= 31) {
f = (d & b) | ((~d) & c);
g = (5 * j + 1) % 16;
} else if (j >= 32 && j <= 47) {
f = b ^ c ^ d;
g = (3 * j + 5) % 16;
} else {
f = c ^ (b | (~d));
g = (7 * j) % 16;
}
tmp = d;
d = c;
c = b;
b = ui(ROTATE_LEFT(
ui(a + f + k[j] + W[(int) g]) ,
r[j]) + b) ;
a = tmp;
}
h0 = ui(h0 + a);
h1 = ui(h1 + b);
h2 = ui(h2 + c);
h3 = ui(h3 + d);
}
return ui2HexString(h0) + ui2HexString(h1)
+ ui2HexString(h2) + ui2HexString(h3);
}
/**
* rotate unsigned int
*/
private static long ROTATE_LEFT(long x, long n) {
return ui((ui(x << n)) | (ui(x >> (32 - n))));
}
/**
* long -> unsigned int
*/
private static long ui(long i) {
return (i & 0x00000000FFFFFFFFL);
}
/**
* return unsigned b
*/
private static long ub(byte b) {
return b < 0 ? 256+b : b;
}
/**
* unsigned int -> little endian -> hex string
*/
private static String ui2HexString(long h) {
h = ui(h);
return Long.toHexString(h & 0xFFL) + Long.toHexString(h >> 8 & 0xFFL) + Long.toHexString(h >> 16 & 0xFFL) + Long.toHexString(h >> 24 & 0xFFL);
}
public static void main(String[] args) {
String str = "";
String md5 = MD5.md5(str);
System.out.println(": " + md5);
// String str = "hello world!";
// String md5 = MD5.md5(str);
// System.out.println(": " + md5);
}
}
注:此代码未经严密测试。
简单测试用例:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import junit.framework.TestCase;
import org.junit.Test;
public class MD5Test extends TestCase {
@Test
public void testMD5() {
for (int i = 0; i < 100000; i++) {
String input = getRandomString(i);
String apiResult = md5JdkAPI(input);
String myResult = MD5.md5(input);
assert apiResult.equals(myResult);
}
}
/** 产生一个随机的字符串 */
public static String getRandomString(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < length; i++) {
int num = random.nextInt(62);
buf.append(str.charAt(num));
}
return buf.toString();
}
/**
* 使用JDK API生成md5
*/
private static String md5JdkAPI(String input) {
byte[] source = input.getBytes();
String s = null;
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };// 用来将字节转换成16进制表示的字符
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(source);
byte tmp[] = md.digest();// MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2];// 每个字节用 16 进制表示的话,使用两个字符, 所以表示成 16
// 进制需要 32 个字符
int k = 0;// 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) {// 从第一个字节开始,对 MD5 的每一个字节// 转换成 16
// 进制字符的转换
byte byte0 = tmp[i];// 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf];// 取字节中高 4 位的数字转换,// >>>
// 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf];// 取字节中低 4 位的数字转换
}
s = new String(str);// 换后的结果转换为字符串
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return s;
}
}
测试用例中给出的MD5方法是JDK中提供的。
测试了长度0~100000的随机字符串的MD5值,结果:
参考:
http://zh.wikipedia.org/wiki/MD5
http://blog.youkuaiyun.com/gxy3509394/article/details/7409284