0


常见的哈希算法总结和对称式加密和非对称式加密的区别

哈希算法

什么是哈希算法?

哈希算法(Hash)又称为是摘要算法(Digest),对任意一组数据利用指定算法得到一个固定长度的输出摘要,其目的是为了验证原始数组是否被篡改;

哈希算法的特点:

相同的输入输出一定是相同的

不同的输入输出大概路是不同的(比如下面的例子)这也就是所谓的哈希碰撞

"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0

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

怎么避免哈希碰撞?

碰撞能不能避免?答案是不能。碰撞是-
一定会出现的,因为输出的字节长度是固定的,string 的hashcode()输出是 4 字节整数,最多只有 4294967296 种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把-一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。碰撞不可怕,我们担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系到哈希算法的安全性。
一个安全的哈希算法必须满足:
•碰撞概率低;
• 不能猜测输出。
不能猜测输出是指:输入的任意一个 bit 的变化会造成输出完全不同,这样就很难从输出反推输入(只能依拿暴
力穷举)。

我们为了避免这样的事情发生就出现的几种常见的哈希算发

常见的哈希算法:(根据碰撞的概率,哈希算法发输出长度越长就越难发生碰撞,就越安全)

下面我们就浅浅的介绍一些MD5算法

MD5:

首先我们创建一个MessageDidest对象,然后调用的它Update()方法,这时候注意传参一定是这个参数的字节值,最后就是加密的方法digest();方法了拿到时一个字节数组,然后遍历这个字节数组,同StringBuilder类拼接在一起转化成16进制的字符串。具体的代码如下

import java.security.MessageDigest;

public class main {
    public static void main(String[] args)  {
        // 创建一个MessageDigest实例:
        MessageDigest md = MessageDigest.getInstance("MD5");
       
        // 反复调用update输入数据:
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        
        // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
        byte[] results = md.digest(); 

        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
            sb.append(String.format("%02x", bite));
        }
        
        System.out.println(sb.toString());
    }
}

SHA-1;

SHA-1也是一种哈希算法,它输出160bits,就是20个字节,他有一系列的算法比如SHA-1,SHA-256,SHA-512,q其代码的实现也和MD5的算法大同小异,就是把算法名称改成“SHA-1”

import java.security.MessageDigest;

public class main {
    public static void main(String[] args)  {
        // 创建一个MessageDigest实例:
        MessageDigest md = MessageDigest.getInstance("SHA-1");
       
        // 反复调用update输入数据:
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        
        // 20 bytes: db8ac1c259eb89d4a131b253bacfca5f319d54f2
        byte[] results = md.digest(); 

        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
            sb.append(String.format("%02x", bite));
        }
        
        System.out.println(sb.toString());
    }
}

那么说了这么多究竟这些算法有什么作用呢?

校验下载的文件

什么意思呢,就是利用相同的输入输出也是相同的,我们在下载一些软件时,经常会下载到一些流氓软件,这就是一些人在其中添加了一些你看不见的东西,所以我们可以下载下来后看一下它的哈希值,看是否和官网爆出的哈希值一样,不一样说明就是有问题的举一个例子。

存储用户密码

这就是算法是第二个作用,你想想数据库管理员知道你的密码,万一数据库被黑客入侵,所有用户的信息就被泄露了,这时候就需要一个算法将用户的密码转化成另一种形式,这样就可以有效的解决了。有人就问那么用户在登录时候怎么办?这时候你是知道那时候是按照什么算法计算出来的,在计算一次,和已经计算出来的哈希值对比一下不就知道了,输出的结果就像下面一样

这时候就会出现另一个名词就是彩虹表,意思就是一些人的密码就是生日啊身份证号码呀什么的。这时候黑客就先计算好一些编码,然后对照着反推出一人的密码,这也就是为什么很重要的密码一般不让使用自己的生日什么,那么什么避免彩虹标呢?就是对每个口令密码额外添加一个随机数,这个方法称为 加盐

