尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

支付宝密钥生成指南:用OpenSSL搞定应用公钥私钥

支付宝密钥生成指南:用OpenSSL搞定应用公钥私钥 对不少第一次接入支付宝支付的同学来说整个流程里最劝退的往往不是写代码而是最前面那道“生成应用公钥和私钥”的门槛。一个朋友上周在群里问控制台让我上传应用公钥我又要拿私钥签名到底先有鸡还是先有蛋其实支付宝不替你生成密钥它只负责验证你上传的公钥RSA 非对称加密体系里私钥必须由你自己保管、自己生成。说穿了这就是 OpenSSL 三条命令的事但真正让新手反复折腾的是三个概念没厘清应用私钥、应用公钥、支付宝公钥它们仨根本不是一回事。这篇文章适合所有准备接入支付宝电脑网站支付、手机网站支付、App 支付或者小程序支付的同学也适合那些已经把密钥生成但一直被验签报错折磨的人。我会从为什么需要这套密钥讲起到完整的生成命令、格式转换、上传配置再到联调阶段最常见的报错和排查链路最后补一点密钥安全管理的经验。1. 为什么支付宝不直接给你一套密钥而要你自己生成1.1 支付请求与回调通知里的双向签名先搞清楚这套密钥在什么环节被用到后面就不会晕。你发起一笔支付订单时你的后端要拿着应用私钥对请求参数签名然后连同签名一起发给支付宝。支付宝收到后用你在控制台上传的应用公钥去验签确认“这确实是你的服务器发来的请求”。订单支付完成后支付宝会往你的回调地址发异步通知。这条通知反过来要由支付宝用自己的私钥签名你的服务器收到后得用支付宝公钥去验签确认“这确实是支付宝发来的而不是有人伪造的回调”。这就是为什么你不仅要自己上传公钥还要从支付宝侧再下载一个它自己的公钥签名验证是双向的。理解这个流程最大的价值在于你能预判哪些环节容易出错私钥保管在本地绝对不能泄露上传的是公钥不能传私钥验签用的是支付宝公钥而不是你自己那对里的公钥。这三条记不住后面每一个报错都会让你怀疑人生。1.2 RSA2 与 RSA算法选错后面全白搭支付宝开放平台现在默认推荐 RSA2也就是 SHA256withRSA密钥长度要求 2048 位旧的 RSASHA1withRSA已经逐渐淘汰。新建应用时遇到“签名方式”选项直接选 RSA2。有人会问RSA2 是不是要重新生成一种新密钥不是。RSA2 和 RSA 只是签名摘要算法的差别密钥本身都是 RSA 密钥对同一对 2048 位密钥既可以跑 RSA 也可以跑 RSA2关键看你代码里sign_type传什么。但我还是建议你配置和代码统一用 RSA2一方面安全性更好另一方面支付宝官方文档和大多数 SDK 的默认值也都朝 RSA2 靠。1.3 三个名词必须一字不差地记住名词谁生成谁保管用处应用私钥你自己用 OpenSSL 生成放在你自己的服务器绝不可泄露对请求参数签名应用公钥由应用私钥推导生成上传到支付宝开放平台控制台支付宝验你的签名支付宝公钥支付宝生成从控制台复制回来存入你的代码/配置文件你验支付宝回调签名很多人嘴上说着“生成支付宝公钥和私钥”实际上要生成的是“应用公钥和应用私钥”而“支付宝公钥”是支付宝给你、你去下载的那个。把这三者关系在脑子里理顺比急着跑命令重要得多。2. 动手之前的准备工作工具、格式和目录规划2.1 OpenSSL 的安装与版本确认生成 RSA 密钥最通用的工具就是 OpenSSL。macOS 和绝大多数 Linux 发行版都自带openssl version如果提示找不到命令macOS 可以用 Homebrew 装brew install openssl。Windows 上则推荐去官方或知名镜像站下载 Win64 OpenSSL 安装包安装时勾选把 OpenSSL 加入 PATH装完之后重新开一个终端窗口再执行openssl version。版本上我建议至少 1.1.1 以上新版本对密码学算法和格式转换的支持更完整。不用纠结最新版本只要openssl version能正常输出就没问题。命令行工具装好后拿一张纸把下面几条命令抄下来后面照着敲就行。2.2 PEM 文件的内部结构与 PKCS1/PKCS8 的区别生成的密钥文件是 PEM 格式纯文本。一个标准的 PEM 公钥文件长这样-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY-----私钥则有两种常见外观-----BEGIN RSA PRIVATE KEY-----是 PKCS1 格式-----BEGIN PRIVATE KEY-----是 PKCS8 格式。支付宝的部分 SDK 对私钥格式有要求比如 Java 生态通常要 PKCS8PHP 的很多封装直接用 PKCS1 也能跑。好消息是OpenSSL 可以在两种格式之间随意转换后面我会给具体命令。这类密钥文件很容易被“复制粘贴时少了行”破坏。我见过太多人把公钥从终端复制到控制台时只复制了一半或者把换行符弄丢导致上传后控制台报“公钥格式错误”。记住PEM 是严格按行解析的-----BEGIN和-----END是完整结构的一部分缺一行整个文件都会废掉。2.3 密钥文件的命名、存放与权限我习惯在一个专门的目录里生成密钥比如项目的keys/目录并且按环境区分keys/ app_private_key.pem # 应用私钥生产环境 app_public_key.pem # 应用公钥上传用 alipay_public_key.pem # 支付宝公钥回调验签用 sandbox_app_private_key.pem sandbox_alipay_public_key.pem密钥文件建议立刻用chmod 600收紧权限防止同服务器上的其他用户读取。提示这个keys/目录必须写进.gitignore。把私钥提交到 Git 仓库基本等于把这笔支付的资金安全丢进垃圾桶。3. 三条命令生成密钥对从生成到验证到格式转换3.1 生成 2048 位 RSA 私钥并导出公钥进入你规划好的密钥目录依次执行openssl genrsa -out app_private_key.pem 2048 openssl rsa -in app_private_key.pem -pubout -out app_public_key.pem第一条命令生成 2048 位的 RSA 私钥。genrsa会利用系统随机数种子生成质数所以每次生成的文件内容都不一样这很正常。第二条命令从私钥中导出对应的公钥写入app_public_key.pem。到这里密钥对已经生成完毕。支付宝控制台上传的是app_public_key.pem的内容也就是以-----BEGIN PUBLIC KEY-----开头的那一整块文本。粘贴时我建议完整复制整个文件内容不要手动去掉BEGIN/END行去不去通常都能识别但保留更稳妥也方便本地文件与线上配置保持一致。3.2 校验密钥对是否匹配提交之前最好确认一下公钥确实由这把私钥导出避免文件名搞混。可以用 diff 对比diff (openssl rsa -in app_private_key.pem -pubout 2/dev/null) app_public_key.pem没有输出就说明两者匹配。我还会顺手看一眼密钥的基本信息openssl pkey -in app_private_key.pem -text -noout输出里能看到Private-Key: (2048 bit)和一堆模数参数。这一步不是必需的但对新手建立“这确实是一对有效密钥”的信心很有帮助。如果你发现 diff 有输出别慌先检查是不是在错误的目录里执行了命令或者文件被覆盖过。3.3 PKCS1 与 PKCS8 的转换不同语言 SDK 对私钥格式的要求不一样。如果 Java 环境报类似algid parse error, not a sequence的错十有八九是私钥格式不满足要求转换一下即可# PKCS1 转 PKCS8 openssl pkcs8 -topk8 -inform PEM -in app_private_key.pem -outform PEM -nocrypt -out app_private_key_pkcs8.pem转换产物以-----BEGIN PRIVATE KEY-----开头。注意这里一定要带上-nocrypt否则 OpenSSL 会提示设置密码保护生成出来的私钥文件带加密口令SDK 加载时反而多一层障碍。绝大部分服务端场景的私钥不设口令靠文件权限和保密来兜底。反过来如果你拿到的是 PKCS8 想转回 PKCS1可以用openssl rsa -in app_private_key_pkcs8.pem -out app_private_key.pem转换时如果报expecting: TRY_PRIVATE_KEY或者unable to load Private Key说明你输入文件的格式和-inform参数对不上调整声明格式即可。4. 把应用公钥上传到支付宝再把支付宝公钥请回来4.1 沙箱环境与生产环境的入口差异开发调试阶段务必先在沙箱环境跑通别拿真实账号和真实资金去试错。支付宝开放平台的控制台里有“沙箱”入口进入沙箱应用详情页后能看到沙箱的 APPID、支付宝网关地址、应用公钥上传区以及一套独立的 RSA2 密钥配置。沙箱环境的公钥私钥和生产环境是两套互不通用。如果图省事沙箱控制台也可以让平台帮你“一键生成”一套密钥但我不推荐在沙箱阶段就直接用平台的密钥原因很简单你用平台生成的私钥练手等于跳过了“自己生成密钥”这个核心动作等上生产环境还要再学一遍不如从一开始就用自己的 OpenSSL 流程。沙箱就是用来折腾的把它当成练兵场。4.2 上传应用公钥时的格式细节进入应用详情页的“开发设置”→“接口加签方式”选择公钥模式然后把app_public_key.pem整个文件的内容粘贴进去。这里有几个容易踩的细节复制时用编辑器打开 PEM 文件全选复制不要把终端里被折行显示的文本直接贴过去终端折行会破坏 Base64 内容。提交后页面通常会提示保存成功并立即展示对应的支付宝公钥如果没看到刷新页面再找一次。如果提示“公钥格式错误”先检查是否整段复制再看文件里有没有混入多余空格或者 Windows 记事本留下的 BOM 头。这里特别强调上传的是“应用公钥”文件不是私钥也不是支付宝公钥。选错文件是最常见的人为失误官方不在每一项旁边都写“这是应用公钥”这种提示全靠自己确认。4.3 拿到支付宝公钥之后先处理好格式再存盘上传应用公钥成功后控制台会展示“支付宝公钥”。你需要把它保存成本地的alipay_public_key.pem文件供校验回调签名使用。看起来很简单但这里埋着一个高频大坑控制台展示的是一段连续的 Base64 字符串而 SDK 里的验签函数通常要求 PEM 格式也就是要带-----BEGIN PUBLIC KEY-----头尾并且 Base64 部分每 64 个字符换一行。正确的做法有两种一是从控制台下载平台提供的“支付宝公钥”文件如果有下载按钮就直接下载格式是现成的二是手动拼装把复制到的 Base64 内容按 64 字符断行包上BEGIN/END标记。手动拼装时尤其注意最后别多出空行。很多“回调验签一直失败”的问题最后查出来都是这个文件格式不对而不是代码逻辑错。5. 联调阶段最常见的验签报错与完整排查链路5.1 Python 报错 “argument should be integer or bytes-like object, not str”这个报错在 Python 里非常经典直接对应很多人搜的“支付宝验签 argument should be integer or bytes-like object”。它出现在你用cryptography库或支付宝 SDK 加载 PEM 公钥时本质原因是load_pem_public_key()这类函数要求传入的是 bytes 字节串而你把读取的文件内容以 str 传进去了。我见过两种常见写法第一种会报错# 错误示例 public_key open(alipay_public_key.pem, r).read() from cryptography.hazmat.primitives.serialization import load_pem_public_key load_pem_public_key(public_key) # TypeError: argument should be integer or bytes-like object, not str改成二进制模式读取问题立刻消失# 正确示例 with open(alipay_public_key.pem, rb) as f: public_key f.read() load_pem_public_key(public_key)如果你确实要用字符串也可以显式.encode(utf-8)转成 bytes。这个报错和密钥内容本身对不对无关纯粹是类型问题。但很多人在这一步会误以为自己生成的公钥有问题反复重新生成密钥白白浪费时间。5.2 验签失败的通用排查步骤如果代码没类型错误但验签始终失败我建议按下面的链路一步步排别跳步确认验签用的是支付宝公钥而不是你自己应用公钥。这是最高频的混淆点。确认待验签的字符串和你签名时的字符串完全一致包括字段顺序、是否包含空值字段。很多 SDK 会把参数按 key 排序后拼成k1v1k2v2千万不要手动改拼接逻辑。确认sign_type传的是RSA2与你在控制台选择的签名方式一致。两边不一致时报错往往就是“验签失败”。确认私钥文件没有被换行符或 BOM 破坏。Windows 下用记事本编辑过 PEM 文件后有时会留下 BOM导致读取内容开头多出隐藏字符。如果是验签异步通知注意支付宝的通知参数里有sign但拼接待验签串时不要包含sign和sign_type这两个字段本身这是新手最容易忽略的。手动拼接待验签字符串时一个典型的正确结构长这样以某些语言的原始验签为例app_id202100...biz_content...charsetutf-8methodalipay.trade.page.paysign_typeRSA2timestamp2025-01-01 12:00:00version1.0顺序按参数名 ASCII 排序值用原始值不要 URL 编码。RSA2 的签名结果是 Base64 字符串验签前通常要先做 Base64 解码再把原文、签名、公钥一起交给验签函数。这些细节SDK 通常帮你封装好了但出问题排查时你得知道它们在背后干了什么。5.3 顺带澄清搜索时容易撞见的“没有公钥无法验证”最近搜索公钥私钥相关问题的人会经常撞见一类完全不同的报错类似“由于没有公钥无法验证下面的签名”或者NO_PUBKEY。这是 Linux 软件源 GPG 密钥缺失的问题和支付宝的 RSA 公钥验签不是一回事。前者是包管理器在验证软件源签名需要导入对应 GPG 公钥后者是支付宝支付链路里的业务签名验证用的工具链和概念都不同。如果你在排查支付问题时看到这类报错基本可以断定搜索方向偏了建议把关键词改成“支付宝验签 你的编程语言 报错原文”。5.4 沙箱环境里的“模拟器”替代方案很多人听说有“支付宝模拟器”这个东西想拿它做回调测试。我的态度是官方并没有一个所谓 1:1 的支付宝模拟器网上的第三方模拟器来源和正确性都不可控。更可靠的验证方案是在沙箱环境跑通支付下单后自己写一段本地脚本模拟支付宝异步通知的请求体往本地回调地址 POST 一遍用支付宝公钥验签再把业务状态推进一遍。这样既可控又安全还能顺便把验签流程的边界测出来。6. 密钥安全与长期维护的几个务实建议6.1 密钥泄漏的典型场景与止损动作私钥一旦泄露别人就能伪装你的应用向支付宝发起签名请求后果非常严重。常见的泄漏场景有这么几类私钥被提交进 Git 仓库、服务器被入侵后密钥文件权限过松、开发环境密钥和生产环境复用、把私钥贴到聊天工具或第三方“转格式网站”里。如果确认私钥泄露立刻去支付宝开放平台控制台“更换应用公钥”。做法是重新生成一对密钥上传新的应用公钥同时通知后端同步替换新的应用私钥。因为支付宝验签用的是你最新上传的应用公钥所以换绑公钥等于让旧的私钥立即失效比你只改服务器上的私钥文件要彻底得多。6.2 密钥轮换给旧私钥留一个缓冲期密钥轮换时别做“秒切”。先让新旧公钥并行使用几天具体做法是后端保留旧私钥用于处理尚未完成的支付流程同时新私钥已经可以签名新的请求等所有进行中的订单结束后再彻底移除旧私钥。支付宝开放平台支持查看公钥更换记录运维时留个心眼别在一次发布里同时换公钥和改代码否则出问题都不知道是哪个环节引起的。6.3 沙箱和生产一定要分开沙箱密钥、沙箱 APPID 和生产环境完全隔离。曾经有人把沙箱 APPID 或沙箱网关配置带到线上结果支付请求全部失败或者更糟的是在沙箱里“支付成功”代码却以为自己收到了正式订单。我的建议是把沙箱配置和生产配置分别做成环境变量启动时显式区分禁止用同一个配置文件跑两个环境。这一步看似和密钥生成无关但实际上密钥泄漏和混用才是大多数支付事故的根源。我在实际项目里的习惯是把openssl genrsa、导出公钥、PKCS8 转换这三步写成一个固定脚本目录结构也固定下来新项目直接复制运行。密钥生成这事本身不难难的是每次都手忙脚乱地临时查命令然后在“上传错文件”“格式不对”“验签类型不对”这几个坑之间反复横跳。把这些动作固化下来之后你会发现支付宝支付集成最让人头疼的部分其实十分钟就能利索地跑完。
返回列表