密钥交换算法 - Java加密与安全

密钥交换算法 - Java加密与安全_第1张图片

密钥交换算法

我们在使用对称加密算法的时候,我们用的是同一个密钥key

密钥交换算法 - Java加密与安全_第2张图片

我们以AES加密为例,当我们需要加密明文,我们需要一个随机生成的key,作为密钥进行加解密,最后我们的问题是如何传递密钥

密钥交换算法 - Java加密与安全_第3张图片

因为不给对方密钥,对方就不能解密,而直接传递密钥,就会被黑客监听,所以问题就变成了如何在不安全的信道上

如何安全的传输密钥

密钥交换算法 - Java加密与安全_第4张图片

密钥算法也就是Diffie-Hellman算法,简称DH算法,他就是为了解决这个问题的,p=509,g=5,A=215,

你收到以后,他也选择一个随机数b,例如456,然后得到g的b次方,然后对p取余数,结果是181,然后计算S等于A的b次方,

除以p的余数,结果是121,乙把计算的B发给甲,甲计算S等于B的a次方,除以p的余数,结果计算的s相同,也是121,

所以最后双方协商的密钥,是121,要注意这个密钥并没有在网络上传输,通过网络传输的是p,g,A,B,但是通过这4个

数,黑客是无法推算出S的,所以更确切的说,DH算法是密钥协商算法,双方最终协商一个共同的密钥

密钥交换算法 - Java加密与安全_第5张图片

我们把小a看成是甲的密钥,大A看成是甲的公钥,小b看成是乙的私钥,大b看成是公钥,DH的算法本质是各自生成自己的

公钥和私钥,然后交换公钥,并且根据自己的私钥和对方的公钥,生成最终的私钥,DH算法通过数学
package com.learn.securl;

import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;

public class DH {
	
	/**
	 * 我们写一个main方法来测试DH协商协议
	 * @param args
	 */
	public static void main(String[] args) {
		// Bob 和 Alice
		/**
		 * 首先我们创建两个Person对象
		 * 一个是Bob,一个是Alice
		 */
		Person bob = new Person("Bob");
		Person alice = new Person("Alice");
		// 各自生成KeyPair:
		/**
		 * 然后Bob和Alice各自生成自己的KeyPair
		 */
		bob.generateKeyPair();
		alice.generateKeyPair();
		// 双方交换各自的PublicKey:
		// Bob根据Alice的PublicKey生成自己的本地密钥:
		/**
		 * 紧接着各自交换publicKey
		 * 由于Bob需要Alice的publicKey生成自己的本地密钥
		 * 所以我们对Bob调用generateSecretKey的时候
		 * 我们需要传入alice.publicKey.getEncoded()
		 */
		bob.generateSecretKey(alice.publicKey.getEncoded());
		// Alice根据Bob的PublicKey生成自己的本地密钥:
		/**
		 * 同样的Alice是通过bob的publicKey来生成自己的本地密钥
		 */
		alice.generateSecretKey(bob.publicKey.getEncoded());
		// 检查双方本地密钥是否相同
		/**
		 * 我们来看一下他们的本地密钥是否相同
		 * 如果双方的SecretKey是相同的
		 * 那么后序的通信就可以使用这个SecretKey
		 * AES加解密
		 * 我们看到Bob的privateKey和publicKey以及Bob生成的secretKey
		 * 我们再来看Alice的privateKey和publicKey以及Alice生成的secretKey
		 * 我们注意到Alice的SecretKey和Bob的SecretKey他们是相同的
		 * 所以在后序的通讯中,
		 * Bob和Alice可以使用这两个相同的key以及AES的加解密
		 */
		bob.printKeys();
		alice.printKeys();
		// 双方的SecretKey相同,后续通信将使用SecretKey作为密钥进行AES加密
		String msgBobToAlice = bob.sendMessage("Hello, Alice!");
		System.out.println("Bob -> Alice: " + msgBobToAlice);
		String aliceDecrypted = alice.receiveMessage(msgBobToAlice);
		System.out.println("Alice decrypted: " + aliceDecrypted);
	}
}

/**
 * 我们首先定义了一个Person类
 * @author Leon.Sun
 *
 */
