密鑰交換算法DH(Java實現)


密鑰交換算法——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算法實現(DHCoder.java)

package 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.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public abstract class DHCoder {
/**
* 非對稱加密密鑰算法
*/
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 key 甲方公鑰
* @return Map 乙方密鑰Map
* @throws Exception
*/
public static Map<String, Object> initKey(byte[] key) throws Exception{
//解析甲方公鑰
//轉換公鑰材料
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//實例化密鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//產生公鑰
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//由甲方公鑰構建乙方密鑰
DHParameterSpec dhParameterSpec = ((DHPublicKey)pubKey).getParams();
//實例化密鑰對生成器
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 dh;

import java.util.Map;

import org.apache.commons.codec.binary.Base64;

public class DHTest {
//甲方公鑰
private static byte[] publicKey1;
//甲方私鑰
private static byte[] privateKey1;
//甲方本地密鑰
private static byte[] key1;
//乙方公鑰
private static byte[] publicKey2;
//乙方私鑰
private static byte[] privateKey2;
//乙方本地密鑰
private static byte[] key2;

/**
* 初始化密鑰
* @throws Exception
*/
public static final void initKey() throws Exception{
//生成甲方密鑰對
Map<String, Object> keyMap1 = DHCoder.initKey();
publicKey1 = DHCoder.getPublicKey(keyMap1);
privateKey1 = DHCoder.getPrivateKey(keyMap1);
System.out.println("甲方公鑰:\n" + Base64.encodeBase64String(publicKey1));
System.out.println("甲方私鑰:\n" + Base64.encodeBase64String(privateKey1));
//由甲方公鑰產生本地密鑰對
Map<String, Object> keyMap2 = DHCoder.initKey(publicKey1);
publicKey2 = DHCoder.getPublicKey(keyMap2);
privateKey2 = DHCoder.getPrivateKey(keyMap2);
System.out.println("乙方公鑰:\n" + Base64.encodeBase64String(publicKey2));
System.out.println("乙方私鑰:\n" + Base64.encodeBase64String(privateKey2));
key1 = DHCoder.getSecretKey(publicKey2, privateKey1);
System.out.println("甲方本地密鑰:\n" + Base64.encodeBase64String(key1));
key2 = DHCoder.getSecretKey(publicKey1, privateKey2);
System.out.println("乙方本地密鑰:\n" + Base64.encodeBase64String(key2));
}

/**
* 主方法
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
initKey();
System.out.println();
System.out.println("===甲方向乙方發送加密數據===");
String input1 = "求知若飢,虛心若愚。";
System.out.println("原文:\n" + input1);
System.out.println("---使用甲方本地密鑰對數據進行加密---");
//使用甲方本地密鑰對數據加密
byte[] encode1 = DHCoder.encrypt(input1.getBytes(), key1);
System.out.println("加密:\n" + Base64.encodeBase64String(encode1));
System.out.println("---使用乙方本地密鑰對數據庫進行解密---");
//使用乙方本地密鑰對數據進行解密
byte[] decode1 = DHCoder.decrypt(encode1, key2);
String output1 = new String(decode1);
System.out.println("解密:\n" + output1);

System.out.println("/~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~..~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~/");
initKey();
System.out.println("===乙方向甲方發送加密數據===");
String input2 = "好好學習,天天向上。";
System.out.println("原文:\n" + input2);
System.out.println("---使用乙方本地密鑰對數據進行加密---");
//使用乙方本地密鑰對數據進行加密
byte[] encode2 = DHCoder.encrypt(input2.getBytes(), key2);
System.out.println("加密:\n" + Base64.encodeBase64String(encode2));
System.out.println("---使用甲方本地密鑰對數據進行解密---");
//使用甲方本地密鑰對數據進行解密
byte[] decode2 = DHCoder.decrypt(encode2, key1);
String output2 = new String(decode2);
System.out.println("解密:\n" + output2);
}
}

4.測試結果







      


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com