论坛首页 Java企业应用论坛

一种简单的给MD5加盐算法

浏览 31912 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (3)
作者 正文
   发表时间:2012-10-22  
mfkvfn 写道
如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。

如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。


好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。

“然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。”

你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”?
0 请登录后投票
   发表时间:2012-10-22  
wyuch 写道
mfkvfn 写道
如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。

如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。


好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。

“然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。”

你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”?


我脚的..你的加密假设在..反查库..能查出任何密码的..基础上..可以认为是md5可逆的...如果真那样...还不如自己写一个加密规则呢...多次加密其实已经能满足一般的需求了...
0 请登录后投票
   发表时间:2012-10-22  
如果非要说多次加密的缺点..那就是碰到密码是123456的bt用户...这应该是...创建密码的时候加以限制..不应该为了这点功能在数据库加个字段..你可以用用户的注册时间+用户名+id+...多了去了..没必要是吧...
0 请登录后投票
   发表时间:2012-10-22  
rox 写道
原来有个朋友介绍的一种方法,不算很保险,但也没太大问题。
同样也是UserName+PassWord再MD5后,截取固定长度。
就是不保存原始的MD5,不影响验证,也最简单。


这种情况不修改用户名还好,如果可以修改用户名怎么弄呢?
0 请登录后投票
   发表时间:2012-10-22   最后修改:2012-10-22
ansjsun 写道
wyuch 写道
mfkvfn 写道
如果“能拿到用户密码的MD5摘要”的人能拿到密码摘要形成的算法。
那么根据算法能很快就从最终的48位中得到16位随机数和32位MD5字符串(具体是1,4,7...为随机数,0,2,3,5,6,8,...为真正的32位MD5)。然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。

如果“能拿到用户密码的MD5摘要”的人拿不到密码摘要形成的算法。那么随便一种办法就可以了,只要对原始密码进行任意处理就可以了(甚至根本用不着MD5,简单的字符替换就可以了)。别人一样得不到密码。


好久没来,偶尔上来发个帖,发现好累呀,得说多少遍呀。

“然后通过32位MD5反查得到“16位随机数+真正的密码”能很快就知道密码(取16位后的部分就可以了)。”

你要么是没理解什么是加盐,要么就是不知道什么是MD5反查库。MD5反查库还能反查出“16位随机数+真正的密码”?


我脚的..你的加密假设在..反查库..能查出任何密码的..基础上..可以认为是md5可逆的...如果真那样...还不如自己写一个加密规则呢...多次加密其实已经能满足一般的需求了...


无语问苍天呀,同学,知道什么是反问吗?
0 请登录后投票
   发表时间:2012-10-22  
python实现
#!/usr/bin/env python
#coding:utf-8

import random
import hashlib

DIGEST_LEN = 16

def generate(password):
    salt = '%0*d%0*d' % (8, random.randint(0, 99999999), 8, random.randint(0, 99999999))
    password = hashlib.md5(str(password)+salt).hexdigest()
    cs = [] # size 48
    for i in range(DIGEST_LEN):
        cs.append(password[i*2])
        cs.append(salt[i])
        cs.append(password[i*2+1])

    return "".join(cs)

def verify(password, md5):
    salt = []
    md5_password = []
    for i in range(DIGEST_LEN):
        salt.append(md5[i * 3 + 1])
        md5_password.append(md5[i * 3])
        md5_password.append(md5[i * 3 + 2])

    return hashlib.md5(str(password) + "".join(salt)).hexdigest() == "".join(md5_password)

if __name__ == '__main__':
    password = generate("123456")
    print verify("123456", password) # print "True"
0 请登录后投票
   发表时间:2012-10-22  
这个对付MD5数据库可以了
0 请登录后投票
   发表时间:2012-10-22  
多此一举,你的用户表pk都能修改的话,说明你的设计很ugly;如果不可修改的话,直接拿pk和密码做md5就ok了。
0 请登录后投票
   发表时间:2012-10-23  
wyuch 写道
lonelybug 写道
作为一个加密函数,你这程序写的性能未免有点低。

首先你Salt补零的过程用循环就会是一个问题。你为什么不初始化一个用16个‘0’的字符串初始化一个StringBuffer,然后再作后面的工作就可以了。

对于后面48位密码产生也是同样的道理。

还有基本上你这种加密没什么意义,因为48位密码的混合顺序是固定的(人为的),而不是consistent的随机产生。


原来是有工具方法StringUtil.leftPad来补齐的,随手改成了不依赖于别的工具函数的写法了。

你要用StringBuffer,为什么不用StringBuilder呢?其实,你将这个函数改成StringBuffer实现还是StringBuilder实现,还是用+号,性能差异都可以忽略不计。

