本文目录导读:

在 PHP 中实现 mTLS(双向 TLS)绑定,主要涉及客户端证书验证和服务端证书验证两个方面,以下是完整的实现方案:
PHP 作为客户端(发起请求)
cURL 实现 mTLS
<?php
$ch = curl_init('https://example.com/api');
// 设置 CA 证书(验证服务端)
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/ca-cert.pem');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
// 客户端证书和私钥
curl_setopt($ch, CURLOPT_SSLCERT, '/path/to/client-cert.pem');
curl_setopt($ch, CURLOPT_SSLKEY, '/path/to/client-key.pem');
curl_setopt($ch, CURLOPT_SSLKEYPASSWD, 'your_key_password'); // 如果私钥有密码
// 如果使用 PKCS#12 格式
// curl_setopt($ch, CURLOPT_SSLCERT, '/path/to/client.p12');
// curl_setopt($ch, CURLOPT_SSLCERTPASSWD, 'p12_password');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error: ' . curl_error($ch);
}
curl_close($ch);
PHP Stream Context 实现
<?php
$context = stream_context_create([
'ssl' => [
'cafile' => '/path/to/ca-cert.pem',
'verify_peer' => true,
'verify_peer_name' => true,
'local_cert' => '/path/to/client-cert.pem',
'local_pk' => '/path/to/client-key.pem',
'passphrase' => 'your_key_password',
'allow_self_signed' => false
]
]);
$result = file_get_contents('https://example.com/api', false, $context);
PHP 作为服务端(接收请求并验证客户端)
Apache 配置示例
<VirtualHost *:443>
ServerName example.com
# HTTPS 配置
SSLEngine on
SSLCertificateFile /path/to/server-cert.pem
SSLCertificateKeyFile /path/to/server-key.pem
# 客户端证书验证
SSLVerifyClient require
SSLVerifyDepth 10
SSLCACertificateFile /path/to/client-ca-cert.pem
# 可选:将客户端证书信息传递给 PHP
SSLOptions +StdEnvVars
DocumentRoot /var/www/html
</VirtualHost>
Nginx 配置
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/server-cert.pem;
ssl_certificate_key /path/to/server-key.pem;
ssl_client_certificate /path/to/client-ca-cert.pem;
ssl_verify_client on;
ssl_verify_depth 10;
location / {
proxy_pass http://php-fpm:9000;
proxy_set_header X-Client-Cert $ssl_client_raw_cert;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
}
}
PHP 获取客户端证书信息
<?php
// 获取客户端证书信息(Apache 环境)
if (isset($_SERVER['SSL_CLIENT_VERIFY'])) {
$verify = $_SERVER['SSL_CLIENT_VERIFY']; // SUCCESS, FAILED, NONE
if ($verify === 'SUCCESS') {
// 证书信息
$certDN = $_SERVER['SSL_CLIENT_S_DN'];
$certIssuer = $_SERVER['SSL_CLIENT_I_DN'];
$certSerial = $_SERVER['SSL_CLIENT_M_SERIAL'];
$certExpiry = $_SERVER['SSL_CLIENT_V_END'];
// 从证书 DN 中提取信息
$parts = explode(',', $certDN);
foreach ($parts as $part) {
list($key, $value) = explode('=', $part, 2);
$info[strtolower(trim($key))] = trim($value);
}
$commonName = $info['cn'] ?? '';
$email = $info['emailaddress'] ?? '';
// 可以基于证书信息做业务逻辑
if (!isAuthorized($commonName, $email)) {
http_response_code(403);
exit('Unauthorized');
}
}
}
// Nginx 环境
if (isset($_SERVER['HTTP_X_CLIENT_VERIFY'])) {
if ($_SERVER['HTTP_X_CLIENT_VERIFY'] === 'SUCCESS') {
$clientDN = $_SERVER['HTTP_X_CLIENT_DN'] ?? '';
// 处理证书信息
}
}
// 更复杂的证书解析(从原始证书)
if (isset($_SERVER['HTTP_X_CLIENT_CERT'])) {
$certPem = "-----BEGIN CERTIFICATE-----\n"
. chunk_split(base64_decode($_SERVER['HTTP_X_CLIENT_CERT']), 64, "\n")
. "-----END CERTIFICATE-----";
$certData = openssl_x509_parse($certPem);
if ($certData) {
$subject = $certData['subject'];
$commonName = $subject['CN'] ?? '';
$serialNumber = $certData['serialNumber'] ?? '';
$validFrom = $certData['validFrom_time_t'] ?? 0;
$validTo = $certData['validTo_time_t'] ?? 0;
}
}
// 自定义授权逻辑
function isAuthorized($cn, $email) {
// 检查证书是否在授权列表中
$authorizedCerts = [
'John Doe' => 'john@example.com',
'Jane Smith' => 'jane@example.com'
];
return isset($authorizedCerts[$cn]) && $authorizedCerts[$cn] === $email;
}
?>
完整的客户端证书生成脚本
#!/bin/bash
# 生成自签名 CA
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=My CA"
# 生成客户端证书
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=John Doe/emailAddress=john@example.com"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 365 -sha256
# 生成 PKCS#12 格式(包含证书和私钥)
openssl pkcs12 -export -out client.p12 -inkey client.key -in client.crt -certfile ca.crt
PHP 框架集成示例(Laravel)
// app/Http/Middleware/VerifyClientCertificate.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyClientCertificate
{
public function handle(Request $request, Closure $next)
{
if (!$this->hasValidClientCertificate($request)) {
return response('Unauthorized', 403);
}
// 将证书信息存入请求属性
$request->attributes->add([
'client_cert' => $this->getClientCertificateData($request)
]);
return $next($request);
}
protected function hasValidClientCertificate($request)
{
// Nginx/Apache 传递的验证结果
return $request->server('SSL_CLIENT_VERIFY') === 'SUCCESS' ||
$request->header('X-Client-Verify') === 'SUCCESS';
}
protected function getClientCertificateData($request)
{
return [
'subject' => $request->server('SSL_CLIENT_S_DN', $request->header('X-Client-DN', '')),
'issuer' => $request->server('SSL_CLIENT_I_DN', ''),
'serial' => $request->server('SSL_CLIENT_M_SERIAL', ''),
];
}
}
mTLS 测试脚本
<?php
// test-mtls.php - 简单的 mTLS 测试
class MTLSClient {
private $certFile;
private $keyFile;
private $caFile;
public function __construct($cert, $key, $ca) {
$this->certFile = $cert;
$this->keyFile = $key;
$this->caFile = $ca;
}
public function request($url, $method = 'GET', $data = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CAINFO => $this->caFile,
CURLOPT_SSLCERT => $this->certFile,
CURLOPT_SSLKEY => $this->keyFile,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_TIMEOUT => 30
]);
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
}
$response = curl_exec($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [
'http_code' => $httpCode,
'response' => $response,
'error' => $error
];
}
}
// 使用示例
$client = new MTLSClient(
'/path/to/client.crt',
'/path/to/client.key',
'/path/to/ca.crt'
);
$result = $client->request('https://api.example.com/secure-endpoint', 'GET');
var_dump($result);
?>
注意事项
- 证书安全:私钥文件权限设为 600,确保只有 web 服务器用户可读
- 证书轮换:实施证书过期监控和自动更新机制
- 错误处理:适当处理各种 SSL/TLS 错误情况
- 性能优化:开启 SSL 会话缓存以提高性能
- 日志记录:记录证书验证失败和被拒绝的请求
通过这些配置和代码,可以全面实现 PHP 应用的 mTLS 绑定。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播