当前位置:   article > 正文

Hash算法总结_hash算法整数

hash算法整数

Hash算法概述

        哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
        哈希算法最重要的特点就是:
                相同的输入一定得到相同的输出;
                不同的输入大概率得到不同的输出。
                所以,哈希算法的目的:为了验证原始数据是否被篡改。

                Java字符串的hashCode()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数.例如:

                "hello".hashCode(); // 0x5e918d2
                "hello, java".hashCode(); // 0x7a9d88e8
                "hello, bob".hashCode(); // 0xa0dbae2f

                 两个相同的字符串永远会计算出相同的hashCode,否则基于hashCode定位的HashMap就无法正常工作。这也是为什么当我们自定义一个class时,覆写equals()方法时我们必须正确覆写hashCode()方法。

Hash碰撞      

        哈希碰撞是指:两个不同的输入得到了两个相同的输出。

        例如:                

"通话".hashCode(); // 0x11ff03

"重地".hashCode(); // 0x11ff03

        哈希碰撞无法避免,因为输出的字节长度是固定的,但是输入的数据是无穷的。所以 必然会发生哈希碰撞。

        一个好的哈希算法必须满足:

                碰撞概率低

                不能猜测出输出(无规律)

        常用的哈希算法 

                

算法输出单位(位)输出长度(字节)
MD512816
SHA-116020
RipeMD-16016020
SHA-25625632
SHA-51251264

                         注:1字节=8位

MD5: 

        MD5算法指MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

        下面是一个简单的使用场景:存储用户密码,对我们的准备存入数据库中的密码进行加密。

  1. import java.security.MessageDigest;
  2. public class main {
  3. public static void main(String[] args) {
  4. String password = "wzhdxtx";
  5. // 创建一个MessageDigest实例:
  6. MessageDigest md = MessageDigest.getInstance("MD5");
  7. //调用update输入数据:
  8. md.update(password.getBytes("UTF-8"));
  9. //获取加密后的字节数组
  10. byte[] results = md.digest();
  11. StringBuilder sb = new StringBuilder();
  12. //将字节数组转换为16进制的字符
  13. for(byte bite : results) {
  14. sb.append(String.format("%02x", bite));
  15. }
  16. System.out.println(sb.toString());
  17. //c5c610f4c4f9fa07c02d6268fe0d6b96
  18. }
  19. }

         这样一来我们的密码就被加密处理了,可以使用密文在数据库中进行存储,但只这样存储,也会遭到黑客手中彩虹表的入侵,于是我们将采用特殊措施来抵御彩虹表攻击:对每个口令额外添加随机数,这个方法我们称为加"盐":

  1. import java.security.MessageDigest;
  2. public class main {
  3. public static void main(String[] args) {
  4. String password = "wzhdxtx";
  5. // 创建一个MessageDigest实例:
  6. MessageDigest md = MessageDigest.getInstance("MD5");
  7. //调用update输入数据:
  8. md.update(password.getBytes("UTF-8"));
  9. //加盐
  10. md.update(UUID.randomUUID().toString().substring(0, 4).getBytes("UTF-8"));
  11. //获取加密后的字节数组
  12. byte[] results = md.digest();
  13. StringBuilder sb = new StringBuilder();
  14. //将字节数组转换为16进制的字符
  15. for(byte bite : results) {
  16. sb.append(String.format("%02x", bite));
  17. }
  18. System.out.println(sb.toString());
  19. }
  20. }
SHA-1 

        SHA-1也是一种哈希算法,它的输出是160 bits,即20字节。

        在Java中使用SHA-1,和MD5完全一样,只需要把算法名称改为"SHA-1"

Hmac算法

        Hmac算法就是一种基于密钥的消息认证码算法,它的全称是Hash-based Message Authentication Code,是一种更安全的消息摘要算法。
