安全加密算法Bcrypt,不再担心数据泄露消息摘要),而加密(Encrypt)是将目标文本转换为不同长度的可逆密文。哈希算法通常旨在生成相同长度的文本,而加密算法生成的文本长度与明文本身的长度相关。哈希算法是不可逆的,而加密算法是可逆的。HASH算法是一种消息摘要算法,不是加密算法,但由于其单向运算,具有一定的不可逆性,成为加密算法的一个组成部分。JDK的String的哈希算法。代码如下:publicinthashCode(){inth=hash;如果(h==0&&value.length>0){charval[]=value;for(inti=0;i>>20)^(h>>>12);returnh^(h>>>7)^(h>>>4);}可以发现,虽然算法不同,但是经过这些移位操作后,对同一个值使用同一个算法计算出来的hash值一定与...相同那么,为什么哈希是不可逆的呢?如果有3和4两个密码,我的加密算法就是简单的3+4,结果是7,但是我不能确定这两个密码是3和4到7,有很多组合,这是最简单的不可逆,所以你只能通过蛮力一个一个地尝试。原文中的部分信息在计算过程中丢失了。一个MD5理论上可以对应多个原文,因为MD5有有限个原文和无限个原文。为什么不可逆的MD5不安全?因为hash算法是固定的,同一个字符串计算出来的hash字符串是固定的,所以可以用下面的方法破解。暴力枚举法:简单粗略地枚举所有原文,计算它们的哈希值,看哪个哈希值与给定的信息摘要一致。字典法:黑客使用一个巨大的字典来存储尽可能多的原文和对应的哈希值。每次用给定的信息摘要查字典,都能很快找到碰撞的结果。彩虹表(rainbow)法:在字典法的基础上改进,以时间换空间。这是现在破解哈希的常用方法。对于单机来说,暴力枚举法的时间成本非常高(以14个字母和数字的组合密码为例,有1.24×10^25种可能,即使计算机能执行10亿次每秒运算次数,需要4亿年才能破解),而且字典法的空间开销非常大(还是以14个字母和数字的组合密码为例,32位哈希的对照表生成的密码字符串将占用5.7×10^14TB的存储空间)。但是,利用分布式计算和分布式存储,仍然可以有效破解MD5算法。因此,这两种方法也被黑客广泛使用。彩虹表开裂如何防御?虽然彩虹表有着如此惊人的破解效率,但是网站的安全人员还是有办法防御彩虹表的。最有效的方法是“加盐”,即在密码的特定位置插入特定的字符串。这个特定的字符串是“Salt(盐)”。加盐前的哈希串完全不同,黑客用彩虹表得到的密码根本就不是真正的密码。即使黑客知道“盐”的内容和加盐的位置,仍然需要修改H函数和R函数,还需要重新生成彩虹表,所以加盐会大大增加使用难度彩虹桌攻击。对于一个网站来说,如果加密算法和salt泄露,针对性的攻击还是很不安全的。因为用同样的加密算法,同样的盐加密后的字符串还是一样的!难度更高的加密算法BcryptBCrypt是由NielsProvos和DavidMazières设计的密码哈希函数。他基于Blowfish密码,于1999年在USENIX上被提出。bcrypt除了加盐抵抗彩虹表攻击外,还有一个很重要的特点就是适应性,可以保证加密速度在特定的范围内,即使计算机的计算能力很高,也可以提高增加迭代次数,使加密速度变慢,从而抵抗暴力搜索攻击。Bcrypt可以简单理解为内部实现了随机加盐处理。使用Bcrypt,每次加密后的密文都是不同的。对于一个密码,Bcrypt每次生成的hash都不一样,那么它是如何验证的呢?虽然对于同一个密码,每次生成的hash都不一样,但是hash中包含了salt(hash生成过程:先随机生成salt,salt与密码进行hash);在接下来的验证中,从hash中取出salt,将salt与密码进行Hash;将结果与存储在数据库中的哈希进行比较。Bcrypt加密算法内置在SpringSecurity中,构建起来也非常简单。代码如下:@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}生成的加密字符串格式如下:$2b$[cost]$[22charactersalt][31characterhash]例如:$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy\__/\/\__________________/\_____________________________/AlgCostSaltHash在上面的例子中,$2a$表示哈希算法的唯一符号。Bcrypt算法在这里表示。10代表成本系数,这里是2的10次方,也就是1024发。N9qo8uLOickgx2ZMRZoMye是对16字节(128bits)的盐进行base64编码得到的22长字符。最后的IjZAgcfl7p92ldGxad68LJZdL17lhWy是一个24字节(192bits)的散列,是通过bash64编码得到的一个31长度的字符。PasswordEncoder接口这个接口是SpringSecurity内置的,如下:publicinterfacePasswordEncoder{Stringencode(CharSequencerawPassword);布尔匹配(CharSequencerawPassword,StringencodedPassword);默认布尔升级编码(字符串编码密码){返回假;}}接口一共有三个方法:encode方法接受的参数是原始密码字符串,返回值是加密后的哈希值,不可逆向解密。该方法通常在向系统添加用户,或者用户注册时使用。matches方法用于检查用户输入的密码rawPassword是否与加密后的哈希值encodedPassword匹配。如果匹配则返回true,说明用户输入的密码rawPassword正确,否则返回fasle。也就是说虽然hash值无法逆向解密,但是可以判断是否与原密码匹配。该方法通常在用户登录时检查用户输入的密码是否正确。upgradeEncoding的作用是判断当前密码是否需要升级。即是否需要重新加密?如果需要则返回true,如果不需要则返回fas。默认实现是返回false。例如,我们可以使用如下示例代码,在用户注册时对用户密码进行加密存储//将User保存到数据库表中,其中包含密码列user.setPassword(passwordEncoder.encode(user.getPassword()));BCryptPasswordEncoder是SpringSecurity推荐的PasswordEncoder接口实现类publicclassPasswordEncoderTest{@TestvoidbCryptPasswordTest(){StringrawPassword="123456";//原始密码StringencodedPassword=passwordEncoder.encode(rawPassword);//加密后的密码System.out.println("rawpassword"+rawPassword);System.out.println("加密哈希密码:"+encodedPassword);System.out.println(rawPassword+"是否匹配"+encodedPassword+":"//密码验证:true+passwordEncoder.matches(rawPassword,encodedPassword));System.out.println("654321是否匹配"+encodedPassword+":"//定义一个错误的验证密码:false+passwordEncoder.matches("654321",encodedPassword));}}上述测试用例执行结果如下。(Note:Forthesameoriginalpassword,thehashpasswordaftereachencryptionisdifferent.ThisisthepowerofBCryptPasswordEncoder.Notonlycanitnotbecracked,butyoucan'tfindaneedleinahaystackthroughthecommonpasswordcomparisontable.),输出如下:原始密码123456加密之后的hash密码:$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm123456是否匹配$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm:true654321是否匹配$2a$10$zt6dUMTjNSyzINTGyiAgluna3mPm7qdgl26vj4tFpsFO6WlK5lXNm:falseBCrypt产生随机盐(盐的作用就是每次做出来的菜tastedifferent).Thisisimportantbecauseitmeanseachencodewillproduceadifferentresult.