1. 程式人生 > >java加密體系-祕鑰交換演算法DH

java加密體系-祕鑰交換演算法DH

金鑰交換演算法(祕鑰協商演算法)——DH

1.簡述

    1976年,W.Diffie和M.Hellman在發表的論文中提出了公鑰加密演算法思想,但當時並沒有給出具體的實施方案,原因在於沒有找到單向函式(也就是訊息摘要演算法),但在該論文中給出了通訊雙方通過資訊交換協商金鑰的演算法,即Diffie-Hellman金鑰交換演算法(簡稱為DH演算法)。該演算法的目的在於讓訊息的收發雙方可以在安全的條件下交換金鑰,以備後續加密/解密使用。因此,DH演算法是第一個金鑰協商演算法,但僅能用於金鑰分配,不能用於加密或者解密訊息。

    DH金鑰交換演算法的安全性基於有限域上的離散對數難題。基於這種安全性,通過DH演算法進行金鑰分配,使得訊息的收發雙方可以安全地交換一個金鑰,再通過這個金鑰對資料進行加密和解密處理。

2.模型分析

    我們以訊息傳遞模型為例,甲方作為傳送者,乙方作為接受者,分述甲乙雙方如何構建金鑰、互動金鑰和加密資料。

    首先,甲乙雙方需要在收發訊息前構建自己的金鑰對,如圖1所示。

    

    甲乙雙方構建金鑰需要經過以下幾個步驟:

    1)由訊息傳送的一方構建金鑰,這裡由甲方構建金鑰。

    2)由構建金鑰的一方向對方公佈其公鑰,這裡由甲方向乙方釋出公鑰。

    3)由訊息接收的一方通過對方公鑰構建自身金鑰,這裡由乙方使用甲方公鑰構建乙方金鑰。

    4)由訊息接收的一方向對方公佈其公鑰,這裡由乙方向甲方公佈公鑰。

    這裡要注意的是,乙方構建自己金鑰對的時候需要使用甲方公鑰作為引數這是很關鍵的一點,如果缺少了這一環節則無法確保甲乙雙方獲得同一個金鑰,訊息加密更無從談起。

    其次,假設甲乙雙方事先約定好了用於資料加密的對稱加密演算法(如AES演算法),並構建本地金鑰(即對稱加密演算法中的金鑰),如圖2所示。

    甲方需要使用自己的私鑰和乙方的公鑰才能構建自己的本地金鑰,乙方需要使用自己的私鑰和甲方的公鑰才能構建自己的本地金鑰。

    雖然甲乙雙方使用了不同的金鑰來構建本地金鑰,但是甲乙兩方得到的金鑰其實是一致的,後面的demo可以證明,也正是基於此,甲乙雙方才能順利地進行加密訊息的傳送。

    最後,甲乙雙方構建了本地金鑰後,可按照基於對稱加密演算法的訊息傳遞模型完成訊息傳遞。如圖4所示。

    作為對稱加密體制向非對稱加密體制的一種過渡,DH演算法僅僅比一般的對稱加密演算法多了金鑰對的構建和本地金鑰的構建這兩項操作,而真正的資料加密/解密操作仍由對稱加密演算法完成。

3.實現

1)DH演算法實現(DHUtil.java)

package com.mpush.dh;
 
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.SecretKeySpec;
 
public abstract class DHUtil {
	
    /**
     * 祕鑰交換演算法(非對稱加密演算法)
     */
	private static final String KEY_ALGORITHM = "DH";
	
    /**
     * 本地金鑰演算法,即對稱加密金鑰演算法
     * 可選DES、DESede或者AES
     */
	private static final String SELECT_ALGORITHM = "AES";
	
	/**
	 * 金鑰長度
	 */
	private static final int KEY_SIZE = 512;
	
	//公鑰
	private static final String PUBLIC_KEY = "DHPublicKey";
	
	//私鑰
	private static final String PRIVATE_KEY = "DHPrivateKey";
	
	
	/**
	 * 初始化金鑰
	 * @return Map 金鑰Map
	 * @throws Exception
	 */
	public static Map<String, Object> initKey() throws Exception{
		//例項化金鑰對生成器
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		//初始化金鑰對生成器
		keyPairGenerator.initialize(KEY_SIZE);
		//生成金鑰對
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		//甲方公鑰
		DHPublicKey publicKey = (DHPublicKey)keyPair.getPublic();
		//甲方私鑰
		DHPrivateKey privateKey = (DHPrivateKey)keyPair.getPrivate();
		//將金鑰對儲存在Map中
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	/**
	 * 加密
	 * @param data 待加密資料
	 * @param key 金鑰
	 * @return byte[] 加密資料
	 * @throws Exception
	 */
    public static byte[] encrypt(byte[] data, byte[] key) throws Exception{
    	//生成本地金鑰
    	SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM);
    	//資料加密
    	Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
    	cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    	return cipher.doFinal(data);
    }
    
