DH (Diffie-Hellman)

本文最后更新于:2023年12月5日 晚上

RSA 既可以用于密钥交换,又可以用于数字签名;ECC 这边就分得比较清楚了:ECDHE 用于密钥交换,ECDSA 用于数字签名。

ECDHE 密钥交换使用的是 DH(Diffie-Hellman)算法。

Deffie-Hellman(简称 DH) 密钥交换是最早的密钥交换算法之一,它使得通信的双方能在非安全的信道中安全的交换密钥,用于加密后续的通信消息。 Whitfield Diffie 和 Martin Hellman 于 1976 提出该算法,之后被应用于安全领域,比如 Https 协议的 TSL(Transport Layer Security)以 DH 算法作为密钥交换算法。

session key 是主密钥,HTTPS 建立连接时使用 SSL/TLS 来交换 session key,后续的传输数据使用的是对称加密,对称加密的密钥就是通过 session key 生成的。

前面我们说过,session key 需要使用非对称加密的方式来传送,非对称加密算法常用的是 RSA,RSA 会把一方生成的 session key 用公钥加密后再发送给对方,可是,万一私钥泄露了,那 session key 也就泄露了。所以 SSL/TLS 使用另一种非对称加密算法来传送 session key,这种算法就是HD,RSA 是“传送”,而 HD 描述为“交换”更准确一些。

HD 的实现机制

假设 A、B 通信

  1. A、B 相互通信一次,确定两个参数:整数 g 和 大素数 p,这个过程叫做“协商”
  2. A 生成隐私数据 a,a < p,计算得出 ga mod p,发送给 B
  3. B 生成隐私数据 b,b < p,计算得出 gb mod p,发送给 A
  4. A 计算 [(gb mod p)^a] %p,得到的结果是预备主密钥 session key
  5. B 计算 [(ga mod p)^b] %p,得到的结果是预备主密钥 session key

[(gb mod p)^a] %p = [(ga mod p)^b] %p = gab mod p,证明过程略,总之 A、B 生成的 session key 是相同的。

通信过程不加密,就是说 g、p、ga mod p、gb mod p 都是公开的,只有公开的这个四个数据,没有 a、b 就算不出来 gab mod p,这样就做到了 session key 由通信双方各自生成,不经过网络传递。

可以抽象的认为:A 的私钥是 a,公钥是 ga mod p;B 的私钥是 b,公钥是 gb mod p,gab mod p 是预备主密钥 session key,最终用于加密的密钥是会话密钥,通过 session key 和 H 派生出来,具体参看下面的 SSHv2 协议中的 DH

openssl 实现

使用生成因子(generator,即整数 g,2 或者 5,默认是 2)2 和随机的 1024-bit 的素数(大素数 p)产生 D0ffie-Hellman 参数,输出保存到参数文件 dh.param:

[root@centos8 data]$openssl dhparam -out dh.param -2 1024
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
...................................+......................................................................................................................................................+......................................................................................................................+...................................................................................+......+.................+......................................+...............+.............................................................................................+.......+........................................+....................................+........................................................................................++*++*++*++*++*
[root@centos8 data]$cat dh.param
-----BEGIN DH PARAMETERS-----
MIGHAoGBAIHnVCwnbBAsIz43ylC5jSXdvbEysPNhe51MiYhjOYiIphx3eKgKOf9h
w47mAlaL9pFTsgtCfwq0gPOOABufWZY3cO0Xqwazfv84LFOH5BcH8M3trGyAc4C5
0kDJ3A2L5pmlVDYXrBhikCtfEMgixqbbMkCpqmKvk1YHvRfSarejAgEC
-----END DH PARAMETERS-----

从 dh.param 中读取 Diffie-Hell 参数,以 C 代码的形式输出到 stdout:

[root@centos8 data]$openssl dhparam -in dh.param -noout -C
static DH *get_dh1024(void)
{
    static unsigned char dhp_1024[] = {
        0x81, 0xE7, 0x54, 0x2C, 0x27, 0x6C, 0x10, 0x2C, 0x23, 0x3E,
        0x37, 0xCA, 0x50, 0xB9, 0x8D, 0x25, 0xDD, 0xBD, 0xB1, 0x32,
        0xB0, 0xF3, 0x61, 0x7B, 0x9D, 0x4C, 0x89, 0x88, 0x63, 0x39,
        0x88, 0x88, 0xA6, 0x1C, 0x77, 0x78, 0xA8, 0x0A, 0x39, 0xFF,
        0x61, 0xC3, 0x8E, 0xE6, 0x02, 0x56, 0x8B, 0xF6, 0x91, 0x53,
        0xB2, 0x0B, 0x42, 0x7F, 0x0A, 0xB4, 0x80, 0xF3, 0x8E, 0x00,
        0x1B, 0x9F, 0x59, 0x96, 0x37, 0x70, 0xED, 0x17, 0xAB, 0x06,
        0xB3, 0x7E, 0xFF, 0x38, 0x2C, 0x53, 0x87, 0xE4, 0x17, 0x07,
        0xF0, 0xCD, 0xED, 0xAC, 0x6C, 0x80, 0x73, 0x80, 0xB9, 0xD2,
        0x40, 0xC9, 0xDC, 0x0D, 0x8B, 0xE6, 0x99, 0xA5, 0x54, 0x36,
        0x17, 0xAC, 0x18, 0x62, 0x90, 0x2B, 0x5F, 0x10, 0xC8, 0x22,
        0xC6, 0xA6, 0xDB, 0x32, 0x40, 0xA9, 0xAA, 0x62, 0xAF, 0x93,
        0x56, 0x07, 0xBD, 0x17, 0xD2, 0x6A, 0xB7, 0xA3
    };
    static unsigned char dhg_1024[] = {
        0x02
    };
    DH *dh = DH_new();
    BIGNUM *p, *g;

    if (dh == NULL)
        return NULL;
    p = BN_bin2bn(dhp_1024, sizeof(dhp_1024), NULL);
    g = BN_bin2bn(dhg_1024, sizeof(dhg_1024), NULL);
    if (p == NULL || g == NULL
            || !DH_set0_pqg(dh, p, NULL, g)) {
        DH_free(dh);
        BN_free(p);
        BN_free(g);
        return NULL;
    }
    return dh;
}

