MD5算法简介

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值,结果:

MD5算法简介_第1张图片

参考:

http://zh.wikipedia.org/wiki/MD5

http://blog.csdn.net/gxy3509394/article/details/7409284




你可能感兴趣的:(MD5算法简介)