PAYATHON 2026

支付宝公钥证书模式唤起支付提示验签错误如何解决?

支付老李

结论

问题通常不在于公钥和私钥不匹配,而是混用了两种验签模式。

使用支付宝公钥证书模式时,不能把”支付宝公钥证书”的文本内容复制到原来的”支付宝公钥”配置项中。.crt 文件是 X.509 证书,并不是普通的 RSA 公钥。证书模式需要同时配置应用公钥证书、支付宝公钥证书和支付宝根证书,并通过支持证书模式的 SDK 完成初始化和验签。

原因分析

支付宝公钥模式通常只需要:

  • 应用私钥
  • 支付宝公钥
  • sign_type=RSA2

支付宝公钥证书模式需要:

  • 应用私钥
  • 应用公钥证书,例如 appCertPublicKey_*.crt
  • 支付宝公钥证书,例如 alipayCertPublicKey_RSA2.crt
  • 支付宝根证书,例如 alipayRootCert.crt
  • sign_type=RSA2

证书文件不仅包含公钥,还包含颁发者、序列号、有效期和签名算法等信息。SDK 会根据证书计算 app_cert_sn 和 alipay_root_cert_sn,再使用支付宝证书验证接口响应和异步通知的签名。

下面这种配置是错误的:

支付宝公钥 = 复制 alipayCertPublicKey_RSA2.crt 中的文本内容

应用公钥与应用私钥匹配,只能说明应用侧的密钥对正确,不能说明支付宝证书、根证书以及 SDK 初始化方式也配置正确。

正确配置步骤

1. 确认开放平台使用的是证书模式

在支付宝开放平台对应的应用中上传应用公钥证书,不要填写普通应用公钥。

同时确认上传的 appCertPublicKey_*.crt 与程序当前使用的应用私钥来自同一套密钥。测试环境与生产环境的证书不能混用,重新生成证书后也不要继续使用原来的私钥或证书。

2. 下载完整的支付宝证书

配置应用公钥证书后,从开放平台下载:

appCertPublicKey_*.crt
alipayCertPublicKey_RSA2.crt
alipayRootCert.crt

不同开放平台页面显示的文件名可能略有不同,以实际下载的文件为准。

三个文件各有用途:

文件用途
appCertPublicKey_*.crt计算应用证书序列号,标识应用证书
alipayCertPublicKey_RSA2.crt验证支付宝响应和异步通知签名
alipayRootCert.crt计算支付宝根证书序列号,校验证书链

不能只配置支付宝公钥证书,也不能用支付宝公钥证书代替根证书。

3. 使用证书模式初始化 SDK

以 Java SDK 的常见配置方式为例:

AlipayConfig config = new AlipayConfig();
config.setServerUrl("https://openapi.alipay.com/gateway.do");
config.setAppId("your_app_id");
config.setPrivateKey("your_application_private_key");
config.setFormat("json");
config.setCharset("UTF-8");
config.setSignType("RSA2");

config.setAppCertPath("/path/to/appCertPublicKey_xxx.crt");
config.setAlipayPublicCertPath("/path/to/alipayCertPublicKey_RSA2.crt");
config.setRootCertPath("/path/to/alipayRootCert.crt");

AlipayClient alipayClient = new DefaultAlipayClient(config);

在证书模式下,不要继续使用普通公钥模式的初始化方式:

new DefaultAlipayClient(
    gateway,
    appId,
    privateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

这里的 alipayPublicKey 参数要求传入普通的支付宝 RSA 公钥,不能传入 .crt 文件内容。

SDK 版本不同,实际类名和配置方法也可能不同。如果当前 SDK 没有证书路径或证书内容相关的配置项,需要先确认该版本是否支持公钥证书模式。

4. 异步通知也要使用证书验签

如果支付已经创建成功,但处理回调时提示验签失败,还需要检查异步通知的验签代码。

Java SDK 的证书验签通常类似:

boolean verified = AlipaySignature.rsaCertCheckV1(
    requestParams,
    "/path/to/alipayCertPublicKey_RSA2.crt",
    "UTF-8",
    "RSA2"
);

不要继续调用需要普通支付宝公钥字符串的验签方法,例如:

AlipaySignature.rsaCheckV1(
    requestParams,
    alipayPublicKey,
    "UTF-8",
    "RSA2"
);

接收异步通知参数时,要保留支付宝发送的原始字符串值。验签前不要自行排序、拼接或进行 JSON 序列化,也不要转换金额格式或修改字符编码。

例如在 Servlet 环境中:

Map<String, String> params = new HashMap<>();

Map<String, String[]> requestParams = request.getParameterMap();
for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
    params.put(entry.getKey(), String.join(",", entry.getValue()));
}

boolean verified = AlipaySignature.rsaCertCheckV1(
    params,
    "/path/to/alipayCertPublicKey_RSA2.crt",
    "UTF-8",
    "RSA2"
);

具体使用哪个验签方法,应以当前支付宝 SDK 提供的证书模式接口为准。

排查清单

可以按以下顺序检查:

  1. 开放平台配置的是”公钥证书模式”,不是普通的”公钥模式”。
  2. 程序中的 app_id 与证书所属应用一致。
  3. 当前应用私钥与 appCertPublicKey_*.crt 属于同一套密钥。
  4. 三个证书文件均已配置,而且没有填错位置。
  5. 没有把 .crt 文件原文填写到普通的 alipayPublicKey 字段。
  6. SDK 使用证书模式完成初始化。
  7. 请求和验签都使用 RSA2,字符集通常为 UTF-8。
  8. 没有混用沙箱证书和正式环境证书。
  9. 更换证书后已经重启服务,旧配置和缓存已经失效。
  10. 容器或服务器可以读取证书文件,路径、权限和挂载位置均正确。
  11. 异步通知验签使用支付宝公钥证书,而不是应用公钥证书。
  12. 验签前没有修改通知参数的原始内容。

可以辅助检查的命令

查看证书基本信息:

openssl x509 -in alipayCertPublicKey_RSA2.crt -noout -subject -issuer -serial -dates

从应用公钥证书中导出公钥:

openssl x509 -in appCertPublicKey_xxx.crt -pubkey -noout > app-cert-public.pem

从应用私钥中导出公钥:

openssl pkey -in app-private-key.pem -pubout > private-derived-public.pem

比较两个公钥:

diff app-cert-public.pem private-derived-public.pem

如果私钥是没有 PEM 头尾的纯 Base64 字符串,需要先根据实际密钥格式进行转换,再使用 OpenSSL。不能直接执行上述命令。

注意事项

不要手工删除证书头尾,再把中间的 Base64 内容当作支付宝公钥。证书中间编码的是完整的 X.509 证书结构,不是单独的 RSA 公钥。

即使通过 OpenSSL 正确提取了证书中的公钥,也不代表证书模式已经配置完成。证书模式还涉及应用证书序列号、支付宝根证书序列号和证书选择逻辑,应由官方 SDK 读取完整证书。

如果业务后台只有”支付宝公钥”一个输入框,没有应用证书、支付宝证书和根证书的配置项,那么该后台很可能只支持普通公钥模式。此时需要升级支付组件以支持证书模式,或者继续使用普通公钥模式。仅复制证书内容无法完成模式切换。

备注:内容仅供参考。