验签前必须确认签名算法和密钥格式;PHP需用openssl_verify()配合循环实现批量验签,公钥须为带头尾标记的PEM格式,签名文件须为二进制原始数据,且须严格校验文件与签名的一一对应关系。

验签前必须确认签名算法和密钥格式
PHP 本身不自带“验签文件批量处理”功能,得靠 openssl_verify() 搭配循环实现。但第一步不是写代码,而是确认签名用的算法(比如 sha256WithRSAEncryption)和密钥类型(PEM 格式公钥,不能是 .pfx/.p12 或未解码的 base64)。常见坑是:用私钥去验、公钥没加 -----BEGIN PUBLIC KEY----- 头尾、或者用了 OpenSSL 1.0 的旧 PEM(无 ASN.1 编码头),导致 openssl_pkey_get_public() 返回 false。
验证方式很简单:
var_dump(openssl_pkey_get_public(file_get_contents('public.pem'))); // 必须输出 resource,否则密钥格式不对
遍历文件并逐个调用 openssl_verify()
批量验签本质就是对每个文件读取原始内容 + 对应签名文件,传给 openssl_verify()。注意三点:签名文件必须是二进制原始数据(不是 base64 字符串,除非你手动 base64_decode());原始文件不能经过任何编码/换行处理;算法标识符要和签名时一致(常用 OPENSSL_ALGO_SHA256)。
典型结构如下:
立即学习“PHP免费学习笔记(深入)”;
$pubkey = openssl_pkey_get_public(file_get_contents('public.pem'));
$files = glob('data/*.dat');
foreach ($files as $file) {
$sigFile = $file . '.sig';
$data = file_get_contents($file);
$sig = file_get_contents($sigFile);
$ok = openssl_verify($data, $sig, $pubkey, OPENSSL_ALGO_SHA256);
echo "$file: " . ($ok === 1 ? 'OK' : ($ok === 0 ? 'FAIL' : 'ERROR')) . "\n";
}
- 如果签名是 base64 存储的,
$sig = base64_decode(file_get_contents($sigFile)); -
openssl_verify()返回1表示验签成功,0是失败,-1是出错(比如密钥无效、参数为空) - 别用
file_get_contents()读超大文件——内存爆掉。单文件 >50MB 建议改用流式哈希(先hash_init('sha256')+hash_update_stream(),再对摘要验签)
错误信息全靠 openssl_error_string() 拿
openssl_verify() 不抛异常,也不返回详细原因。一旦结果是 -1,就得立刻调 openssl_error_string() 清空并读取错误栈——而且要循环调,因为可能堆了多条。
实操建议在验签后加一段:
if ($ok === -1) {
while ($e = openssl_error_string()) {
error_log("OpenSSL error: $e");
}
}
常见报错包括:error:0906D06C:PEM routines:PEM_read_bio:no start line(公钥格式错)、error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:invalid padding(签名被篡改或算法不匹配)。
不要忽略签名与文件的绑定关系校验
批量场景下最容易被绕过的点:没核对签名文件名是否和原文件严格对应。比如 report.pdf 配了 invoice.pdf.sig,程序却照常去读——验签通过,但验的不是它自己的签名。
安全做法是强制命名规则,并校验:
$basename = pathinfo($file, PATHINFO_FILENAME);
$sigFile = dirname($file) . '/' . $basename . '.sig';
if (!is_file($sigFile)) {
echo "$file: missing signature\n";
continue;
}
更严谨的还可以加文件大小/修改时间比对,或把文件哈希也写进签名元数据里——但这已超出“简单验签”范畴。真正上线前,务必用已知伪造签名的文件跑一遍测试,确认失败路径能捕获且不静默吞错。



















