phpenv默认不支持ssl双向认证,因其apache多为精简版、未启用mod_ssl或缺少openssl完整支持;必须手动配置sslcacertificatefile、sslverifyclient require及ssloptions +exportcertdata,并导入ca根证书至系统信任库。

phpEnv 是 Windows 下的本地 PHP 开发环境套件(类似 XAMPP),默认不支持 SSL 双向认证,因为它的 Apache 编译时通常未启用 mod_ssl,且缺少 OpenSSL 的完整配置能力。直接在 phpEnv 界面点“开启 HTTPS”只能实现单向认证(浏览器验证服务器),双向认证必须手动介入底层配置。
确认 Apache 是否真正支持 SSL 双向认证
phpEnv 自带的 Apache 多数是精简版,httpd -M | findstr ssl 很可能不输出 ssl_module (shared),或即使有,也缺少 SSLVerifyClient require 所需的依赖(如 OpenSSL 1.1.1+、正确编译的 mod_ssl)。不要依赖 phpEnv 控制面板里“HTTPS 开关”的状态——它只是改了端口和简单重定向。
- 打开命令行,进入 phpEnv 安装目录下的
Apache\bin,执行httpd -V,检查-D SSL是否在编译参数中 - 执行
httpd -M,确认输出包含ssl_module (shared)和setenvif_module (shared)(后者用于条件判断) - 若缺失,说明该 phpEnv 版本无法原生支持双向认证,需换用 WampServer 或自行编译 Apache
生成并放置 CA、服务端、客户端证书(关键三要素缺一不可)
双向认证要求服务器能校验客户端证书,因此必须有一个自建 CA(不能用 Let's Encrypt 等公开 CA,因其不签发客户端证书)。所有证书需用同一套根密钥签发,否则 SSLVerifyDepth 校验会失败。
- 在
C:\phpEnv\Apache\conf\ssl新建目录,放入以下 4 个文件:ca.crt(CA 根证书)、server.crt(服务端证书)、server.key(服务端私钥)、client.crt(客户端证书,供浏览器或 curl 使用) - 生成顺序必须是:先
openssl genrsa -out ca.key 2048→openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt→ 再用此ca.crt签发server.crt和client.crt - 客户端证书导出为
.pfx或.p12格式(含私钥)才能被 Chrome/Firefox 导入:openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12
修改 httpd-ssl.conf 启用客户端证书校验
phpEnv 的 HTTPS 配置通常位于 Apache\conf\extra\httpd-ssl.conf,但默认只配了 SSLCertificateFile 和 SSLCertificateKeyFile,没开验证。必须手动补全三项核心指令:
SSLVerifyClient require SSLVerifyDepth 2 SSLCACertificateFile "C:/phpEnv/Apache/conf/ssl/ca.crt"
注意路径必须是正斜杠、绝对路径,且 SSLVerifyDepth 值要 ≥ CA 证书链级数(自建 CA 一般填 1 或 2);SSLVerifyClient 设为 require 才强制校验,设为 optional 仅用于调试(浏览器仍可跳过)。
- 确保
<virtualhost _default_:443></virtualhost>块内已加载mod_ssl,即开头有LoadModule ssl_module modules/mod_ssl.so - 添加
SSLStrictSNIVHostCheck off避免 SNI 兼容问题(尤其旧版 phpEnv) - 重启 Apache 后,访问 https 页面若提示“您的连接不是私密连接”,说明证书未被信任——需手动将
ca.crt导入系统“受信任的根证书颁发机构”
PHP 代码中读取客户端证书信息
Apache 双向认证成功后,会把客户端证书字段注入 $_SERVER 超全局变量,但 phpEnv 默认未开启传递。需在 httpd-ssl.conf 的 VirtualHost 块内加一行:
SSLOptions +StdEnvVars +ExportCertData
之后可在 PHP 中拿到:
var_dump($_SERVER['SSL_CLIENT_S_DN_CN']); // 客户端证书里的 Common Name var_dump($_SERVER['SSL_CLIENT_M_SERIAL']); // 序列号,可用于白名单校验
- 如果
$_SERVER里没有这些键,说明SSLOptions没生效,或 Apache 版本太老不支持ExportCertData - 生产环境务必校验
SSL_CLIENT_VERIFY === 'SUCCESS',而非只看 CN 字段——攻击者可伪造证书内容 - Windows 下 phpEnv 的 Apache 有时会把证书字段名转成大写(如
SSL_CLIENT_S_DN_CN→SSL_CLIENT_S_DN_CN),建议用array_keys($_SERVER)实际打印确认
真正卡住多数人的不是生成证书,而是 Apache 模块缺失和 SSLOptions 传递失效——这两点在 phpEnv 文档里完全没提,得靠 httpd -t 校验配置、error.log 查 SSL 握手失败原因才能定位。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