简答的代码实现如下

package mds.com;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

// 消息摘要算法工具类
    public class MessageDigestUtils {
        private static MessageDigest mddigest=null;//下面的方法都是静态方法所以创建一个静态成员变量

        // 按照MD5算法计算哈希值
        public static String digestByMd5(String source) throws NoSuchAlgorithmException {
            //加盐
            String salt=UUID.randomUUID().toString().substring(0,5);
             mddigest=MessageDigest.getInstance("MD5");
            byte[] tempsource=(source+salt).getBytes();
            byte[]  password=digest(tempsource);
            String result=toHexString(password);
            return result;                
        }

        // 按照SHA-1算法计算哈希值
        public static String digestBySHA1(String source) throws NoSuchAlgorithmException {
            //加盐
            String salt=UUID.randomUUID().toString().substring(0,5);
             mddigest=MessageDigest.getInstance("SHA-1");
            byte[] tempsource=(source+salt).getBytes();
            byte[]  password=digest(tempsource);
            String result=toHexString(password);
            return result;    
            
        }

        // 加密
        private static byte[] digest(byte[] source) {
            mddigest.update(source);
            return mddigest.digest();
        }

        // 将字节数组转换成16进制字符串
        private static String toHexString(byte[] byteArray) {
            StringBuilder sb=new StringBuilder();
            for (byte b : byteArray) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        }
    }

    

Hmac算法;

上面我们说的彩虹表是什么,如何避免彩虹表的出现,这个算法有效的解决了这个问题

1.Hmac 算法就是一种基于密钥的消息认证码算法,它的全称是 Hash-based Message Authentication code是-种史安全的消息摘要算法。
2.Himac 算法总是和某种哈希算法配合起来用的。例如,我们使用 MD5 算法,对应的就是Hmac MD5 算法,它相手“加盐”的 MD5
HmacMD5 = md5 (secure random key, input)

因此,HmacMD5 可以看作带有一个安全的key 的MD5。使用HmaCMD5 而不是用 MD5 加salt,有如下好处:
1.HmaCMD5 使用的 key 长度是 64 字节,更安全;
2.Hmac 是标准算法,同样适用于 SHA-1 等其他哈希算法;
3.Hmac 输出和原有的哈希算法长度—致
可见,Himac 本质上就是把 key 混入摘要的算法。验证此哈希时,除了原始的输入数据,还要提供 key。为了保证安全,我们不会自己指定 key,而是通过 Java 标准库的 KeyGenerator 金成-个安全的随机的 ke。

下面是实现代码:

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

public class main {
    public static void main(String[] args) throws NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException, InvalidKeyException {
        
        // 获取HmacMD5秘钥生成器
        KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
        
        // 产生秘钥
        SecretKey secreKey = keyGen.generateKey();
        
        // 打印随机生成的秘钥:
        byte[] keyArray = secreKey.getEncoded();
        StringBuilder key = new StringBuilder();
        for(byte bite:keyArray) {
            key.append(String.format("%02x", bite));
        }
        System.out.println(key);
        
        // 使用HmacMD5加密
        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(secreKey); // 初始化秘钥
        mac.update("HelloWorld".getBytes("UTF-8"));
        byte[] resultArray = mac.doFinal();
        
        StringBuilder result = new StringBuilder();
        for(byte bite:resultArray) {
            result.append(String.format("%02x", bite));
        }
        System.out.println(result);
    }
}

这和传统的MD5算法有什么区别?

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

