PHP怎么写密钥格式不兼容_PHP密钥格式不兼容处理说明【说明】

2次阅读

php密钥格式不兼容主因是openssl对pem编码、头尾标记、换行符、加密方式及密钥类型校验严格;需确保标准pem封装、无bom/windows换行、格式匹配php版本、ec/eddsa密钥选用对应函数且权限正常。

PHP怎么写密钥格式不兼容_PHP密钥格式不兼容处理说明【说明】

PHP 中密钥格式不兼容,通常不是 PHP 本身的问题,而是 OpenSSL 加载密钥时对 PEM 编码、头部标记、换行符、加密方式或密钥类型(RSA/EC/EdDSA)的校验变严格导致的。直接用 openssl_pkey_get_private()openssl_pkey_get_public() 报错 Error:0909006C:PEM routines:get_name:no start lineerror:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag,基本都属于这类。

密钥开头结尾不对:必须严格匹配 PEM 标准标记

OpenSSL 在 PHP 中要求密钥必须以标准 PEM 头尾包裹,且中间 Base64 内容不能有空行、多余空格或 Windows 换行符(rn)。常见错误包括:

  • 用文本编辑器手动拼接了 -----BEGIN RSA PRIVATE KEY----- 但实际是 PKCS#8 格式,应改用 -----BEGIN PRIVATE KEY-----
  • 公钥用了 ssh-rsa AAAA... 这种 OpenSSH 单行格式,PHP 的 openssl_pkey_get_public() 完全不认,必须转成 PEM:ssh-keygen -f id_rsa.pub -e -m pem
  • 私钥被 openssl pkcs8 -topk8 转过但没加 -nocrypt,导致带密码保护,PHP 默认不支持交互式解密

PKCS#1 和 PKCS#8 格式混用:PHP 各版本行为不一致

PHP 7.1+ 对 PKCS#8 支持较好,但老版本(如 5.6)只原生支持 PKCS#1(-----BEGIN RSA PRIVATE KEY-----)。若你拿到的是现代工具(如 ssh-keygen -t rsa -m PEM 默认输出)生成的 PKCS#8 私钥,而运行环境是 PHP 7.0 或更低,就会失败。

  • 检查格式:用 openssl asn1parse -i -in key.pem,若第一层是 SEQUENCE 包含 Object IDENTIFIER 1.2.840.113549.1.8.1(即 pkcs-8),说明是 PKCS#8
  • 降级转换(无密码时):openssl pkcs8 -in key.pem -nocrypt -outform pem -out key_pkcs1.pem
  • PHP 代码中不要硬编码头尾,优先用 file_get_contents() 读取原始字符串再传入函数,避免意外截断

Windows 换行符和 BOM 导致解析失败

用记事本保存 PEM 文件极易引入 UTF-8 BOM 或 rn,而 PHP 的 OpenSSL 绑定对这些非常敏感——哪怕开头多一个 x00xEFxBBxBF,都会报 no start line

立即学习PHP免费学习笔记(深入)”;

  • hexdump -C key.pem | head -5 检查前几个字节,确认没有 BOM(ef bb bf
  • 确保换行是 unix 风格(n),可用 dos2unix key.pem 或在 VS Code 中右下角切换 “LF”
  • PHP 中可预处理:用 trim(str_replace("r", "", $pem)) 去掉回车和首尾空白,再传给 openssl_pkey_get_private()

EC 密钥或 Ed25519 密钥被当 RSA 用

PHP 7.1+ 支持 EC(椭圆曲线)密钥,但函数调用方式一样,真正出问题的是密钥内容与算法预期不匹配。例如用 openssl_sign($data, $sig, $pkey, 'sha256WithRSAEncryption') 去签 EC 私钥,会静默失败或报 error:0906D06C:PEM routines:PEM_read_bio:no start line(因算法不匹配触发底层解析异常)。

  • 先用 openssl pkey -in key.pem -text -noout 看类型:输出含 EC PRIVATE KEYid-ecPublicKey 就是 EC;含 ED25519 则需 PHP 7.4+ 且扩展开启 ext/sodium,不能走 OpenSSL 函数
  • EC 公钥不能用 openssl_pkey_get_public() 直接加载旧格式(如 SEC1),需确保是标准 PEM:-----BEGIN PUBLIC KEY-----(不是 -----BEGIN EC PUBLIC KEY-----
  • Ed25519 密钥完全不兼容 OpenSSL 扩展,必须改用 sodium_crypto_sign_keypair_from_secretkey() 等 Sodium 函数

最常被忽略的一点:密钥文件权限和 SELinux 上下文(尤其在 centos/RHEL 上),即使格式全对,file_get_contents() 读出来是空字符串,也会让后续所有 OpenSSL 函数返回 false —— 记得先 var_dump(file_get_contents($path)) 确认内容真实加载成功。

text=ZqhQzanResources