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

哈希算法

什么是哈希算法?

哈希算法(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.数字签名,对称加密不可以用于数字签名和数宁监别,非对称加口密可以用于数字签名和数字鉴别
 

  • 26
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RSA 是一种非对称加密算法,它可以用于数据加密和数字签名。下面是 RSA 加密算法的实现过程: 1. 选择两个大素数 p 和 q,并计算 n = p*q。 2. 计算欧拉函数 φ(n) = (p-1)*(q-1)。 3. 选择一个小于 φ(n) 的整数 e,使得 e 和 φ(n) 互质。 4. 计算 e 的模反元素 d,即满足 (d*e) mod φ(n) = 1 的整数 d。可以使用扩展欧几里得算法来计算 d。 5. 公钥为 (n, e),私钥为 (n, d)。 6. 加密时,将消息 m 转换成整数 M,然后计算密文 C = M^e mod n。 7. 解密时,将密文 C 计算出明文 M = C^d mod n。 下面是 Python 实现 RSA 加密算法的代码: ```python import random def gcd(a, b): if b == 0: return a else: return gcd(b, a % b) def extended_gcd(a, b): if b == 0: return (a, 1, 0) else: d, x, y = extended_gcd(b, a % b) return (d, y, x - (a // b) * y) def mod_inverse(a, m): d, x, y = extended_gcd(a, m) if d != 1: raise ValueError("No inverse exists") else: return x % m def generate_key_pair(p, q): n = p * q phi = (p - 1) * (q - 1) e = random.randrange(1, phi) while gcd(e, phi) != 1: e = random.randrange(1, phi) d = mod_inverse(e, phi) return ((n, e), (n, d)) def encrypt(msg, public_key): n, e = public_key m = int.from_bytes(msg.encode(), 'big') c = pow(m, e, n) return c.to_bytes((c.bit_length() + 7) // 8, 'big').decode() def decrypt(cipher, private_key): n, d = private_key c = int.from_bytes(cipher.encode(), 'big') m = pow(c, d, n) return m.to_bytes((m.bit_length() + 7) // 8, 'big').decode() # 选择两个大素数 p 和 q p = 61 q = 53 # 生成公钥和私钥 public_key, private_key = generate_key_pair(p, q) print("公钥:", public_key) print("私钥:", private_key) # 加密和解密 msg = "Hello, world!" cipher = encrypt(msg, public_key) print("密文:", cipher) plaintext = decrypt(cipher, private_key) print("明文:", plaintext) ``` 需要注意的是,由于 RSA 加密算法使用了大整数计算,因此在处理大数时需要使用适当的库来避免溢出等问题。在 Python 中,可以使用内置的 `pow()` 函数来计算幂取模,也可以使用第三方库(如 `gmpy2` )来进行大整数运算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值