在本地或测试服务器上开发 Web 应用或 API 时,通常需要 HTTPS 来测试 Service Workers、地理位置 API、剪贴板 API、WebCrypto 或混合内容策略等功能。在这个阶段获取真实的 SSL 证书太过分了 — 用 OpenSSL 生成的自签证书是最快、最免费的替代方案。本指南逐步讲解:安装 OpenSSL、理解证书文件类型、生成带主体备用名称 (SAN) 的密钥和证书、配置 Apache 和 Nginx,以及将证书添加到系统信任存储,使浏览器警告完全消失。

什么是自签 SSL 证书,何时应使用它?

自签 SSL 证书是你使用 OpenSSL 自己生成和签署的 X.509 证书,而不是向 DigiCert、Sectigo 或 Let's Encrypt 等受信任的证书颁发机构 (CA) 提交证书签名请求 (CSR)。因为浏览器不识别签发机构,它们会显示"你的连接不安全"或 NET::ERR_CERT_AUTHORITY_INVALID 等安全警告。

尽管有此限制,自签证书在以下几种情况下是正确的工具:

重要:切勿在面向公众的生产服务器上使用自签证书。所有主要浏览器都会在访问者继续前用整页警告阻止他们,严重损害信任和转化率。

了解证书文件类型

生成证书前,了解每种文件类型的用途会很有帮助:

文件 / 扩展名 说明 使用者
server.key 私钥 — 保持机密,永不分享 Apache / Nginx Web 服务器
server.csr 证书签名请求 — 发送给 CA(或自签) CA 提交或自签
server.crt PEM 格式的证书 — 公开证书 Web 服务器 + 信任存储导入
server.pem PEM 格式 — 与 .crt 相同,某些工具需要此扩展名 Linux CA 存储、Docker、Node.js
server.pfx / .p12 PKCS#12 — 在单个二进制文件中捆绑密钥 + 证书 Windows IIS、.NET、Java KeyStore

对于运行 Apache 或 Nginx 的基于 Linux 的 Web 服务器,你将主要使用 PEM 格式的 .key(私钥)和 .crt(证书)文件 — 这是以 -----BEGIN CERTIFICATE----- 开头的 Base64 编码文本格式。

验证 OpenSSL 并准备环境

OpenSSL 在大多数 Linux 发行版和 macOS 上都已预装。Windows 用户必须单独安装。用以下命令确认可用性:

openssl version

预期输出:

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

如果未安装 OpenSSL,使用相应的包管理器:

# Ubuntu / Debian
sudo apt-get update && sudo apt-get install -y openssl

# CentOS / RHEL / Rocky Linux
sudo dnf install -y openssl

# macOS 通过 Homebrew
brew install openssl

# Windows:从 https://slproweb.com/products/Win32OpenSSL.html 下载 Win32/Win64 OpenSSL
# 或使用 WSL (Windows Subsystem for Linux) 直接运行 Linux 命令。

为证书文件创建工作目录:

mkdir -p ~/ssl-certs && cd ~/ssl-certs

方法 1 — 快速一行命令证书

最快的方法在单一命令中结合密钥生成和证书创建。这适合快速开发环境设置:

openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \
  -days 365 -nodes \
  -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyCompany Dev/OU=Development/CN=localhost"

参数说明:

运行后,你将获得两个文件:server.key(私钥)和 server.crt(证书),两者都可在 Web 服务器中配置。

方法 2 — 带主体备用名称 (SAN) 的证书

现代浏览器要求证书包含主体备用名称 (SAN),否则会显示额外警告 — "缺少主体备用名称" — 即使你已接受证书。以下方法使用配置文件创建覆盖多个主机名和 IP 地址的证书:

# Create SAN configuration file
cat > san.cnf << 'EOF'
[req]
default_bits       = 4096
prompt             = no
default_md         = sha256
distinguished_name = dn
x509_extensions    = v3_req

[dn]
C  = TH
ST = Bangkok
L  = Bangkok
O  = 我的公司开发
CN = localhost

[v3_req]
subjectAltName = @alt_names
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
DNS.3 = dev.local
DNS.4 = *.dev.local
DNS.5 = staging.myapp.com
IP.1  = 127.0.0.1
IP.2  = 192.168.1.100
EOF

# 从配置生成证书
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \
  -days 365 -nodes -config san.cnf

这张单一证书覆盖 localhost、*.localhost、dev.local、*.dev.local、staging.myapp.com 以及指定的 IP 地址 — 无需为开发环境中的每个主机名生成单独的证书。

方法 3 — 分步证书生成

为了获得更好的控制或需要保留 CSR 以提交给内部 CA 时,将流程分为三个明确步骤:

