# 证书制作

基于 Let's Encrypt ACME v2 协议，免费申请和管理 SSL/TLS 证书。

<details>
<summary>这里有什么</summary>

## 快速开始

1. 在申请表输入**域名**（一行一个）和**联系邮箱**
2. 先用 **Staging 测试环境**验证流程（不限频率）
3. 通过后切换为 **Production 生产环境**，正式申请证书
4. 将生成的 `fullchain.pem` 和 `private.pem` 配置到 Web 服务器

---

## 申请步骤详解

### 1. 域名准备

- 域名须已解析到本服务器 IP，否则 HTTP-01 验证会失败
- 支持 SAN 多域名证书：第一个域名为主域名，其余为附加域名
- 支持裸域名和 `www` 子域名同时申请

### 2. 邮箱

用于 Let's Encrypt 账户注册，接收证书到期提醒邮件。

### 3. 环境选择

| 环境 | 说明 |
|------|------|
| Staging（测试） | 无速率限制，颁发**不受信任**的测试证书（浏览器会警告） |
| Production（生产） | 颁发受浏览器信任的正式证书，**每域名每周限 5 次** |

### 4. HTTP-01 验证

Let's Encrypt 会访问 `http://<你的域名>/.well-known/acme-challenge/<token>` 验证域名控制权。请确保：

- `.well-known/acme-challenge/` 目录存在且 **PHP 进程用户可写**
- Nginx 未拦截 `/.well-known/` 路径
- 80 端口可从公网访问

---

## 证书部署

### Nginx 配置示例

```nginx
server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate     /usr/share/nginx/html/ssl_certs/your-domain.com/fullchain.pem;
    ssl_certificate_key /usr/share/nginx/html/ssl_certs/your-domain.com/private.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=63072000" always;

    location / {
        root /usr/share/nginx/html;
        index index.php index.html;
    }
}
```

配置后执行 `nginx -t && nginx -s reload` 生效。

---

## 自动续期

证书有效期 **90 天**。本页面提供四种语言的续期脚本，**无需 PHP 环境即可独立运行**：

| 语言 | 脚本文件 | 依赖 |
|------|----------|------|
| PHP | `cert_renew.php` | PHP + openssl + curl |
| Python | `cert_renew.py` | Python 3.6+ stdlib + openssl + curl |
| Perl | `cert_renew.pl` | Perl 5 核心模块 + openssl + curl |
| Bash | `cert_renew.sh` | bash + openssl + curl + python3 |

### Crontab 配置

```bash
# 每天早上 3 点检查并续期（PHP 版）
0 3 * * * php /usr/share/nginx/html/api/cert_renew.php --email=admin@example.com >> /var/log/cert-renew.log 2>&1
```

续期脚本会扫描 `ssl_certs/` 目录下所有证书，**到期前 30 天**自动申请新证书。

### 续期参数

| 参数 | 说明 |
|------|------|
| `--email=xxx` | Let's Encrypt 账户邮箱 |
| `--force` | 强制续期，忽略 30 天窗口 |
| `--dry-run` | 试运行，仅检查不申请 |
| `--staging` | 使用测试环境 |

---

## 文件目录结构

```
ssl_certs/
├── account/
│   ├── private.pem        # ACME 账户私钥
│   ├── account_url.txt    # 账户 URL
│   └── email.txt          # 账户邮箱
├── your-domain.com/
│   ├── fullchain.pem      # 完整证书链
│   ├── private.pem        # 证书私钥
│   └── cert.pem           # 单独证书（不含链）
└── ...

.well-known/acme-challenge/   # HTTP-01 验证临时文件（自动清理）
```

---

## 常见问题

**Q: 测试环境证书浏览器不信任？**  
A: 正常现象。Staging 证书由 `(STAGING) Let's Encrypt` 颁发，仅供测试。切换到 Production 即可获得受信任的证书。

**Q: 验证失败？**  
A: 检查：1) 域名 DNS 是否解析到本服务器 2) `.well-known/acme-challenge/` 目录权限 3) 80 端口是否开放。

**Q: 如何续期？**  
A: 下载续期脚本，配置 crontab 即可自动续期。也可在页面重新申请（会覆盖旧证书）。

**Q: 证书存储在哪里？**  
A: `ssl_certs/<域名>/` 目录下。该目录已加入 `.gitignore`，私钥不会入库。

**Q: 测试和生产有什么区别？**  

⏺ Let's Encrypt 的测试环境 (Staging) 和生产环境 (Production) 核心区别：

| | Staging（测试）| Production（生产）|
|------|:----------|:------------|
|证书信任 | 浏览器不信任，访问会报警告   |浏览器受信任，正常 HTTPS |
|速率限制 |无限制，随便申请   | 每域名每周 5 次，超限锁定 7 天 |
|颁发机构 | (STAGING) Let's Encrypt   | Let's Encrypt / R3    |         
|用途     | 测试流程是否跑通     | 正式上线使用  |      
|API 地址 | acme-staging-v02.api.letsencrypt.org | acme-v02.api.letsencrypt.org |

  建议流程： 先在 Staging 测试整个申请链路（域名验证、challenge、CSR、下载），确认无报错后再切 Production 申请正式证书。避免因流程错误浪费生产环境的额度。

</details>