Android-DH 秘鑰交換

Android-RSA 分段加密解密
Android-Openssl創(chuàng)建RSA公鑰和私鑰
Android-AES加解密
Android-DH 秘鑰交換

1. DH(Diffie-Hellman) 介紹

DH 是 Whitfield Diffie 和 Martin Hellman 在1976年共同發(fā)明的一種秘鑰交換算法。主要用于在不安全的網(wǎng)絡(luò)上客戶端和服務端通過交換公鑰,生成一個相同的秘鑰,并將該秘鑰作為對稱加密算法的秘鑰,達到使對稱加密算法的秘鑰可以動態(tài)修改的目的。這樣便提高了數(shù)據(jù)在網(wǎng)絡(luò)上傳輸?shù)陌踩浴?br> DH 總共包含四個部分,分別是:質(zhì)數(shù)原根對、公鑰、私鑰和秘鑰。

2. DH 秘鑰交換流程(交換公鑰生成共同的秘鑰)

1. 客戶端和服務端使用相同的質(zhì)數(shù)原根對:P=23 和 G=5,這是秘鑰交換的必須條件。

2. 服務端生成隨機整數(shù) A = 6,并將 A 作為私鑰,使用公鑰計算公式:
公鑰 = G 的 A 次方 取余 P,等于 Math.pow(5,6) % 23,服務端的公鑰為: 8。

服務端計算公鑰.png

3. 客戶端生成隨機整數(shù) B = 7,并將 B 作為私鑰,使用公鑰計算公式:
公鑰 = G 的 B 次方 取余 P,等于 Math.pow(5,7) % 23,客戶端的公鑰為: 17。

客戶端計算公鑰.png

4. 服務端用客戶端的公鑰生成秘鑰,使用秘鑰計算公式:
秘鑰 = 17 的 A 次方 取余 P,等于 Math.pow(17,6) % 23,服務端的秘鑰為: 12。

服務端計算秘鑰.png

5. 客戶端用服務端的公鑰生成秘鑰,使用秘鑰計算公式:
秘鑰 = 8 的 B 次方 取余 P,等于 Math.pow(8,7) % 23,客戶端的秘鑰為: 12。

客戶端計算秘鑰.png

客戶端和服務端通過交換公鑰,生成了相同的秘鑰。

3. DH 代碼實現(xiàn)

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;

/**
 * DH 秘鑰交換
 */
public class DH {
   /**
     * DH 秘鑰長度
     */
    private static final int KEY_LENGTH = 1024;

    /**
     * 秘鑰交換算法
     */
    private static final String KEY_ALGORITHM = "DH";
    /**
     * 獲取 DH 公鑰
     */
    public static final String DH_PUBLIC_KEY = "DHPublicKey";
    /**
     * 獲取 DH 私鑰
     */
    public static final String DH_PRIVATE_KEY = "DHPrivateKey";
    