第 1 步:生成私钥

# RSA 4096 位(推荐)
openssl genrsa -out server.key 4096

# 备选:ECDSA P-256(密钥生成和 TLS 握手更快)
openssl ecparam -genkey -name prime256v1 -out server.key

第 2 步:创建 CSR

openssl req -new -key server.key -out server.csr \
  -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyOrg/CN=localhost"

第 3 步:自签 CSR

openssl x509 -req -in server.csr -signkey server.key \
  -out server.crt -days 365 \
  -extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1")

如果你稍后决定由组织的内部 CA 签署而不是自签,保留 CSR (server.csr) 很有用。

验证证书

在部署生成的证书前务必检查它以确认详情正确:

# 查看完整证书详情
openssl x509 -in server.crt -text -noout

# 仅查看主体、颁发者和有效期日期
openssl x509 -in server.crt -subject -issuer -dates -noout

# 确认私钥与证书匹配(MD5 哈希必须相同)
openssl x509 -noout -modulus -in server.crt | md5sum
openssl rsa -noout -modulus -in server.key | md5sum

# 确认 SAN 条目
openssl x509 -in server.crt -text -noout | grep -A2 "Subject Alternative Name"

输出中要验证的关键字段:

配置 Web 服务器

将证书文件复制到标准系统位置,然后配置 Web 服务器使用它们。下面的示例假设证书存储在 /etc/ssl/self-signed/:

# 复制证书文件
sudo mkdir -p /etc/ssl/self-signed
sudo cp server.key /etc/ssl/self-signed/server.key
sudo cp server.crt /etc/ssl/self-signed/server.crt
sudo chmod 600 /etc/ssl/self-signed/server.key
sudo chmod 644 /etc/ssl/self-signed/server.crt

Nginx 配置

server {
    listen 80;
    server_name localhost dev.local;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name localhost dev.local;

    ssl_certificate     /etc/ssl/self-signed/server.crt;
    ssl_certificate_key /etc/ssl/self-signed/server.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    root  /var/www/html;
    index index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}

Apache 配置

<VirtualHost *:443>
    ServerName localhost
    ServerAlias dev.local

    SSLEngine on
    SSLCertificateFile    /etc/ssl/self-signed/server.crt
    SSLCertificateKeyFile /etc/ssl/self-signed/server.key

    SSLProtocol           all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite        HIGH:!aNULL:!MD5

    DocumentRoot /var/www/html
    <Directory /var/www/html>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    ServerName localhost
    Redirect permanent / https://localhost/
</VirtualHost>

进行更改后测试并重启 Web 服务器:

# Nginx
sudo nginx -t && sudo systemctl reload nginx

# Apache
sudo apachectl configtest && sudo systemctl reload apache2

将证书添加到系统信任存储

要在开发环境中完全消除浏览器警告,将证书导入操作系统的信任存储。这必须在每个开发人员机器上单独进行。

macOS

# 通过命令行
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain server.crt

# 通过钥匙串访问 GUI:
# 1. 双击 server.crt 打开钥匙串访问
# 2. 找到证书条目 — 双击它
# 3. 展开信任 → "使用此证书时" → 始终信任

Linux — Ubuntu / Debian

sudo cp server.crt /usr/local/share/ca-certificates/dev-localhost.crt
sudo update-ca-certificates
# 输出:1 added, 0 removed; done.

Windows

