论坛首页 Java企业应用论坛

一种简单的给MD5加盐算法

浏览 31738 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (3)
作者 正文
   发表时间:2012-10-23  
lonelybug 写道

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


拜托仔细读一读我的帖子吧,求您了。RSA算法听着您的这种逻辑都得哭了。
0 请登录后投票
   发表时间:2012-10-23  
lonelybug 写道

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

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 generate(String password) {  
        int i1 = new Random().nextInt(99999999);  
        int i2 = new Random().nextInt(99999999);  
        String salt = String.valueOf(i1) + String.valueOf(i2);  
        if (salt.length() < 16) {  
            int len = salt.length();  
            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];  
            cs[i + 1] = cs2[i / 3];  
            cs[i + 2] = cs1[i / 3 * 2 + 1];  
        }  
        return new String(cs);  
    }  


1 为什么随机2个8位数字然后相加,然后前面补0?直接生成 1*~9*(其中*表示15位数字)的不就行了。
2 password先toCharArray再用下标读。为什么不直接用String.charAt(int index)方法?
0 请登录后投票
   发表时间:2012-10-24   最后修改:2012-10-24
wyuch 写道
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同学您先动手再发言,不要看了几本书,就想着四处找人毛病,因为别人用了个加句就犯了你什么大忌似的,寻章摘句老雕虫不好玩。




你做的测试叫做100万次,不叫做1600万次,因为,每次登录时调用的是整个函数,不是只是单独的一次补零操作。

为了更好的测试你的算法是否真的有效率,我觉得除了时间复杂度方面,我们还需要在空间复杂度上面测试一下。

下面的截图是我在我的i7 3500k 8G内存 heap开1G的情况下跑了你的算法1亿次--我相信很多网站每天的登陆次数远远超过1000万次,比如renren,face*book,weibo,twitter等,当然,作为一个基础工具类的加密算法,我相信很多其他方面也会应用到,比如产生ID或者session key(Open API应用场景),所以,我假设1亿次。

第一个图是用StringBuilder,第二图是+号方法。

很简单可以看出,为了一个加密功能Heap基本要用掉300MB-400MB,而StringBuilder稳定在50MB一下,我相信很多网站对于你的算法所消耗的Heap是不太能承受的。

  • 大小: 151.8 KB
  • 大小: 158.7 KB
0 请登录后投票
   发表时间:2012-10-24  
wyuch 写道
lonelybug 写道

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


拜托仔细读一读我的帖子吧,求您了。RSA算法听着您的这种逻辑都得哭了。


可惜你做的不是RSA算法,公钥型加密算法和你这个加Salt的MD5有着本质的区别。我不需要再读一遍你的帖子。
我觉得你倒是可以从新看看RSA的介绍文章。
0 请登录后投票
   发表时间:2012-10-24  
另外看着LZ带有很强烈的感情色彩的渲染回复贴,我觉得LZ其实适合一些艺术方面的创作,因为软件设计更需要的是客观和理智的分析。

哪怕是一点点地性能损失或者提升,对于很多系统来说都随着时间的延续带来一些难以预测影响。

PS:我依然觉得LZ强大的内心可以把很多有所谓变得无所谓让人深深的敬佩。

0 请登录后投票
   发表时间:2012-10-24  
lonelybug 写道
wyuch 写道
lonelybug 写道

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


拜托仔细读一读我的帖子吧,求您了。RSA算法听着您的这种逻辑都得哭了。


可惜你做的不是RSA算法,公钥型加密算法和你这个加Salt的MD5有着本质的区别。我不需要再读一遍你的帖子。
我觉得你倒是可以从新看看RSA的介绍文章。


比喻知道吧?你不需要再读一遍,因为你不需要读就可以根据你的强大内心发表评论。也不知道你的“如果salt公开了那还叫作salt”是从哪本书上看到的。
0 请登录后投票
   发表时间:2012-10-24  
lonelybug 写道
另外看着LZ带有很强烈的感情色彩的渲染回复贴,我觉得LZ其实适合一些艺术方面的创作,因为软件设计更需要的是客观和理智的分析。

哪怕是一点点地性能损失或者提升,对于很多系统来说都随着时间的延续带来一些难以预测影响。

PS:我依然觉得LZ强大的内心可以把很多有所谓变得无所谓让人深深的敬佩。



“因为软件设计更需要的是客观和理智的分析”,非常赞同,所以你可以多耐心一点读懂别人帖子再发言。
0 请登录后投票
   发表时间:2012-10-24   最后修改:2012-10-24
lonelybug 写道

你做的测试叫做100万次,不叫做1600万次,因为,每次登录时调用的是整个函数,不是只是单独的一次补零操作。

为了更好的测试你的算法是否真的有效率,我觉得除了时间复杂度方面,我们还需要在空间复杂度上面测试一下。

下面的截图是我在我的i7 3500k 8G内存 heap开1G的情况下跑了你的算法1亿次--我相信很多网站每天的登陆次数远远超过1000万次,比如renren,face*book,weibo,twitter等,当然,作为一个基础工具类的加密算法,我相信很多其他方面也会应用到,比如产生ID或者session key(Open API应用场景),所以,我假设1亿次。

