为确保传输数据安全,推荐使用安全协议,如 https,无法使用时才使用该方案(通过协商密钥算法协商会话密钥,然后通过密钥加密传输数据)。
📌 术语定义
- 公钥
-
公钥会进入网络传输的,该部分是公开的,需要确保使用者感知的公钥是正确的。
- 私钥
-
密钥中私钥部分不允许公开。
DH密钥交换(协商)算法-
全称为
Diffie-Hellman,该算法仅传输双方各自的公钥,即可使得双方各自计算出相同的key,可以使用这个key作为密钥来加密敏感信息。可确保攻击者通过监听网络与抓包请求获取到双方公钥也无法计算出key值。-
关键点:需要保证在互换双方公钥时不被中间人进行替换,否则可能遭受中间人攻击。
-
- 共享密钥/会话密钥
-
DH算法中,双方进行密钥(公钥)交换后,通过对方公钥和己方私钥生成的Key值(双方结果相同),只要私钥不被窃取/破解,该值就是安全的。 - 数据密钥
-
真正用于加密数据的key,每次会话随机生成,使用该密钥加密敏感数据,然后用共享密钥和共享iv(
shareIv)进行加密,随后该值将被销毁。-
发送数据时,会将
使用该值加密过的敏感数据密文、共享iv、使共享密钥加密过的数据密钥(该值)传递给对方,对方会先解密出数据密钥明文,然后解密出本次传输的敏感信息。 -
该值仅在内存中使用时临时生成和恢复,用后即销毁,因为该值是临时的,且不在网络中传输,所以可以保证敏感数据的安全。
-
- 安全会话标识
-
类似SessionId,用于记录对方是谁,通过该值来判断是否已经进行秘钥交换、获取共享秘钥,双方都会保存该标识。
- AES支持算法
-
加密用的规则,请求方所支持的AES加密种类,如果支持多个,用分号分割。这些值取决于代码实现的种类。
- AES算法选型
-
响应方从请求方的AES-supports挑选一个随机的加密方法,作为本次会话中使用的AES加密算法。
- B2S(bytes[])
-
伪代码简写,代表将
bytes[]转化为字符串,一般采用base64编码。
🗨 密钥协商流程明细
客户端-发起协商请求
-
生成
ECDH的公私钥对(公钥ClientPublicKey和私钥ClientPrivateKey) -
生成一个唯一的会话ID(
X-Sid) -
生成客户端支持的加/解密规则(
aesSupports) -
生成请求接口用的认证
Token-
Token的签名为:
<X-Sid>B2S(<ClientPublicKey>) -
如 X-Sid='ABC',B2S(<ClientPublicKey>)='XYZ',则最终为
ABCXYZ
-
-
将Token放入header中,将S(PublicKeyA)也即转换为HEX的公钥、X-Sid、AES-supports字段放入
Body中,并调用服务端的协商接口。 -
可选步骤:填充negotiateVersion为1,代表使用第一个版本的协商算法(ECDH),该字段只是为了后续版本可能更新,只有一套方案时可以直接采用默认值,节省网络开销。
| 位置 | 字段名 | 字段值表达式 | 说明 |
|---|---|---|---|
Header |
X-Sid |
|
安全会话唯一标识,使用UUID即可 |
Header |
Token |
|
用于判断传输未被篡改 |
Body |
aesSupports |
["AES-ECB-192", "AES-CBC-256"] |
支持的算法类型,自然越多越好 |
Body |
publicKey |
|
客户端公钥的64进制编码 |
Body |
negotiateVersion |
|
秘钥交换算法版本,可空,默认为 |
服务器-服务端处理协商请求
-
收到客户端的协商请求,进行参数校验
-
参数完整性校验
-
参数格式校验
-
Token有效性校验
-
-
根据
negotiateVersion版本,做一些事情,这里只有ECDH,直接进行ECDH的椭圆曲线选择过程,简单起见,默认使用ANSI X9.62 Prime 256v1 -
生成自己的公私钥对(
ServerPublicKey、ServerPrivateKey) -
对
publicKey64进制解码得到客户端公钥值ClientPublicKey -
根据
ServerPublicKey、ServerPrivateKey、ClientPublicKey生成共享密钥shareKey -
根据
ServerPublicKey、ClientPublicKey生成共享加密矢量shareIv -
将
aesSupports与自己支持的aes加密算法列表做交集处理,在交集中随机选择一个作为本次协商的加密规则aesAlgorithm -
生成一个协商有效时间
expireTime,如:30分钟 -
缓存协商结果
-
包括:
X-Sid、aesAlgorithm、shareKey、shareIv、expireTime
-
-
生成一个双向认证用的Token。
-
Token的签名为:
X-Sid+B2S(ServerPublicKey)+AES-param+expireTime
-
-
返回协商结果。
| 位置 | 字段名 | 字段值表达式 or 举例 | 说明 |
|---|---|---|---|
Header |
X-Sid |
|
安全会话唯一标识 |
Header |
Token |
|
用于判断传输未被篡改 |
Body |
aesAlgorithm |
|
本次会话使用的加密算法 |
Body |
publicKey |
|
服务端公钥的64进制编码 |
Body |
expireTime |
|
会话过期时间,秒 |
🔒 数据传输加解密明细
客户端-发起数据传输请求
-
查找是否已与目标服务有进行协商且未过期
-
未过期则使用
-
否则重新进行协商
-
具体处理流程见 [_加密流程]
| 位置 | 字段名 | 字段值表达式 or 举例 | 说明 |
|---|---|---|---|
Header |
X-Sid |
|
安全会话唯一标识 |
Header |
X-Dk |
|
数据秘钥密文64进制编码 |
Header |
Token |
|
用于判断传输未被篡改 |
Body |
aesAlgorithm |
"AES-CBC-256" |
本次会话使用的加密算法 |
Body |
publicKey |
B2S( |
服务端公钥的64进制编码 |
Body |
expireTime |
|
会话过期时间,秒 |
|
为了避免客户请求前未过期,收到服务端响应时缓存意外过期情况。可以在请求前通过快照机制处理,不推荐判断是否 |
服务端-处理前解密
业务处理前先进行 [_解密流程]
服务端-业务处理后响应前加密
| 见 [_加密流程] |
客户端-处理服务端响应
| 见 [_加密流程] |
📕 加解密流程说明
加密流程
-
随机生成
数据秘钥(dataKey,也成临时加密秘钥) -
以
dataKey为密钥,shareIv为加密矢量,采用aesAlgorithm对应算法加密敏感数据(data),并对其进行64进制编码得到敏感数据密文(dataCipher) -
以
shareKey为密钥,shareIv为加密矢量,采用aesAlgorithm对应算法加密数据秘钥(dataKey),并对其进行64进制编码得到数据秘钥密文(dataKeyCipher) -
生成认证Token,签名为:
X-Sid+dataCipher
解密流程
-
校验请求合法性
-
校验 Header 中字段的完整性
-
校验Token的合法性
-
-
判断
X-Sid对应的协商是否过期,如果过期,直接返回错误码,提示需要重新协商。 -
从
X-Sid对应的缓存中取出协商缓存 -
对数据秘钥密文
dataKeyCipherbase64解码,再根据 共享秘钥shareKey、加密矢量shareIv以及加密算法aesAlgorithm对其解密得到数据秘钥(dataKey)。 -
对敏感数据密文
dataCipherbase64解码,再根据 数据秘钥dataKey、加密矢量shareIv以及加密算法aesAlgorithm对其解密得到敏感数据明文(data)