对于交易支付相关的网络服务,需要保障安全、私密的网络通信。此时需要依赖 TLS 、身份验证和授权机制。我们先来了解基本概念。
TLS
TLS(Transfer Layer Security) 指传输层安全协议,它包括记录协议(例如 TCP )和握手协议,在下文中仅关注握手协议。
它的一种常见用法是和 HTTP 结合使用成为 HTTPS 。 HTTPS 的 S 还可以指代 SSL(Secure Sockets Layer),它是 TLS 的前身,目前也会用 SSL 指代 TLS。
那么,TLS 是如何在不可信的网络环境中实现安全地通信的呢?
首先,在建立连接的过程(即握手),完成密钥协商和身份验证。连接建立后,在每次请求中,使用密钥对数据加密来保证数据的保密性;使用签名和验签保证数据的完整性。
TLS 协议有1.1、1.2、1.3,当前使用的主流是 1.2。我们推荐使用最新的 TLS 1.3,它修复了 1.2 中的安全漏洞问题。
下面我们将从 TLS 1.1 开始了解整个 TLS 的演变过程。
TLS 1.1
下图是完整的握手流程:
*
表示的消息是可选发送的。
第一步:Client 向 Server 发送 ClientHello 消息。消息包括生成密钥的随机数1。 第二步:Server 发送 ServerHello 消息。消息包括:
- 生成密钥的随机数2。
- Certificate(服务端证书):包含签名算法、公钥等信息。
- ServerKeyExchange(服务端密钥交换信息):包括密钥交换算法(RSA 或 DH ),仅当服务端证书信息不足以让客户端完成密钥协商时,才会补充发送。
- CertificateRequest(证书请求):当服务端需要客户端提供证书时,发送此消息。包括消息可供选择的算法等。
第三步:Server 发送 SeverHelloDone 消息。消息包括 ServerHello 完成的关联数据。 第四步:Client 收到 ServerHelloDone 消息,验证服务端证书有效性。然后发送以下消息内容:
- Certificate(客户端证书):仅当服务端发送证书请求消息时发送。
- ClientKeyExchange(客户端密钥交换信息):生成密钥的最后一部分信息。如果密钥交换算法为 RSA ,则为使用服务端证书中的公钥加密后的随机数3;如果为 DH 算法则为 DH 参数。
- CertificateVerify(客户端证书验证):消息为使用客户端证书对应的私钥签名。仅当发送了客户端证书时发送,以供服务端验证客户端证书。
- 完成:客户端在更改了密钥规范信息后,此时客户端已具备生成密钥的算法和生成信息,生成密钥。 然后使用密钥加密并发送完成消息。
第五步:服务端接收到第四步消息,客户端身份和消息验证通过,生成密钥并使用密钥加密并发送完成消息。
以上就是 TLS 握手的完整流程。简单来说,就是客户端和服务器交换随机数完成密钥协商,通过签名和验签完成身份认证。
TLS 1.2
TLS 1.2 的握手流程和 TLS 1.1 完全一致:
相对 TLS 1.1,TLS 1.2 主要收紧限制了密钥协商中使用的算法,使用更新的安全算法。例如使用 SHA256 替换 MD5/SHA1。
TLS 1.3
TLS 1.3 是一个不向下兼容的协议,它极大地提高了通信安全和简化流程,是我们了解的重点。它的主要改进点如下:
- 已支持的对称加密算法中,仅保留 AEAD 类型的算法;更改密码套件概念
- 添加 0-TTR 模式
- 提供向前保密
- ServerHello 后的消息全部加密
下图是完整的握手流程:
握手流程简化成了3步:KeyExch(密钥交换)、Server Params(生成服务器参数)、Auth(身份验证)。
这三步中有很多可选参数,根据密钥交换模式不同选择传输。密钥交换支持 (EC)DHE、PSK-only、PSK with (EC)DHE。我们先来看这三种模式的含义。
密钥交换模式为 (EC)DHE
DH(Diffie–Hellman key exchange),即 Diffie–Hellman 密钥交换,是一种在公共通道中安全交换密钥的数学方法。假设通信为 a、b,基于 DH 生成密钥的流程如下:
- a、b 约定参数 p、q(用于生成g)、g。
- a、b 分别生成随机数作为私钥 xa、xb。
- a 根据
g ^ xa mod p
生成公钥 ya,b 根据g ^ xb mod p
生成公钥 yb。 - a 根据
(yb ^ xa) mod p
生成统一变量 zz,b 根据(ya ^ xb) mod p
生成统一变量 zz。 - 双方分别得到 zz,根据约定算法计算得到密钥。
整个过程通过自己的私钥,对方的公钥以及 p、q、g 参数、约定的密钥计算算法,就可以生成密钥。第三方需要计算得到私钥很困难,需要解决离散对数问题。
DHE(Diffie-Hellman Ephemeral)是临时 DHE,临时指的是公私钥对,每一次使用双方都会重新生成公私钥对,因此每一次都能计算得到不同的密钥。
这样的好处是每个连接都能生成不同密钥,当前连接的密钥泄漏,不会影响在此之前建立的连接。这个特性就叫 FS(Forward Secrecy)前向保密。
EC(Elliptic Curve)指基于椭圆曲线,椭圆曲线密码学是一种公钥计算方法,它把公私钥作为椭圆上的一个点,指定私钥和椭圆曲线参数能计算得到公钥。使用它的好处是能提高密钥生成效率和加强安全性。
因此,(EC)DHE 的含义为 (基于椭圆曲线密码学得到公私钥的)临时 DH 算法。EC 是可选的。
密钥交换模式为 PSK-only
PSK(Pre-shared key)即预共享密钥,它可以在连接建立之前通过某种安全通道交换,例如离线部署;也可以在新的连接中使用上一次连接中设定的 PSK ID 来恢复使用上一次连接的 PSK。
PSK-only 即指只使用 PSK 的密钥交换模式,密钥只基于 PSK 生成。
使用它的好处是能实现 零往返时间(0-RRT) 模式。基于 PSK 建立的连接,客户端可以在 ClientHello 中就发送数据。同时使用 PSK 加密数据,验证服务端身份是否合法。
虽然这种模式可以节省时间,但是有一定的安全限制。
密钥交换模式为 PSK with (EC)DHE
PSK with (EC)DHE 即结合 (EC)DHE 的 PSK 。PSK 和 (EC)DHE 的 zz 参数一起作为参数生成密钥。这样做的好处是,相比 PSK-only,能够保证前向安全。
握手流程
现在来看握手流程。
第一步,Client 向 Server 发送 ClientHello 消息。包括:
- 密钥共享(key_share):若密钥交换模式包含 (EC)DHE,则包括客户端的 (EC)DHE 使用的参数信息,例如 p、q、g,公钥。客户端也可以发送空内容,让服务端选择参数。
- 签名算法(signature_algorithms):在 TLS 1.3 中,仅允许 RSA、ECDSA、EdDSA 算法。
- PSK 交换模式(psk_key_exchange_modes):密钥交换模式为 PSK-only 时,值为
psk_key
;密钥交换模式为 PSK with(EC)DHE 时,值为psk_dhe_key
。 - PSK
第二步,Server 向 Client 发送 ServerHello 消息。包括:
- 密钥共享(key_share):服务端的 (EC)DHE 使用的参数。例如公钥。
- PSK
- 加密扩展(EncryptedExtensions):对不需要确定加密参数的 ClientHello 扩展的响应。这是使用
server_handshake_traffic_secret
加密的数据。 - 证书请求(CertificateRequest):当服务端需要客户端提供证书时,发送此消息。包括消息可供选择的算法等。
- 服务端证书(Certificate)
- 服务端证书验证(CertificateVerify):使用服务端证书对应的私钥签名。以供客户端验服务端证书。
- 完成(Finished)消息:该消息为一个 MAC,
server_handshake_traffic_secret
作为密钥,使用 HMAC 计算得出。客户端需要校验该消息是否合法。 - 应用消息(Application Data):请求的应用消息。
第三步,客户端验证服务端的身份。它会发送如下消息:
- 客户端证书(Certificate):当服务端请求客户端提供证书时发送。
- 客户端证书验证消息(CertificateVerify):使用客户端证书对应的私钥签名。以供服务端验证客户端证书。
- 完成(Finished)消息:该消息为一个 MAC,
client_handshake_traffic_secret
作为密钥,使用 HMAC 计算得出。服务端需要校验该消息是否合法。 - 应用消息(Application Data):请求的应用消息。
至此,握手完成。握手流程中出现了一些密钥,用于加密数据或者生成 MAC。这也是 TLS 1.3 的一个特性:将身份验证、密钥交换机制中加密和生成 hash 值的密钥分开,以获得更高的安全性。
我们来了解下这些密钥是如何生成的。
密钥生成机制和方法
TLS 1.2 使用的是 KDF(Key Derivation Function) 密钥派生函数来得到密钥。它通过使用伪随机函数来获取所需格式的密钥。
TLS 1.3 使用的是 HKDF(HMAC KDF)[RFC5869],即基于 HMAC 的 KDF 算法。它的原理是先提取再拓展。先采用输入的密钥生成材料并从中提取固定长度的伪随机密钥,然后将伪随机密钥拓展为几个额外的伪随机密钥。在 TLS 1.3 中,这些伪随机密钥就被用于身份验证、加密等不同用途。
HKDF 的两个步骤分别为 HKDF-Extract 和 Derive-Secret。
HKDF-Extract 的 计算方法如下图:
代码语言:javascript复制 HKDF-Extract(salt, IKM) -> PRK
PRK = HMAC-Hash(salt, IKM)
salt 为可选参数,使用盐可以提高 HKDF 算法的强度。 IKM(input keying material)为输入密钥材料,它可以为空输出 PKR(伪随机密钥)。
Derive-Secret 的计算方法如下图:
它的输入为 Secret(密钥)、Label。
下图是 TLS 1.3 的密钥派生体系:
HKDF-Extract
从上方获取 salt,从左边获取 IKM,输出内容在中间,输出名称在右边。Derive-Secret
的 Secret 参数由传入箭头指示。例如,Early Secret 是用于生成 client_early_traffic_secret 的 Secret。- 0 表示输入长度为0的参数。
Derive的输入原材料为 PSK 和 (EC)DHE 中生成的 ZZ。
第一步, PSK 作为输入密钥, 和长度为0的盐通过 HKDF-Extract 生成 Early Secret。
第二步,将 Early Secret 和固定字段、ClientHello 结合,通过 Derive-Secret 方法计算得到 binder_key、client_early_traffic_secret、early_exporter_master_secret。
第三步, (EC)DHE 的 ZZ 作为输入密钥,和 Derive-Secret(Early Secret, "derived", "")
的计算值作为盐,通过 HKDF-Extract 计算得到 Handshake Secret。Handshake Secret 和固定字段、ClientHello、ServerHello 结合,通过 Derive-Secret 方法计算得到 client_handshake_traffic_secret、server_handshake_traffic_secret。
第四步,Derive-Secret(Handshake Secret, "derived", "")
作为盐通过 HKDF-Extract 得到 Master Secret。Master Secret 和固定字段、ClientHello 、server Finished 等得到4个 Secret。
上述过程用于握手和通信的密钥如下:
client_early_traffic_secret
加密 0-RTT 的数据; sender_handshake_traffic_secret
加密握手过程的数据; sender_application_traffic_secret_N
加密通信过程的应用数据。
以上是 TLS 1.3 的大概流程。
TLS 能够保障建立安全隐私的网络通道。基于 TLS,客户端和服务器需要通过身份验证来授权,从而完成获取资源。我们先来看身份验证和授权的方式。
身份验证
客户端在请求服务器获取资源前,第一步是证明自己的身份,服务端再根据客户端的身份授权。证明自己的身份,即身份验证(Authentification)。
身份验证大部分是单向的,由服务端直接或依赖第三方来验证客户端的身份。基于互不信任原则,也可以使用双向验证,即客户端和服务端互相验证。
在 HTTP 请求中,使用凭据验证身份。凭据可以是静态或动态生成的,它随着每次请求传输。常见的凭据中,静态的包括用户密码、API 密钥等;动态的包括数字签名。
- 用户密码:最不安全的一种凭据,一般不会使用这种方式。凭据被窃取即意味着用户信息被窃取。
- API 密钥:较为常见的身份验证凭据。这是服务端提供与客户端唯一对应的 API 密钥。
- 数字签名:基于非对称密钥体系,使用私钥生成签名,公钥验证签名。
从不可抵赖性以及被窃取后可能造成的严重程度来看,凭据选择的优先级为数字签名 > API 密钥 > 用户密码。
授权
授权(Authorization)是指向经过身份验证的参与方授予执行某项操作的权限的操作。 授权的核心是授权凭据。
服务端可以直接或依赖第三方来授权客户端。直接授权使用的凭据和身份验证凭据一样,它结合内部授权机制实现。第三方授权通过第三方授权服务器获取授权凭据,主要依赖于采用 OAuth 2.0 标准的第三方实现。
OAuth 2.0
OAuth 2.0 [RFC6749] 是一个行业标准的授权协议。我们需要了解4个角色、2个资源、3个流程。
4个角色为Client(客户端)、 Resource Owner(资源拥有者)、Authorization Server(授权服务器)、Resource Server(资源服务器)。
2个资源分别为 Authorization Grant(授权授予)、Access Token(访问令牌)。
3个流程如下图:
第一步,客户端先向资源拥有者发送授权请求,获取授权授予。
第二步,客户端向授权服务器发送授权授予,获取资源的访问令牌。
第三步,客户端向资源服务器发送访问令牌,获取受保护的资源。
OAuth 2.0 中的访问令牌就是授权凭据。获取访问令牌有4种方式:
- 授权码方式:通过授权码请求授权服务器获取令牌。
- (授权码)隐藏式:无需授权码,客户端请求授权服务器获取令牌给前端。适用于请求服务没有后端服务的情景。
- 密码式:通过用户密码请求授权服务器获取令牌。
- 凭证式:通过 client_id 和 client_secret 请求授权服务器获取令牌,适用于命令行场景。
出于安全考虑,推荐使用授权码和凭据式。
HTTP 身份验证和授权方案
HTTP 提供了一个主流的身份验证和授权框架 [rfc7235],它的主要流程如下(图来自 HTTP authentication):
客户端需要在 HTTP 头部 Authorization 中放入凭据。若凭据无效,服务器会返回 401 Unauthorized;若授权凭据有效但权限不足以访问给定资源,服务器会返回 403 Forbidden。
凭据有几种类型,上面的 Authorization 头中的 Basic 就指基础类型,对凭据 Base64 编码。其他的类型还有:
- Bearer: 基于 OAuth 2.0,表示 OAuth 2.0 的令牌。
- Digest:Basic的增强版,对凭据使用 SHA-256 或 MD5 生成 HASH 值。
- HOBA:HTTP Origin-Bound Authentication,凭据为共享密钥签出的数字签名。
- Mutual:基于双向认证,凭据为分别计算得到的共同密钥计算得到的数字签名。
- AWS4-HMAC-SHA256:AWS 服务的特定类型。
在此方案中,身份验证和授权都是依靠同一个凭据完成的,该凭据被放在 Authorization 。在其他实现中,也可以放在包体或者 URL。**出于安全考虑,推荐凭据放在 Authorization 头部或者包体。 **
实现
了解了身份验证和授权的基本概念和方式,我们来看支付网关实际是怎么做的。
国外主流支付网关的做法是基于 OAuth 2.0 来验证身份和授权,国内主流支付网关的做法是基于数字签名来验证身份和授权。
基于 OAuth 2.0 和 HTTP 身份验证授权框架
PayPal 就是基于 OAuth 2.0 和 HTTP 身份验证授权框架实现的典型例子。
PayPal 使用凭据式获取 access token 。获取 token 后,在后续请求中,结合 HTTP 身份验证框架,将 token 设置在 Authorization 头,向资源服务器请求。
代码语言:javascript复制graph LR
A[Client] -->| client ID: Client Sercret | B(Authorization Server)
B -->| access token | A
代码语言:javascript复制graph LR
A[Client] --> | Authorization: Bearer access token | B(Resource Server)
基于数字签名
微信支付和支付宝都是基于数字签名实现身份验证和授权的。
以微信支付为例:
商户在微信支付的商户平台获取商户公钥、私钥、微信支付平台证书,在向微信支付请求时,使用商户私钥对请求按照特定规则签名,并放在 Authorization 头中。微信支付返回时,会使用微信支付平台私钥对返回签名,并放在返回的 Authorization 中。
数字签名除了有身份验证的能力,还能保证消息的完整性。一旦消息被篡改了,计算得到的签名和消息体就会对应不上。
代码语言:javascript复制graph LR
A[商户] --> | Authorization: 认证类型 签名 | B(微信支付)
B[微信支付] --> | Authorization: 认证类型 签名 | A(商户)
参考文档
wiki
- PRF
- AEAD
- Elliptic-curve Diffie–Hellman
- Diffie-Hellman Ephemeral
- HKDF
- Probabilistic signature scheme
rfc
- The Transport Layer Security (TLS) Protocol
- The Transport Layer Security (TLS) Protocol Version 1.2
- The Transport Layer Security (TLS) Protocol Version 1.3
- Pre-Shared Key Ciphersuites for Transport Layer Security (TLS)
- Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)
- Diffie-Hellman Key Agreement Method
- HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
- HMAC
- Hypertext Transfer Protocol (HTTP/1.1): Authentication
- The 'Basic' HTTP Authentication Scheme
- The OAuth 2.0 Authorization Framework: Bearer Token Usage
其他
- TLS中的密钥计算