Hmac算法总是和某种哈希算法配合起来用的。

        例如,我们使用MD5算法,对应的就是Hmac MD5算法,它相当于“加盐”的MD5:HmacMD5 ≈ md5(secure_random_key, input)
        因此,HmacMD5可以看作带有一个安全的key的MD5。使用HmacMD5而不是用MD5加salt,有如下好处:
        HmacMD5使用的key长度是64字节,更安全;
        Hmac是标准算法,同样适用于SHA-1等其他哈希算法;
        Hmac输出和原有的哈希算法长度一致。

   Hmac本质上就是把key混入摘要的算法。

        

        以下例子是基于秘钥对密码进行加密的简单例子:

        步骤:

                1.通过名称HmacMD5获取KeyGenerator实例;
                2.通过KeyGenerator创建一个SecretKey实例;
                3.通过名称HmacMD5获取Mac实例;
                4.用SecretKey初始化Mac实例;
                5.对Mac实例反复调用update(byte[])输入数据;
                6.调用Mac实例的doFinal()获取最终的哈希值。

  1. public static void main1(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
  2. String password = "wzhdxtx";
  3. //获取HmacMD5秘钥生成器
  4. KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
  5. //产生秘钥
  6. SecretKey secretKey = keyGen.generateKey();
  7. System.out.println(Arrays.toString(secretKey.getEncoded()));//秘钥
  8. System.out.println(secretKey.getEncoded().length);//秘钥长度
  9. //秘钥加载为16进制字符串
  10. byte[] keyArray = secreKey.getEncoded();
  11. StringBuilder key = new StringBuilder();
  12. for(byte bite:keyArray) {
  13. key.append(String.format("%02x", bite));
  14. }
  15. //初始化Mac
  16. Mac mac = Mac.getInstance("HmacMD5");
  17. mac.init(secretKey);//初始化秘钥
  18. mac.update(password.getBytes());//更新原始加密内容
  19. byte[] bytes = mac.doFinal();//加密处理,并获取加密结果
  20. System.out.println(Arrays.toString(bytes));//加密结果
  21. System.out.println(bytes.length);//加密结果(字节数组的长度)
  22. System.out.println(HashTools.toHex(bytes));//将加密结果转换为16进制
  23. }

         Hmac算法是一种标准的基于密钥的哈希算法,可以配合MD5、SHA-1等哈希算法,计算的摘要长度和原摘要算法长度相同。

         既然密码加了密,那么一定是要将生成的秘钥记录下来的,以便下次登录时验证。

        下面例子是获得的秘钥记录下来,验证密码是否正确的例子:

                假定获得的秘钥字节数组是:[-119, 86, -110, -8, -74, -71, -83, 119, -72, 88, -10, -6, -91, 100, 97, 93, -37, -79, 51, -20, -101, 126, 88, 34, -106, -21, 18, -21, 91, 44, 51, 104, 47, -15, 23, 97, 0, -85, -41, 4, -43, -42, 117, -45, -109, -65, 71, -48, 31, 57, -121, 90, 7, 118, 50, -39, -68, 60, -13, -128, -18, 74, -4, -7]

                秘钥的16进制:  895692f8b6b9ad77b858f6faa564615ddbb133ec9b7e582296eb12eb5b2c33682ff1176100abd704d5d675d393bf47d01f39875a077632d9bc3cf380ee4afcf9

                加密的结果是:ab5f04bb76aa2b7f427e2ceb3d6241ed

        1.保留的秘钥是字节数组形式

  1. public static void main(String[] args) {
  2. String password = "wzhdxtx";
  3. //秘钥
  4. byte[] bytes = {[-119, 86, -110, -8, -74, -71, -83, 119,
  5. -72, 88, -10, -6, -91, 100, 97, 93, -37, -79, 51, -20, -101, 126,
  6. 88, 34, -106, -21, 18, -21, 91, 44, 51, 104, 47, -15, 23, 97,
  7. 0, -85, -41, 4, -43, -42, 117, -45, -109, -65, 71, -48,
  8. 31, 57, -121, 90, 7, 118, 50, -39, -68, 60, -13, -128, -18, 74,
  9. -4, -7]};//
  10. try {
  11. //恢复秘钥(字节数组)
  12. SecretKey key = new SecretKeySpec(bytes, "HmacMD5");
  13. Mac mac = Mac.getInstance("HmacMD5");
  14. mac.init(key);
  15. mac.update(password.getBytes());
  16. byte[] results = mac.doFinal()
  17. StringBuilder sb = new StringBuilder();
  18. for(byte bite : results) {
  19. sb.append(String.format("%02x", bite));
  20. }
  21. System.out.println(sb.toString());
  22. } catch (InvalidKeyException e) {
  23. e.printStackTrace();
  24. } catch (NoSuchAlgorithmException e) {
  25. e.printStackTrace();
  26. } catch (IllegalStateException e) {
  27. e.printStackTrace();
  28. }
  29. }

        得出结果与加密结果一致。 

        2.保留的秘钥是16进制形式

        

  1. public static void main(String[] args) {
  2. String password = "wzhdxtx";
  3. //秘钥
  4. String keyStr = "895692f8b6b9ad77b858f6faa564615ddbb133ec9b7e582296eb12eb5b2c33682f"
  5. + "f1176100abd704d5d675d393bf47d01f39875a077632d9bc3cf380ee4afcf9";
  6. byte[] bytes = new byte[64];
  7. //将2位16进制字符转换成一个字节
  8. for(int i=0,j=0;i<keyStr.length();i+=2,j++) {
  9. String s = keyStr.substring(i, i+2);
  10. bytes[j] = (byte)Integer.parseInt(s, 16);
  11. }
  12. System.out.println(Arrays.toString(bytes));
  13. try {
  14. //恢复秘钥(字节数组)
  15. SecretKey key = new SecretKeySpec(bytes, "HmacMD5");
  16. Mac mac = Mac.getInstance("HmacMD5");
  17. mac.init(key);
  18. mac.update(password.getBytes());
  19. byte[] result = mac.doFinal()
  20. StringBuilder sb = new StringBuilder();
  21. for(byte bite : results) {
  22. sb.append(String.format("%02x", bite));
  23. }
  24. System.out.println(sb.toString());
  25. } catch (InvalidKeyException e) {
  26. e.printStackTrace();
  27. } catch (NoSuchAlgorithmException e) {
  28. e.printStackTrace();
  29. } catch (IllegalStateException e) {
  30. e.printStackTrace();
  31. }
  32. }

         得出结果与加密结果一致。

        BouncyCastle(哈希算法和加密算法的第三方库)

                bcprov-jdk15on-1.70-lib.rar

                (jar包可以在官方网站下载)   

             Java标准库的java.security包提供了一种标准机制,允许第三方提供商无缝接入。我们要使用BouncyCastle提供的RipeMD160算法,需要先把BouncyCastle注册一下:

                

  1. public class Main {
  2. public static void main(String[] args) throws Exception {
  3. // 注册BouncyCastle提供的通知类对象BouncyCastleProvider
  4. Security.addProvider(new BouncyCastleProvider());
  5. // 获取RipeMD160算法的"消息摘要对象"(加密对象)
  6. MessageDigest md = MessageDigest.getInstance("RipeMD160");
  7. // 更新原始数据
  8. md.update("HelloWorld".getBytes());
  9. // 获取消息摘要(加密)
  10. byte[] result = md.digest();
  11. // 消息摘要的字节长度和内容
  12. System.out.println(result.length); // 160位=20字节
  13. System.out.println(Arrays.toString(result));
  14. // 16进制内容字符串
  15. //参数1:1代表正整数
  16. //参数2:字节数组
  17. String hex = new BigInteger(1,result).toString(16);
  18. System.out.println(hex.length()); // 20字节=40个字符
  19. System.out.println(hex);
  20. }
  21. }

        

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号