# PowerShell(以管理员身份运行)
Import-Certificate -FilePath "C:\path\to\server.crt" `
  -CertStoreLocation Cert:\LocalMachine\Root

# 或通过 GUI:
# 1. 双击 server.crt → 安装证书
# 2. 存储位置:本地计算机 → 下一步
# 3. 将所有证书放入:受信任的根证书颁发机构 → 确定 → 完成

专业提示 — 面向团队的 mkcert:如果你的团队频繁生成自签证书,考虑 mkcert (github.com/FiloSottile/mkcert)。它自动创建本地 CA 并用单一命令将其安装到每个操作系统的系统信任存储中:mkcert -install。然后用 mkcert localhost 127.0.0.1 ::1 生成受信任证书 — 无需 OpenSSL 配置文件。一次分发 CA 证书 (rootCA.pem) 给所有团队成员,随后由 mkcert 生成的每个证书都将自动被信任。

在 Docker 和 Node.js 中使用自签 SSL

在容器化开发环境中经常需要自签证书。以下是最常见的模式:

Node.js HTTPS 服务器

const https = require('https');
const fs    = require('fs');

const options = {
  key:  fs.readFileSync('/path/to/server.key'),
  cert: fs.readFileSync('/path/to/server.crt'),
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('Hello HTTPS\n');
}).listen(443, () => console.log('HTTPS server running on :443'));

Docker Compose 搭配 Nginx + SSL

version: '3.8'
services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./ssl/server.crt:/etc/ssl/certs/server.crt:ro
      - ./ssl/server.key:/etc/ssl/private/server.key:ro
      - ./html:/usr/share/nginx/html:ro

在开发工具中禁用 SSL 验证

某些工具默认拒绝自签证书。这些标志仅在开发中使用 — 在生产环境中始终重新启用证书验证:

# curl
curl -k https://localhost/api/health

# wget
wget --no-check-certificate https://localhost/file.zip

# Python requests
import requests
response = requests.get('https://localhost', verify=False)

# Node.js 环境变量
export NODE_TLS_REJECT_UNAUTHORIZED=0

# Git(按存储库设置,不是全局)
git -c http.sslVerify=false clone https://localhost/repo.git

为团队创建私有证书颁发机构

如果你的团队频繁使用内部 HTTPS 服务,更好的长期解决方案是创建私有根 CA,一次分发其证书给每个团队成员,然后用它签署所有服务器证书。新证书将自动被信任,无需任何额外导入。

# 第 1 步:创建根 CA 私钥
openssl genrsa -out ca.key 4096

# 第 2 步:创建根 CA 证书(10 年有效期)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
  -out ca.crt \
  -subj "/C=TH/ST=Bangkok/O=MyCompany Dev CA/CN=MyCompany Development Root CA"

# 第 3 步:创建服务器私钥
openssl genrsa -out server.key 4096

# 第 4 步:创建服务器 CSR
openssl req -new -key server.key -out server.csr \
  -subj "/C=TH/ST=Bangkok/O=MyCompany/CN=dev.mycompany.local"

# 第 5 步:用 CA 签署服务器 CSR
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out server.crt -days 365 -sha256 \
  -extfile <(printf "subjectAltName=DNS:dev.mycompany.local,DNS:*.dev.mycompany.local,IP:192.168.1.100")

分发 ca.crt 给每个团队成员一次安装在其信任存储中。随后由此 CA 签署的所有服务器证书都将自动被信任 — 无需额外的逐个证书导入。

续期自签证书

与 CA 签发的证书不同,自签证书没有续期流程 — 当旧证书过期或主体信息更改时,你只需生成新证书。检查过期日期:

# 检查过期日期
openssl x509 -in server.crt -noout -enddate
# notAfter=Jun  9 00:00:00 2027 GMT

# 使用保存的配置文件重新生成
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \
  -days 365 -nodes -config san.cnf

# 续期后重启 Web 服务器
sudo systemctl reload nginx

对于自动化环境,添加此命令到 cron 作业,在证书过期前 30 天运行,之后重启 Web 服务器。

常见问题

自签 SSL 与 CA 签发证书的区别是什么?

自签 SSL 证书是由你使用 OpenSSL 生成和签署的 — 不涉及证书颁发机构。浏览器不识别签发机构,所以会显示"你的连接不安全"的警告。CA 签发的证书(来自 DigiCert、Let's Encrypt 等)受全球浏览器信任。自签证书仅应用于开发、测试或内部工具中,且你能控制谁可以访问服务。

我能在生产服务器上使用自签 SSL 吗?

强烈不建议。所有主要浏览器 — Chrome、Firefox、Safari 和 Edge — 都会在访问者到达你的网站前显示整页的"不安全"警告。这会严重破坏用户信任和转化率。API 客户端也默认拒绝自签证书,除非你明确禁用验证。对于生产环境,使用来自受信任 CA 的证书或免费的 Let's Encrypt 服务。

我如何让浏览器信任自签 SSL 证书?

将 .crt 文件导入操作系统的信任存储。在 macOS 上,使用钥匙串访问或 sudo security add-trusted-cert。在 Windows 上,使用 certmgr.msc → 受信任的根证书颁发机构 → 导入。在 Linux (Ubuntu/Debian) 上,复制到 /usr/local/share/ca-certificates/ 并运行 sudo update-ca-certificates。这仅适用于你控制的计算机 — 无法让外部用户信任证书。

自签 SSL 证书的有效期有多长?

你可以用 -days 参数自己设置有效期。常见值是 365(一年)或 3650(十年)用于开发环境。但现代浏览器 — 尤其是 iOS 和 macOS 上的 Safari — 强制要求证书有效期最长约 825 天。有效期更长的证书可能触发额外安全警告。最佳实践是每年重新生成证书,或在主体信息更改时重新生成。

AsiaGB 的所有用途 SSL 证书

DV、OV、EV 和通配符 SSL 证书,起价 1,000 泰铢/年,包括完整安装支持。

查看 SSL 证书