散列算法进行数据验证与加密

散列算法

散列是信息的提炼,一般其长度要比信息小得多,且为一个固定长度。加密性强的散列必定是不可逆的,这就意味着经过散列结果,没法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将致使散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具备相同散列结果的两条信息。具备这些特性的散列结果就能够用于验证信息是否被修改。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

虽然散列算法并非加密算法,因为拥有不可逆的特性也经常使用于密码的加密保存。安全

 

干扰项 - 盐(Salt)

相同的明文用一样的加密方法(如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

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

 

MessageDigest示例

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秒的值。这样,你的程序就能够尽量保证安全,而又不影响到用户体验。

 

BCrypt示例

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
明文与密文匹配成功
相关文章
相关标签/搜索