那又是怎么恢复,或者是怎么验证的呢?上面加密的时候第一部是生成密钥,然后通过方法加密,只要我们知道随机密钥就能恢复,根据byte[]数组,下面是实现的代码

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        String str= "saikudhgsi";
        // 通过密钥是字节数组恢复密钥
        byte[] keyarray = { -45, 96, -50, 87, 26, -43, -3, -61, 91, -47, -91, -68, -106, -31, -102, -34, -22, -13, 30,
                -42, 119, -59, -55, -48, -123, -21, -55, -68, -23, -54, 27, -50, -91, -63, 74, 79, 71, -51, 114, 66,
                -42, -18, 5, -81, 111, 0, -11, -72, 40, -85, 41, 32, 11, 93, -32, -24, -22, -117, 102, 44, 58, -41, -31,
                -56 };
        // 作用是回复密钥将上面密钥的字节数组恢复成密钥
        SecretKey key = new SecretKeySpec(keyarray, "HmacMd5");
        // 加密操作
        Mac mac = Mac.getInstance("HmacMd5");
        // 初始化
        mac.init(key);
        //更新
        mac.update(str.getBytes());
        //加密操作
        byte[] resultbyte=mac.doFinal();
        StringBuilder resultsb = new StringBuilder();
        for (byte b : resultbyte) {
            resultsb.append(String.format("%02x", b));
        }
        System.out.println("加密的结果为" + resultsb);
        System.out.println("加密的结果长度为" + resultsb.length());

    }

Bouncycastle

Bouncycastle就是提供的许多加密算法和哈希算法的第三方开源库,它提供的java核心库没有的算法。比如我们上面说的RipeMD5哈希算法

当然首先我们先把这个第三方的架包导进来,然后算法代码过程其实和上面MD5和SHA-1也是大同小异

public class Main {
    public static void main(String[] args) throws Exception {
        // 注册BouncyCastle提供的通知类对象BouncyCastleProvider
        Security.addProvider(new BouncyCastleProvider());

        // 获取RipeMD160算法的"消息摘要对象"(加密对象)
        MessageDigest md = MessageDigest.getInstance("RipeMD160");

        // 更新原始数据
        md.update("HelloWorld".getBytes());

        // 获取消息摘要(加密)
        byte[] result = md.digest();

        // 消息摘要的字节长度和内容
        System.out.println(result.length); // 160位=20字节
        System.out.println(Arrays.toString(result));

        // 16进制内容字符串
        String hex = new BigInteger(1,result).toString(16);
        System.out.println(hex.length()); // 20字节=40个字符
        System.out.println(hex);
    }
}

对称加密

什么是对称加密?

是指加密和解密时候用同一个密钥,那么在软件开发中常见的对称加密算法有哪些?

因为DES算法密钥太短所以现在已经不安全了,AES已经取代的DES位置,我们就简单介绍一下AES

AES (Advanced Encryption Standard)是取代其前任标准(DES)而成为新标准的一种对称空码算法。全世界的企业和密码学家提交了多个对称密码算法作为AES的候选,最终在2000年从这些候选算法中选出了一种名为 Riindael 的对称密码算法,并将其确定为了AES.Rindael是由比利时密码学家Joan Daemen和Vincent Rimen设汁的分组密码算法.今后会有越来越多的密码软件支持这种算法Riindael的分组长度为128比特,密钥长度可以以32比特为单位在128比特到256比特的范围内进行选择 (不过 在AES的规格中,密钥长度只有128.192和256比特三种

import java.security.*;
import java.util.Base64;

import javax.crypto.*;
import javax.crypto.spec.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message(原始信息): " + message);
        
        // 128位密钥 = 16 bytes Key:
        byte[] key = "1234567890abcdef".getBytes();
        
        // 加密:
        byte[] data = message.getBytes();
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted(加密内容): " + 
                            Base64.getEncoder().encodeToString(encrypted));
        
        // 解密:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted(解密内容): " + new String(decrypted));
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 创建密码对象,需要传入算法/工作模式/填充模式
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        
        // 根据key的字节内容,"恢复"秘钥对象
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        
        // 初始化秘钥:设置加密模式ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        
        // 根据原始内容(字节),进行加密
        return cipher.doFinal(input);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 创建密码对象,需要传入算法/工作模式/填充模式
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        
        // 根据key的字节内容,"恢复"秘钥对象
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        
        // 初始化秘钥:设置解密模式DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        
        // 根据原始内容(字节),进行解密
        return cipher.doFinal(input);
    }
}

