最稳方案是用 phpseclib 而非 OpenSSL 扩展,因其纯 PHP 实现、兼容 PEM/DER/PKCS#8 私钥、默认 PKCS#1 v1.5 填充且与 Java/Node.js 兼容;需显式设哈希(如 sha256)、正确处理原始字节流、验签前 base64_decode 签名并确保公钥由 $rsa->getPublicKey() 导出。

PHP 中用 phpseclib 生成和验证 RSA 签名最稳
别用 Composer 自带的 openssl 扩展硬凑——它对私钥格式、填充方式、哈希算法耦合太紧,一换环境就报 error:0909006C:PEM routines:get_name:no start line 或 signature verification failed。直接上 phpseclib/phpseclib,纯 PHP 实现,不依赖 OpenSSL 配置,私钥 PEM/DER/PKCS#8 全兼容。
安装命令:
composer require phpseclib/phpseclib:^3.0
关键点:
-
Crypt\RSA类默认用 PKCS#1 v1.5 填充,和大多数 Java/Node.js 签名服务对得上 - 私钥加载失败?先检查是否被 PHP 的
file_get_contents()读取时自动去除了换行——必须用file_get_contents($path, false)(第二个参数不能省) - 签名前务必显式调用
$rsa->setHash('sha256'),否则默认是 sha1,验签必失败
签名时传错数据类型会导致 Invalid signature
常见错误:把已 base64 编码的字符串再喂给 $rsa->sign(),或者传了 JSON 字符串但没去空格/换行。签名是对原始字节流操作,不是对编码后文本。
正确做法:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 待签名内容必须是原始二进制或 UTF-8 字符串(如
"order_id=123&amount=100") - 如果上游给的是 base64,请先
base64_decode()回原始 bytes 再签名 - JSON 数据建议先
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)标准化,再签名 - 签名结果是二进制字符串,需要 base64 编码传输:
base64_encode($rsa->sign($data))
验签失败?先确认公钥格式和哈希是否严格一致
公钥不等于“把私钥里的 PRIVATE KEY 换成 PUBLIC KEY”——那是无效操作。必须从私钥导出标准 PEM 公钥:
$publicKey = $rsa->getPublicKey(); // 返回 PEM 格式字符串直接存这个字符串,别手改。
验签代码里最容易漏的三件事:
- 传入的签名必须是 base64 解码后的原始二进制:
$rsa->verify($data, base64_decode($signature)) - 验签前必须和签名时用同一哈希:
$rsa->setHash('sha256'),且不能在中间调用setSignatureMode()改变模式 - 如果对方用的是 SHA-256 + PSS 填充(比如某些银行接口),要显式设:
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1)→ 改成CRYPT_RSA_SIGNATURE_PSS,并配$rsa->setMGFHash('sha256')
私钥密码保护导致 Unable to read key
phpseclib 默认不支持加密私钥(带 passphrase 的 PEM)。遇到这个错,有两个路子:
- 临时解密私钥:用 OpenSSL 命令行移除密码:
openssl rsa -in key_encrypted.pem -out key_decrypted.pem
(注意保管好解密后文件) - 升级到 v3.0+ 后可用
$rsa->loadKey($pem, CRYPT_RSA_PRIVATE_FORMAT_PKCS8)并配合$rsa->setPassword('xxx'),但仅限 PKCS#8 格式;PKCS#1 加密私钥仍不支持 - 生产环境别用密码私钥——签名服务通常靠文件权限(
chmod 600)和进程隔离保障安全
真正麻烦的从来不是“怎么签”,而是双方对“什么算同一份数据”没共识:换行符、空格、字段顺序、编码、时间戳精度……这些细节不拉齐,再标准的 RSA 也验不过。每次对接新系统,先抓包比对原始待签名字符串的 hex 值,比看文档管用。

