    /**
     * 解密
     * @param data 待解密資料
     * @param key 金鑰
     * @return byte[] 解密資料
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, byte[] key) throws Exception{
    	//生成本地金鑰
    	SecretKey secretKey = new SecretKeySpec(key, SELECT_ALGORITHM);
    	//資料揭祕
    	Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
    	cipher.init(Cipher.DECRYPT_MODE, secretKey);
    	return cipher.doFinal(data);
    }
    
    /**
     * 構建金鑰
     * @param publicKey 公鑰
     * @param privateKey 私鑰
     * @return byte[] 本地金鑰
     * @throws Exception
     */
    public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception{
    	//例項化金鑰工廠
    	KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    	//初始化公鑰
    	//金鑰材料轉換
    	X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
    	//產生公鑰
    	PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
    	//初始化私鑰
    	//金鑰材料轉換
    	PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
    	//產生私鑰
    	PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
    	//例項化
    	KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
    	//初始化
    	keyAgree.init(priKey);
    	keyAgree.doPhase(pubKey, true);
    	//生成本地金鑰
    	SecretKey secretKey = keyAgree.generateSecret(SELECT_ALGORITHM);
    	return secretKey.getEncoded();
    }
    
    /**
     * 取得私鑰
     * @param keyMap 金鑰Map
     * @return byte[] 私鑰
     * @throws Exception
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception{
    	Key key = (Key) keyMap.get(PRIVATE_KEY);
    	return key.getEncoded();
    }
    
    /**
     * 取得公鑰
     * @param keyMap 金鑰Map
     * @return byte[] 公鑰
     * @throws Exception
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception{
    	Key key = (Key) keyMap.get(PUBLIC_KEY);
    	return key.getEncoded();
    }
}

2)DH演算法測試(DHTest.java)

package com.mpush.dh;

import java.util.Map;
import org.apache.commons.codec.binary.Hex;

public class DHTest {
	
	public static void main(String[] args) throws Exception {
		//構建a本地祕鑰對
		Map<String, Object> aKeyMap = DHUtil.initKey();
		byte[] aPublickeyBytes = DHUtil.getPublicKey(aKeyMap);
		byte[] aPrivatekeyBytes = DHUtil.getPrivateKey(aKeyMap);
		//公佈公鑰
		System.out.println("a公佈公鑰:"+Hex.encodeHexString(aPublickeyBytes));
		
		//構建b本地祕鑰對
		Map<String, Object> bKeyMap = DHUtil.initKey();
		byte[] bPublickeyBytes = DHUtil.getPublicKey(bKeyMap);
		byte[] bPrivatekeyBytes = DHUtil.getPrivateKey(bKeyMap);
		//公佈公鑰
		System.out.println("b公佈公鑰:"+Hex.encodeHexString(bPublickeyBytes));//實際開發中要保證公鑰傳輸安全(數字簽名)
		
		//a構建共享祕鑰
		byte[] aSecretKey = DHUtil.getSecretKey(bPublickeyBytes, aPrivatekeyBytes);
		String aSecretKeyStr = Hex.encodeHexString(aSecretKey);
		System.out.println("a構建共享祕鑰:"+aSecretKeyStr);
		//b構建共享祕鑰
		byte[] bSecretKey = DHUtil.getSecretKey(aPublickeyBytes, bPrivatekeyBytes);
		String bSecretKeyStr = Hex.encodeHexString(bSecretKey);
		System.out.println("b構建共享祕鑰:"+bSecretKeyStr);
		//c中間人構建共享祕鑰
		/*byte[] cSecretKey = DHUtil.getSecretKey(bPublickeyBytes, bPrivatekeyBytes);
		String cSecretKeyStr = Hex.encodeHexString(cSecretKey);
		System.out.println("c構建共享祕鑰:"+cSecretKeyStr);*/
		//祕鑰構建對比
		System.out.println(aSecretKeyStr.equals(bSecretKeyStr));
		
		//a傳送訊息
		String msg = "你好,癲狗";
		byte[] encryptMsg = DHUtil.encrypt(msg.getBytes(), aSecretKey);
		//b解密訊息
		byte[] decryptMsg = DHUtil.decrypt(encryptMsg, bSecretKey);
		System.out.println(new String(decryptMsg));
		
	}

}

4.測試結果

a公佈公鑰:3081df30819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4020201800343000240216c56913593ed52e468b81945a141e6b4f1f1cf7e6b77d1200c7cef145c9e7ad3fb9bfa05e79f0c79f545bd017b49696394fd173724ba9ee3e94ff97f3e52aa
b公佈公鑰:3081e030819706092a864886f70d010301308189024100fca682ce8e12caba26efccf7110e526db078b05edecbcd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e12ed0899bcd132acd50d99151bdc43ee737592e170240678471b27a9cf44ee91a49c5147db1a9aaf244f05a434d6486931d2d14271b9e35030b71fd73da179069b32e2935630e1c2062354d0da20a6c416e50be794ca4020201800344000241009a961219af79cc295dabbf476a6c4696ecc89482baba405ea9ba73a14df0c5faf8c8dff95f6ce1a31a5827dab3f4797986e7435314a61b33dc25385a5a64868c
a構建共享祕鑰:4a9ddf389f6712c04c7a48c46b845fc7fa685093f49bf734cc702d9bbca595a9
b構建共享祕鑰:4a9ddf389f6712c04c7a48c46b845fc7fa685093f49bf734cc702d9bbca595a9
true
你好,癲狗