Appearance
RabbitMQ 证书配置
概述
TLS 证书是建立安全通信的基础。本文档详细介绍 RabbitMQ 所需证书的生成、配置和管理方法,包括自签名证书和 CA 签名证书的完整流程。
核心知识点
证书类型
┌─────────────────────────────────────────────────────────────┐
│ 证书类型对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 自签名证书 CA 签名证书 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ • 适合测试环境 │ │ • 适合生产环境 │ │
│ │ • 快速生成 │ │ • 受信任的证书 │ │
│ │ • 无需 CA │ │ • 需要 CA 签发 │ │
│ │ • 浏览器警告 │ │ • 无浏览器警告 │ │
│ │ • 零成本 │ │ • 可能有费用 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 证书链结构: │
│ │
│ ┌─────────┐ │
│ │ Root CA │ ← 根证书颁发机构 │
│ └────┬────┘ │
│ │ │
│ ┌────┴────┐ │
│ │Intermediate│ ← 中间证书颁发机构 │
│ │ CA │ │
│ └────┬────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ Server │ ← 服务器证书 │
│ │ Cert │ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘证书文件格式
| 格式 | 扩展名 | 说明 | 用途 |
|---|---|---|---|
| PEM | .pem, .crt, .cer | Base64 编码 | 最常用,RabbitMQ 默认 |
| DER | .der, .cer | 二进制格式 | Java 平台常用 |
| PKCS#12 | .p12, .pfx | 包含私钥和证书 | Windows 平台 |
| PKCS#7 | .p7b, .p7c | 证书链格式 | 证书链导出 |
证书文件说明
RabbitMQ TLS 所需文件:
1. ca_certificate.pem - CA 证书(用于验证客户端证书)
2. server_certificate.pem - 服务器证书
3. server_key.pem - 服务器私钥
4. client_certificate.pem - 客户端证书(可选,双向认证)
5. client_key.pem - 客户端私钥(可选,双向认证)证书生成步骤
方式一:自签名证书(测试环境)
步骤 1:创建 CA 证书
bash
#!/bin/bash
# 创建工作目录
mkdir -p /tmp/rabbitmq-ssl
cd /tmp/rabbitmq-ssl
# 生成 CA 私钥
openssl genrsa -out ca.key 4096
# 生成 CA 证书
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca_certificate.pem \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=MyCompany Root CA"
# 验证 CA 证书
openssl x509 -in ca_certificate.pem -text -noout步骤 2:生成服务器证书
bash
#!/bin/bash
# 创建服务器私钥
openssl genrsa -out server_key.pem 4096
# 创建证书签名请求 (CSR)
openssl req -new -key server_key.pem -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=rabbitmq.example.com"
# 创建扩展配置文件
cat > server_ext.cnf << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = rabbitmq.example.com
DNS.2 = localhost
DNS.3 = *.rabbitmq.example.com
IP.1 = 127.0.0.1
IP.2 = 192.168.1.100
EOF
# 使用 CA 签发服务器证书
openssl x509 -req -in server.csr -CA ca_certificate.pem -CAkey ca.key \
-CAcreateserial -out server_certificate.pem -days 365 -sha256 \
-extfile server_ext.cnf
# 验证服务器证书
openssl x509 -in server_certificate.pem -text -noout步骤 3:生成客户端证书(双向认证)
bash
#!/bin/bash
# 创建客户端私钥
openssl genrsa -out client_key.pem 4096
# 创建客户端 CSR
openssl req -new -key client_key.pem -out client.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=client.example.com"
# 创建客户端扩展配置
cat > client_ext.cnf << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
EOF
# 使用 CA 签发客户端证书
openssl x509 -req -in client.csr -CA ca_certificate.pem -CAkey ca.key \
-CAcreateserial -out client_certificate.pem -days 365 -sha256 \
-extfile client_ext.cnf
# 验证客户端证书
openssl x509 -in client_certificate.pem -text -noout步骤 4:验证证书链
bash
#!/bin/bash
# 验证证书链
openssl verify -CAfile ca_certificate.pem server_certificate.pem
openssl verify -CAfile ca_certificate.pem client_certificate.pem
# 检查证书匹配
openssl x509 -noout -modulus -in server_certificate.pem | openssl md5
openssl rsa -noout -modulus -in server_key.pem | openssl md5步骤 5:部署证书
bash
#!/bin/bash
# 创建 RabbitMQ SSL 目录
sudo mkdir -p /etc/rabbitmq/ssl
# 复制证书文件
sudo cp ca_certificate.pem /etc/rabbitmq/ssl/
sudo cp server_certificate.pem /etc/rabbitmq/ssl/
sudo cp server_key.pem /etc/rabbitmq/ssl/
# 设置权限
sudo chown rabbitmq:rabbitmq /etc/rabbitmq/ssl/*
sudo chmod 600 /etc/rabbitmq/ssl/server_key.pem
sudo chmod 644 /etc/rabbitmq/ssl/ca_certificate.pem
sudo chmod 644 /etc/rabbitmq/ssl/server_certificate.pem方式二:Let's Encrypt 免费证书(生产环境)
bash
#!/bin/bash
# 安装 certbot
sudo apt-get update
sudo apt-get install -y certbot
# 申请证书(需要域名解析到服务器)
sudo certbot certonly --standalone -d rabbitmq.example.com
# 证书位置
# /etc/letsencrypt/live/rabbitmq.example.com/fullchain.pem - 服务器证书链
# /etc/letsencrypt/live/rabbitmq.example.com/privkey.pem - 私钥
# 创建 RabbitMQ 使用的证书目录
sudo mkdir -p /etc/rabbitmq/ssl
# 复制证书
sudo cp /etc/letsencrypt/live/rabbitmq.example.com/fullchain.pem /etc/rabbitmq/ssl/server_certificate.pem
sudo cp /etc/letsencrypt/live/rabbitmq.example.com/privkey.pem /etc/rabbitmq/ssl/server_key.pem
# 下载 Let's Encrypt CA 证书
sudo wget -O /etc/rabbitmq/ssl/ca_certificate.pem https://letsencrypt.org/certs/isrgrootx1.pem
# 设置权限
sudo chown -R rabbitmq:rabbitmq /etc/rabbitmq/ssl
sudo chmod 600 /etc/rabbitmq/ssl/server_key.pem
# 设置自动续期
sudo crontab -e
# 添加:0 0 1 * * certbot renew --quiet && systemctl restart rabbitmq-server方式三:商业 CA 证书
bash
#!/bin/bash
# 1. 生成私钥
openssl genrsa -out server_key.pem 2048
# 2. 生成 CSR
openssl req -new -key server_key.pem -out server.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=rabbitmq.example.com"
# 3. 将 server.csr 提交给 CA 机构
# 4. 收到证书后保存为 server_certificate.pem
# 5. 下载 CA 中间证书并合并
cat server_certificate.pem intermediate.pem > server_certificate_chain.pem
# 6. 部署
sudo cp server_certificate_chain.pem /etc/rabbitmq/ssl/server_certificate.pem
sudo cp server_key.pem /etc/rabbitmq/ssl/RabbitMQ 证书配置
基础配置
conf
# /etc/rabbitmq/rabbitmq.conf
# TLS 监听端口
listeners.ssl.default = 5671
# 证书文件路径
ssl_options.cacertfile = /etc/rabbitmq/ssl/ca_certificate.pem
ssl_options.certfile = /etc/rabbitmq/ssl/server_certificate.pem
ssl_options.keyfile = /etc/rabbitmq/ssl/server_key.pem
# 私钥密码(如果有)
# ssl_options.password = your_password完整生产配置
conf
# /etc/rabbitmq/rabbitmq.conf
# TLS 监听器
listeners.ssl.default = 5671
# 证书配置
ssl_options.cacertfile = /etc/rabbitmq/ssl/ca_certificate.pem
ssl_options.certfile = /etc/rabbitmq/ssl/server_certificate.pem
ssl_options.keyfile = /etc/rabbitmq/ssl/server_key.pem
# TLS 版本
ssl_options.versions.1 = tlsv1.3
ssl_options.versions.2 = tlsv1.2
# 加密套件
ssl_options.ciphers.1 = TLS_AES_256_GCM_SHA384
ssl_options.ciphers.2 = TLS_AES_128_GCM_SHA256
ssl_options.ciphers.3 = ECDHE-RSA-AES256-GCM-SHA384
# 客户端验证
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false
ssl_options.depth = 2
# 会话配置
ssl_options.session_timeout = 3600
ssl_options.honor_cipher_order = true
# 管理 UI TLS
management.ssl.port = 15671
management.ssl.cacertfile = /etc/rabbitmq/ssl/ca_certificate.pem
management.ssl.certfile = /etc/rabbitmq/ssl/server_certificate.pem
management.ssl.keyfile = /etc/rabbitmq/ssl/server_key.pemPHP 代码示例
证书生成工具类
php
<?php
class CertificateGenerator
{
private $outputDir;
private $caConfig = [
'country' => 'CN',
'state' => 'Beijing',
'city' => 'Beijing',
'organization' => 'MyCompany',
'organizationalUnit' => 'IT'
];
public function __construct(string $outputDir)
{
$this->outputDir = $outputDir;
if (!is_dir($outputDir)) {
mkdir($outputDir, 0700, true);
}
}
public function generateCA(int $keyBits = 4096, int $days = 3650): array
{
$dn = [
'countryName' => $this->caConfig['country'],
'stateOrProvinceName' => $this->caConfig['state'],
'localityName' => $this->caConfig['city'],
'organizationName' => $this->caConfig['organization'],
'organizationalUnitName' => $this->caConfig['organizationalUnit'],
'commonName' => $this->caConfig['organization'] . ' Root CA'
];
$privateKey = openssl_pkey_new([
'private_key_bits' => $keyBits,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
$csr = openssl_csr_new($dn, $privateKey);
$certificate = openssl_csr_sign($csr, null, $privateKey, $days);
openssl_x509_export($certificate, $certOut);
openssl_pkey_export($privateKey, $keyOut);
$caCertPath = $this->outputDir . '/ca_certificate.pem';
$caKeyPath = $this->outputDir . '/ca_key.pem';
file_put_contents($caCertPath, $certOut);
file_put_contents($caKeyPath, $keyOut);
chmod($caKeyPath, 0600);
return [
'certificate' => $caCertPath,
'private_key' => $caKeyPath
];
}
public function generateServerCertificate(
string $caCertPath,
string $caKeyPath,
string $commonName,
array $altNames = [],
int $keyBits = 4096,
int $days = 365
): array {
$dn = [
'countryName' => $this->caConfig['country'],
'stateOrProvinceName' => $this->caConfig['state'],
'localityName' => $this->caConfig['city'],
'organizationName' => $this->caConfig['organization'],
'organizationalUnitName' => $this->caConfig['organizationalUnit'],
'commonName' => $commonName
];
$privateKey = openssl_pkey_new([
'private_key_bits' => $keyBits,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
$csr = openssl_csr_new($dn, $privateKey);
$caCert = openssl_x509_read(file_get_contents($caCertPath));
$caKey = openssl_pkey_get_private(file_get_contents($caKeyPath));
$san = $this->buildSubjectAltNames($altNames);
$certificate = openssl_csr_sign($csr, $caCert, $caKey, $days, [
'digest_alg' => 'sha256',
'x509_extensions' => $this->getServerExtensions($san)
]);
openssl_x509_export($certificate, $certOut);
openssl_pkey_export($privateKey, $keyOut);
$serverCertPath = $this->outputDir . '/server_certificate.pem';
$serverKeyPath = $this->outputDir . '/server_key.pem';
file_put_contents($serverCertPath, $certOut);
file_put_contents($serverKeyPath, $keyOut);
chmod($serverKeyPath, 0600);
return [
'certificate' => $serverCertPath,
'private_key' => $serverKeyPath
];
}
public function generateClientCertificate(
string $caCertPath,
string $caKeyPath,
string $commonName,
int $keyBits = 4096,
int $days = 365
): array {
$dn = [
'countryName' => $this->caConfig['country'],
'stateOrProvinceName' => $this->caConfig['state'],
'localityName' => $this->caConfig['city'],
'organizationName' => $this->caConfig['organization'],
'organizationalUnitName' => $this->caConfig['organizationalUnit'],
'commonName' => $commonName
];
$privateKey = openssl_pkey_new([
'private_key_bits' => $keyBits,
'private_key_type' => OPENSSL_KEYTYPE_RSA
]);
$csr = openssl_csr_new($dn, $privateKey);
$caCert = openssl_x509_read(file_get_contents($caCertPath));
$caKey = openssl_pkey_get_private(file_get_contents($caKeyPath));
$certificate = openssl_csr_sign($csr, $caCert, $caKey, $days, [
'digest_alg' => 'sha256',
'x509_extensions' => $this->getClientExtensions()
]);
openssl_x509_export($certificate, $certOut);
openssl_pkey_export($privateKey, $keyOut);
$clientCertPath = $this->outputDir . '/client_certificate.pem';
$clientKeyPath = $this->outputDir . '/client_key.pem';
file_put_contents($clientCertPath, $certOut);
file_put_contents($clientKeyPath, $keyOut);
chmod($clientKeyPath, 0600);
return [
'certificate' => $clientCertPath,
'private_key' => $clientKeyPath
];
}
private function buildSubjectAltNames(array $altNames): string
{
$san = [];
$dnsIndex = 1;
$ipIndex = 1;
foreach ($altNames as $type => $values) {
foreach ((array)$values as $value) {
if ($type === 'dns') {
$san[] = "DNS.{$dnsIndex} = {$value}";
$dnsIndex++;
} elseif ($type === 'ip') {
$san[] = "IP.{$ipIndex} = {$value}";
$ipIndex++;
}
}
}
return implode("\n", $san);
}
private function getServerExtensions(string $san = ''): string
{
$ext = <<<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
{$san}
EOF;
return $ext;
}
private function getClientExtensions(): string
{
return <<<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF;
}
}
// 使用示例
$generator = new CertificateGenerator('/tmp/rabbitmq-ssl');
// 生成 CA
$ca = $generator->generateCA();
echo "CA 证书: {$ca['certificate']}\n";
// 生成服务器证书
$server = $generator->generateServerCertificate(
$ca['certificate'],
$ca['private_key'],
'rabbitmq.example.com',
[
'dns' => ['rabbitmq.example.com', 'localhost', '*.rabbitmq.example.com'],
'ip' => ['127.0.0.1', '192.168.1.100']
]
);
echo "服务器证书: {$server['certificate']}\n";
// 生成客户端证书
$client = $generator->generateClientCertificate(
$ca['certificate'],
$ca['private_key'],
'client.example.com'
);
echo "客户端证书: {$client['certificate']}\n";证书信息查看工具
php
<?php
class CertificateReader
{
public function readCertificate(string $path): array
{
if (!file_exists($path)) {
throw new RuntimeException("证书文件不存在: {$path}");
}
$content = file_get_contents($path);
$cert = openssl_x509_read($content);
if ($cert === false) {
throw new RuntimeException("无法解析证书: {$path}");
}
$info = openssl_x509_parse($cert);
return [
'subject' => $this->formatDN($info['subject']),
'issuer' => $this->formatDN($info['issuer']),
'serial_number' => $info['serialNumber'],
'version' => $info['version'],
'valid_from' => date('Y-m-d H:i:s', $info['validFrom_time_t']),
'valid_to' => date('Y-m-d H:i:s', $info['validTo_time_t']),
'is_valid' => $info['validTo_time_t'] > time() && $info['validFrom_time_t'] <= time(),
'days_remaining' => max(0, floor(($info['validTo_time_t'] - time()) / 86400)),
'signature_algorithm' => $info['signatureTypeSN'] ?? 'unknown',
'purposes' => $this->getPurposes($cert),
'extensions' => $info['extensions'] ?? []
];
}
public function readPrivateKey(string $path, string $passphrase = null): array
{
if (!file_exists($path)) {
throw new RuntimeException("私钥文件不存在: {$path}");
}
$content = file_get_contents($path);
$key = openssl_pkey_get_private($content, $passphrase);
if ($key === false) {
throw new RuntimeException("无法解析私钥: {$path}");
}
$details = openssl_pkey_get_details($key);
return [
'type' => $this->getKeyTypeName($details['type']),
'bits' => $details['bits'],
'is_encrypted' => strpos($content, 'ENCRYPTED') !== false
];
}
public function verifyCertificateChain(string $certPath, string $caPath): bool
{
$cert = file_get_contents($certPath);
$ca = file_get_contents($caPath);
$certResource = openssl_x509_read($cert);
$result = openssl_x509_checkpurpose($certResource, X509_PURPOSE_ANY, [$ca]);
return $result === true;
}
public function checkCertificateMatch(string $certPath, string $keyPath): bool
{
$cert = file_get_contents($certPath);
$key = file_get_contents($keyPath);
$certModulus = openssl_x509_parse($cert);
$keyResource = openssl_pkey_get_private($key);
return openssl_x509_check_private_key(openssl_x509_read($cert), $keyResource);
}
private function formatDN(array $dn): string
{
$parts = [];
foreach ($dn as $key => $value) {
$parts[] = "{$key}={$value}";
}
return implode(', ', $parts);
}
private function getPurposes($cert): array
{
$purposes = [];
$purposeConstants = [
X509_PURPOSE_SSL_CLIENT => 'ssl_client',
X509_PURPOSE_SSL_SERVER => 'ssl_server',
X509_PURPOSE_SMIME_SIGN => 'smime_sign',
X509_PURPOSE_SMIME_ENCRYPT => 'smime_encrypt',
X509_PURPOSE_CRL_SIGN => 'crl_sign',
X509_PURPOSE_ANY => 'any'
];
foreach ($purposeConstants as $constant => $name) {
$result = openssl_x509_checkpurpose($cert, $constant);
$purposes[$name] = $result === true;
}
return $purposes;
}
private function getKeyTypeName(int $type): string
{
$types = [
OPENSSL_KEYTYPE_RSA => 'RSA',
OPENSSL_KEYTYPE_DSA => 'DSA',
OPENSSL_KEYTYPE_DH => 'DH',
OPENSSL_KEYTYPE_EC => 'EC'
];
return $types[$type] ?? 'Unknown';
}
}
// 使用示例
$reader = new CertificateReader();
$certInfo = $reader->readCertificate('/etc/rabbitmq/ssl/server_certificate.pem');
print_r($certInfo);
$keyInfo = $reader->readPrivateKey('/etc/rabbitmq/ssl/server_key.pem');
print_r($keyInfo);
$chainValid = $reader->verifyCertificateChain(
'/etc/rabbitmq/ssl/server_certificate.pem',
'/etc/rabbitmq/ssl/ca_certificate.pem'
);
echo "证书链验证: " . ($chainValid ? '通过' : '失败') . "\n";实际应用场景
场景一:自动化证书部署
php
<?php
class CertificateDeployer
{
private $rabbitmqSslDir = '/etc/rabbitmq/ssl';
private $backupDir = '/backup/rabbitmq/ssl';
public function deploy(array $certificates): array
{
$results = [
'backup' => null,
'deployed' => [],
'errors' => []
];
$results['backup'] = $this->backupExisting();
foreach ($certificates as $name => $content) {
try {
$path = $this->rabbitmqSslDir . '/' . $name;
file_put_contents($path, $content);
if (strpos($name, 'key') !== false) {
chmod($path, 0600);
} else {
chmod($path, 0644);
}
chown($path, 'rabbitmq');
chgrp($path, 'rabbitmq');
$results['deployed'][] = $name;
} catch (Exception $e) {
$results['errors'][] = [
'file' => $name,
'error' => $e->getMessage()
];
}
}
return $results;
}
private function backupExisting(): ?string
{
if (!is_dir($this->rabbitmqSslDir)) {
return null;
}
$timestamp = date('Ymd_His');
$backupPath = $this->backupDir . '/' . $timestamp;
if (!is_dir($backupPath)) {
mkdir($backupPath, 0700, true);
}
$files = glob($this->rabbitmqSslDir . '/*.pem');
foreach ($files as $file) {
copy($file, $backupPath . '/' . basename($file));
}
return $backupPath;
}
public function rollback(string $backupPath): bool
{
if (!is_dir($backupPath)) {
return false;
}
$files = glob($backupPath . '/*.pem');
foreach ($files as $file) {
copy($file, $this->rabbitmqSslDir . '/' . basename($file));
}
return true;
}
}常见问题与解决方案
问题 1:证书权限错误
错误信息:
Error: unable to read server key file解决方案:
bash
# 检查文件权限
ls -la /etc/rabbitmq/ssl/
# 修复权限
chmod 600 /etc/rabbitmq/ssl/server_key.pem
chmod 644 /etc/rabbitmq/ssl/server_certificate.pem
chmod 644 /etc/rabbitmq/ssl/ca_certificate.pem
chown rabbitmq:rabbitmq /etc/rabbitmq/ssl/*问题 2:证书链不完整
解决方案:
bash
# 合并证书链
cat server_certificate.pem intermediate.pem > server_certificate_chain.pem
# 验证证书链
openssl verify -CAfile ca_certificate.pem -untrusted intermediate.pem server_certificate_chain.pem问题 3:私钥与证书不匹配
解决方案:
bash
# 检查私钥和证书的模数是否匹配
openssl x509 -noout -modulus -in server_certificate.pem | openssl md5
openssl rsa -noout -modulus -in server_key.pem | openssl md5
# 两个 MD5 值应该相同最佳实践建议
1. 证书管理清单
php
<?php
class CertificateChecklist
{
public function validate(array $paths): array
{
$checks = [
'files_exist' => $this->checkFilesExist($paths),
'permissions' => $this->checkPermissions($paths),
'validity' => $this->checkValidity($paths),
'chain' => $this->checkChain($paths),
'match' => $this->checkKeyCertMatch($paths)
];
$allPassed = !in_array(false, array_column($checks, 'passed'), true);
return [
'all_passed' => $allPassed,
'checks' => $checks
];
}
private function checkFilesExist(array $paths): array
{
$missing = [];
foreach ($paths as $name => $path) {
if (!file_exists($path)) {
$missing[] = $name;
}
}
return [
'passed' => empty($missing),
'missing' => $missing
];
}
private function checkPermissions(array $paths): array
{
$issues = [];
foreach ($paths as $name => $path) {
if (!file_exists($path)) continue;
$perms = fileperms($path);
if (strpos($name, 'key') !== false && ($perms & 077) !== 0) {
$issues[] = "{$name}: 私钥权限过于宽松";
}
}
return [
'passed' => empty($issues),
'issues' => $issues
];
}
private function checkValidity(array $paths): array
{
$reader = new CertificateReader();
$issues = [];
foreach (['ca_cert', 'server_cert'] as $name) {
if (!isset($paths[$name]) || !file_exists($paths[$name])) continue;
$info = $reader->readCertificate($paths[$name]);
if (!$info['is_valid']) {
$issues[] = "{$name}: 证书无效或已过期";
} elseif ($info['days_remaining'] < 30) {
$issues[] = "{$name}: 证书将在 {$info['days_remaining']} 天后过期";
}
}
return [
'passed' => empty($issues),
'issues' => $issues
];
}
private function checkChain(array $paths): array
{
if (!isset($paths['ca_cert']) || !isset($paths['server_cert'])) {
return ['passed' => true, 'message' => '跳过证书链检查'];
}
$reader = new CertificateReader();
$valid = $reader->verifyCertificateChain($paths['server_cert'], $paths['ca_cert']);
return [
'passed' => $valid,
'message' => $valid ? '证书链验证通过' : '证书链验证失败'
];
}
private function checkKeyCertMatch(array $paths): array
{
if (!isset($paths['server_key']) || !isset($paths['server_cert'])) {
return ['passed' => true, 'message' => '跳过密钥匹配检查'];
}
$reader = new CertificateReader();
$match = $reader->checkCertificateMatch($paths['server_cert'], $paths['server_key']);
return [
'passed' => $match,
'message' => $match ? '私钥与证书匹配' : '私钥与证书不匹配'
];
}
}安全注意事项
重要警告
- 私钥保护:私钥文件必须设置 600 权限,仅允许所有者读取
- 证书备份:定期备份证书和私钥到安全位置
- 有效期监控:设置告警监控证书过期时间
- 安全传输:证书文件传输必须使用加密通道
- 访问控制:限制证书目录的访问权限
相关链接
- SSL/TLS 加密 - TLS 加密配置详解
- 双向认证 - mTLS 双向认证配置
- 客户端证书验证 - 客户端证书验证
- 认证机制概述 - RabbitMQ 认证体系