   /**
     * 甲方初始化 公鑰 和 私鑰
     *
     * @return 可以通過 {@link this#getPublicKey(Map)} 獲取公鑰,
     * 可以通過 {@link this#getPrivateKey(Map)} 獲取私鑰鑰
     */
    public static Map<String, Object> initDHKey() {
        try {
            // 實例化密鑰對生成器
            KeyPairGenerator keyPairGenerator = 
            KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 初始化密鑰對生成器 默認是 1024 512-1024 & 64的倍數(shù)
            keyPairGenerator.initialize(KEY_LENGTH);
            // 生成密鑰對
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 得到甲方公鑰
            DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
            // 得到甲方私鑰
            DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
            // 將公鑰和私鑰封裝在Map中, 方便之后使用
            Map<String, Object> keyMap = new HashMap<>();
            keyMap.put(DH_PUBLIC_KEY, publicKey);
            keyMap.put(DH_PRIVATE_KEY, privateKey);
            return keyMap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
  
   /**
     * 乙方根據(jù)甲方公鑰初始化并返回密鑰對
     *
     * @param hexStrPubKey 甲方的公鑰 16 進制字符串
     * @return 可以通過 {@link this#getPublicKey(Map)} 獲取公鑰,
     * 可以通過 {@link this#getPrivateKey(Map)} 獲取私鑰鑰
     */
    public static Map<String, Object> initDHKey(String hexStrPubKey) {
        return initDHKey(getPublicKey(hexStrPubKey));
    }

    /**
     * 乙方根據(jù)甲方公鑰初始化并返回密鑰對
     *
     * @param dhPublicKey 甲方的公鑰 16 進制字符串
     * @return 可以通過 {@link this#getPublicKey(Map)} 獲取公鑰,
     * 可以通過 {@link this#getPrivateKey(Map)} 獲取私鑰鑰
     */
    public static Map<String, Object> initDHKey(byte[] dhPublicKey) {
        return initDHKey(getPublicKey(dhPublicKey));
    }

        /**
     * 乙方根據(jù)甲方公鑰初始化并返回密鑰對
     *
     * @param dhPublicKey 甲方的公鑰
     * @return 可以通過 {@link this#getPublicKey(Map)} 獲取公鑰,
     * 可以通過 {@link this#getPrivateKey(Map)} 獲取私鑰鑰
     */
    public static Map<String, Object> initDHKey(DHPublicKey dhPublicKey) {
        try {
            // 實例化密鑰對生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 用甲方公鑰初始化密鑰對生成器
            keyPairGenerator.initialize(dhPublicKey.getParams());
            // 產(chǎn)生密鑰對
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 得到乙方公鑰
            DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
            // 得到乙方私鑰
            DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
            // 將公鑰和私鑰封裝在Map中, 方便之后使用
            Map<String, Object> keyMap = new HashMap<>();
            keyMap.put(DH_PUBLIC_KEY, publicKey);
            keyMap.put(DH_PRIVATE_KEY, privateKey);
            return keyMap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

  /**
     * 根據(jù)對方的公鑰和自己的私鑰生成 本地密鑰,返回SecretKey對象的16進制字符串
     *
     * @param publicKey  公鑰
     * @param privateKey 私鑰
     * @return 秘鑰 16 進制字符串
     */
    public static String getSecretKeyHexString(DHPublicKey publicKey, DHPrivateKey privateKey) {
        byte[] secretKey = getSecretKeyBytes(publicKey, privateKey);
        return DataUtils.byte2HexString(secretKey);
    }

   /**
     * 根據(jù)對方的公鑰和自己的私鑰生成 本地密鑰,返回的是SecretKey對象的字節(jié)數(shù)組
     *
     * @param publicKey  公鑰
     * @param privateKey 私鑰
     * @return 秘鑰數(shù)組
     */
    public static byte[] getSecretKeyBytes(DHPublicKey publicKey, DHPrivateKey privateKey) {
        try {
            // 實例化 KeyAgreement
            KeyAgreement keyAgreement = KeyAgreement.getInstance(KEY_ALGORITHM);
            // 用自己的私鑰初始化keyAgreement
            keyAgreement.init(privateKey);
            // 結(jié)合對方的公鑰進行運算
            keyAgreement.doPhase(publicKey, true);
            // 開始生成本地密鑰 SecretKey
            // https://blog.csdn.net/fengzun_yi/article/details/104497160
            return keyAgreement.generateSecret();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

   /**
     * 獲取 DHPublicKey 對象
     *
     * @param hexStrPubKey DH 16 進制公鑰字符串
     */
    public static DHPublicKey getPublicKey(String hexStrPubKey) {
        byte[] pubKey = DataUtils.hexString2Byte(hexStrPubKey);
        return getPublicKey(pubKey);
    }

    /**
     * 獲取 DHPublicKey 對象
     *
     * @param publicKey DH 公鑰數(shù)組
     */
    public static DHPublicKey getPublicKey(byte[] publicKey) {
        try {
            // 實例化密鑰工廠
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 將公鑰從字節(jié)數(shù)組轉(zhuǎn)換為PublicKey
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKey);
            return (DHPublicKey) keyFactory.generatePublic(pubKeySpec);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 獲取 DHPrivateKey 對象
     *
     * @param hexStrPrvKey DH 16 進制私鑰字符串
     */
    public static DHPrivateKey getPrivateKey(String hexStrPrvKey) {
        byte[] prvKey = DataUtils.hexString2Byte(hexStrPrvKey);
        return getPrivateKey(prvKey);
    }

    /**
     * 獲取 DHPrivateKey 對象
     *
     * @param privateKey DH 私鑰數(shù)組
     */
    public static DHPrivateKey getPrivateKey(byte[] privateKey) {
        try {
            // 實例化密鑰工廠
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 將私鑰從字節(jié)數(shù)組轉(zhuǎn)換為 PrivateKey
            PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(privateKey);
            return (DHPrivateKey) keyFactory.generatePrivate(priKeySpec);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 獲取 DHPublicKey 對象 16 進制字符串
     */
    public static String getPublicKeyHexString(DHPublicKey dhPublicKey) {
        return DataUtils.byte2HexString(dhPublicKey.getEncoded());
    }

    /**
     * 從 Map 中取得私鑰
     */
    public static String getPrivateKeyHexString(DHPrivateKey dhPrivateKey) {
        return DataUtils.byte2HexString(dhPrivateKey.getEncoded());
    }

    /**
     * 從 Map 中取得公鑰
     */
    public static DHPublicKey getPublicKey(Map<String, Object> keyMap) {
        return (DHPublicKey) keyMap.get(DH_PUBLIC_KEY);
    }

    /**
     * 從 Map 中取得私鑰
     */
    public static DHPrivateKey getPrivateKey(Map<String, Object> keyMap) {
        return (DHPrivateKey) keyMap.get(DH_PRIVATE_KEY);
    }
}

4. 數(shù)據(jù)工具類

import android.util.Base64;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;

/**
 * 數(shù)據(jù)工具類
 */
public class DataUtils {
   /**
     * 將 Base64 字符串 解碼成 字節(jié)數(shù)組
     */
    public static byte[] base64Decode(String data) {
        return Base64.decode(data, Base64.NO_WRAP);
    }
    /**
     * 將 字節(jié)數(shù)組 轉(zhuǎn)換成 Base64 編碼
     */
    public static String base64Encode(byte[] data) {
        return Base64.encodeToString(data, Base64.NO_WRAP);
    }
}

5. DH 使用測試

public class{
    public static void main(String[] args) {
            // =================== 甲方 ===================
            Map<String, Object> map1 = DH.initDHKey();
            DHPublicKey dhPublicKey1 = DH.getPublicKey(map1);
            DHPrivateKey dhPrivateKey1 = DH.getPrivateKey(map1);
            logDHKey("甲-公鑰: " + DH.getPublicKeyHexString(dhPublicKey1));
            logDHKey("甲-私鑰: " + DH.getPrivateKeyHexString(dhPrivateKey1));

            // =================== 乙方 ===================
            Map<String, Object> map2 = DH.initDHKey(dhPublicKey1);
            DHPublicKey dhPublicKey2 = DH.getPublicKey(map2);
            DHPrivateKey dhPrivateKey2 = DH.getPrivateKey(map2);
            logDHKey("乙-公鑰: " + DH.getPublicKeyHexString(dhPublicKey2));
            logDHKey("乙-私鑰: " + DH.getPrivateKeyHexString(dhPrivateKey2));

            // =================== 甲方-計算秘鑰 ===================
            // 乙方的公鑰 和 自己的私鑰
            String secretKey1 = DH.getSecretKeyHexString(dhPublicKey2, dhPrivateKey1);
            logDHKey("甲-秘鑰: " + secretKey1);

            // =================== 乙方-計算秘鑰 ===================
            // 甲方的公鑰 和 自己的私鑰
            String secretKey2 = DH.getSecretKeyHexString(dhPublicKey1, dhPrivateKey2);
            logDHKey("乙-秘鑰: " + secretKey2);
            if (secretKey1.equals(secretKey2)) {
                logDHKey("兩個秘鑰相等...");
            }
    }

    public static void logDHKey(String msg){
        System.out.println(msg);
    }
}



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容