密钥交换算法DH

366 阅读3分钟

对称加密算法提高数据安全性的同时带来了密钥管理的复杂性。消息收发双方若想发送加密消息,必须事先约定好加密算法并发放密钥。而如何安全传递密钥,成为对称加密算法的一个棘手问题。密钥交换算法(Diffie-Hellman算法,简称DH算法)成为解决这一问题的钥匙。

1976年,Whitfield Diffie和Martin Hellman在发表的论文中提出了公钥加密算法思想,但当时并没有给出具体的实施方案,原因在于没有找到单向散列函数(也即消息摘要算法),但在该文中给出了通信双方通过信息交换协商密钥的算法,即Diffie-Hellman算法(简称DH算法)。该算法的目的在于让消息的收发双方可以在安全的条件下交换密钥以备后续加解密使用。因此DH算法是第一个密钥协商算法,但仅能用于密钥分配,不能用于加解密消息。

DH密钥交换算法的安全性基于有限域上的离散对数难题

2006年,Hellman建议将该算法称为Diffie-Hellman-Merkle密钥交换算法,以表彰Ralph Merkle对公钥密码学发明的贡献:

该系统...从此被称为Diffie-Hellman密钥交换算法。虽然该系统最初是由Diffie和我在一篇论文中描述的,但它是一个公钥分发系统,是Merkle开发的一个概念,因此如果要与名称相关联,则应将其称为“ Diffie-Hellman-Merkle密钥交换算法”。我希望这能有助于承认Merkle对公钥密码学发明的同等贡献。

模型分析:

  1. 由消息发送方Alice利用DH算法构建密钥对
  2. Alice将构建的密钥对的公钥发布给Bob
  3. Bob利用得到的公钥作为参数利用DH算法构建自己的密钥对
  4. Bob将构建的密钥对的公钥发布给Alice
  5. Alice用自己的私钥和Bob发布的公钥利用AES算法生成加解密使用的本地密钥,Bob用自己的私钥和Alice发布的公钥利用AES算法生成加解密使用的本地密钥。
package com.fulcrum.encrypt;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Diffie-Hellman Key Exchange between Two parties
 * Alice-sender, Bob-receiver
 */
public class DHKeyAgreement2 {

    private static final String DH_ALGORITHM = "DH";
    private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
    private static final byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    private static final IvParameterSpec ivSpec = new IvParameterSpec(iv);

    public static void main(String[] args) throws Exception{
        Map<String, byte[]> skMap = dh();
        byte[] aliceSecretKey = skMap.get("aliceSecretKey");
        byte[] bobSecretKey = skMap.get("bobSecretKey");
        System.out.println("密钥是否相等:"+Arrays.equals(aliceSecretKey, bobSecretKey));
        System.out.println("加解密not Initial Vector:"+encryptDecryptNotIv(skMap));
        System.out.println("加解密use Initial Vector:"+encryptDecryptUseIv(skMap));
    }

    /**
     * Diffie-Hellman Key Exchange Algorithm
     * @return
     * @throws Exception
     */
    public static Map<String,byte[]> dh() throws Exception{
        KeyPairGenerator aliceKeyPairGenerator = KeyPairGenerator.getInstance(DH_ALGORITHM);
        aliceKeyPairGenerator.initialize(2048);//key size
        KeyPair aliceKeyPair = aliceKeyPairGenerator.generateKeyPair();
        KeyAgreement aliceKeyAgree = KeyAgreement.getInstance(DH_ALGORITHM);
        aliceKeyAgree.init(aliceKeyPair.getPrivate());
        byte[] alicePubKeyEnc = aliceKeyPair.getPublic().getEncoded();
        KeyFactory bobKeyFactory = KeyFactory.getInstance(DH_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEnc);
        PublicKey alicePubKey = bobKeyFactory.generatePublic(x509KeySpec);
        DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();
        KeyPairGenerator bobKeyPairGenerator = KeyPairGenerator.getInstance(DH_ALGORITHM);
        bobKeyPairGenerator.initialize(dhParamFromAlicePubKey);
        KeyPair bobKeyPair = bobKeyPairGenerator.generateKeyPair();
        KeyAgreement bobKeyAgree = KeyAgreement.getInstance(DH_ALGORITHM);
        bobKeyAgree.init(bobKeyPair.getPrivate());
        byte[] bobPubKeyEnc = bobKeyPair.getPublic().getEncoded();
        KeyFactory aliceKeyFactory = KeyFactory.getInstance(DH_ALGORITHM);
        x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
        PublicKey bobPubKey = aliceKeyFactory.generatePublic(x509KeySpec);
        aliceKeyAgree.doPhase(bobPubKey, true);
        bobKeyAgree.doPhase(alicePubKey, true);
        byte[] aliceSecretKey = aliceKeyAgree.generateSecret();
        byte[] bobSecretKey = bobKeyAgree.generateSecret();
        Map<String,byte[]> skMap = new HashMap<>();
        skMap.put("aliceSecretKey", aliceSecretKey);
        skMap.put("bobSecretKey", bobSecretKey);
        return skMap;
    }

    /**
     * 使用Initial Vector
     * @param skMap
     * @return
     * @throws Exception
     */
    public static boolean encryptDecryptUseIv(Map<String, byte[]> skMap) throws Exception{
        byte[] aliceSecretKey = skMap.get("aliceSecretKey");
        byte[] bobSecretKey = skMap.get("bobSecretKey");
        SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSecretKey,0,16,"AES");
        SecretKeySpec bobAesKey = new SecretKeySpec(bobSecretKey,0,16,"AES");
        String text = "空山新雨后,天气晚来秋。";
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, bobAesKey, ivSpec);
        byte[] cipherText = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
        cipher.init(Cipher.DECRYPT_MODE, aliceAesKey, ivSpec);
        String recoverText = new String(cipher.doFinal(cipherText));
        return text.equals(recoverText);
    }

    /**
     * 不使用Initial Vector
     * @param skMap
     * @return
     * @throws Exception
     */
    public static boolean encryptDecryptNotIv(Map<String, byte[]> skMap) throws Exception{
        byte[] aliceSecretKey = skMap.get("aliceSecretKey");
        byte[] bobSecretKey = skMap.get("bobSecretKey");
        SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSecretKey,0,16,"AES");
        SecretKeySpec bobAesKey = new SecretKeySpec(bobSecretKey,0,16,"AES");
        String text = "空山新雨后,天气晚来秋。";
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
        byte[] cipherText = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
        AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
        aesParams.init(cipher.getParameters().getEncoded());
        cipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
        String recoverText = new String(cipher.doFinal(cipherText));
        return text.equals(recoverText);
    }

}

截屏2024-03-12 21.21.18.png