上面是加密和解密的全过程

1.根据算法名称/工作模式/填充模式获取Cipher实例

2.根据算法名称初始换一个SecretKey实例,

3.使用SerectKey初始化一个Ciper实例,并且设置加密还是解密模式

4.doFinal()进行加密或者解密

非对称加密

什么是非对称加密?

首先和对称加密做一个对比,对称加密解密只需要一个密钥,而非对称加密需要两个,分别是公钥和私钥,用自己的公钥加密,解密时候用自己的私钥解密。那么对称加密有主要有AES算法非对称算法也有自己的算那就是RSA算法,以及非对称算法是是怎么实现的?

import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;

// RSA
public class Main {
    public static void main(String[] args) throws Exception {
        // 明文:
        byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");

        // 创建公钥/私钥对:
        Human alice = new Human("Alice");

        // 用Alice的公钥加密:
        // 获取Alice的公钥,并输出
        byte[] pk = alice.getPublicKey();
        System.out.println(String.format("public key(公钥): %x", new BigInteger(1, pk)));

        // 使用公钥加密
        byte[] encrypted = alice.encrypt(plain);
        System.out.println(String.format("encrypted(加密): %x", new BigInteger(1, encrypted)));

        // 用Alice的私钥解密:
        // 获取Alice的私钥,并输出
        byte[] sk = alice.getPrivateKey();
        System.out.println(String.format("private key(私钥): %x", new BigInteger(1, sk)));

        // 使用私钥解密
        byte[] decrypted = alice.decrypt(encrypted);
        System.out.println("decrypted(解密): " + new String(decrypted, "UTF-8"));
    }
}

// 用户类
class Human {
    // 姓名
    String name;

    // 私钥:
    PrivateKey sk;

    // 公钥:
    PublicKey pk;

    // 构造方法
    public Human(String name) throws GeneralSecurityException {
        // 初始化姓名
        this.name = name;

        // 生成公钥/私钥对:
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        kpGen.initialize(1024);
        KeyPair kp = kpGen.generateKeyPair();

        this.sk = kp.getPrivate();
        this.pk = kp.getPublic();
    }

    // 把私钥导出为字节
    public byte[] getPrivateKey() {
        return this.sk.getEncoded();
    }

    // 把公钥导出为字节
    public byte[] getPublicKey() {
        return this.pk.getEncoded();
    }

    // 用公钥加密:
    public byte[] encrypt(byte[] message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, this.pk); // 使用公钥进行初始化
        return cipher.doFinal(message);
    }

    // 用私钥解密:
    public byte[] decrypt(byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, this.sk); // 使用私钥进行初始化
        return cipher.doFinal(input);
    }
}

区别

说完对称加密和非对称加密,那么他们的不同或者说有什么区别?

1.对称加密算法加密和解密使用的 密钥的是相同的,而非对称加密算法有两个空钥,也就是加密和解密所使用的密钥是不同的,加密用公钥,解密是私钥。
2.各自算法不同对称算法是(AES)非对称算法是(RSA)
3.对称加密算法运行速度快,非对称算法运行速度慢。
4安全性不司,对称加密算法,在传输的过程容易被窃听,不安全而能非对称加密, 算法就可以很好的避免这个问题
5.数字签名,对称加密不可以用于数字签名和数宁监别,非对称加口密可以用于数字签名和数字鉴别


本文转载自: https://blog.csdn.net/qq_58155240/article/details/125945068
版权归原作者 小张要秃头 所有, 如有侵权,请联系我们删除。

“常见的哈希算法总结和对称式加密和非对称式加密的区别”的评论:

还没有评论