3.1 DH算法简介

非对称加密算法 - DH算法

DH算法是非对称加密算法的鼻祖,为非对称加密算法奠定了基础,主要用途是进行密钥交换

DH算法历史

1976年非对称加密算法思想被提出,但是当时并没有给出具体算法和方案,因为当时没有研究出单向函数(也就是信息摘要算法还没出现),但是IEEE的期刊(作者:W.Diffie和M.Hellman)中给出了通信时双方如何通过信息交换协商密钥的算法,也就是DH算法,通过该算法双方可以协商对称加密的密钥。

DH算法的目的仅在于<b>双方在安全的环境下协商一个加解密的密钥</b>,因此仅仅用于密钥分配,不能用于加解密消息。

应用场景

仅用于密钥交换场景,不适用于数据传输的加解密,如AB两个系统需要交换密钥,则过程如下:

  • A系统构建密钥:构建一对公私密钥Private Key1和Public Key1;
  • A系统向B系统公布自己的公钥(Public Key1);
  • B系统使用A公布的公钥(Public Key1)建立一对密钥:Private Key2和Public Key2;
  • B系统向A系统公布自己的公钥Public Key2;
  • A系统使用自己的私钥Private Key1和B系统的公钥Public Key2构建本地密钥;
  • B系统使用自己的私钥Private Key2和A系统的公钥Public Key1构建本地密钥;

关键点:B系统使用A系统的公钥建立加密用的Key;

本地密钥用来加解密数据;

虽然AB系统使用了不同的密钥建立自己的本地密钥,但是AB系统获得本地密钥是一致的。

流程描述

sequenceDiagram
A->> A: 构建密钥对:private key1 和 public key1
A->> B: 公布自己的公钥: public key1
B->> B: 使用public key1构建自己的密钥对 private key2 和 public key2;
B-->> A: 返回自己的public key2;
A->> A: 使用private key1 和 public key2 构建本地密钥;
B->> B : 使用private key2 和 public key1构建本地密钥;

Java中算法实现

Java提供DH算法的实现,不用使用第三方开源包,DH算法的产生的密钥长度在512到1024之间,必须是64的倍数,默认是1024。

A系统建立自己的密钥对

此过程不需要参数

/***
     * A系统产生密钥,这个过程不需要参数,由DH算法计算得出<br/>
     * 内部使用一些安全的随机函数随机计算出一个公私钥<br>
     * 计算后的公私钥要存储下来,存储二进制数据
     * 
     * @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算法专业密钥前行转换获取的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系统发送自己的公钥给B后,B才能建立自己的密钥对:

/**
     * B构建自己的公私钥,要使用A的公钥构建<br>
     * DH算法接受公钥,并构建自己的密钥对<br>
     * 这个过程中用到了一些密钥格式转换对象,不是重点<br>
     * B也要保持自己的密钥对,二进制形式
     * 
     * @param pubKey
     * @return
     * @throws Exception
     */
    public static Map<String, Key> initBSysKey(byte[] pubKey) throws Exception {

        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
        // 密钥格式转换对象
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
        // 转换PublicKey格式
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        // 构建DH算法参数
        DHParameterSpec dhParamSpec = ((DHPublicKey) publicKey).getParams();
        // 使用DH算法创建密钥对
        KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
        keyPairGr.initialize(dhParamSpec);
        KeyPair keyPair = keyPairGr.generateKeyPair();
        // 创建的公私钥
        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系统都需要对方已经发送了公钥给自己,使用自己的私钥和对方的公钥建立密钥对。

/**
     * 使用对方的公钥和自己的私钥构建对称加密的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);
        // 密钥编码转换对象
        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(SECRET_ALGORITH);
        return secretKey.getEncoded();
    }

AB系统加解密数据

使用构建的本地密钥对进行数据加解密:

/**
     * 使用本地密钥加密数据
     * 
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
        // 构建本地密钥
        SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 使用本地密钥解密数据
     * 
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
        // 构建本地密钥
        SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

完整的代码流程

NOTE:下面的代码最后加解密步骤无法完成,之前提过,对称加密技术,JAVA的AES仅支持128位长度的密钥,DH最少产生512的密钥,所以一般Java系统无法支持,需要下载授权文件。

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的一些参数
    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系统构建自己的公私钥
        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将自己的公钥发给B,B构建自己的公私钥
        // 一般都是发送二进制或者base64等数据
        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系统产生自己的本地对称加密算法密钥
        byte[] aSecretKey = getSecretKey(bPubKey, aPriKey);
        byte[] bSecretKey = getSecretKey(aPubKey, bPriKey);

        // 转换为字符串比较下
        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加密数据,B解密数据,能正常加解密
        String input = "A要发送给B的数据";
        byte[] data = encrypt(aSecretKey, input.getBytes());
        byte[] rs = decrypt(bSecretKey, data);
        log("A Send : %s , B Recive : %s ", toBase64(data), new String(rs));
    }

    /***
     * A系统产生密钥,这个过程不需要参数,由DH算法计算得出<br/>
     * 内部使用一些安全的随机函数随机计算出一个公私钥<br>
     * 计算后的公私钥要存储下来,存储二进制数据
     * 
     * @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算法专业密钥前行转换获取的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的公钥构建<br>
     * DH算法接受公钥,并构建自己的密钥对<br>
     * 这个过程中用到了一些密钥格式转换对象,不是重点<br>
     * B也要保持自己的密钥对,二进制形式
     * 
     * @param pubKey
     * @return
     * @throws Exception
     */
    public static Map<String, Key> initBSysKey(byte[] pubKey) throws Exception {

        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITH);
        // 密钥格式转换对象
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKey);
        // 转换PublicKey格式
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        // 构建DH算法参数
        DHParameterSpec dhParamSpec = ((DHPublicKey) publicKey).getParams();
        // 使用DH算法创建密钥对
        KeyPairGenerator keyPairGr = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
        keyPairGr.initialize(dhParamSpec);
        KeyPair keyPair = keyPairGr.generateKeyPair();
        // 创建的公私钥
        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;
    }

    /**
     * 使用对方的公钥和自己的私钥构建对称加密的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);
        // 密钥编码转换对象
        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(SECRET_ALGORITH);
        return secretKey.getEncoded();
    }

    /**
     * 使用本地密钥加密数据
     * 
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
        // 构建本地密钥
        SecretKey secretKey = new SecretKeySpec(key, SECRET_ALGORITH);
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 使用本地密钥解密数据
     * 
     * @param key
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
        // 构建本地密钥
        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));
    }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了加密算法的作用,以及数字证书的出现...
    纳兰三少阅读 1,901评论 1 6
  • 专题一,主要介绍HTTPS建立安全链接的原理,包括非对称加密、对称加密、CA认证等知识,还包括对一些业界常用算法的...
    有何不可12317阅读 296评论 0 0
  • 文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了加密算法的作用,以及数字证书的出现...
    sunny冲哥阅读 1,365评论 0 3
  • 简介 在早期,互联网通信是直接以明文进行传输,几乎没任何安全性可言,在你发送数据到接收者手上,经过n个网络设备,在...
    li_zw阅读 4,207评论 0 4
  • 7:45我们从榆中一中出发前往学农地点。经历了一路的颠簸,11点我们终于平安到达。在当地农户的带领下,我...
    习习晨风阅读 210评论 0 0