Featured image of post 支付宝当面付 - 公钥私钥生成

支付宝当面付 - 公钥私钥生成

- 谁还用官方的生成工具啊 = =

前言

接入支付宝当面付时,开放平台要求为应用配置接口加签方式。选择“公钥”模式、加签算法 RSA2(即 SHA256withRSA,密钥长度 2048 位)时,需要我们自己生成一对 RSA 密钥:

  • 应用私钥:自己保存,用来给请求签名;
  • 应用公钥:上传到支付宝开放平台,支付宝用它验证请求的签名。

上传应用公钥后,平台会返回一个 支付宝公钥,用来在项目里验证支付宝返回的数据和异步通知。

官方有密钥生成工具,但其实一条 openssl 命令就能搞定。

密钥的流向

步骤

  1. 生成 私钥(RSA 2048 位)。注意:私钥需要保存好,不要泄露。

    1
    
    openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private_key.pem
    

    较新的 OpenSSL / LibreSSL 用 genpkey 生成 RSA 密钥默认就是 2048 位,所以原来的写法 openssl genpkey -algorithm RSA -out private_key.pem 在新版本上结果一样;但老版本的默认值可能是 1024 位,显式写上 rsa_keygen_bits:2048 更稳妥。

  2. 从上述 私钥 中导出 公钥。注意:公钥需要上传到支付宝开放平台,不要泄露 私钥。

    1
    
    openssl rsa -pubout -in private_key.pem -out public_key.pem
    
  3. 把 公钥 上传到支付宝开放平台(接口加签方式选择“公钥”,算法 RSA2),获取 支付宝公钥。

  4. 将第一步得到的 私钥 和平台给的 支付宝公钥 配置到项目中即可。

密钥格式说明

私钥:PKCS#8 与 PKCS#1

openssl genpkey 输出的是 PKCS#8 格式,文件头是 -----BEGIN PRIVATE KEY-----。支付宝的 Java SDK 使用的就是 PKCS#8 格式的私钥。

部分非 Java 语言的 SDK 或旧代码要求 PKCS#1 格式(文件头是 -----BEGIN RSA PRIVATE KEY-----),可以这样转换:

1
2
# OpenSSL 3.x:需要加 -traditional 才会输出 PKCS#1
openssl rsa -in private_key.pem -traditional -out private_key_pkcs1.pem

OpenSSL 1.1.x 以及 macOS 自带的 LibreSSL 下,openssl rsa -in private_key.pem -out private_key_pkcs1.pem 默认就输出 PKCS#1,不需要(也不认识)-traditional。具体用哪种,以所用 SDK 的文档为准。

公钥

openssl rsa -pubout 导出的公钥文件头是 -----BEGIN PUBLIC KEY-----(X.509 SubjectPublicKeyInfo 格式),这正是开放平台需要的格式,不用转换。

去掉头尾和换行

开放平台上传公钥、以及很多 SDK 配置私钥时,填的是一整行纯 Base64 字符串,不带 -----BEGIN ...----- / -----END ...----- 头尾,也不带换行。可以这样处理:

1
2
3
4
5
# 上传到开放平台的应用公钥
grep -v '^-----' public_key.pem | tr -d '\n' > public_key.txt

# 填到项目配置里的应用私钥(需要单行字符串时)
grep -v '^-----' private_key.pem | tr -d '\n' > private_key.txt

命令与输出示意

注意事项

  • 私钥只保存在服务端,不要提交到 Git 仓库,也不要放到前端或客户端里。
  • 自己验证一下密钥长度:openssl rsa -in private_key.pem -noout -text | head -1,OpenSSL 3.x 输出 Private-Key: (2048 bit, 2 primes),LibreSSL 输出 RSA Private-Key: (2048 bit)。
  • macOS 自带的 /usr/bin/openssl 实际上是 LibreSSL,可以用 openssl version 确认,上面的命令里只有 -traditional 不通用。
  • 支付宝公钥 和 应用公钥 不是一个东西,项目里要配置的是平台返回的支付宝公钥,别填反了。

参考

由 Hugo 强力驱动