这也不是加密,只是在摘要中加入干扰项,增加反查的难度的。我前面已经说过了,只要干扰算法是公开的,人家都可以用一样的方式重新构造MD5库再反查。


我对你的“心宽”表达非常“敬仰”。

String salt = String.valueOf(i1) + String.valueOf(i2); 
        if (salt.length() < 16) { 
            for (int i = 0; i < 16 - salt.length(); i++) { 
                salt += "0"; 
            } 
        }

这段代码,你知道如果你用+的话会产生多少次String的拷贝么?
读读thinking in java你就不会这么心宽的人为可以忽略不计。

另外,我没明白你要说啥,如果干扰算法公开都可以重构md5库反差,那你做的是什么?如果salt公开了那还叫作salt?我实在没搞懂你的逻辑。

0 请登录后投票
   发表时间:2012-10-23  
lonelybug 写道
wyuch 写道
lonelybug 写道
作为一个加密函数,你这程序写的性能未免有点低。

首先你Salt补零的过程用循环就会是一个问题。你为什么不初始化一个用16个‘0’的字符串初始化一个StringBuffer,然后再作后面的工作就可以了。

对于后面48位密码产生也是同样的道理。

还有基本上你这种加密没什么意义,因为48位密码的混合顺序是固定的(人为的),而不是consistent的随机产生。


原来是有工具方法StringUtil.leftPad来补齐的,随手改成了不依赖于别的工具函数的写法了。

你要用StringBuffer,为什么不用StringBuilder呢?其实,你将这个函数改成StringBuffer实现还是StringBuilder实现,还是用+号,性能差异都可以忽略不计。

这也不是加密,只是在摘要中加入干扰项,增加反查的难度的。我前面已经说过了,只要干扰算法是公开的,人家都可以用一样的方式重新构造MD5库再反查。


我对你的“心宽”表达非常“敬仰”。

String salt = String.valueOf(i1) + String.valueOf(i2); 
        if (salt.length() < 16) { 
            for (int i = 0; i < 16 - salt.length(); i++) { 
                salt += "0"; 
            } 
        }

这段代码,你知道如果你用+的话会产生多少次String的拷贝么?
读读thinking in java你就不会这么心宽的人为可以忽略不计。

另外,我没明白你要说啥,如果干扰算法公开都可以重构md5库反差,那你做的是什么?如果salt公开了那还叫作salt?我实在没搞懂你的逻辑。



public static String generate1(String password) {
 		int i1 = new Random().nextInt(99999999);
 		int i2 = new Random().nextInt(99999999);
 		String salt = String.valueOf(i1) + String.valueOf(i2);
 		int len = salt.length();
 		if (len < 16) {
 			for (int i = 0; i < 16 - len; i++) {
 				salt += "0";
 			}
 		}
 		password = md5Hex(password + salt);
 		char[] cs1 = password.toCharArray();
 		char[] cs2 = salt.toCharArray();
 		char[] cs = new char[48];
 		for (int i = 0; i < 48; i += 3) {
 			cs[i] = cs1[i / 3 * 2];
 			char c = cs2[i / 3];
 			cs[i + 1] = c;
 			cs[i + 2] = cs1[i / 3 * 2 + 1];
 		}
 		return new String(cs);
 	}
 
 	public static String generate2(String password) {
 		int i1 = new Random().nextInt(99999999);
 		int i2 = new Random().nextInt(99999999);
 		String salt = String.valueOf(i1) + String.valueOf(i2);
 		int len = salt.length();
 		if (len < 16) {
 			StringBuffer sb = new StringBuffer(salt);
 			for (int i = 0; i < 16 - len; i++) {
 				sb.append("0");
 			}
 			salt = sb.toString();
 		}
 		password = md5Hex(password + salt);
 		char[] cs1 = password.toCharArray();
 		char[] cs2 = salt.toCharArray();
 		char[] cs = new char[48];
 		for (int i = 0; i < 48; i += 3) {
 			cs[i] = cs1[i / 3 * 2];
 			cs[i + 1] = cs2[i / 3];
 			cs[i + 2] = cs1[i / 3 * 2 + 1];
 		}
 		return new String(cs);
 	}
 
 	public static String generate3(String password) {
 		int i1 = new Random().nextInt(99999999);
 		int i2 = new Random().nextInt(99999999);
 		String salt = String.valueOf(i1) + String.valueOf(i2);
 		int len = salt.length();
 		if (len < 16) {
 			StringBuilder sb = new StringBuilder(salt);
 			for (int i = 0; i < 16 - len; i++) {
 				sb.append("0");
 			}
 			salt = sb.toString();
 		}
 		password = md5Hex(password + salt);
 		char[] cs1 = password.toCharArray();
 		char[] cs2 = salt.toCharArray();
 		char[] cs = new char[48];
 		for (int i = 0; i < 48; i += 3) {
 			cs[i] = cs1[i / 3 * 2];
 			cs[i + 1] = cs2[i / 3];
 			cs[i + 2] = cs1[i / 3 * 2 + 1];
 		}
 		return new String(cs);
 	}
 
 	/**
 	 * 获取十六进制字符串形式的MD5摘要
 	 */
 	public static String md5Hex(String src) {
 		try {
 			MessageDigest md5 = MessageDigest.getInstance("MD5");
 			byte[] bs = md5.digest(src.getBytes());
 			return new String(new Hex().encode(bs));
 		} catch (Exception e) {
 			return null;
 		}
 	}
 
 	public static void main(String[] args) {
 		long t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			generate1("admin");
 		}
 		System.out.println("使用加号:\t" + (System.currentTimeMillis() - t));
 
 		t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			generate2("admin");
 		}
 		System.out.println("使用StringBuilder:\t" + (System.currentTimeMillis() - t));
 
 		t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			generate3("admin");
 		}
 		System.out.println("使用StringBuffer:\t" + (System.currentTimeMillis() - t));
 	}


