本文目录导读:

- 核心工具:PHP OpenSSL 扩展
- 生成数字证书(自签名证书或CA签发)
- 验证证书有效性
- 使用证书进行 HTTPS 通信(客户端/服务器)
- 使用证书进行加密/解密(非对称加密)
- 完整项目结构建议
- 常见问题与注意事项
在PHP项目中实现数字证书,通常涉及以下几个方面:生成、加载、验证、签名和加密,由于PHP本身不直接管理证书存储,通常需要借助 OpenSSL 扩展 来完成。
以下是具体的实现方案和代码示例:
核心工具:PHP OpenSSL 扩展
确保你的 PHP 环境中启用了 OpenSSL 扩展,可以通过 phpinfo() 查看,或在命令行输入:
php -m | grep openssl
如果未启用,需要在 php.ini 中取消注释 extension=openssl。
生成数字证书(自签名证书或CA签发)
生成密钥对和自签名证书(使用 PHP 代码)
<?php
// 1. 生成私钥
$privateKey = openssl_pkey_new([
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);
// 2. 生成证书签名请求 (CSR) 信息
$dn = [
"countryName" => "CN",
"stateOrProvinceName" => "Beijing",
"localityName" => "Beijing",
"organizationName" => "My Company",
"commonName" => "www.example.com",
"emailAddress" => "admin@example.com"
];
// 3. 生成 CSR(可选步骤,为证书生成做准备)
$csr = openssl_csr_new($dn, $privateKey, ['digest_alg' => 'sha256']);
// 4. 生成自签名证书(有效期为365天)
$cert = openssl_csr_sign($csr, null, $privateKey, 365, ['digest_alg' => 'sha256']);
// 5. 导出为 PEM 格式
openssl_pkey_export($privateKey, $privateKeyPem);
openssl_x509_export($cert, $certPem);
// 6. 保存到文件(供长期使用)
file_put_contents('server.key', $privateKeyPem);
file_put_contents('server.crt', $certPem);
echo "证书生成成功\n";
?>
注意:自签名证书仅适用于开发或内部测试环境,生产环境应由受信任的 CA 签发。
生成 CA 签发的证书(模拟CA流程)
你可以编写一个类,实现小型的 CA 功能,包括签发证书、吊销等。
验证证书有效性
验证证书是否过期、是否被篡改
<?php
$certContent = file_get_contents('server.crt');
$cert = openssl_x509_read($certContent);
// 解析证书信息
$certInfo = openssl_x509_parse($cert);
echo "颁发给: " . $certInfo['subject']['CN'] . "\n";
echo "有效期: " . date('Y-m-d', $certInfo['validFrom_time_t']) . " 至 " . date('Y-m-d', $certInfo['validTo_time_t']) . "\n";
// 检查证书是否过期
if (time() > $certInfo['validTo_time_t']) {
echo "证书已过期\n";
} else {
echo "证书有效\n";
}
// 释放资源
openssl_x509_free($cert);
?>
验证证书链(是否由受信任的CA签发)
<?php
$cert = file_get_contents('server.crt');
$caCert = file_get_contents('ca.crt'); // 你的CA根证书
// 使用 openssl 命令行验证(推荐)
$cmd = "echo " . escapeshellarg($cert) . " | openssl verify -CAfile " . escapeshellarg($caCert) . " -CApath /dev/null";
exec($cmd, $output, $returnVar);
if ($returnVar === 0) {
echo "证书验证通过\n";
} else {
echo "证书验证失败: " . implode("\n", $output) . "\n";
}
?>
或者使用 PHP 的 openssl_x509_checkpurpose,但它功能有限,复杂场景建议用命令行或 OpenSSL C API 扩展。
使用证书进行 HTTPS 通信(客户端/服务器)
作为 HTTPS 客户端(验证服务器证书)
<?php
$url = "https://secure.example.com/api";
// 创建SSL上下文
$contextOptions = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/path/to/ca-bundle.crt', // 受信任的CA证书列表
'CN_match' => 'secure.example.com', // 验证主机名(PHP 5.6+ 已自动处理)
'capture_peer_cert' => true, // 获取对端证书
]
];
$streamContext = stream_context_create($contextOptions);
// 发起请求
$response = file_get_contents($url, false, $streamContext);
echo $response;
?>
作为 HTTPS 服务器(配置 SSL 证书)
在 Web 服务器(Nginx/Apache)中配置,而非 PHP 层面,PHP 仅提供 $_SERVER['HTTPS'] 判断。
但如果你想在 PHP 中创建一个简单的 SSL 服务器(例如开发测试),可以使用 stream_socket_server:
<?php
$ctx = stream_context_create([
'ssl' => [
'local_cert' => 'server.crt',
'local_pk' => 'server.key',
'verify_peer' => false,
'allow_self_signed' => true,
]
]);
$server = stream_socket_server('tls://0.0.0.0:8443', $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $ctx);
while ($conn = @stream_socket_accept($server)) {
fwrite($conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello SSL World\n");
fclose($conn);
}
?>
使用证书进行加密/解密(非对称加密)
用公钥加密,私钥解密
<?php
$data = "敏感数据123";
// 加载公钥(用于加密)
$publicKey = openssl_pkey_get_public(file_get_contents('server.crt'));
openssl_public_encrypt($data, $encrypted, $publicKey);
// 加载私钥(用于解密)
$privateKey = openssl_pkey_get_private(file_get_contents('server.key'));
openssl_private_decrypt($encrypted, $decrypted, $privateKey);
echo "解密结果: " . $decrypted . "\n";
?>
证书签名与验签
<?php
$data = "重要信息";
// 加载私钥签名
$privateKey = openssl_pkey_get_private(file_get_contents('server.key'));
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
// 加载公钥验签
$publicKey = openssl_pkey_get_public(file_get_contents('server.crt'));
$result = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
if ($result === 1) {
echo "签名验证成功\n";
} elseif ($result === 0) {
echo "签名验证失败\n";
} else {
echo "验签出错\n";
}
?>
完整项目结构建议
对于实际项目,推荐封装一个证书管理类:
app/
├── Services/
│ ├── CertificateService.php
│ ├── OpenSSLHelper.php
├── config/
│ ├── cert.php # 证书路径配置
├── storage/
│ ├── certs/
│ │ ├── ca.crt
│ │ ├── server.crt
│ │ ├── server.key
CertificateService.php 示例方法:
| 方法 | 功能 |
|---|---|
generateSelfSignedCert() |
生成自签名证书 |
signCsr($csr, $caCert, $caKey) |
用CA签发证书 |
verifyCert($cert) |
验证证书有效性 |
encryptWithPublicKey($data) |
公钥加密 |
decryptWithPrivateKey($data) |
私钥解密 |
signData($data) |
数据签名 |
verifySignature($data, $signature) |
验签 |
常见问题与注意事项
| 问题 | 原因与解决 |
|---|---|
openssl_pkey_new() 返回 false |
检查 OpenSSL 配置,确保 php.ini 中 openssl.cafile 等配置正确 |
| 证书验证失败(PHP 5.6+) | 默认启用证书验证,需指定 cafile 或禁用验证(仅测试) |
| 性能问题 | 非对称加密慢于对称加密,建议混合加密:用证书加密随机对称密钥,再用该密钥加密数据 |
| 私钥安全 | 私钥必须严格保护,权限设为 600,不要硬编码在源代码中 |
| 与云服务集成 | 支付、推送等第三方服务通常提供 API 密钥,不需要处理数字证书(除非要求双向SSL) |
在 PHP 中实现数字证书的核心是 OpenSSL 扩展,主要步骤为:生成密钥对 → 创建证书(或通过CA签发)→ 加载进行加解密、签名验签、验证通信,对于生产环境,建议使用系统级工具(如 OpenSSL 命令行)生成证书,PHP 专注于业务逻辑调用,如果需要完整的 PKI 体系,可以考虑集成第三方库如 phpseclib(纯 PHP 实现,不依赖 OpenSSL 扩展)。