别再传输秘钥了——Android使用DH秘钥交换简介
最近跟一些做 Android 开发的同学聊天,发现很多团队在加密方面,普遍使用的策略是:
客户端预置RSA公钥。
客户端本地随机生成对称秘钥key。
RSA对公钥加密+签名,交给服务端。
服务端用RSA私钥做验证签名+解密还原key。
双端用生成的对称密钥,使用对称加密对消息体加密。
我记得09年诺基亚还没有衰败的时候,J2ME上的应用程序就采用这种加密的方式了,十年过去了还是同样的流程,大抵是移动端开发者对于性能、UI、动画等过于关注,持久化、加密等就趋向于“用现成的”。然而现在TLS协议的发展已经到了TLS1.3,加密协议的发展愈发不再倾向于在通信时传递秘钥,哪怕是加密秘钥。
因此今天打算介绍一种并不需要传输秘钥的加密流程——Diffie–Hellman密钥交换(以下简称DH)。实际上,DH算法早已经在Java类库里支持了,是用于双方在可能被窃听环境下安全交换密钥的一种方法,一般来说并不需要引入第三方加密库就可以使用。
具体算法流程如下:
取素数p和整数a,a是p的一个原根,公开a和p。
A选择随机数XA
B选择随机数XB
每一方都将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 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();