对称加密算法与非对称加密算法

一、对称加密算法

1、概述

对称加算法就是传统的==用一个密钥进行加密和解密==,例如,常用的 WinZip 和WinRAR 对压缩包的加密和解密,就是使用对称加密算法

2、常用的对称加密算法
算法密钥长度工作模式t填充模式
DES56/54ECB/CBC/PCBC/CTRNoPadding/PKCS5Padding
AES128/192/256ECB/CBC/PCBC/CTRNoPadding/PKCS5Padding/PKCS7Padding
IDEA128ECBPKCS5Padding/PKCS7Padding

说明:

  • 密钥长度直接决定加密强度
  • 工作模式和填充模式可以看成是对称加密算法的参数和格式选择
  • DES算法由于密钥过短,可以在短时间内被暴力破解,所以现在已经不安全了。
3、使用 AES 加密

AES算法是目前应用最广泛的加密算法。比较常见的工作模式是ECB和CBC。

1、ECB 模式

ECB模式是最简单的AES加密模式,它需要一个固定长度的密钥固定的明文会生成固定的密文

解密与加密均使用同一个密钥,所以安全性较低

2、AES 加密 + ECB 模式
// AES + ECB
public class Demo01 {
	public static void main(String[] args) throws GeneralSecurityException {
		// 原文:
		String message = "天生我材必有用飞流直下三千尺";
		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_
		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初始化Cipher实例,并设置加密或解密模式
  4. 传入明文或密文,获得密文或明文
3、CBC 模式

CBC模式,需要一个随机数作为 IV 参数,这样对于同一份明文,每次生成的密文都不同

4、AES 加密 + CBC 模式
//AES + CBC
public class Demo02 {
	public static void main(String[] args) throws Exception {
		// 原文:
		String message = "Hello";
		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");

		// 恢复秘钥对象
		SecretKey keySpec = new SecretKeySpec(key, "AES");
		// CBC模式需要生成一个16 bytes的initialization vector:
		SecureRandom sr = SecureRandom.getInstanceStrong();// 生成器
		byte[] iv = sr.generateSeed(16);
		System.out.println("iv字节数组内容:" + Arrays.toString(iv));
		System.out.println("iv字节数组长度:" + iv.length);

		// 随机数封装成 IvParameterSpec 参数对象
		IvParameterSpec ivps = new IvParameterSpec(iv);
		// 初始化秘钥:操作模式、秘钥、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);
        System.arraycopy(input, 16, data, 0, data.length);
        
      
        
        // 解密:
       Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
       SecretKeySpec keySpec=new SecretKeySpec(key, "AES");
       IvParameterSpec ivps = new IvParameterSpec(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;
	}
}

步骤与 ECB 模式步骤相似,只需要额外添加一个随机生成的 IV 参数,然后将这个参数加密后与原文合并,解密时也需要将合并的数据拆分,分别获取 IV 参数和加密后的原文

5、小结
  • 对称加密算法使用同一个密钥进行加密和解密,常用算法有DES、AES和IDEA等;
  • 密钥长度由算法设计决定,AES的密钥长度是128/192/256位;
  • 使用对称加密算法需要指定算法名称、工作模式和填充模式。

二、密钥交换算法

1、概述

AES 加密算法,需要将密钥进行传输,在这个过程中存在安全隐患,导致密钥泄露因此,需要使用密钥交换算法:DH

DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。

2、步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOzHanTA-1689078393304)(E:\桌面\870edf07cee6af069cffdd61c74ac66.png)]

  1. Alice 和 Bob 均有自己的私钥
  2. 将各自的私钥进行计算,会产生一个公钥
  3. 将各自产生的公钥传给对方
  4. 然后结合自己的私钥和对方的公钥进行计算,双方可以得到一个相同的共享密钥
  5. 通过对比最后产生的共享密钥

由于在这个过程中,只有公钥进行传输,私钥并没有参与传输,因此保证了数据在传输过程的安全性

3、代码示例
public class Demo03 {
	public static void main(String[] args) {
		// Bob和Alice:
        Person bob = new Person("Bob");
        Person alice = new Person("Alice");
        
        // 各自生成KeyPair: 公钥+私钥
        bob.generateKeyPair();
        alice.generateKeyPair();

        // 双方交换各自的PublicKey(公钥):
        // Bob根据Alice的PublicKey生成自己的本地密钥(共享公钥):
        bob.generateSecretKey(alice.publicKey.getEncoded());
       
        // Alice根据Bob的PublicKey生成自己的本地密钥(共享公钥):
        alice.generateSecretKey(bob.publicKey.getEncoded());

        // 检查双方的本地密钥是否相同:
        bob.printKeys();
        alice.printKeys();
        
        //使用共享密钥,继续dosth
	}
}

//用户类
class Person {
	public final String name; // 姓名

