3. 生成算法选型:对称与非对称签名、JWT标准、HMAC与RSA实现示例

生成的核心是数字签名,它决定了的防篡改能力和分发效率。支付SDK中常见的签名算法分为对称签名(如HMAC)和非对称签名(如RSA、ECDSA),而JWT(JSON Web )则提供了标准化的载荷结构和签名流程。本章从原理、选型对比到具体实现,帮助你在支付场景中做出正确选择。

3.1 对称签名与非对称签名对比

维度 对称签名 (HMAC) 非对称签名 (RSA)
密钥 签名与验证使用同一个密钥(对称密钥) 私钥签名,公钥验证(密钥对)
性能 快,适合高QPS场景 慢约10~100倍(取决于密钥长度)
密钥分发 需通过安全信道传递到验证方;分发困难 公钥可公开,私钥仅签名方持有;分发简单
适用场景 内部系统间、微服务网关(共享密钥) 跨机构通信、开放平台、第三方SDK集成
安全风险 密钥泄露后所有可被伪造 私钥泄露则威胁签名方,公钥泄露不影响验证
选型经验:支付SDK对公网开放的API,优先使用非对称签名(如RSA或ECDSA);内部服务间调用,使用HMAC可降低计算延迟。

3.2 JWT标准与结构

JWT(RFC 7519)定义了一种紧凑的、自包含的格式,由三个Base64URL编码的部分组成,以点号分隔:

  • Header:声明类型(typ: JWT)和签名算法(alg: HS256 / RS256)。
  • Payload:携带声明(claims),如用户ID、过期时间(exp)、权限(scope)等。支付SDK中常用 sub(商户ID)、iat(签发时间)、exp(过期时间)。
  • Signature:对前两部分的签名,确保完整性。

标准JWT示例(未编码):

Header: {"alg":"RS256","typ":"JWT"}
Payload: {"sub":"merchant_123","iat":1719800000,"exp":1719803600,"scope":"payment:write"}
Signature: 
支付SDK注意:Payload中禁止存放敏感数据(如银行卡号、CVV),JWT仅签名未加密。如需加密,应使用JWE标准。

3.3 HMAC实现示例(对称签名)

HMAC(Hash-based Message Authentication Code)结合哈希函数与密钥生成签名。支付SDK中常用HMAC-SHA256。

生成HMAC JWT

// 示例(伪代码风格,实际语言可映射)
import hmac
import hashlib
import base64

def create_hmac_jwt(payload, secret):
    header = b'{"alg":"HS256","typ":"JWT"}'
    b64_header = base64url_encode(header)
    b64_payload = base64url_encode(payload)
    signing_input = b64_header + b'.' + b64_payload
    signature = hmac.new(secret, signing_input, hashlib.sha256).digest()
    b64_sig = base64url_encode(signature)
    return b64_header + b'.' + b64_payload + b'.' + b64_sig

验证HMAC JWT

def verify_hmac_jwt(, secret):
    parts = .split('.')
    if len(parts) != 3: return False
    b64_header, b64_payload, b64_sig = parts
    signing_input = b64_header + '.' + b64_payload
    expected_sig = hmac.new(secret, signing_input.encode(), hashlib.sha256).digest()
    actual_sig = base64url_decode(b64_sig)
    return hmac.compare_digest(expected_sig, actual_sig)

3.4 RSA实现示例(非对称签名)

RSA使用私钥签名、公钥验签,支持长度2048位及以上。支付SDK中算法标识为 RS256(RSA + SHA-256)。

生成RSA JWT

from cryptography.hazmat.primitives import hashes, asymmetric
from cryptography.hazmat.primitives.asymmetric import rsa, padding

def create_rsa_jwt(payload, private_key):
    header = b'{"alg":"RS256","typ":"JWT"}'
    b64_header = base64url_encode(header)
    b64_payload = base64url_encode(payload)
    signing_input = b64_header + b'.' + b64_payload
    signature = private_key.sign(
        signing_input,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    b64_sig = base64url_encode(signature)
    return b64_header + b'.' + b64_payload + b'.' + b64_sig

验证RSA JWT

def verify_rsa_jwt(, public_key):
    parts = .split('.')
    b64_header, b64_payload, b64_sig = parts
    signing_input = b64_header + '.' + b64_payload
    signature = base64url_decode(b64_sig)
    try:
        public_key.verify(
            signature,
            signing_input.encode(),
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return True
    except Exception:
        return False
常见错误:
  • 密钥格式不统一:HMAC需二进制密钥,RSA需PEM格式,使用前务必正确解析。
  • 算法标识与实现不匹配:Header中alg声明“HS256”但实际使用RSA签名,导致验签失败。
  • Base64URL填充处理:JWT使用无填充的Base64URL,标准Base64解码前需补等号,否则报错。
  • 过期时间未校验:未检查 exp 声明导致过期仍被放行。

3.5 算法选型性能对比

算法 密钥长度 签名耗时(微秒) 验签耗时(微秒) 长度
HMAC-SHA256 256 bit ~15 ~15 约180字符
RSA-2048 2048 bit ~800 ~20 约400字符
ECDSA P-256 256 bit ~25 ~25 约160字符

建议:高吞吐的支付内网服务可使用HMAC;对外API推荐RSA或ECDSA(ECC性能优于RSA,同等安全强度下密钥更短)。

3.6 调试与部署要点

在支付SDK集成阶段,以下步骤可帮助快速定位问题:

  1. 打印签名前的签名输入(Header + "." + Payload),与验证方对齐字节流。
  2. 使用已知测试向量:jwt.io或官方工具验证签名正误。
  3. 检查时区与时间偏差:exp和iat使用UTC unix时间戳,服务器允许设置几秒的偏移容忍度。
  4. 密钥轮换:部署时提供多版本公钥(kid字段),支持平滑过渡。

3.7 性能优化策略

  • 缓存公钥对象:RSA公钥解析较慢,第一次加载后复用,避免每次验签都重新解析PEM。
  • 池化签名器:对HMAC使用线程安全的 hmac 实例,或使用 HmacPool 减少对象创建。
  • 批量验证:对于MQ或事件驱动的支付回调,可批量读取后并行验签,利用多核。
  • 移除不必要的Payload声明:减小体积可降低网络传输与Base64编码开销。
对称与非对称JWT签名验证流程 对称签名(HMAC)与非对称签名(RSA)JWT 생성/验证对比 对称(HMAC) Header + Payload (Base64URL) 共享密钥 HMAC-SHA256 生成签名 JWT (xxx.yyy.zzz) Header + Payload (Base64URL) 私钥(签名) RSA-SHA256 生成签名 JWT (xxx.yyy.zzz) 验证端 验证端 相同共享密钥验证签名 公钥验证签名 通过 / 拒绝 通过 / 拒绝 密钥必须安全分发 公钥可公开,私钥保密

3.8 常见错误排查清单

错误现象 可能原因 解决方案
Signature verification failed 密钥不匹配 / 签名输入不同(如空格、换行差异) 对比签名前的字节流;检查密钥格式(如HMAC需二进制)
JWT expired exp 时间小于当前时间 检查系统时钟同步(NTP);增加 leeway 容忍
Invalid algorithm Header中的alg与验签实现不一致(如HS256 vs RS256) 统一算法标识;拒绝非预期alg防止算法混淆攻击
Base64URL解码错误 未处理标准Base64与URL安全变体的差异(+ / -、/ / _、=去掉) 使用专用 base64url 库或手动替换字符
生产建议:支付SDK中对JWT的验证应拒绝 alg: none 的,并始终检查 typ 是否为 JWT,防止类型混淆。