马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
本帖最后由 pbai 于 2026-8-24 10:42 编辑
PBIDEA:用 uo_crypto 做 RSA 签名与验签,对接开放平台
阅读说明
1. 适用版本:PB 12.5 及以上;本文组件来自 PBIDEA(websuite.pbl 的 uo_crypto / uo_rsa),需配套 PbIdea.dll 运行库(含 RSA 签名、PKCS#1/PKCS#8 密钥解析)。
2. 支持数据库:本文为纯签名/验签,不依赖数据库;仅在"把签名回传服务器做业务校验"等扩展场景会用到你已有的后端库(按你实际后端选择,本文不绑定具体 DBMS)。
3. 操作系统与环境要求:Windows 7+;已安装 PBIDEA 运行库(PbIdea.dll 等),并把对应 pbl(websuite.pbl)导入工程。
4. 难度系数:★★★★☆(涉及密钥格式对齐、哈希算法选择与 UTF-8 编码细节,建议先读过 Day5 国密 SM4 一文了解 PbIdea.dll 的调用方式与编码约定)。
5. 其它阅读说明:示例私钥/公钥请用你自己的测试密钥替换;不要把真实业务私钥硬编码进源码提交到版本库。
一、为什么要用 RSA 签名
很多开放平台(支付、政务、第三方 API)要求你证明请求确实是你发出的、且中途没被篡改。做法是:
- 你用私钥对报文(或报文摘要)做一次"签名",生成一个签名串随请求一起发出;
- 平台用你事先登记过的公钥去"验签",验过才认这笔请求。
RSA 签名本质是"用私钥加密摘要、用公钥解密验证",它和"RSA 加密"不是一回事:加密是为了保密,签名是为了防抵赖和防篡改。本文只讲签名/验签这一件事,把 uo_crypto 上相关的 API 摸透。
二、核心 API 全貌(对照 websuite.pbl/uo_crypto)
uo_crypto 直接声明在 system library "PbIdea.dll" 里,和 RSA 签名/验签相关的有这几组(方法名取自真实导出源码,可直接抄):
| 方法 | 签名(关键信息) | 用途 | | SignWithRSA | (privateKey, text, method [, utf8 [, password]]) 返回 string | 用 PEM 私钥对字符串签名,返回 base64 签名 | | SignWithRSAVerify | (publicKey, text, sign, method [, utf8]) 返回 boolean | 用 PEM 公钥验签,返回是否通过 | | AlignKey | (key [, opensslKey [, privateKey]]) 返回 string | 把各种换行/头尾缺失的 PEM 补齐成标准格式,签名前务必先对齐 | | GenerateRSAKey | (publicFile, privateFile [, keylen [, pkcs]]) 返回 boolean | 生成 RSA 密钥对文件 | | ToUTF8 / FromUTF8 | (string) 返回 blob / (blob) 返回 string | 字符串与 UTF-8 字节互转(uo_crypto 的对象方法,调用写作 luo_crypto.ToUTF8(...) / luo_crypto.FromUTF8(...),不是裸全局函数) | | Base64Encode / Base64Decode | (blob [, urlsafe [, removeEqualSign]]) | 字节与 base64 互转 |
method 可取:sha1 / sha224 / sha256 / sha384 / sha512 / md4 / md5 / md5_sha1(注释里写明支持这些值)。开放平台现在几乎都用 sha256。
注意:还有另一个对象 uo_rsa(websuite.pbl/uo_rsa.sru),它的 sign/verify 接收的是 blob 原文(如 rsa.sign(luo_crypto.ToUTF8(原文), 私钥, "sha256", pss)),签名结果再用 luo_crypto.Base64Encode 收口。"RSA 签名本身是对任意二进制数据的摘要做运算,不局限于字符串"——图片、PDF、音视频等文件字节都能签。本文主推 uo_crypto.SignWithRSA 这条"字符串报文"省心链路(最常见于开放平台对接);当原文是二进制文件或需要 PSS 填充时,走 uo_rsa 的 blob 链路,详见第五节。
另外要澄清:上面列的是 uo_crypto 与"RSA 签名/验签"直接相关的 API;uo_crypto 其实也有 blob 级的 RSA 加解密(EncryptByPublicKey/EncryptByPrivateKey/DecryptByPublicKey/DecryptByPrivateKey,见第一节代码定义第 13~18 行),只是没有把 PSS/OAEP 包装成独立方法。本文聚焦"签名/验签",加解密不在主线范围内,但你知道它有即可,遇到"用公钥加密对称密钥"之类需求直接换这组方法。
三、最常用的一条链路:私钥签名 + 公钥验签
3.1 签名
前置:窗口 w_demo 上放一个按钮 cb_sign;工程已导入 websuite.pbl;准备你自己的 PEM 格式私钥字符串(下面用 ls_pri 占位,务必换成你自己的测试私钥)。
步骤:1) 声明 uo_crypto 并 create;2) 用 AlignKey 把私钥补齐为标准 PEM;3) 调用 SignWithRSA 得到 base64 签名;4) 用 MessageBox 看结果。
- // 示例输入:待签名原文(实际场景替换为真实业务报文)
- string ls_text
- ls_text = 'order_no=20260824&amount=100×tamp=1766467200'
- // 示例输入:你的 PEM 格式私钥(占位,请替换为真实测试私钥,切勿提交生产私钥)
- string ls_pri
- ls_pri = '-----BEGIN PRIVATE KEY-----'
- ls_pri = ls_pri + '(此处填入你的 BASE64 私钥内容,多行拼接)'
- ls_pri = ls_pri + '-----END PRIVATE KEY-----'
- // 示例输入:哈希算法,开放平台常用 sha256
- string ls_method
- ls_method = 'sha256'
- // 示例输入:原文是否按 UTF-8 编码参与签名(true=按 UTF-8)
- boolean lb_utf8
- lb_utf8 = true
- // 对齐私钥为标准 PEM(补齐头尾与换行,避免签名失败)
- uo_crypto luo_crypto
- luo_crypto = create uo_crypto
- string ls_pri_align
- ls_pri_align = luo_crypto.AlignKey(ls_pri, true, true)
- // 用私钥签名,返回 base64 签名串
- string ls_sign
- ls_sign = luo_crypto.SignWithRSA(ls_pri_align, ls_text, ls_method, lb_utf8)
- MessageBox('RSA 签名结果(base64)', ls_sign)
复制代码
3.2 验签
前置:同上,准备好与上面私钥配对的 PEM 公钥(占位替换);签名串用上面得到的 ls_sign。
步骤:用 AlignKey 对齐公钥 → 调 SignWithRSAVerify → MessageBox 显示"通过/失败"。
- // 示例输入:待验签原文(必须与签名时完全一致,一字不差)
- string ls_text
- ls_text = 'order_no=20260824&amount=100×tamp=1766467200'
- // 示例输入:与私钥配对的 PEM 公钥(占位,请替换)
- string ls_pub
- ls_pub = '-----BEGIN PUBLIC KEY-----'
- ls_pub = ls_pub + '(此处填入你的 BASE64 公钥内容,多行拼接)'
- ls_pub = ls_pub + '-----END PUBLIC KEY-----'
- // 示例输入:上面签名得到的 base64 串
- string ls_sign
- ls_sign = '(填入 3.1 生成的签名串)'
- // 示例输入:哈希算法与编码须与签名时一致
- string ls_method
- ls_method = 'sha256'
- boolean lb_utf8
- lb_utf8 = true
- uo_crypto luo_crypto
- luo_crypto = create uo_crypto
- string ls_pub_align
- ls_pub_align = luo_crypto.AlignKey(ls_pub, true, false)
- boolean lb_ok
- lb_ok = luo_crypto.SignWithRSAVerify(ls_pub_align, ls_text, ls_sign, ls_method, lb_utf8)
- if lb_ok then
- MessageBox('验签结果', '签名验证通过')
- else
- MessageBox('验签结果', '签名验证失败')
- end if
复制代码
四、生成一对测试密钥
如果你手头还没有密钥,可以用 GenerateRSAKey 直接落盘一对 PEM 文件,再用 uo_file 读出来交给 AlignKey 展示成标准格式。
前置:工程已导入 websuite.pbl(含 uo_crypto、uo_file);当前目录可写。
步骤:调 GenerateRSAKey 生成 pub.key/pri.key → 用 uo_file 读文本 → AlignKey 对齐 → MessageBox 看公钥。
- // 示例输入:生成密钥的目标文件名与长度
- string ls_pub_file
- ls_pub_file = 'pub.key'
- string ls_pri_file
- ls_pri_file = 'pri.key'
- // 示例输入:密钥位数,2048 为当前通用安全强度
- int li_keylen
- li_keylen = 2048
- // 示例输入:密钥格式,pkcs#8 更通用(true=pkcs#1, false=pkcs#8)
- boolean lb_pkcs1
- lb_pkcs1 = false
- uo_crypto luo_crypto
- luo_crypto = create uo_crypto
- uo_file luo_file
- luo_file = create uo_file
- boolean lb_gen
- lb_gen = luo_crypto.GenerateRSAKey(ls_pub_file, ls_pri_file, li_keylen, 'pkcs#8')
- if lb_gen then
- string ls_pub
- ls_pub = luo_file.StringFromTextFile(ls_pub_file)
- string ls_pub_align
- ls_pub_align = luo_crypto.AlignKey(ls_pub, lb_pkcs1, false)
- MessageBox('生成的公钥(PEM)', ls_pub_align)
- else
- MessageBox('提示', '密钥生成失败')
- end if
复制代码
五、与 uo_rsa 的 blob 链路对比 / 进阶
上面 SignWithRSA 是"字符串进、base64 出"的省心链路。如果你的原文是二进制(比如要签一个文件内容),或想用 PSS 填充,可以走 uo_rsa 的 blob 链路:
- luo_rsa.sign(luo_crypto.ToUTF8(原文), 私钥, "sha256", false) 返回的还是 blob(第 4 参 use_pss 为布尔量,false=PKCS#1 v1.5、true=PSS),需要再 luo_crypto.Base64Encode(result) 收口成字符串;
- luo_rsa.verify(luo_crypto.ToUTF8(原文), 公钥, "sha256", 签名base64, false) 返回 boolean(签名串用字符串重载,blob 重载把第 4 参换成 blob 即可)。
两者对比:uo_crypto.SignWithRSA 适合"字符串报文 + 标准 PEM"的开放平台对接,步骤最少;uo_rsa 适合"二进制原文 / 需要 PSS"的场景,但要自己管 blob 与 base64 收口。日常对接平台优先用前者。
5.1 实例:给一个文件(PDF / 图片 / 任意二进制)做签名
签文件才是"签名不限字符串"最典型的应用。先用 uo_file.BlobFromFile 把文件读成 blob,再交给 uo_rsa.sign,完全不需要先把文件"变成字符串"。
前置:工程已导入 websuite.pbl(含 uo_crypto、uo_rsa、uo_file);准备一个测试文件(如 d:\test\doc.pdf)和配对的 PEM 私钥。
步骤:1) 读文件成 blob;2) 对齐私钥;3) uo_rsa.sign(blob, 私钥, "sha256") 得 blob 签名;4) Base64Encode 收口;5) MessageBox 看结果。
- // 示例输入:待签名的文件(任意二进制,PDF/图片/音视频均可)
- string ls_file
- ls_file = 'd:\test\doc.pdf'
- // 示例输入:你的 PEM 格式私钥(占位,请替换为真实测试私钥)
- string ls_pri
- ls_pri = '-----BEGIN PRIVATE KEY-----'
- ls_pri = ls_pri + '(此处填入你的 BASE64 私钥内容,多行拼接)'
- ls_pri = ls_pri + '-----END PRIVATE KEY-----'
- // 示例输入:哈希算法,sha256 通用
- string ls_method
- ls_method = 'sha256'
- uo_crypto luo_crypto
- luo_crypto = create uo_crypto
- uo_rsa luo_rsa
- luo_rsa = create uo_rsa
- uo_file luo_file
- luo_file = create uo_file
- // 把文件读成 blob(这就是被签名的"原文",无需先转字符串)
- blob lb_data
- lb_data = luo_file.BlobFromFile(ls_file)
- // 对齐私钥为标准 PEM
- string ls_pri_align
- ls_pri_align = luo_crypto.AlignKey(ls_pri, true, true)
- // 对 blob 原文签名(第 4 参 pss=false 用 PKCS#1 v1.5,true 用 PSS)
- blob lb_sign
- lb_sign = luo_rsa.sign(lb_data, ls_pri_align, ls_method, false)
- // blob 签名收口成 base64 字符串便于传输
- string ls_sign_b64
- ls_sign_b64 = luo_crypto.Base64Encode(lb_sign)
- MessageBox('文件签名结果(base64)', ls_sign_b64)
复制代码
进阶扩展:如果你的平台要求的是 PKCS#7 带证书签名(常见于电子签章、政务回执),uo_crypto 还提供 PKCS7sign(blob data, pfx证书, 密码, algo) 与 PKCS7verify(...)(源码定义第 263~268 行)。注意第一参 data 是 blob——文件/字节直接传 blob 即可,pfx 证书可以是文件读出的 blob 或 base64 解码后的 blob,传入直接出签名串,不必自己拆私钥。这是 RSA 签名的一个常用变体,遇到此类需求直接换这对方法。
六、边界情况与常见坑
- 原文不一致验签必定失败:验签时 text 必须与签名时的 text 逐字节一致(空格、参数顺序、换行都算)。开放平台通常要求按固定规则拼接后再签名,拼接错了验签 100% 不过。
- UTF-8 开关两边必须一致:签名传 utf8=true 表示把字符串先转 UTF-8 再算摘要;验签也必须 utf8=true,否则两边摘要对不上。中文报文务必两端统一。
- 密钥必须先 AlignKey:你从配置或接口拿到的 PEM 经常缺头尾、\n 被转成空格或丢失。直接拿去签名会报"密钥格式错误",先用 AlignKey(key, opensslKey, privateKey) 补齐(公钥 privateKey=false、私钥 privateKey=true)最稳。
- method 要匹配:签名用 sha256 验签也必须 sha256;平台文档写 SHA256withRSA 对应的就是 method='sha256'。
- 私钥别进版本库:示例里把私钥写死只是为了演示。真实项目把私钥放配置文件或加密存储,且不要提交到 git。
- PKCS#1 还是 PKCS#8:GenerateRSAKey 的 pkcs 参数与 AlignKey 的 opensslKey 要对应;和平台对接前先确认对方要的是哪种格式,用错了一样签不出来。
- 二进制原文别硬塞字符串链路:uo_crypto.SignWithRSA 的入参 text 是 string 类型(源码第 40~42 行),没有 blob 重载。要签文件/图片等二进制数据,请用第五节 uo_rsa.sign(blob, ...) 的 blob 链路,配合 uo_file.BlobFromFile 读文件——这正是"签名不限字符串"的那条路。
七、小结
RSA 签名/验签在 PBIDEA 里就靠 uo_crypto.SignWithRSA / SignWithRSAVerify 两条主线:私钥签名出 base64,公钥验签拿 boolean。记住三件事——签名前先 AlignKey 对齐密钥、UTF-8 与 method 两端一致、原文一字不差。需要二进制(文件/图片)或 PSS 时切到 uo_rsa 的 blob 链路(配合 uo_file.BlobFromFile 读文件),需要带证书签章时切到 PKCS7sign。签名本质是对任意二进制数据的摘要做运算,不局限于字符串——字符串报文走 SignWithRSA,文件字节走 uo_rsa.sign(blob,...)。把这一套跑通,对接绝大多数开放平台的签名校验都没问题了。
|