PAYATHON 2026

寻找官方且经过测试的支付宝签名与加密解密工具类

支付老李

明确结论

参考实现应优先从支付宝开放平台的目标接口文档、官方 SDK 下载页或官方代码仓库中获取。如果目标接口采用支付宝开放平台的通用签名机制,可以重点参考官方 Java SDK 中的 AlipaySignature 等工具类。Java SDK 源码较完整,适用范围也较广,适合用来核对 Ruby 实现。

不要直接移植博客或零散代码片段中的所谓“支付宝工具类”。签名失败往往不是 RSA 算法出了问题,而是待签名字符串、字符编码、参数过滤规则或响应验签范围不一致。

支付宝不同产品、接口版本和接入模式可能使用不同协议。因此,具体实现必须遵循目标接口文档规定的 sign_type、密钥格式、签名字段和验签方式。

去哪里找官方参考实现

可以按以下顺序查找:

  1. 打开支付宝开放平台的目标 API 文档。
  2. 查看文档中的“SDK 调用”“签名机制”“接口工具”或“示例代码”。
  3. 下载官方 Java SDK 或对应语言的 SDK。
  4. 在 SDK 源码中搜索以下类或方法:
    • AlipaySignature
    • rsaSign
    • rsaCheckV1
    • rsa256CheckContent
    • getSignContent
  5. 使用开放平台提供的签名验签工具,交叉验证 Ruby 实现。

官方 SDK 的包名和方法名可能随版本变化,不能只根据类名判断实现是否适用于当前接口。更可靠的依据是接口文档引用的 SDK 版本及其源码。

在采用支付宝开放平台通用协议的 Java SDK 中,相关工具通常位于类似下面的包中:

com.alipay.api.internal.util.AlipaySignature

如果接口还涉及 AES 解密,可以在同一版本的 SDK 中继续搜索 AlipayEncrypt、aesEncrypt、aesDecrypt 等名称。具体类名以实际下载的官方 SDK 为准。

Ruby 版 RSA2 签名参考

下面是一份通用的 RSA2 签名示例,可以作为移植时的代码骨架。参数过滤规则和待签名内容仍需根据具体支付宝接口确认。

require "openssl"
require "base64"

module AlipayCrypto
  module_function

  def sign_content(params)
    params
      .reject { |key, value| key.to_s == "sign" || value.nil? || value.to_s.empty? }
      .sort_by { |key, _value| key.to_s }
      .map { |key, value| "#{key}=#{value}" }
      .join("&")
  end

  def rsa2_sign(content, private_key_pem)
    private_key = OpenSSL::PKey::RSA.new(private_key_pem)
    signature = private_key.sign(OpenSSL::Digest::SHA256.new, content)
    Base64.strict_encode64(signature)
  end

  def rsa2_verify(content, signature, public_key_pem)
    public_key = OpenSSL::PKey::RSA.new(public_key_pem)
    decoded_signature = Base64.strict_decode64(signature)

    public_key.verify(
      OpenSSL::Digest::SHA256.new,
      decoded_signature,
      content
    )
  rescue ArgumentError, OpenSSL::PKey::PKeyError
    false
  end
end

调用示例:

params = {
  "app_id" => "your_app_id",
  "method" => "alipay.trade.page.pay",
  "charset" => "utf-8",
  "sign_type" => "RSA2",
  "timestamp" => "2026-09-09 10:00:00",
  "version" => "1.0",
  "biz_content" => '{"out_trade_no":"ORDER_1001","total_amount":"0.01"}'
}

content = AlipayCrypto.sign_content(params)
signature = AlipayCrypto.rsa2_sign(content, private_key_pem)

puts content
puts signature

这里采用的是:

RSA + SHA-256 + Base64

这就是支付宝文档通常所说的 RSA2 签名方式。如果目标接口明确要求 RSA,其摘要算法通常不同,不能直接使用 SHA256。

公钥和私钥格式处理

Ruby 的 OpenSSL::PKey::RSA 通常需要 PEM 格式的密钥。如果配置中保存的是不含头尾标记的 Base64 内容,可以先补上 PEM 标记:

def wrap_private_key(base64_key)
  body = base64_key.scan(/.{1,64}/).join("\n")

  <<~PEM
    -----BEGIN PRIVATE KEY-----
    #{body}
    -----END PRIVATE KEY-----
  PEM
end

def wrap_public_key(base64_key)
  body = base64_key.scan(/.{1,64}/).join("\n")

  <<~PEM
    -----BEGIN PUBLIC KEY-----
    #{body}
    -----END PUBLIC KEY-----
  PEM
end

私钥可能采用 PKCS#1,也可能采用 PKCS#8。两种格式的 PEM 头通常分别是:

-----BEGIN RSA PRIVATE KEY-----

和:

-----BEGIN PRIVATE KEY-----

不要试图只修改 PEM 头来“转换”密钥格式。如果格式不匹配,应使用 OpenSSL 等工具完成真正的格式转换。

如何证明 Ruby 实现正确

验证实现时,最好准备一组固定测试向量,而不是只尝试一次线上请求:

  1. 固定一组参数、私钥和支付宝公钥。
  2. 使用官方 SDK 生成待签名字符串和签名。
  3. 使用 Ruby 对同一个字符串签名。
  4. 分别进行交叉验签:
    • 用 Ruby 验证官方 SDK 生成的签名;
    • 用官方 SDK 验证 Ruby 生成的签名。
  5. 继续测试中文、空值、特殊字符和嵌套 JSON。

对于 RSA PKCS#1 v1.5 签名,如果密钥、摘要算法和原文相同,通常可以直接比较 Base64 结果。即便结果可以直接比较,验签是否成功仍是更重要的判断标准。

容易踩坑的地方

待签名字符串必须完全一致

是否排除空值和 sign 参数、是否按参数名排序,以及 biz_content 是否保留原始字符串,都要严格遵循接口文档。

例如,下面两个 JSON 的语义相同,但作为签名原文时并不相同:

{"a":1,"b":2}
{ "b": 2, "a": 1 }

完成签名后,不要重新格式化 JSON,也不要先解析再序列化或改变字段顺序。

URL 编码和签名是两个阶段

通常需要先根据原始参数值生成待签名字符串,再把签名结果作为请求参数进行 URL 编码。除非具体接口文档明确要求,否则不能直接对 URL 编码后的整段查询字符串签名。

响应验签不能想当然

支付宝同步响应、异步通知和部分特殊产品可能采用不同的验签内容。有些场景验证参数集合,有些场景验证响应中的特定原始内容。不要自行解析、重排或重新生成响应 JSON 后再验签,应按照接口文档和官方 SDK 的处理方式提取待验签内容。

“支付宝公钥”不是“应用公钥”

应用私钥用于生成签名,支付宝公钥用于验证支付宝返回的签名。应用公钥通常需要配置到支付宝开放平台,但不能用来验证支付宝响应。

如果使用支付宝公钥证书模式,还需要按证书模式处理,不能把证书文件简单地当作普通公钥字符串。

加密与签名不是一回事

RSA2 签名用于确认数据来源和完整性,不负责隐藏数据。只有当接口文档明确要求 encrypt_type、AES 密钥或特定加密流程时,才需要实现加密和解密。

RSA 签名代码不能代替 AES 解密,也不要为普通支付请求自行增加加密步骤。

比较可靠的实现方式是以目标接口文档为准,选择同一时期的支付宝官方 Java SDK 作为参考,逐项核对参数规范化、RSA2 签名、响应验签和可选的 AES 处理逻辑,最后使用官方工具和固定测试向量验证 Ruby 版本。

备注:内容仅供参考。