SSHv2 协议中的 DH

  1. Client 发送 e 值给 Server:

SSHv2 中 DH 的 p 和 g 固定,没有协商阶段

e = ga mod p

  1. Server 发送自身公钥、f、s 给 Client

f = gb mod p

H = hash(V_C||V_S||I_C||I_S||K_S||e||f||K)

类型 说明
string V_C 客户端的初始报文(版本信息:SSH-2.0-xxx,不含结尾的 CR 和 LF)
string V_S 服务器的初始报文
string I_C 客户端 SSH_MSG_KEX_INIT 的有效载荷(不含开头的数据长度值)
string I_S 服务器的同上
string K_S 主机秘钥(dh gex reply(33)过程服务器发送 host key (RSA 公钥))
mpint e 客户端 DH 公钥
mpint f 服务器 DH 公钥
mpint K 共同 DH 计算结果

s 是用 Server 私钥对 H 加密后的结果

  1. Client 根据/.ssh/known_hosts 文件,匹配 Server 公钥,如果/.ssh/known_hosts 文件中没有 Server 公钥,说明是第一次建立连接,需要用户手动允许保存 Server 公钥
  2. Client 计算预备主密钥 session key、同样的方式计算 H。使用 Server 公钥解密 s,解密得出的 H 和自己算出的 H 如果一样,则向 Server 发送 NEW KEYS,标志着密钥交换成功
  3. 自此,Client 和 Server 交换了 预备主密钥 K 和 H 值,通过前面协商的算法中的 密钥派生算法 导出各个业务的密钥:加密、MAC、IV,同时把 H 作为会话 ID(session ID)

TLS1.2 协议中的 DH

  1. Client 和 Server 相互协商版本号 和 各种算法,并互相发送随机数 和 session ID

  1. Server 发送数字证书给 Client
  2. Server 发送 Pubkey 值给 Client,数字签名用来确保 Pubkey 的合法完整

TLS1.2 中 DH 的 p 和 g 固定,没有协商阶段

Pubkey = gb mod p

  1. Client 校验证书的合法性;Client 计算出预备主密钥 session key;Client 发送 Pubkey 值给 Server

Pubkey = ga mod p

  1. Server 计算出预备主密钥 session key;Server 发送 New Session Ticket、Change Cipher Spec、Encrypted Handshake Message 给 Client:

    1. Encrypted Handshake Message:使用密钥加密的信息,目的一个是告诉服务端整个握手过程收到了什么数据,发送了什么数据,保证中间没人篡改报文,二是确认密钥的正确性,如果这个报文加解密校验成功,那么对称密钥就是正确的

    2. Change Cipher Spec:编码改变通知,告知客户端,服务端已经切换到选定的加密套件(Cipher Suite),表示随后的信息都将用双方商定的加密方法和密钥发送。

    3. New Session Ticket:这里有必要比较一下 session ticket 和 session ID 这两个角色:

      如果出于某种原因,对话中断,就需要建立 SSL 连接,Client 在 client hello 阶段携带之前的 session ID,服务端确认 session ID 存在,双方就不再进行握手阶段剩余的步骤,而直接用已有的对话密钥进行加密通信,提高了重连的效率。

      可是,session ID 往往只保留在一台服务器上(session ID 的存储及复用共享需要使用 redis 或 memcache 来实现),假如轮询到集群中其它服务器,无法识别该 session id,就无法恢复对话,session ticket 就是为了解决这个问题而诞生的,目前只有 Firefox 和 Chrome 浏览器支持。客户端不再发送 session ID,而是发送一个服务器在上一次对话中发送过来的 session ticket。这个 session ticket 是加密的,只有服务器才能解密,其中包括本次对话的主要信息,比如对话密钥和加密方法。当服务器收到 session ticket 以后,解密后就不必重新生成对话密钥了。

  2. 自此 Client 和 Server 都有了三个值:Client 随机数、Server 随机数、session key。这三个值可以计算出会话密钥,后面的通信使用会话密钥加密。


DH (Diffie-Hellman)
http://blog.lujinkai.cn/运维/基础/加密和安全/DH (Diffie-Hellman)/
作者
像方便面一样的男子
发布于
2020年12月9日
更新于
2023年12月5日
许可协议