用户密码加密编码使用 Bcrypt 代替 MD5,SHA1和SHA256

先入为主阐述一下Bcrypt编码算法两个优点

  1. 相同的原文每次编码出来的密文不相同
  2. 编码的速度很慢

是的这就是Bcrypt的优点,有经验的读者看上去会觉得奇怪,这还算得上是“优点”吗!特别是像编码的速度慢这一点。在这里为了说明这是所谓的“优点”不得不先从用户密码存储安全方面说起:
首先提及MD5编码算法大家马上就会联想到这是一种可以破解的加密算法,破解的方法主要是采用“彩虹表“(不清楚可在网上搜索),上述攻击方法最根本就是相同的原文经过MD5编码之后产生相同的密文,(简单说就是相同密码得出相同的密文,那么反过来就可以推导出原文),有经验的开发者会采用salt和多重MD5编码的方式来避免密文被破解,如果攻击者预先知道或者能够获取到足够多的信息上述两种方式也是很容易被攻破的,说到这里不喻而明Bcrypt第一个优点就是为了解决这种问题。当然MD5是一个相对过时的哈希编码算法,当前就算简单起见也应当选择SHA256等相对安全性更高的哈希编码算法。那第二点为何编码的速度很慢有什么帮助,你想想通过彩虹表攻击之前必需预先准备一套容量足够大的表碰撞表,如果能过MD5算法和Bcrypt作对比,因为Bcrypt足够慢而致依生成碰撞表付出更大量的计算量,这种困难可以使得生成碰撞表变得不现实,所以破解的难度就相应增大,正因为上述的原因所以说Bcrypt更适合作为用户密码加密编码的算法来使用,并且在使用的过程中不涉及多重编码,Salt和Pepper等问题,所以使用它的API使得代码更简洁和易懂。
以下是Bcrypt编码算法的实现,该类是引用自Spring Security模块中的
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder

/**
 * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients
 * can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a SecureRandom
 * instance. The larger the strength parameter the more work will have to be done
 * (exponentially) to hash the passwords. The default value is 10.
 *
 * @author Dave Syer
 *
 */
public class BCryptPasswordEncoder implements PasswordEncoder {
	private Pattern BCRYPT_PATTERN = Pattern
			.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
	private final Log logger = LogFactory.getLog(getClass());

	private final int strength;

	private final SecureRandom random;

	public BCryptPasswordEncoder() {
		this(-1);
	}

	/**
	 * @param strength the log rounds to use, between 4 and 31
	 */
	public BCryptPasswordEncoder(int strength) {
		this(strength, null);
	}

	/**
	 * @param strength the log rounds to use, between 4 and 31
	 * @param random the secure random instance to use
	 *
	 */
	public BCryptPasswordEncoder(int strength, SecureRandom random) {
		if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
			throw new IllegalArgumentException("Bad strength");
		}
		this.strength = strength;
		this.random = random;
	}

	public String encode(CharSequence rawPassword) {
		String salt;
		if (strength > 0) {
			if (random != null) {
				salt = BCrypt.gensalt(strength, random);
			}
			else {
				salt = BCrypt.gensalt(strength);
			}
		}
		else {
			salt = BCrypt.gensalt();
		}
		return BCrypt.hashpw(rawPassword.toString(), salt);
	}

	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		if (encodedPassword == null || encodedPassword.length() == 0) {
			logger.warn("Empty encoded password");
			return false;
		}

		if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
			logger.warn("Encoded password does not look like BCrypt");
			return false;
		}

		return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
	}
}

测试的代码如下:

	@Test
	public void testBCryptPasswordEncoder() {
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(6);
		for (int i = 0; i < 3; i++) {
			String encodedText = bCryptPasswordEncoder.encode("567123TR");
			System.out.println(String.format("第%d次编码的结果:%s", i, encodedText));
		}
	}

最终测试的结果:

0次编码的结果:$2a$06$ahAG0Hqch1kqCKueL8ecw.XFU8HHiO9k.HWuwXVd4iRiaVYd1Lko61次编码的结果:$2a$06$IRmgGohEnNrC1xnhv0MomOly0AiIfBucBaTbx7QoZyj2lyilZCf9K2次编码的结果:$2a$06$dX.dZmRHMBC8RESaolKYU.u0cJmf70LRQbpnLvmZycOKPI4t34RJ2

扩展延伸
在某些应用场景BCrypt的就不大适用,如当系统需要采用Digest,HMac和AES对称加密等消息鉴别的场景,这类场景是需要共享一个相同的密钥的时候,Bcrypt因为相同原文输出不同的密钥,所以就无法共享相同的密钥。
Bcrypt编码算法应用在区块链技术上,很多人听说过比特币挖矿的原理是找sha256算法下计算出满足特定条件的nonce值,这种计算是要经过大量的运算,所以算力大的节点得到挖矿奖励的可能性越高,这点使得区块网络被算力大的控制,所以有些币考虑到这点,用Bcrypt算法替代sha256使得矿主无法通过投入算力来控制整个区块网络(因为Bcrypt算法慢),感兴趣的读者可以找相关资料。

最后基于上述建议在用户密码加密编码中放弃MD5吧,因为它即过时又缺乏安全性。

  • 14
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

victorkevin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值