class Person {
	/**
	 * 我们定义了一个name字段,表示Person名字
	 */
	public final String name;
	/**
	 * publicKey表示public Key
	 */
	public PublicKey publicKey;
	/**
	 * privateKey表示private Key
	 */
	private PrivateKey privateKey;
	/**
	 * 这个把表示最后生成的secretKey
	 */
	private SecretKey secretKey;
	
	/**
	 * 我们定义个方法来初始化Person的名字
	 * @param name
	 */
	public Person(String name) {
		this.name = name;
	}
	/**
	 * 生成本地KeyPair
	 * 
	 * 紧接着我们定义一个generateKeyPair
	 */
	public void generateKeyPair() {
		try {
			/**
			 * 我们通过KeyPairGenerator.getInstance传入DH表示我们要生成KeyPairGenerator
			 * 用于DH算法
			 */
			KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH");
			/**
			 * 表示我们要生成512位的keyPair
			 */
			kpGen.initialize(512);
			/**
			 * 我们通过generateKeyPair所获得一个KeyPair对象
			 */
			KeyPair kp = kpGen.generateKeyPair();
			/**
			 * 紧接着我们通过getPrivate和getPublic分别获得了privateKey和publicKey
			 */
			this.privateKey = kp.getPrivate();
			this.publicKey = kp.getPublic();
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 生成密钥
	 * 
	 * 当通信的双方把publicKey交给双方的时候,
	 * 我们都可以从对方接收到public key这个字节中回复public key对象
	 * 
	 * @param receivedPubKeyBytes
	 */
	public void generateSecretKey(byte[] receivedPubKeyBytes) {
		try {
			// 从byte[]恢复PublicKey
			/**
			 * 我们通过X509EncodedKeySpec这个类传入收到的字节数组
			 * 就可以恢复公钥
			 */
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
					receivedPubKeyBytes);
			KeyFactory kf = KeyFactory.getInstance("DH");
			/**
			 * 我们可以通过generatePublic
			 */
			PublicKey receivedPublicKey = kf.generatePublic(keySpec);
			// 生成本地密钥:
			/**
			 * 我们通过KeyAgreement.getInstance传入DH得到一个KeyAgreement对象
			 */
			KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
			/**
			 * 然后我们通过init方法传入自己的private key
			 */
			keyAgreement.init(this.privateKey); // 自己的PrivateKey
			/**
			 * 然后我们通过doPhase传入对方的publicKey,
			 */
			keyAgreement.doPhase(receivedPublicKey, true);// 对方的PublicKey
			// 生成AES密钥:
			/**
			 * 这个时候我们通过generateSecret传入AES表示我们要生成一个AES的密钥
			 * 这个SecretKey就是我们将来要用的密钥
			 */
			this.secretKey = keyAgreement.generateSecret("AES");
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 打印密钥
	 * 
	 * 我们用printKeys打印privateKey,publicKey,secretKey
	 */
	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.getEncoded()));
	}
	/**
	 * 发送加密消息
	 * 
	 * 我们用sendMessage方法用来测试AES加密
	 * 
	 * @param message
	 * @return
	 */
	public String sendMessage(String message) {
		try {
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.ENCRYPT_MODE, this.secretKey);
			byte[] data = cipher.doFinal(message.getBytes("UTF-8"));
			return Base64.getEncoder().encodeToString(data);
		} catch (GeneralSecurityException | IOException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 接收加密消息并解密
	 * 
	 * 我们用receiveMessage来测试收到的信息,
	 * 并且用AES解密
	 * 
	 * @param message
	 * @return
	 */
	public String receiveMessage(String message) {
		try {
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
			byte[] data = cipher.doFinal(Base64.getDecoder().decode(message));
			return new String(data, "UTF-8");
		} catch (GeneralSecurityException | IOException e) {
			throw new RuntimeException(e);
		}
	}
}

密钥交换算法 - Java加密与安全_第6张图片

DH算法不能避免中间人攻击,如果黑客假冒乙和甲交互密钥,同时又假冒乙和甲交互密钥,他就可以成功的进行攻击


最后我们总结一下:

1. DH算法是一种密钥交换协议,通信双方通过一个不安全的信道协商密钥,然后进行对称加密传输

2. DH算法并没有解决中间人攻击的问题

 

你可能感兴趣的:(密钥交换算法 - Java加密与安全)