为确保传输数据安全,推荐使用安全协议,如 https,无法使用时才使用该方案(通过协商密钥算法协商会话密钥,然后通过密钥加密传输数据)。

术语定义:

  • 公钥、私钥

    • 公钥会进入网络传输的,该部分是公开的。

    • 私钥由部分不允许公开。

  • DH 密钥交换(协商)算法

    • 全称为Diffie-Hellman ,该算法仅传输双方各自的公钥即可使得双方计算出相同的key,可以使用这个key作为密钥加密信息;即使攻击者只获取到双方公钥也无法计算出Key值。

    • 关键点:需要保证在互换双方公钥时不被中间人进行替换,否则可能遭受中间人攻击。

  • 共享密钥/会话密钥

    • DH算法中,双方进行密钥(公钥)交换后,通过对方公钥和己方私钥生成的Key值(双方结果相同)。

    • 只要私钥不被破解,该值就是安全的。

  • 数据密钥

    • 真正用于加密数据的key,每次会话随机生成,使用该密钥加密敏感数据,然后用共享密钥和共享iv( shareIv )进行加密,随后该值将被销毁。

    • 发送数据时,会将 使用该值加密过的敏感数据密文共享iv使共享密钥加密过的数据密钥(该值) 传递给对方,对方会先解密出 数据密钥明文 ,然后解密出本次传输的敏感信息。

    • 该值仅在内存中使用时临时生成和恢复,用后即销毁,因为该值是临时的,且不在网络中传输,所以可以保证敏感数据的安全。

  • 安全会话标识

    • 类似SessionId,用于记录对方是谁,通过该值来判断是否已经进行秘钥交换、获取共享秘钥,双方都会保存该标识。

  • AES支持算法

    • 加密用的规则,请求方所支持的AES加密种类,如果支持多个,用分号分割。这些值取决于代码实现的种类。

  • AES算法选型

    • 响应方从请求方的AES-supports挑选一个随机的加密方法,作为本次会话中使用的AES加密算法。

  • B2S(bytes[])

    • 代表将bytes[] 转化为字符串,一般采用base64编码。

交互流程

  • 整个加密流程的实现,分成客户端部分和服务端部分,对于大部分的服务,均需实现(既会提供给服务调用,也会调用其他服务)。

  • 传输加密前,双方需要通过协商,来确定加密敏感信息所用的秘钥。

  • 每一次的协商,都会生成一个唯一的 X-Sid ,用于双方后续的通信。当服务端发现 X-Sid 过期时,需要返回约定好的错误码,让客户端重新发起协商和请求。

