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.csdn.net/gxy3509394/article/details/7409284