Android-DH 秘钥交换

Android-RSA 分段加密解密
Android-Openssl创建RSA公钥和私钥
Android-AES加解密
Android-DH 秘钥交换

1. DH(Diffie-Hellman) 介绍

DH 是 Whitfield Diffie 和 Martin Hellman 在1976年共同发明的一种秘钥交换算法。主要用于在不安全的网络上客户端和服务端通过交换公钥,生成一个相同的秘钥,并将该秘钥作为对称加密算法的秘钥,达到使对称加密算法的秘钥可以动态修改的目的。这样便提高了数据在网络上传输的安全性。
DH 总共包含四个部分,分别是:质数原根对、公钥、私钥和秘钥。

2. DH 秘钥交换流程(交换公钥生成共同的秘钥)

1. 客户端和服务端使用相同的质数原根对:P=23 和 G=5,这是秘钥交换的必须条件。

2. 服务端生成随机整数 A = 6,并将 A 作为私钥,使用公钥计算公式:
公钥 = G 的 A 次方 取余 P,等于 Math.pow(5,6) % 23,服务端的公钥为: 8。

服务端计算公钥.png

3. 客户端生成随机整数 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 代码实现

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 initDHKey() {
        try {
            // 实例化密钥对生成器
            KeyPairGenerator keyPairGenerator = 
            KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 初始化密钥对生成器 默认是 1024 512-1024 & 64的倍数
            keyPairGenerator.initialize(KEY_LENGTH);
            // 生成密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 得到甲方公钥
            DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
            // 得到甲方私钥
            DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
            // 将公钥和私钥封装在Map中, 方便之后使用
            Map keyMap = new HashMap<>();
            keyMap.put(DH_PUBLIC_KEY, publicKey);
            keyMap.put(DH_PRIVATE_KEY, privateKey);
            return keyMap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
  
   /**
     * 乙方根据甲方公钥初始化并返回密钥对
     *
     * @param hexStrPubKey 甲方的公钥 16 进制字符串
     * @return 可以通过 {@link this#getPublicKey(Map)} 获取公钥,
     * 可以通过 {@link this#getPrivateKey(Map)} 获取私钥钥
     */
    public static Map initDHKey(String hexStrPubKey) {
        return initDHKey(getPublicKey(hexStrPubKey));
    }

    /**
     * 乙方根据甲方公钥初始化并返回密钥对
     *
     * @param dhPublicKey 甲方的公钥 16 进制字符串
     * @return 可以通过 {@link this#getPublicKey(Map)} 获取公钥,
     * 可以通过 {@link this#getPrivateKey(Map)} 获取私钥钥
     */
    public static Map initDHKey(byte[] dhPublicKey) {
        return initDHKey(getPublicKey(dhPublicKey));
    }

        /**
     * 乙方根据甲方公钥初始化并返回密钥对
     *
     * @param dhPublicKey 甲方的公钥
     * @return 可以通过 {@link this#getPublicKey(Map)} 获取公钥,
     * 可以通过 {@link this#getPrivateKey(Map)} 获取私钥钥
     */
    public static Map initDHKey(DHPublicKey dhPublicKey) {
        try {
            // 实例化密钥对生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 用甲方公钥初始化密钥对生成器
            keyPairGenerator.initialize(dhPublicKey.getParams());
            // 产生密钥对
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 得到乙方公钥
            DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
            // 得到乙方私钥
            DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
            // 将公钥和私钥封装在Map中, 方便之后使用
            Map keyMap = new HashMap<>();
            keyMap.put(DH_PUBLIC_KEY, publicKey);
            keyMap.put(DH_PRIVATE_KEY, privateKey);
            return keyMap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

  /**
     * 根据对方的公钥和自己的私钥生成 本地密钥,返回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);
    }

   /**
     * 根据对方的公钥和自己的私钥生成 本地密钥,返回的是SecretKey对象的字节数组
     *
     * @param publicKey  公钥
     * @param privateKey 私钥
     * @return 秘钥数组
     */
    public static byte[] getSecretKeyBytes(DHPublicKey publicKey, DHPrivateKey privateKey) {
        try {
            // 实例化 KeyAgreement
            KeyAgreement keyAgreement = KeyAgreement.getInstance(KEY_ALGORITHM);
            // 用自己的私钥初始化keyAgreement
            keyAgreement.init(privateKey);
            // 结合对方的公钥进行运算
            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 公钥数组
     */
    public static DHPublicKey getPublicKey(byte[] publicKey) {
        try {
            // 实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 将公钥从字节数组转换为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 私钥数组
     */
    public static DHPrivateKey getPrivateKey(byte[] privateKey) {
        try {
            // 实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 将私钥从字节数组转换为 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 keyMap) {
        return (DHPublicKey) keyMap.get(DH_PUBLIC_KEY);
    }

    /**
     * 从 Map 中取得私钥
     */
    public static DHPrivateKey getPrivateKey(Map keyMap) {
        return (DHPrivateKey) keyMap.get(DH_PRIVATE_KEY);
    }
}

4. 数据工具类

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;

/**
 * 数据工具类
 */
public class DataUtils {
   /**
     * 将 Base64 字符串 解码成 字节数组
     */
    public static byte[] base64Decode(String data) {
        return Base64.decode(data, Base64.NO_WRAP);
    }
    /**
     * 将 字节数组 转换成 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 map1 = DH.initDHKey();
            DHPublicKey dhPublicKey1 = DH.getPublicKey(map1);
            DHPrivateKey dhPrivateKey1 = DH.getPrivateKey(map1);
            logDHKey("甲-公钥: " + DH.getPublicKeyHexString(dhPublicKey1));
            logDHKey("甲-私钥: " + DH.getPrivateKeyHexString(dhPrivateKey1));

            // =================== 乙方 ===================
            Map 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);
    }
}



你可能感兴趣的:(Android-DH 秘钥交换)