MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的信息变换产生一个128位的消息摘要。
原理
1、数据填充
对信息进行数据填充,使信息的长度对512取模得448,设信息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。填充方法是在信息后面填充第一位为1,其余为0。填充完后,信息的长度就为N*512+448(bit)。
2、填充信息长度
原信息长度(二进制位数)用64位表示。如果信息长度大于264,则只使用其低64位的值,即(信息长度对264取模),并且填充到前面一步得到的结果后面。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍数。这样做的原因是为满足后面处理中对信息长度的要求。
3、数据处理
准备需要用到的数据:
4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
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));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。
用途
-
防止被篡改
1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5. -
防止直接看到明文
现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。) -
防止抵赖(数字签名)
这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。
使用MD5加密
32位
public final static String encrypt32(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的MessageDigest对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.digest();
// 把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
16位
直接取32位加密之后中间的16位。
public final static String encrypt16(String s) {
return encrypt32(s).substring(8, 24);
}
8位
将得到的16位md5字符串进行高8位和低8位异或。
public final static String encrypt8(String s) {
String target = encrypt16(s);
StringBuilder result = new StringBuilder(8);
for (int i = 0; i < 8; i++) {
int high = Integer.parseInt(Character.toString(target.charAt(i)), 16);
int low = Integer.parseInt(Character.toString(target.charAt(i + 8)), 16);
int xor = high ^ low & 0xf;
result.append(Integer.toHexString(xor));
}
return result.toString();
}
加密文件
对文件进行加密得到加密字符串。
public final static String encryptFile(File file) {
if (file == null) return null;
FileInputStream fis = null;
DigestInputStream dis;
try {
fis = new FileInputStream(file);
MessageDigest md = MessageDigest.getInstance("MD5");
dis = new DigestInputStream(fis, md);
byte[] buffer = new byte[256 * 1024];
while (dis.read(buffer) > 0) ;
md = dis.getMessageDigest();
return bytes2HexString(md.digest());
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
return null;
} finally {
fis.close;
}
}
private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static String bytes2HexString(byte[] bytes) {
if (bytes == null) return null;
int len = bytes.length;
if (len <= 0) return null;
char[] ret = new char[len << 1];
for (int i = 0, j = 0; i < len; i++) {
ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];
ret[j++] = hexDigits[bytes[i] & 0x0f];
}
return new String(ret);
}
MD5加密算法改进
在使用MD5加密算法对明文加密之后,对密文进行改变,在密文中截取一段数据并丢弃,然后使用随机数填充被丢弃的数据,且整个过程不改变MD5加密后的位数。加密过程如下:
- 对明文进行MD5加密,获得密文
- 使用截取函数截取加密后的密文,从第K个位置开始截取N位数值,得到密码A,其中A=left(md5(明文), K-1)
- 使用截取函数截取加密后的明文的N位数后的值B,其中 B=right(md5(明文), MD5加密后的位数 - (K + N - 1))
- 使用随机函数gen_key(N)填充被截取的N个值
- 变换后的密码值为A & get_key(N) & B
解密过程:先对输入的明文进行加密,接着从K处截取前半部分得到A′,后半部分得到B′,然后从数据库中读出密码中的A和B部分,最后如果A=A′并且B=B′,则认为用户输入的密码跟数据库中的密码是匹配的。
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!