如何在PHP OpenSSL中获取网站的公证书PEM文件?
问题描述
在FireFox中获取网站PEM格式公证书的步骤如下:
- 访问目标网站(如
https://stackoverflow.com),点击地址栏的锁形图标; - 依次点击「连接安全」→「更多信息」→「安全」→「查看证书」;
- 在弹出窗口中下载「PEM (cert)」,得到完整证书内容。
我尝试用PHP代码实现相同操作,但得到的是公钥而非目标证书。我的代码如下:
$url = "ssl://stackoverflow.com:443"; // Use ssl:// or tls:// rather than https:// $context = stream_context_create( ["ssl" => array( "capture_peer_cert" => true, //"capture_peer_cert_chain" => true, 'protocol' => 'SSLv3' )] ); $client = stream_socket_client( $url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context ); var_dump( $errno ); var_dump( $errstr ); $params = stream_context_get_params( $client ); //var_dump( $params ); $loc = $params["options"]["ssl"]["peer_certificate"]; $key = openssl_pkey_get_public( $loc ); $details = openssl_pkey_get_details( $key ); var_dump( $details["key"] );
最终得到的是公钥格式内容,而非FireFox中获取的证书。尝试多种修改仍未解决,请问需如何调整代码才能得到相同的PEM证书?
解决方案
你的代码问题出在提取了证书中的公钥,而非证书本身。要获取完整的PEM格式证书,需要使用openssl_x509_export()函数直接导出捕获到的证书资源,而不是提取公钥。
调整后的代码如下:
$url = "ssl://stackoverflow.com:443"; $context = stream_context_create([ "ssl" => [ "capture_peer_cert" => true, // 建议使用更安全的TLS协议,不要固定SSLv3 'protocol' => 'TLSv1.2' ] ]); $client = stream_socket_client($url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); if (!$client) { die("连接失败: $errstr ($errno)"); } $params = stream_context_get_params($client); $certResource = $params["options"]["ssl"]["peer_certificate"]; // 导出PEM格式证书 $pemCert = ''; openssl_x509_export($certResource, $pemCert); // 输出证书内容 echo $pemCert; fclose($client);
关键修改点说明:
- 替换核心逻辑:去掉
openssl_pkey_get_public()和openssl_pkey_get_details()的调用,改用openssl_x509_export()直接将证书资源转为PEM格式字符串。 - 升级安全协议:将
SSLv3替换为TLSv1.2(或更高版本如TLSv1.3),SSLv3已被废弃,存在严重安全漏洞。 - 增加错误处理:添加连接失败的判断,避免后续代码在连接异常时抛出错误。
修改后输出的内容就和FireFox中下载的「PEM (cert)」完全一致。
内容的提问来源于stack exchange,提问作者Boycott A.I.
相关产品推荐
相关产品推荐