第一个图是用StringBuilder,第二图是+号方法。

很简单可以看出,为了一个加密功能Heap基本要用掉300MB-400MB,而StringBuilder稳定在50MB一下,我相信很多网站对于你的算法所消耗的Heap是不太能承受的。



额,知道换成StringBuilder了。

“你做的测试叫做100万次,不叫做1600万次”。我是说1600万次字符串连接呀,你老是看不清楚。

算法中没有任何地方会请求超过10K的内存,所以我觉得你可以分析一下这样一段简单代码为什么会使用300多M的内存。我的看法:这是因为你的heap相对于这个算法开得很高,GC可以较长一段时间不需要收集,导致看起来算法占用的内存很高,实际上算法运行需要的内存撑死不过几M。加号和StringBuilder这种空间方面的表象实际上依然是一个加号多创建了对象的问题,在考虑内存回收的附加因素后,依然可以归结为时间复杂度。

一个比较好的测试方法时加一个JVM参数-Xmx10m,让两种方法都在10M heap里运行1亿次。我的测试结果(10万次)如下:

使用加号: 11093
使用StringBuilder: 10891
使用StringBuffer: 11000

是不是可以忽略不计?“我相信很多网站对于你的算法所消耗的Heap是不太能承受的。”这很显然是一个想当然的判断。

你关于我的性格的结论是不是有点下得太早了?欢迎你继续发言。
0 请登录后投票
   发表时间:2012-10-24  
lonelybug 写道


使用加号: 11093
使用StringBuilder: 10891
使用StringBuffer: 11000

是不是可以忽略不计?“我相信很多网站对于你的算法所消耗的Heap是不太能承受的。”这很显然是一个想当然的判断。

你关于我的性格的结论是不是有点下得太早了?欢迎你继续发言。



我是在看不下去了...你怎么测试的呢..我这里用你这段代码
public class TestString {
	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));
	}

}



100万结果:
使用加号: 1266
StringBuilder: 221
StringBuffer: 600

1000万结果:
使用加号: 18662
StringBuilder: 2534
StringBuffer: 3597


16个字符相加..如果将来不满足改成32个字符...效率..算了 我再试试(以上测试都是在-Xmx10m情况下)

当前JVM占用的内存总数6
使用加号: 38734
StringBuilder: 5215
StringBuffer: 6444
当前JVM占用的内存总数6


cpu占用率很高...

+的情况下..会创建16个字符串+一个空字符,并且16个字符串是递增大小的.就光创建对象..是StringBuilde的16倍.别说.虚拟机把字符串放到常量池这种.高深的问题了..程序的优化..不应当指望虚拟机来做.当内存小的时候.然后频繁gc..这么明显一个问题..明明错了..你就改了就可以了.有必要.争论这个么...还有..大家也没时间深入研究你的内心思维..理解难免有偏差..还有StringBuilder 和StringBuffer ..你没必要这么诋毁人家.就算StringBuffer线程安全.效率不高.最坏情况也和+差不多的..




0 请登录后投票
   发表时间:2012-10-24  
wyuch 写道
lonelybug 写道


使用加号: 11093
使用StringBuilder: 10891
使用StringBuffer: 11000

是不是可以忽略不计?“我相信很多网站对于你的算法所消耗的Heap是不太能承受的。”这很显然是一个想当然的判断。

你关于我的性格的结论是不是有点下得太早了?欢迎你继续发言。



我是在看不下去了...你怎么测试的呢..我这里用你这段代码
public class TestString {
	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));
	}

}



100万结果:
使用加号: 1266
StringBuilder: 221
StringBuffer: 600

1000万结果:
使用加号: 18662
StringBuilder: 2534
StringBuffer: 3597


16个字符相加..如果将来不满足改成32个字符...效率..算了 我再试试(以上测试都是在-Xmx10m情况下)

当前JVM占用的内存总数6
使用加号: 38734
StringBuilder: 5215
StringBuffer: 6444
当前JVM占用的内存总数6


cpu占用率很高...

+的情况下..会创建16个字符串+一个空字符,并且16个字符串是递增大小的.就光创建对象..是StringBuilde的16倍.别说.虚拟机把字符串放到常量池这种.高深的问题了..程序的优化..不应当指望虚拟机来做.当内存小的时候.然后频繁gc..这么明显一个问题..明明错了..你就改了就可以了.有必要.争论这个么...还有..大家也没时间深入研究你的内心思维..理解难免有偏差..还有StringBuilder 和StringBuffer ..你没必要这么诋毁人家.就算StringBuffer线程安全.效率不高.最坏情况也和+差不多的..






。。。。。。。

他们是在讨论PasswordUtil执行多次的效率问题,和你的不是一个吧,你用错了代码吧。

我觉得既然是评论,就得把人家的意思看懂,不然大家讨论起来风马牛不相及,边上看的人也累。
0 请登录后投票
论坛首页 Java企业应用版

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