因为支付宝官方的sdk并未提供Swift的版本,所以需要自己实现签名和验签。本文使用swift版本5.7+版本配合krzyzanowskim/CryptoSwift实现。当然如果使用vapor/jwt-kit也可以的,用jwt-kit会更方便(无需处理密钥格式),验证过是可以走通的,因为我需要使用CryptoSwift的其他功能,所以这里以CryptoSwift为例。

密钥的处理

1、生成应用公钥和应用私钥

下载支付宝的开放平台密钥工具,生成密钥,加密算法为RSA2,生成应用公钥和应用私钥,工具默认生成的是X.509/SPKI 和 PKCS#8,需要转为PKCS#1格式。

2、获取支付宝公钥

2025-08-12T04:01:37.png

在支付宝支付控制后台,设置加签方式密钥方式,之后将工具应用公钥复制到填写应用公钥输入框,点击确认上传,获得支付宝公钥。然后下载支付宝公钥,会得到一个公钥的txt文件。应用公钥的用处已完成,可以删除。

3、处理支付宝公钥

打开下载的支付宝公钥txt文件,在头部和尾部添加标识

//头部增加
-----BEGIN PUBLIC KEY-----
//尾部增加
-----END PUBLIC KEY-----

改造之后的文件内容类似于

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ……
-----END PUBLIC KEY-----

命令行执行下面命令转换为pkcs1格式,会得到alipayPublicKey_RSA2_pkcs1.txt文件,后续使用

openssl rsa -pubin -in alipayPublicKey_RSA2.txt -RSAPublicKey_out -out alipayPublicKey_RSA2_pkcs1.txt

4、应用私钥处理

开放平台密钥工具生成的应用私钥txt文件重命名为private_pkcs8.txt,打开文件在头部和尾部分别增加以下标识

//头部增加
-----BEGIN PRIVATE KEY-----
//尾部增加
-----END PRIVATE KEY-----

改造之后的文件内容类似于

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASC……
-----END PRIVATE KEY-----

命令行执行以下命令转换为PKCS#1格式,会得到private_pkcs1.txt文件,留作后续使用

openssl pkey -in private_pkcs8.txt -out private_pkcs1.txt -traditional

发起支付

关键点:

1、表单的gateway.do需要设置charset,否则默认是gbk格式

var html = "<html><body onload='document.forms[0].submit()'>"
html += "<form method='POST' action='https://openapi.alipay.com/gateway.do?charset=utf-8'>"

2、构建post参数字典,加密时不要去掉sign_type,使用pkcs#1格式的私钥签名
3、签名传值传进去的是摘要,而不是原始数据,所以使用可以使用

需要验签字符串拼接

let unsignedString = parameters
            .filter { $0.key != "sign"}
            .sorted(by: { $0.key < $1.key })
            .map { "\($0)=\($1)" }
            .joined(separator: "&")
privateKeyRAS.sign(Data(unsignedString.utf8).byteArray.sha256(), variant: .digest_pkcs1v15_SHA256)

或者

privateKeyRAS.sign(Data(unsignedString.utf8).byteArray, variant: .message_pkcs1v15_SHA256)

支付验签

关键点:

  1. 验签使用前面生成的pkcs#1格式的支付宝公钥,而不是工具生成的应用公钥
  2. 在通知返回参数列表中,除去sign、sign_type两个参数外,凡是通知返回回来的参数皆是待验签的参数。(生活号异步待验签参数需保留sign_type参数)
  3. 将剩下参数进行url_decode, 然后进行字典排序,组成字符串,得到待签名字符串;
  4. 将签名参数(sign)使用 base64 解码为字节码串;

验签字符串拼接

let unsignedString = params
            .filter { $0.key != "sign" && $0.value != "" && $0.key != "sign_type" }
            .sorted(by: { $0.key < $1.key })
            .map { "\($0.key)=\($0.value)" }
            .joined(separator: "&")
let publicKeyRAS = try RSA(rawRepresentation: publicKeyData)
let result = try publicKeyRAS.verify(signature: signData.byteArray, for: Data(unsignedString.utf8).byteArray, variant: .message_pkcs1v15_SHA256)
return result
最后修改:2025 年 08 月 12 日
请我喝杯可乐,请随意打赏: ☞已打赏列表