PHP openssl签发非自签名证书报PEM_read_bio:no start line错误
报错根因
0906D06C:PEM routines:PEM_read_bio:no start line 是OpenSSL的通用解析错误,触发原因是OpenSSL读取PEM格式内容时,找不到合法的PEM头尾标记(即-----BEGIN 对应类型-----/-----END 对应类型-----结构)。你的代码存在3个直接触发该错误的问题:
openssl_csr_sign()函数不会自动读取磁盘文件,你直接把文件名字符串"client.crt"作为CSR参数传入,OpenSSL会把这个纯字符串当PEM内容解析,自然找不到合法起始标记- 传入的CA证书参数
"CA.crt"、CA私钥参数"CA.key"同样是文件路径字符串,函数不会自动加载文件内容,同样会触发解析失败 - 变量逻辑错误:CSR是证书签发请求,后缀通常为
.csr,你赋值的client.crt是已签发的证书文件,本身就不是待签名的CSR,就算正确读取内容也无法完成签名。
修复方案
先通过file_get_contents()加载所有需要的PEM文件内容再传入函数,尽量使用文件绝对路径避免相对路径解析错误,参考修正代码:
<?php // 加载待签名的CSR文件内容,替换为你实际的CSR文件路径 $csrPath = __DIR__ . "/client.csr"; $csrdata = file_get_contents($csrPath); if (!$csrdata) { exit("CSR文件读取失败,请检查路径与权限"); } // 加载CA根证书内容 $caCertPath = __DIR__ . "/CA.crt"; $cacert = file_get_contents($caCertPath); if (!$cacert) { exit("CA证书读取失败,请检查路径与权限"); } // 组装CA私钥参数 $caKeyPath = __DIR__ . "/CA.key"; $caKeyPass = "la_cle_secrete_de_votre_certificat"; // 如果服务器开启open_basedir限制导致私钥路径无法读取,就先读取私钥内容再传入: // $privkey = [file_get_contents($caKeyPath), $caKeyPass]; $privkey = [$caKeyPath, $caKeyPass]; // 签名配置,按需调整 $signConfig = [ 'digest_alg' => 'sha256', 'x509_extensions' => 'v3_req' ]; // 执行签名,有效期365天 $usercert = openssl_csr_sign($csrdata, $cacert, $privkey, 365, $signConfig); if (!$usercert) { exit("证书签发失败"); } // 导出最终证书 openssl_x509_export($usercert, $certout); echo "<br>最终签发证书:<br>" . nl2br($certout); // 打印所有OpenSSL错误 while (($e = openssl_error_string()) !== false) { echo $e . "<br>"; } ?>
额外排查点
- 检查所有PEM文件格式合法性:用文本编辑器打开
client.csr、CA.crt、CA.key,确认文件开头、结尾存在对应类型的PEM标记,没有多余的空白、乱码。如果文件是DER二进制格式,需要先转为PEM格式才能被OpenSSL解析。 - 确认PHP运行进程对上述三个文件有读取权限,Linux环境下CA私钥建议设置为600权限,避免权限过宽被读取,同时保证属主和PHP运行用户一致。
- 如果是通过表单接收用户上传/粘贴的CSR内容,要注意过滤HTML转义字符、多余的换行空格,不能丢失PEM头尾标记,否则也会触发同样的解析错误。
内容的提问来源于stack exchange,提问作者Fabrizio Imis
相关产品推荐
相关产品推荐

