常见哈希算法总结、对称式加密与非对称式加密的对比。

本文详细介绍了哈希算法如MD5和SHA-1,以及它们在数据校验中的作用。接着讲解了对称加密如AES的ECB和CBC模式,展示了加密和解密过程。同时,提到了非对称加密的RSA算法,强调了公钥和私钥的使用场景。最后对比了对称加密与非对称加密的特点。
摘要由CSDN通过智能技术生成

一.哈希算法

哈希算法(hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个长度固定的输出摘要。哈希算法的目的:为了验证原始数据是否被篡改。

哈希算法最重要的特点

1.相同的输入一定得到相同的输出。

2.不同的输入大概率得不到相同的输出。

以字符串为例,调用hashcode()方法,输出的是固定的4字节int整数。

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

2.常见的哈希算法

 我们首先以MD5为例,看看如何输入计算哈希。

创建MEssageDigest实例,调用getInstance()方法,并传入MD5类型,继而调用update()方法更新原始数据,创建字节数组调用digest()方法,通过Stringbuilder()将遍历出来的字节拼接起来。具体代码如下:

public static void main(String[] args) {
		String str = "wbjxxmynhmyzgq";
		
		try {//根据当前算法,获取加密工具
			MessageDigest mag = MessageDigest.getInstance("MD5");
			
			//更新原始数据
			mag.update(str.getBytes());
			//加密后的字节数组,转换成字符串
			byte[] byteArray = mag.digest();
			
			StringBuilder result = new StringBuilder();
			for(byte bute : byteArray) {
				result.append(String.format("%02x", bute));

			}
			System.out.println(result);

		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

SHA-1:,为了避免彩虹表攻击,我们可以对数据进行“加盐”处理,也就是说对口令添加随机数SHA-1与MD5一致,只将算法类型换成SHA-1,具体代码如下:

public static void main(String[] args) {
		String password = "wbjxxmy";
		//加盐(安全)
		String salt = UUID.randomUUID().toString().substring(0,5);
		System.out.println(salt);
		
		try {//获取SHA-1算法的工具对象
			MessageDigest digest = MessageDigest.getInstance("SHA-1");
			digest.update(password.getBytes());
			digest.update(salt.getBytes());
			
			byte[] resultByteArray = digest.digest();
			System.out.println(Arrays.toString(resultByteArray));
			
			
			StringBuilder sb = new StringBuilder();
			for(byte s :resultByteArray) {
				sb.append(String.format("%02x", s));
				
			}
			System.out.println("SHA-1加密:"+sb);
			System.out.println("----------------------");

Hmac算法:Hmac算法就是一种基于密钥的消息认证算法,他的全称是Hsh-basedMessage Authrntication Code,是一种更安全的消息摘要算法。

Hmac算法总是和某种哈希算法配合起来用的,例如,我们使用MD5,算法,对应的就是Hmac MD5算法,它就相当于“加盐”的MD5,因此hmac可以看作是一个更安全的MD5,首先创建KeyGenerator实例调用getInstance()方法,并传入参数。通过KeyGenerator对象调用generateKey()方法生成密钥,通过密钥对象调用getEncoded()方法获取密钥字节数组,

最后遍历字节数组,并通过Stringbuilder进行拼接。创建Mac实例对象,调用getInstance()方法,传入参数,调用init()初始化密钥,调用update()更新原始内容,调用doFinal()进行加密,并存入字节数组中。最后遍历数组,通过Stringbuilder进行拼接。具体代码实现如下:

String password = "xlfjkcs";
		try {//生成密钥
			//密钥生成器KeyGenerator
			KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
			
			//生成密钥Key
			SecretKey key  = keyGen.generateKey();
			
			//获取密钥Key的字节数组(64)
			byte[] keyByteArray = key.getEncoded();
			System.out.println("密钥长度:"+keyByteArray.length+"字节");
			
			StringBuilder sb = new StringBuilder();
			for(byte b :keyByteArray) {
				sb.append(String.format("%02x", b));
			}
			System.out.println("密钥内容:"+sb);
			System.out.println("密钥内容长度:"+sb.length());
			
			
			//2.使用密钥,进行加密
			//获取算法对象
			
			Mac mac = Mac.getInstance("HmacMD5");
			
			//初始化密钥
			mac.init(key);
			
			//更新原始内容
			mac.update(password.getBytes());
			
			//加密
			byte[] resultByteArray = mac.doFinal();
			System.out.println("加密结果:"+resultByteArray.length+"字节");
			
			StringBuilder resultstr = new StringBuilder();
			for(byte s :resultByteArray) {
				resultstr.append(String.format("%02x", s));
			}
			System.out.println("加密结果:"+resultstr);
			System.out.println("加密结果长度:"+resultstr.length());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

RipeMD160算法:第三方开源库提供的算法,具体代码实现如下:

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);
    }
}

对称式加密:对称式加密算法就是用一个密码进行加密和解密,在软件开发中,常见的对称加密算法有如下:

 当然,现在使用最广泛的就是AES加密,其中比较常见的工作模式是ECBCBC。

以ECB模式为例:我们将它的加密和解密封装在两个方法中,方便直接调用。


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);
    }
}

ECB模式下,需要一个固定长度的密钥,固定的明文会生成固定的密文,这种一对一的加密方式会导致安全性降低,更好的方式是通过CBC模式,他需要一个随机数作为IV参数,这样对于同一份明文,每次生成的密文都不同:



public class Main {
	public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message(原始信息): " + message);
        
        // 256位密钥 = 32 bytes Key:
        byte[] key = "1234567890abcdef1234567890abcdef".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 {
        // 设置算法/工作模式CBC/填充
    	Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    	
    	// 恢复秘钥对象
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        
        // CBC模式需要生成一个16 bytes的initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16); // 生成16个字节的随机数
        System.out.println(Arrays.toString(iv));
        IvParameterSpec ivps = new IvParameterSpec(iv); // 随机数封装成IvParameterSpec参数对象
        
        // 初始化秘钥:操作模式、秘钥、IV参数
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        
        // 加密
        byte[] data = cipher.doFinal(input);
        
        // IV不需要保密,把IV和密文一起返回:
        return join(iv, data);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 把input分割成IV和密文:
        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        
        System.arraycopy(input, 0, iv, 0, 16); // IV
        System.arraycopy(input, 16, data, 0, data.length); //密文
        System.out.println(Arrays.toString(iv));
        
        // 解密:
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 密码对象
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // 恢复秘钥
        IvParameterSpec ivps = new IvParameterSpec(iv); // 恢复IV
        
        // 初始化秘钥:操作模式、秘钥、IV参数
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        
        // 解密操作
        return cipher.doFinal(data);
    }
    
    // 合并数组
    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
}

非对称式加密:加密和解密使用的不是相同的密钥,只有同一个公钥—私钥才能正常的加解密。

例如:小卢要加密一个文件给小吴,他应该首先向小吴索取他的公钥,然后用索取的公钥加密,然后把加密的文件发给小吴,此文件只有小吴的私钥解开,因为小吴的私钥在自己手里,所以,除了了小吴,没有人能解开此文件。

典型算法RSA,代码实现如下:


// 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);
	}
}

对称加密与非对称加密对比:

对称加密在加密或解密中,使用的都是相同的密钥,而非对称加密则是使用密钥对(公钥---私钥)使用公钥进行加密,解密时则必须使用该公钥对应的私钥进行解密。

请各位大佬指正。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值