关于加号:
说了随手改的,不是每个人都有洁癖见不得加号,而且你有洁癖也拜托实事求是公允一点嘛。最受不了的是,你还不仔细读人家的帖子就开始批评了,说半天都白说了。关于加号性能,我说可以忽略不计是有根据的,就比如上面这个程序里。我实际测试了三次,得分都相差无几。如下所示:

第一次:
使用加号: 11203
StringBuilder: 11422
StringBuffer: 11406

第二次:
使用加号: 11203
StringBuilder: 11391
StringBuffer: 11453

第三次
使用加号: 11203
StringBuilder: 11312
StringBuffer: 11485

原因何在?一是因为主要是运算是MD5,字符串运算所占比较非常小。二是因为加号不是每次都执行(90%的情况下随机出来的结果都已经是2个8位数了),而StringBuffer每次都new了实例,导致自身的性能优势被抵消甚至处于劣势了。额,细心的同学肯定发现了,我太“宽心”地让new StringBuffer每次都执行,可以改造成根据需要才new嘛,好吧,那样StringBuffer会相对于加号取得2%左右的性能优势。

那么对于大量的字符串连接,StringBuffer到底有多大优势呢,我也写了个测试例子如下:
	public static void main(String[] args) {
 		main1();
 		main2();
 		main3();
 	}
 
 	public static void main1() {
 		long t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			String str = "";
 			for (int j = 0; j < 16; j++) {
 				str += "0";
 			}
 		}
 		System.out.println("使用加号:\t" + (System.currentTimeMillis() - t));
 	}
 
 	public static void main2() {
 		long t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			StringBuilder sb = new StringBuilder(16);
 			for (int j = 0; j < 16; j++) {
 				sb.append("0");
 			}
 		}
 		System.out.println("StringBuilder:\t" + (System.currentTimeMillis() - t));
 	}
 
 	public static void main3() {
 		long t = System.currentTimeMillis();
 		for (int i = 0; i < 1000000; i++) {
 			StringBuffer sb = new StringBuffer(16);
 			for (int j = 0; j < 16; j++) {
 				sb.append("0");
 			}
 			sb.toString();
 		}
 		System.out.println("StringBuffer:\t" + (System.currentTimeMillis() - t));
 	}


第一次:
使用加号: 3484
StringBuilder: 578
StringBuffer: 1422

第二次:
使用加号: 3532
StringBuilder: 562
StringBuffer: 1406

第三次:
使用加号: 3578
StringBuilder: 562
StringBuffer: 1407

根据测试结果,每个方法每次测试都执行了100万*16=1600万次的字符串连接操作,最慢的加号费时不过3.5秒,StringBuffer要快一些,只需要1.5秒,StringBuilder性能最好,只需要不到0.6秒。

总结陈词如下:
1、lonelybug同学说的对,对于大量的字符串连接操作StringBuffer相对加号有性能优势。
2、不过也得分场合,对于少量的字符串连接,您也用不着担心,确实是可以忽略不计的,说不定您不小心没把new语句放到if时面时StringBuffer反而处于劣势了。
3、一个应用每天就算有一千万次登录,PasswordUtil里面的加号运算所费CPU时间全部加一起都不超过1秒。
4、对于局部变量,StringBuffer已经不推荐使用了,JDK1.5以后建议使用StringBuilder,因为StringBuilder不需要同步,所以相对需要同步的StringBuffer有性能优势。lonelybug用不着把StringBuffer当个宝了。
5、建议lonelybug同学您先动手再发言,不要看了几本书,就想着四处找人毛病,因为别人用了个加句就犯了你什么大忌似的,寻章摘句老雕虫不好玩。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics