美文网首页
哈希算法(Hash)之二

哈希算法(Hash)之二

作者: 喏喏2021 | 来源:发表于2021-12-07 18:17 被阅读0次

    1. 概念

    • 哈希算法,也叫摘要算法(Digest)
    • 定义:对于任意长度的输入,得到固定长度的输出
    • 特点
      1. 相同的输入得到相同的输出
      2. 不同的输入尽最大可能得到不同的输出
      3. 我们不能根据已有的输出,猜测出其它输出
    • 哈希碰撞
      1. 不同的输入得到了相同的输出,当然我们需要尽可能地减少这种情况
      2. 理论上应该是输出长度越大,碰撞的概率会越小

    2. 常用算法及长度

    MD5 128 bits
    SHA-1 160 bits
    RipeMD-160 160 bits
    SHA-256 256 bits
    SHA-512 512 bits

    3. 用途

    • 防止文件篡改,对文件进行hash算法后,得到了原始的哈希值,如果文件发生变化后,相应的hash值就会发生变化
    • 用作密码保存使用,原文密码经hash算法后,得到的内容和已保存的加密密码一致,则表示验证通过
      问题1:如果我们拿到了库中加密的密码,挨个去尝试,不是很容易就能破解一些密码吗?
      答: 是这样的,这个是基础暴力破解的方法,聪明一点的呢,可以使用彩虹表的方法,即使用一些常用的组合来生成相应的密码
      这样根据生成的密码,匹配一下已有的密码,很快就可以推导出原文
      问题2: 面对彩虹表的破解方法,我们有什么应对之策呢?
      答:答案也是有的,我们可以在哈希算法时,加入一些随机数,我们称之为盐salt,别人不知道这个salt时,也很难推出原文, 这样新的加密过程就是这样:
      newPassword = hash(password + salt)

    4. 代码示例

    • 这里我们用JDK自带的API进行MD5加密
    import java.security.MessageDigest;
    MessageDigest md = MessageDigest.getInstance("MD5");
    String input = "123456";
    md.update(input.getBytes("UTF-8"));
    byte[] result = md.digest();
    System.out.println(new BigInteger(1,result).toString(16)); //按字节挨个输出16进制字符
    //e10adc3949ba59abbe56e057f20f883e
    
    • SHA256哈希算法
      上面的代码主体都不动,只是将算法改为SHA-256
    MessageDigest md = MessageDigest.getInstance("SHA-256");  //只是调整了算法名,最终我们得到的结果如下
    //8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
    

    有些哈希算法可能JDK自带的没有,这时我们可以引入第三方库,如bouncycastle
    <dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.65</version>
    </dependency>
    这样,代码中添加一下算法,就可以体验其它哈希算法了,很棒!

    Security.addProvider(new BouncyCastleProvider());
    MessageDigest md = MessageDigest.getInstance("RipeMD160"); //同样是123456,用RipeMD160加密后就是如下的输出
    //d8913df37b24c97f28f840114d05bd110dbb2e44
    

    5. Hmac算法

    • 全称 Hash-based Message Authentication Code 基于密钥的消息认证码算法
    • 特点
      1. hmac的随机码,可以由java标准库生成,会更加安全
      2. hmac是标准算法,可以适用于SHA-256等各种hash算法
      3. 输出和原算法输出长度一致
      4. 当然,我们需要把生成的key,也一并保存下来
    • 示例
    1. 生成key
    import javax.crypto.KeyGenerator;
    KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); //生成KeyGenerator
    SecretKey key = keyGen.generateKey(); //生成key
    byte[] skeys = key.getEncoded(); //获得key的字节数组
    String keyStr = new BigInteger(1,skeys).toString(16); //方便打印查看
    System.out.println(keyStr);
    //919bfbaa38011cb1e696b6c99ddb61fbd06036478d2be176503efd6e0ba120260c240f675663bfe24ad003533b8e284d0294b7f8c2e6696157cce66b3cfe367e
    
    1. 用hmacMD5加密
    Mac mac = Mac.getInstance("HmacMD5");
    mac.init(key); //使用前面生成key,初始化
    mac.update("hello".getBytes());
    byte[] resultData = mac.doFinal(); //加密,得到结果字节数组
    String result = new BigInteger(1,resultData).toString(16); //加密后内容输出
    System.out.println(result); 
    //a28fbd6a086ce428dc26380a7aab1cc8
    
    1. 用hmacMD5对数据验证
    SecretKey key1 = new SecretKeySpec(skeys,"HmacMD5"); //这里使用前面生成的key数组
    Mac mac1 = Mac.getInstance("HmacMD5");
    mac1.init(key1);
    mac1.update("hello".getBytes());
    byte[] resultData1 = mac1.doFinal(); //得到实际验证的加密数组
    String result1 = new BigInteger(1,resultData1).toString(16);  //输出查看,当然和前面的输出是一样的
    System.out.println(result1);
    //a28fbd6a086ce428dc26380a7aab1cc8
    

    注:验证的时候,我们一般需要从库中读取出keys值,用密码原文+keys值,计算出验证密文,方便和已保存的密文进行比较
    遗留问题:库中一般保存的是16进制的字符,大家有没有好的工具方法,将16进制字符串,转为需要的字符数组,将2位16进制字符转为1个字节?
    欢迎留言,谢谢!

    相关文章

      网友评论

          本文标题:哈希算法(Hash)之二

          本文链接:https://www.haomeiwen.com/subject/bsitfrtx.html