openssl_sign()、openssl_verify() 和 openssl_pkey_* 系列函数是对 PHP 构建时所用 OpenSSL 的薄封装——官方 php:8.5.11 镜像中为 3.5.7——因此曲线和行为都与之一致:DER 签名、随机 nonce、不做 low-S 规范化,验证时接受 high-S。哈希算法始终由算法参数决定;没有可以对预先计算的摘要签名的函数。
openssl_verify() 的返回值是 PHP 中的经典陷阱。签名有效时返回 1,无效时返回 0,签名根本无法解析时返回 -1——在检查中,来自 WebCrypto 或 JWT 的 64 字节原始 r‖s 签名得到的就是这个结果。在 PHP 中 -1 为真,因此 if (openssl_verify(...)) 会接受垃圾数据;应该像示例那样用 === 1 比较。原始签名必须先封装为 DER,每个 PHP JWT 库在内部都是这样做的;DER ⇄ 原始格式转换器可以展示这两种格式。
密钥以 PEM 字符串、证书或密钥资源的形式传入:SPKI PEM 和 X.509 PEM 可以使用,而裸 SPKI DER 字节会被直接拒绝。私钥可以从 PKCS#8(未加密或加密)和 SEC1 EC PRIVATE KEY 加载,openssl_pkey_export() 写出 PKCS#8。曲线涵盖 P-224 到 P-521、secp256k1 和 Brainpool;Ed25519 和 Ed448 密钥通过 OPENSSL_KEYTYPE_ED25519 和 OPENSSL_KEYTYPE_ED448 使用,算法参数为 0。
尽管所链接的 OpenSSL 实现了后量子算法,PHP 代码却无法使用它们:同一镜像中的 CLI 列出了 ML-DSA,但扩展没有为它定义密钥类型。处理 JWT 时,应使用 firebase/php-jwt、web-token 或 lcobucci/jwt,而不是手动拼装令牌。