客户端-发起协商请求

  1. 生成 ECDH 的公私钥对(公钥 ClientPublicKey 和私钥 ClientPrivateKey

  2. 生成一个唯一的会话ID(X-Sid

  3. 生成客户端支持的加/解密规则(aesSupports

  4. 生成请求接口用的认证 Token

    • Token的签名为:<X-Sid>B2S(<ClientPublicKey>)

    • 如 X-Sid='ABC',B2S(<ClientPublicKey>)='XYZ',则最终为 ABCXYZ

  5. 将Token放入header中,将S(PublicKeyA)也即转换为HEX的公钥、X-Sid、AES-supports字段放入 Body 中,并调用服务端的协商接口。

  6. 可选步骤:填充 negotiateVersion1,代表使用第一个版本的协商算法(ECDH),该字段只是为了后续版本可能更新,只有一套方案时可以直接采用默认值,节省网络开销。

Table 1. 发起协商请求所需参数
位置 字段名 字段值表达式 说明

Header

X-Sid

UUID

安全会话唯一标识,使用UUID即可

Header

Token

<X-Sid>B2S(<ClientPublicKey>)

用于判断传输未被篡改

Body

aesSupports

["AES-ECB-192", "AES-CBC-256"]

支持的算法类型,自然越多越好

Body

publicKey

B2S(<ClientPublicKey>)

客户端公钥的64进制编码

Body

negotiateVersion

"1"

秘钥交换算法版本,可空,默认为 1

服务器-服务端处理协商请求

  1. 收到客户端的协商请求,进行参数校验

    • 参数完整性校验

    • 参数格式校验

    • Token 有效性校验

  2. 根据 negotiateVersion 版本,做一些事情,这里只有ECDH,直接进行ECDH的椭圆曲线选择过程,简单起见,默认使用 ANSI X9.62 Prime 256v1

  3. 生成自己的公私钥对(ServerPublicKeyServerPrivateKey

  4. publicKey 64进制解码得到客户端公钥值 ClientPublicKey

  5. 根据 ServerPublicKeyServerPrivateKeyClientPublicKey 生成共享密钥 shareKey

  6. 根据 ServerPublicKeyClientPublicKey 生成共享加密矢量 shareIv

  7. aesSupports 与自己支持的aes加密算法列表做交集处理,在交集中随机选择一个作为本次协商的加密规则 aesAlgorithm

  8. 生成一个协商有效时间 expireTime,如:30分钟

  9. 缓存协商结果

    • 包括:X-SidaesAlgorithmshareKeyshareIvexpireTime

  10. 生成一个双向认证用的Token。

    • Token的签名为:X-Sid + B2S(ServerPublicKey) + AES-param + expireTime

  11. 返回协商结果。

Table 2. 响应协商请求结果
位置 字段名 字段值表达式 or 举例 说明

Header

X-Sid

UUID

安全会话唯一标识

Header

Token

X-Sid + B2S(ServerPublicKey) + AES-param + expireTime

用于判断传输未被篡改

Body

aesAlgorithm

"AES-CBC-256"

本次会话使用的加密算法

Body

publicKey

B2S(<ServerPublicKey>)

服务端公钥的64进制编码

Body

expireTime

1800

会话过期时间,秒

客户端-处理协商返回结果

  1. 收到服务端的协商请求,进行 Token 有效性校验

  2. 根据 ServerPublicKeyClientPublicKeyClientPrivateKey 生成共享密钥 shareKey

  3. 根据 ServerPublicKeyClientPublicKey 生成共享加密矢量 shareIv

  4. 缓存协商结果

    • 包括:X-SidaesAlgorithmshareKeyshareIvexpireTime

至此,协商阶段结束。为了保证客户端发起时,服务端信息未失效,故设计中客户端缓存需提前过期。如 expireTime /= 2 或减少固定时间等。

客户端-发起数据传输请求

  1. 查找是否已与目标服务有进行协商且未过期

    • 未过期则使用

    • 否则重新进行协商 include::[tags=encrypt]

Table 3. 响应协商请求结果
位置 字段名 字段值表达式 or 举例 说明

Header

X-Sid

UUID

安全会话唯一标识

Header

X-Dk

Random

数据秘钥密文64进制编码

Header

Token

X-Sid + dataCipher

用于判断传输未被篡改

Body

aesAlgorithm

"AES-CBC-256"

本次会话使用的加密算法

Body

publicKey

B2S(ServerPublicKey)

服务端公钥的64进制编码

Body

expireTime

1800

会话过期时间,秒

为了避免客户请求前未过期,收到服务端响应时缓存意外过期情况。可以在请求前通过快照机制处理,不推荐判断是否 即将过期 (过期时间小于接口最大超时时间)。

服务端-数据传输处理

处理前解密

include::[tags=decrypt]

业务处理

业务处理后响应前加密

注:与客户端请求前加密流程相同

include::[tags=encrypt]

客户端-处理服务端响应

include::[tags=decrypt]

业务处理

  1. 随机生成 数据秘钥dataKey,也成临时加密秘钥)

  2. dataKey 为密钥, shareIv 为加密矢量,采用 aesAlgorithm 对应算法加密 敏感数据data),并对其进行64进制编码得到敏感数据密文(dataCipher

  3. shareKey 为密钥,shareIv`为加密矢量,采用 `aesAlgorithm 对应算法加密 数据秘钥dataKey),并对其进行64进制编码得到数据秘钥密文(dataKeyCipher

  4. 生成认证Token,签名为:X-Sid + dataCipher

  1. 校验请求合法性

    • 校验 Header 中字段的完整性

    • 校验Token的合法性

  2. 判断 X-Sid 对应的协商是否过期,如果过期,直接返回错误码,提示需要重新协商。

  3. X-Sid 对应的缓存中取出协商缓存

  4. 对数据秘钥密文 dataKeyCipher base64解码,再根据 共享秘钥 shareKey、加密矢量 shareIv 以及加密算法 aesAlgorithm 对其解密得到 数据秘钥dataKey)。

  5. 对敏感数据密文 dataCipher base64解码,再根据 数据秘钥 dataKey、加密矢量 shareIv 以及加密算法 aesAlgorithm 对其解密得到 敏感数据明文data