	// 密钥
	public PublicKey publicKey; // 公钥
	private PrivateKey privateKey; // 私钥
	private byte[] secretKey; // 本地秘钥(共享密钥)

	// 构造方法
	public Person(String name) {
		this.name = name;
	}

	// 生成本地KeyPair:(公钥+私钥)
	public void generateKeyPair() {
		KeyPairGenerator kpGen;
		try {
			
			// 创建DH算法的“密钥对”生成器
			kpGen = KeyPairGenerator.getInstance("DH");
			kpGen.initialize(512);
			
			KeyPair kp=kpGen.generateKeyPair();
			this.privateKey=kp.getPrivate();
			this.publicKey=kp.getPublic();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
	}

	// 按照 "对方的公钥" => 生成"共享密钥"
	public void generateSecretKey(byte[] receivedPubKeyBytes) {
	
		
		try {
			// 从byte[] 恢复PublicKey
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes);

			// 根据DH算法获取KeyFactory
			KeyFactory kf = KeyFactory.getInstance("DH");

			// 通过KeyFactory创建公钥
			PublicKey recePublicKey = kf.generatePublic(keySpec);

			// 创建密钥协议对象(用于密钥协商)
			KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");

			keyAgreement.init(this.privateKey); // 初始化“自己的PrivateKey”
			keyAgreement.doPhase(recePublicKey, true); // 根据"对方的PublicKey"

			// 生成SecretKey本地密钥(共享公钥)
			this.secretKey = keyAgreement.generateSecret();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
				
	}

	public void printKeys() {
		System.out.printf("Name: %s\n", this.name);
		System.out.printf("Private key: %x\n", new BigInteger(1, this.privateKey.getEncoded()));
		System.out.printf("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded()));
		System.out.printf("Secret key: %x\n", new BigInteger(1, this.secretKey));
	}
}

三、非对称加密算法(RSA)

1、概述

非对称加密:加密和解密使用的不是相同的密钥,用户A密钥加密后所得的信息,只能用用户A的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。只有同一个公钥-私钥对才能正常加解密。

非对称加密的缺点:运算速度非常慢,比对称加密要慢很多。

2、案例

小明要加密一个消息发送给小红,他应该首先向小红索取她的公钥,然后,他用小红的公钥加密,把加密结果发送给小红,此文件只能由小红的私钥解开,因为小红的私钥在她自己手里,所以,除了小红,没有任何人能解开此加密结果。

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

		// 创建公钥/私钥对
		Human hong = new Human("小红");
		Human ming = new Human("小明");
		
		// 小明使用小红的公钥进行加密
		// 1.获取小红的公钥
		PublicKey hongPublicKey = hong.getPublicKey();
		System.out.println(String.format("小红的public key(公钥): %x", new BigInteger(1, hongPublicKey.getEncoded())));

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

		// 小红使用自己的私钥解密:
		// 1.获取小红的私钥,并输出
		PrivateKey hongPrivateKey = hong.getPrivateKey();
		System.out.println(String.format("小红的private key(私钥): %x", new BigInteger(1, hongPrivateKey.getEncoded())));

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

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

	// 私钥:
	PrivateKey privatekey;

	// 公钥:
	PublicKey publickey;

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

		// 生成公钥/私钥对:
		KeyPairGenerator kpGen=KeyPairGenerator.getInstance("RSA");
		kpGen.initialize(1024);
		KeyPair kp=kpGen.generateKeyPair();
		
		this.privatekey=kp.getPrivate();
		this.publickey=kp.getPublic();
		
	}

	// 把私钥导出为字节
	public PrivateKey getPrivateKey() {
		return this.privatekey;
	}

	// 把公钥导出为字节
	public PublicKey getPublicKey() {
		return this.publickey;
	}

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

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

RSA的公钥和私钥都可以通过getEncoded()方法获得以byte[]表示的二进制数据,并根据需要保存到文件中。要从byte[]数组恢复公钥或私钥,可以这么写:

byte[] pkData = ...
byte[] skData = ...
KeyFactory kf = KeyFactory.getInstance("RSA");
// 恢复公钥:
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
PublicKey pk = kf.generatePublic(pkSpec);
// 恢复私钥:
PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
PrivateKey sk = kf.generatePrivate(skSpec);
3、小结
  • 非对称加密就是加密和解密使用的不是相同的密钥,只有同一个公钥-私钥对才能正常加解密;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kⅈꫛᧁ269

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值