别再传输秘钥了——Android 使用 DH 秘钥交换简介

别再传输秘钥了——Android使用DH秘钥交换简介

最近跟一些做 Android 开发的同学聊天,发现很多团队在加密方面,普遍使用的策略是:

  1. 客户端预置RSA公钥。
  2. 客户端本地随机生成对称秘钥key。
  3. RSA对公钥加密+签名,交给服务端。
  4. 服务端用RSA私钥做验证签名+解密还原key。
  5. 双端用生成的对称密钥,使用对称加密对消息体加密。

我记得09年诺基亚还没有衰败的时候,J2ME上的应用程序就采用这种加密的方式了,十年过去了还是同样的流程,大抵是移动端开发者对于性能、UI、动画等过于关注,持久化、加密等就趋向于“用现成的”。然而现在TLS协议的发展已经到了TLS1.3,加密协议的发展愈发不再倾向于在通信时传递秘钥,哪怕是加密秘钥。

因此今天打算介绍一种并不需要传输秘钥的加密流程——Diffie–Hellman密钥交换(以下简称DH)。实际上,DH算法早已经在Java类库里支持了,是用于双方在可能被窃听环境下安全交换密钥的一种方法,一般来说并不需要引入第三方加密库就可以使用。

具体算法流程如下:

取素数p和整数a,a是p的一个原根,公开a和p。

A选择随机数XA<p,并计算YA=a^XA mod p。

B选择随机数XB<p,并计算YB=a^XB mod p。

每一方都将X保密而将Y公开让另一方得到。

A计算密钥的方式是:K=(YB) ^XA modp

B计算密钥的方式是:K=(YA) ^XB modp

证明:

                     (YB)^ XA mod p = (a^XB modp)^ XA mod p

                         = (a^XB)^ XA mod p = (a^XA) ^XB mod p    (<-- 密钥即为 a^(XA*XB) mod p)

                         =(a^XA modp)^ XB mod p= (YA) ^XB mod p


由于XA和XB是保密的,而第三方只有p、a、YB、YA可以利用,只有通过取离散对数来确定密钥,但对于大的素数p,计算离散对数是十分困难的。

晕了吗?晕了吧??嗯,那我说人话。

实际上大部分看到上面计算流程都会感觉比较懵逼,但在DH的实际使用中,虽然可以显式的约定素数和素数原根,但一般来说都会使用证书生成的方式来做。让我用比较通俗的方式阐述一下这个过程:

Step1. 服务端生成DH公私钥

```
//创建KeyPairGenerator的实例,选用DH算法
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");

//初始化密钥长度,默认1024,可选范围512-65536 & 64的倍数
keyPairGenerator.initialize(1024);

//生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
DHPublicKey dhPublicKey = (DHPublicKey) keyPair.getPublic();
DHPrivateKey dhPrivateKey = (DHPrivateKey) keyPair.getPrivate();
top Created with Sketch.