非對稱加密算法 - DH算法
DH算法是非對稱加密算法的鼻祖轮纫,為非對稱加密算法奠定了基礎(chǔ)捎稚,主要用途是進(jìn)行密鑰交換棉饶。
DH算法歷史
1976年非對稱加密算法思想被提出表箭,但是當(dāng)時(shí)并沒有給出具體算法和方案赁咙,因?yàn)楫?dāng)時(shí)沒有研究出單向函數(shù)
(也就是信息摘要算法還沒出現(xiàn)),但是IEEE的期刊(作者:W.Diffie和M.Hellman)中給出了通信時(shí)雙方如何通過信息交換協(xié)商密鑰
的算法燃逻,也就是DH算法序目,通過該算法雙方可以協(xié)商對稱加密的密鑰。
DH算法的目的僅在于<b>雙方在安全的環(huán)境下協(xié)商一個(gè)加解密的密鑰</b>伯襟,因此僅僅用于密鑰分配猿涨,不能用于加解密消息。
應(yīng)用場景
僅用于密鑰交換場景姆怪,不適用于數(shù)據(jù)傳輸?shù)募咏饷芘炎鏏B兩個(gè)系統(tǒng)需要交換密鑰澡绩,則過程如下:
- A系統(tǒng)構(gòu)建密鑰:構(gòu)建一對公私密鑰Private Key1和Public Key1;
- A系統(tǒng)向B系統(tǒng)公布自己的公鑰(Public Key1)俺附;
- B系統(tǒng)使用A公布的公鑰(Public Key1)建立一對密鑰:Private Key2和Public Key2肥卡;
- B系統(tǒng)向A系統(tǒng)公布自己的公鑰Public Key2;
- A系統(tǒng)使用自己的私鑰Private Key1和B系統(tǒng)的公鑰Public Key2構(gòu)建本地密鑰事镣;
- B系統(tǒng)使用自己的私鑰Private Key2和A系統(tǒng)的公鑰Public Key1構(gòu)建本地密鑰步鉴;
關(guān)鍵點(diǎn):B系統(tǒng)使用A系統(tǒng)的
公鑰
建立加密用的Key;本地密鑰用來加解密數(shù)據(jù)璃哟;
雖然AB系統(tǒng)使用了不同的密鑰建立自己的本地密鑰氛琢,但是AB系統(tǒng)獲得本地密鑰是一致的。
流程描述
sequenceDiagram
A->> A: 構(gòu)建密鑰對:private key1 和 public key1
A->> B: 公布自己的公鑰: public key1
B->> B: 使用public key1構(gòu)建自己的密鑰對 private key2 和 public key2随闪;
B-->> A: 返回自己的public key2阳似;
A->> A: 使用private key1 和 public key2 構(gòu)建本地密鑰;
B->> B : 使用private key2 和 public key1構(gòu)建本地密鑰铐伴;
Java中算法實(shí)現(xiàn)
Java提供DH
算法的實(shí)現(xiàn)撮奏,不用使用第三方開源包,DH算法的產(chǎn)生的密鑰長度在512到1024
之間当宴,必須是64的倍數(shù)畜吊,默認(rèn)是1024。
A系統(tǒng)建立自己的密鑰對
此過程不需要參數(shù)
/***
* A系統(tǒng)產(chǎn)生密鑰,這個(gè)過程不需要參數(shù),由DH算法計(jì)算得出<br/>
* 內(nèi)部使用一些安全的隨機(jī)函數(shù)隨機(jī)計(jì)算出一個(gè)公私鑰<br>
* 計(jì)算后的公私鑰要存儲下來,存儲二進(jìn)制數(shù)據(jù)
*
* @return
* @throws Exception
*/
public static Map<String, Key> initASysKey() throws Exception {
// 使用DH算法生成公司密鑰
KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(KEY_ALGORITH);
keyPairGr.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGr.generateKeyPair();
// 可以使用DB算法專業(yè)密鑰前行轉(zhuǎn)換獲取的PublicKey和PrivateKey
// DHPublicKey dhPK = (DHPublicKey) publicKey;
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 存儲,傳輸密鑰
Map<String, Key> keyMap = new HashMap<String, Key>(2);
keyMap.put(MAP_KEY_PUBLIC, publicKey);
keyMap.put(MAP_KEY_PRIVATGE, privateKey);
return keyMap;
}
B建立自己的密鑰對
需要A系統(tǒng)發(fā)送自己的公鑰給B后即供,B才能建立自己的密鑰對:
/**
* B構(gòu)建自己的公私鑰,要使用A的公鑰構(gòu)建<br>
* DH算法接受公鑰,并構(gòu)建自己的密鑰對<br>
* 這個(gè)過程中用到了一些密鑰格式轉(zhuǎn)換對象,不是重點(diǎn)<br>
* B也要保持自己的密鑰對,二進(jìn)制形式
*
* @param pubKey
* @return
* @throws Exception
*/
public static Map<String, Key> initBSysKey(byte[] pubKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
// 密鑰格式轉(zhuǎn)換對象
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
// 轉(zhuǎn)換PublicKey格式
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 構(gòu)建DH算法參數(shù)
DHParameterSpec dhParamSpec = ((DHPublicKey) publicKey).getParams();
// 使用DH算法創(chuàng)建密鑰對
KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
keyPairGr.initialize(dhParamSpec);
KeyPair keyPair = keyPairGr.generateKeyPair();
// 創(chuàng)建的公私鑰
DHPublicKey publicKey1 = (DHPublicKey) keyPair.getPublic();
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 存儲傳輸密鑰
Map<String, Key> keyMap = new HashMap<String, Key>(2);
keyMap.put(MAP_KEY_PUBLIC, publicKey1);
keyMap.put(MAP_KEY_PRIVATGE, privateKey);
return keyMap;
}
AB分別建立自己的本地密鑰
A定拟、B系統(tǒng)都需要對方已經(jīng)發(fā)送了公鑰給自己,使用自己的私鑰和對方的公鑰建立密鑰對逗嫡。
/**
* 使用對方的公鑰和自己的私鑰構(gòu)建對稱加密的SecretKey<br>
*
* @param publicKey
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception {
// 建立密鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
// 密鑰編碼轉(zhuǎn)換對象
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
// 轉(zhuǎn)換公鑰格式
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 密鑰編碼轉(zhuǎn)換對象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 轉(zhuǎn)換私鑰格式
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 利用公鑰和私鑰創(chuàng)建本地密鑰
KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
// 創(chuàng)建了一個(gè)本地密鑰
SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITH);
return secretKey.getEncoded();
}
AB系統(tǒng)加解密數(shù)據(jù)
使用構(gòu)建的本地密鑰對進(jìn)行數(shù)據(jù)加解密:
/**
* 使用本地密鑰加密數(shù)據(jù)
*
* @param key
* @param data
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
// 構(gòu)建本地密鑰
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 使用本地密鑰解密數(shù)據(jù)
*
* @param key
* @param data
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
// 構(gòu)建本地密鑰
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
完整的代碼流程
NOTE:下面的代碼最后加解密步驟無法完成青自,之前提過,對稱加密技術(shù)驱证,JAVA的AES僅支持128位長度的密鑰延窜,DH最少產(chǎn)生512的密鑰,所以一般Java系統(tǒng)無法支持抹锄,需要下載授權(quán)文件逆瑞。
public class DHTest {
// 密鑰交換算法
public static final String KEY_ALGORITH = "DH";
// 對稱加密算法
public static final String SECRET_ALGORITH = "AES";
// DH算法的密鑰長度
private static final int KEY_SIZE = 512;
// Map的一些參數(shù)
private static final String MAP_KEY_PUBLIC = "DHPublicKey";
private static final String MAP_KEY_PRIVATGE = "DHPrivateKey";
public static void main(String[] args) throws Exception {
// A系統(tǒng)構(gòu)建自己的公私鑰
Map<String, Key> aKeyMap = initASysKey();
byte[] aPubKey = aKeyMap.get(MAP_KEY_PUBLIC).getEncoded();
byte[] aPriKey = aKeyMap.get(MAP_KEY_PRIVATGE).getEncoded();
logKeyMap("A", aPubKey, aPriKey);
// A將自己的公鑰發(fā)給B,B構(gòu)建自己的公私鑰
// 一般都是發(fā)送二進(jìn)制或者base64等數(shù)據(jù)
Map<String, Key> bKeyMap = initBSysKey(aPubKey);
byte[] bPubKey = bKeyMap.get(MAP_KEY_PUBLIC).getEncoded();
byte[] bPriKey = bKeyMap.get(MAP_KEY_PRIVATGE).getEncoded();
logKeyMap("B", bPubKey, bPriKey);
// A B系統(tǒng)產(chǎn)生自己的本地對稱加密算法密鑰
byte[] aSecretKey = getSecretKey(bPubKey, aPriKey);
byte[] bSecretKey = getSecretKey(aPubKey, bPriKey);
// 轉(zhuǎn)換為字符串比較下
String aSecKeyStr = toBase64(aSecretKey);
String bSecKeyStr = toBase64(bSecretKey);
log("A SecretKey : %s", aSecKeyStr);
log("B SecretKey : %s", bSecKeyStr);
log("A B SecretKey equeals : %s", aSecKeyStr.equals(bSecKeyStr));
log("%s", aSecretKey.length);
// A加密數(shù)據(jù),B解密數(shù)據(jù),能正常加解密
String input = "A要發(fā)送給B的數(shù)據(jù)";
byte[] data = encrypt(aSecretKey, input.getBytes());
byte[] rs = decrypt(bSecretKey, data);
log("A Send : %s , B Recive : %s ", toBase64(data), new String(rs));
}
/***
* A系統(tǒng)產(chǎn)生密鑰,這個(gè)過程不需要參數(shù),由DH算法計(jì)算得出<br/>
* 內(nèi)部使用一些安全的隨機(jī)函數(shù)隨機(jī)計(jì)算出一個(gè)公私鑰<br>
* 計(jì)算后的公私鑰要存儲下來,存儲二進(jìn)制數(shù)據(jù)
*
* @return
* @throws Exception
*/
public static Map<String, Key> initASysKey() throws Exception {
// 使用DH算法生成公司密鑰
KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(KEY_ALGORITH);
keyPairGr.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGr.generateKeyPair();
// 可以使用DB算法專業(yè)密鑰前行轉(zhuǎn)換獲取的PublicKey和PrivateKey
// DHPublicKey dhPK = (DHPublicKey) publicKey;
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 存儲,傳輸密鑰
Map<String, Key> keyMap = new HashMap<String, Key>(2);
keyMap.put(MAP_KEY_PUBLIC, publicKey);
keyMap.put(MAP_KEY_PRIVATGE, privateKey);
return keyMap;
}
/**
* B構(gòu)建自己的公私鑰,要使用A的公鑰構(gòu)建<br>
* DH算法接受公鑰,并構(gòu)建自己的密鑰對<br>
* 這個(gè)過程中用到了一些密鑰格式轉(zhuǎn)換對象,不是重點(diǎn)<br>
* B也要保持自己的密鑰對,二進(jìn)制形式
*
* @param pubKey
* @return
* @throws Exception
*/
public static Map<String, Key> initBSysKey(byte[] pubKey) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
// 密鑰格式轉(zhuǎn)換對象
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
// 轉(zhuǎn)換PublicKey格式
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 構(gòu)建DH算法參數(shù)
DHParameterSpec dhParamSpec = ((DHPublicKey) publicKey).getParams();
// 使用DH算法創(chuàng)建密鑰對
KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
keyPairGr.initialize(dhParamSpec);
KeyPair keyPair = keyPairGr.generateKeyPair();
// 創(chuàng)建的公私鑰
DHPublicKey publicKey1 = (DHPublicKey) keyPair.getPublic();
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 存儲傳輸密鑰
Map<String, Key> keyMap = new HashMap<String, Key>(2);
keyMap.put(MAP_KEY_PUBLIC, publicKey1);
keyMap.put(MAP_KEY_PRIVATGE, privateKey);
return keyMap;
}
/**
* 使用對方的公鑰和自己的私鑰構(gòu)建對稱加密的SecretKey<br>
*
* @param publicKey
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception {
// 建立密鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
// 密鑰編碼轉(zhuǎn)換對象
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
// 轉(zhuǎn)換公鑰格式
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 密鑰編碼轉(zhuǎn)換對象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 轉(zhuǎn)換私鑰格式
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 利用公鑰和私鑰創(chuàng)建本地密鑰
KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
// 創(chuàng)建了一個(gè)本地密鑰
SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITH);
return secretKey.getEncoded();
}
/**
* 使用本地密鑰加密數(shù)據(jù)
*
* @param key
* @param data
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
// 構(gòu)建本地密鑰
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 使用本地密鑰解密數(shù)據(jù)
*
* @param key
* @param data
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
// 構(gòu)建本地密鑰
SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
public static void log(String tmp, Object... params) {
System.out.println(String.format(tmp, params));
}
public static void logKeyMap(String sysName, byte[] pubKey, byte[] priKey) {
log("%s Public Key : %s", sysName, toBase64(pubKey));
log("%s Private Key : %s", sysName, toBase64(priKey));
}
private static String toBase64(byte[] data) {
return new String(Base64.getEncoder().encode(data));
}
}