散列是信息的提炼,一般其长度要比信息小得多,且为一个固定长度。加密性强的散列必定是不可逆的,这就意味着经过散列结果,没法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将致使散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具备相同散列结果的两条信息。具备这些特性的散列结果就能够用于验证信息是否被修改。html
单向散列函数通常用于产生消息摘要,密钥加密等,常见的有:java
MD5(Message Digest Algorithm 5):算法
MD5的全称是Message-Digest Algorithm 5(信息-摘要算法)。这种算法较为古老,因为MD5的弱点被不断发现以及计算机能力不断的提高,经过碰撞的方法有可能构造两个具备相同MD5的信息,使MD5算法在目前的安全环境下有一点落伍。数据库 |
SHA(Secure Hash Algorithm):apache
安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不一样,它们对应到不一样字符串的机率很高。SHA家族的五个算法,分别是SHA-一、SHA-22四、SHA-25六、SHA-384,和SHA-512,后四者有时并称为SHA-2。SHA-1在许多安全协议中广为使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5(更早以前被广为使用的散列函数)的后继者。但SHA-1的安全性现在被密码学家严重质疑。虽然至今还没有出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然类似;所以有些人开始发展其余替代的散列算法。 |
散列算法在信息安全方面的应用主要体如今如下的3个方面: 文件校验、数字签名和鉴权协议。api
虽然散列算法并非加密算法,因为拥有不可逆的特性也经常使用于密码的加密保存。安全
相同的明文用一样的加密方法(如MD5)进行加密会获得相同的密文。oracle
如用MD5的方式加密“123456”,你总会获得密文“E10ADC3949BA59ABBE56E057F20F883E”。less
那么,当数据库信息泄漏时,若是你的密码设置的比较简单,对方是很容易猜到你的密码,或者经过彩虹表来破解你的密码。dom
所以,你须要在明文中添加干扰项-盐(Salt)。
对于只加密,但不解密的算法,如MD5,SHA1。咱们须要把盐和密文都存在数据库中,用户输入密码时,咱们把用户密码和盐组成新的明文,进行加密,而后获得密文,最后对比该密文是否与库中密文匹配。
在生成盐值时咱们要注意一下几点来提升破解难度:
一、不要使用过短的盐值
若是盐值过短,攻击者能够构造一个查询表包含全部可能的盐值。
二、不要使用重复的盐值
每次哈希加密都使用相同的盐值是很容易犯的一个错误,这个盐值要么被硬编码到程序里,要么只在第一次使用时随机得到。这样加盐的方式是作无用功,由于两个相同的密码依然会获得相同的哈希值。攻击者仍然可使用反向查表法对每一个值进行字典攻击,只须要把盐值应用到每一个猜想的密码上再进行哈希便可。
对于每一个用户的每一个密码,盐值都应该是独一无二的。每当有新用户注册或者修改密码,都应该使用新的盐值进行加密。而且这个盐值也应该足够长,使得有足够多的盐值以供加密。一个好的标准的是:盐值至少和哈希函数的输出同样长;盐值应该被储存和密码哈希一块儿储存在帐户数据表中。
三、不要将盐值简单的添加在先后
因为每每盐与密文都是同时存储在数据库中的,因此不少状况都是盐与秘文同时泄露。因此加盐时最好不要过于简单的将盐直接加在明文先后。而是使用打散或屡次插入的方式混入明文提升算法破解难度。
四、盐值应该使用基于加密的伪随机数生成器(Cryptographically Secure Pseudo-Random Number Generator – CSPRNG)来生成。
CSPRNG和普通的随机数生成器有很大不一样。物如其名,CSPRNG专门被设计成用于加密,它能提供高度随机和没法预测的随机数。咱们显然不但愿本身的盐值被猜想到,因此必定要使用CSPRNG。Java的java.security.SecureRandom类能够用来生成随机数。
MessageDigest是java.security提供的一个散列算法类。
java se 8中支持如下类型的散列算法:
Algorithm Name | Description |
---|---|
MD2 | The MD2 message digest algorithm as defined in RFC 1319. |
MD5 | The MD5 message digest algorithm as defined in RFC 1321. |
SHA-1 SHA-224 SHA-256 SHA-384 SHA-512 |
Hash algorithms defined in the FIPS PUB 180-4. Secure hash algorithms - SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 - for computing a condensed representation of electronic data (message). When a message of any length less than 2^64 bits (for SHA-1, SHA-224, and SHA-256) or less than 2^128 (for SHA-384 and SHA-512) is input to a hash algorithm, the result is an output called a message digest. A message digest ranges in length from 160 to 512 bits, depending on the algorithm. |
API:https://docs.oracle.com/javase/8/docs/api/index.html
import org.apache.commons.codec.binary.Hex; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * 散列算法(MessageDigest实现) */ public class HashAlgorithm { public static void main(String[] args){ HashAlgorithm hashAlgorithm = new HashAlgorithm(); //生成盐值 String salt = hashAlgorithm.getSalt(); System.out.println("盐值:" + salt); //使用明文生成散列值 String message = "ceshimima"; String hash = hashAlgorithm.getHash(message,salt,"SHA-512"); System.out.println("散列值:" + hash); //验证实文与散列是否匹配 boolean b = hashAlgorithm.verification(message,salt,"SHA-512",hash); if(b){ System.out.println("明文与散列匹配成功"); }else{ System.out.println("明文与散列匹配失败"); } } /** * 生成盐值 * @return */ public String getSalt(){ //使用SecureRandom类生成伪随机数做为盐值 SecureRandom random = new SecureRandom(); byte bytes[] = new byte[20]; random.nextBytes(bytes); String salt = Hex.encodeHexString(bytes); return salt; } /** * 给明文加盐 * @param message 明文 * @return */ private String addSalt(String message,String salt){ //明文加在盐值的中间,增长破解难度 StringBuilder saltb = new StringBuilder(salt); saltb.insert(15,message); return saltb.toString(); } /** * 生成散列 * @param message 明文 * @param salt 盐值 * @param algorithm 算法 MD五、SHA-一、SHA-25六、SHA-512等 * @return */ public String getHash(String message,String salt,String algorithm){ //明文加盐 String salt_message = addSalt(message,salt); try { //使用MessageDigest生成散列 MessageDigest md = MessageDigest.getInstance(algorithm); //添加明文 md.update(salt_message.getBytes()); //生成散列值 byte[] toChapter1Digest = md.digest(); return Hex.encodeHexString(toChapter1Digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * 验证实文与散列是否匹配 * @param message 明文 * @param salt 盐值 * @param algorithm 算法 MD五、SHA-一、SHA-25六、SHA-512等 * @param hash 散列 * @return */ public boolean verification(String message,String salt,String algorithm,String hash){ //使用明文从新生成散列与散列值比较 String hash1 = getHash(message,salt,algorithm); boolean b = hash1.equals(hash); return b; } }
执行结果
盐值:e4cf17d1437377256970dc80965f3c6e5e5d772f 散列值:d90cfa4a922b385e0c5f1816156b98895a0917334efd66379033ebb661ffbdd675fd860c634c41164965e8bf4e489563265404c955f528f3d81a3974ee997aed 明文与散列匹配成功
加盐使攻击者没法采用特定的查询表和彩虹表快速破解大量哈希值,可是却不能阻止他们使用字典攻击或暴力攻击。高端的显卡(GPU)和定制的硬件能够每秒进行数十亿次哈希计算,所以这类攻击依然能够很高效。为了下降攻击者的效率,咱们可使用一种叫作密钥扩展的技术。
这种技术的思想就是把哈希函数变得很慢,因而即便有着超高性能的GPU或定制硬件,字典攻击和暴力攻击也会慢得让攻击者没法接受。最终的目标是把哈希函数的速度降到足以让攻击者望而却步,但形成的延迟又不至于引发用户的注意。
密钥扩展的实现是依靠一种CPU密集型哈希函数。不要尝试本身发明简单的迭代哈希加密,若是迭代不够多,是能够被高效的硬件快速并行计算出来的,就和普通哈希同样。应该使用标准的算法,好比PBKDF2或者bcrypt。
这类算法使用一个安全因子或迭代次数做为参数,这个值决定了哈希函数会有多慢。对于桌面软件或者手机软件,获取参数最好的办法就是执行一个简短的性能基准测试,找到使哈希函数大约耗费0.5秒的值。这样,你的程序就能够尽量保证安全,而又不影响到用户体验。
jBCrypt官网:http://www.mindrot.org/projects/jBCrypt/
import org.mindrot.jbcrypt.BCrypt; /** * 散列算法(BCrypt实现) */ public class BCryptHashAlgorithm { public static void main(String[] args){ BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(); //使用BCrypt算法加密 String password = "ceshimima"; String ciphertext = hashAlgorithm.encryption(password); System.out.println("密文:" + ciphertext); //验证实文与密文是否匹配 boolean b = hashAlgorithm.verification(password, ciphertext); if(b){ System.out.println("明文与密文匹配成功"); }else{ System.out.println("明文与密文匹配失败"); } } /** * 加密方法 * @param password 密码明文 * @return 密码密文 */ public String encryption(String password){ //建立盐值(log_rounds表明重复处理的轮次。轮次越多破解难度越大,可是效率也越低。 // 其复杂度是指数级递增,也就是传4的时候是2的4次方,默认传输为10的时候是2的10次方) String salt = BCrypt.gensalt(12); //生成密文 String hashed = BCrypt.hashpw(password,salt); return hashed; } /** * 验证方法 * @param password 密码明文 * @param ciphertext 密码密文 * @return 是否一致 true 一致 false 不一致 */ public boolean verification(String password,String ciphertext){ //验证实文与密文是否匹配 //BCrypt的盐值是包含于密文之中的,因此不须要另外保存和传递盐值 boolean b = BCrypt.checkpw(password, ciphertext); return b; } }
执行结果
密文:$2a$12$MpHWUewiJOHrLRyZAc5tReGFfo102cggHTKsqf4N/hFfr3XvMz8Oa 明文与密文匹配成功