znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第09章 - 高级功能与技巧

本章介绍 acme.sh 的高级功能,包括 ECC 证书、通配符证书、多域名证书、DNS 别名模式、证书格式转换、调试技巧等,帮助你充分发挥 acme.sh 的潜力。


9.1 ECC 证书(椭圆曲线密码)

9.1.1 ECC 证书的优势

对比项 RSA-2048 ECC-256
安全级别 约等同于 3072 位 ECC 等同于 RSA-3072
密钥大小 2048 位 256 位
TLS 握手速度 较慢 更快(CPU 占用更低)
证书文件大小 较大 更小
兼容性 所有系统和设备 不支持 Windows XP 及极老旧浏览器

对于现代服务器(2024 年+),推荐使用 EC-256(prime256v1)或 EC-384(secp384r1)。

9.1.2 申请 ECC 证书

# EC-256(推荐,性能最佳)
acme.sh --issue --dns dns_cf \
  -d example.com -d *.example.com \
  --keylength ec-256

# EC-384(更高安全级别)
acme.sh --issue --dns dns_cf \
  -d example.com -d *.example.com \
  --keylength ec-384

# RSA-4096(极高安全要求场景)
acme.sh --issue --dns dns_cf \
  -d example.com -d *.example.com \
  --keylength 4096

# 默认(RSA-2048)
acme.sh --issue --dns dns_cf \
  -d example.com -d *.example.com

9.1.3 安装 ECC 证书

申请 ECC 证书后,安装时需要加 --ecc 参数:

acme.sh --install-cert -d example.com --ecc \
  --key-file       /etc/nginx/ssl/ec/privkey.pem \
  --fullchain-file /etc/nginx/ssl/ec/fullchain.pem \
  --reloadcmd      "systemctl reload nginx"

9.1.4 同域名 RSA + ECC 双证书部署

对于高并发、对性能要求较高的网站,可以同时申请 RSA 和 ECC 两套证书,现代客户端会自动选择 ECC(旧版浏览器降级使用 RSA):

# 申请 RSA 证书(默认路径)
acme.sh --issue --dns dns_cf -d example.com -d *.example.com

# 申请 ECC 证书(存储在 example.com_ecc 目录下)
acme.sh --issue --dns dns_cf -d example.com -d *.example.com --keylength ec-256

# 安装 RSA 证书
acme.sh --install-cert -d example.com \
  --key-file       /etc/nginx/ssl/rsa/privkey.pem \
  --fullchain-file /etc/nginx/ssl/rsa/fullchain.pem \
  --reloadcmd      "systemctl reload nginx"

# 安装 ECC 证书
acme.sh --install-cert -d example.com --ecc \
  --key-file       /etc/nginx/ssl/ecc/privkey.pem \
  --fullchain-file /etc/nginx/ssl/ecc/fullchain.pem \
  --reloadcmd      "systemctl reload nginx"

Nginx 双证书配置(Nginx 1.11.0+):

server {
    listen 443 ssl http2;
    server_name example.com;

    # RSA 证书(兼容旧版客户端)
    ssl_certificate     /etc/nginx/ssl/rsa/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/rsa/privkey.pem;

    # ECC 证书(现代客户端优先使用)
    ssl_certificate     /etc/nginx/ssl/ecc/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/ecc/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
}

9.2 通配符证书

9.2.1 通配符证书的特点

通配符证书(Wildcard Certificate)*.example.com 的特点:

  • 覆盖 example.com所有直接子域www.example.comapi.example.commail.example.com
  • 不覆盖根域名 example.com 本身(需要单独加 -d example.com
  • 不覆盖多级通配符:sub.api.example.com 不被 *.example.com 覆盖
  • 必须使用 DNS-01 验证(HTTP-01 无法验证通配符)

9.2.2 申请通配符证书

# 同时覆盖根域名和所有直接子域
acme.sh --issue --dns dns_cf \
  -d example.com \
  -d *.example.com

# 仅通配符(不含根域名)
acme.sh --issue --dns dns_cf \
  -d *.example.com

9.2.3 多级通配符

如果需要同时覆盖多个层级,需要申请多个通配符:

# 覆盖 example.com、*.example.com 和 *.api.example.com
acme.sh --issue --dns dns_cf \
  -d example.com \
  -d *.example.com \
  -d *.api.example.com

9.3 多域名 SAN 证书

9.3.1 SAN 证书说明

SAN(Subject Alternative Names)证书允许一张证书覆盖多个不相关的域名,非常适合在一台服务器上托管多个网站的场景。

Let’s Encrypt/ZeroSSL 每张证书最多支持 100 个 SAN 域名

9.3.2 申请多域名 SAN 证书

# 在同一个 DNS 服务商管理的多个域名
export CF_Token="your_cf_token"
export CF_Account_ID="your_account_id"

acme.sh --issue --dns dns_cf \
  -d example.com \
  -d www.example.com \
  -d api.example.com \
  -d *.example.com \
  -d another-domain.com \
  -d *.another-domain.com

9.3.3 混合 DNS 服务商 SAN 证书

如果多个域名分布在不同的 DNS 服务商,需要为每个域名单独指定 DNS API:

acme.sh --issue \
  -d example.com --dns dns_cf \
  -d another-domain.com --dns dns_ali \
  -d third-domain.com -w /var/www/html

9.4 DNS 别名模式深度解析

9.4.1 应用场景

DNS 别名模式(DNS Alias Mode)解决了以下几个痛点:

场景一:主域名 DNS 无 API

主域名 example.com 的 DNS 托管在不支持 API 的服务商(如部分国内小型 DNS 服务商),但希望自动续期。

场景二:安全隔离

不希望将主域名的 DNS 操作权限(API Key)存放在 Web 服务器上(避免服务器被入侵后 DNS 被劫持)。

场景三:集中化验证

多台服务器的多个域名,都通过一个统一的 DNS API 账户(如 Cloudflare)进行验证,简化管理。

9.4.2 完整配置步骤

前提:准备一个辅助域名 challenge-dns.io,托管在 Cloudflare。

第一步:在主域名的 DNS 控制台,为每个需要申请证书的域名添加 CNAME(一次性操作):

_acme-challenge.example.com      CNAME  _acme-challenge.challenge-dns.io
_acme-challenge.api.example.com  CNAME  _acme-challenge.challenge-dns.io

第二步:申请证书时使用 --challenge-alias 参数:

export CF_Token="your_cf_token_for_challenge_dns_io"
export CF_Account_ID="your_cf_account_id"

# 申请 example.com 证书
acme.sh --issue --dns dns_cf \
  -d example.com -d *.example.com \
  --challenge-alias challenge-dns.io

# 申请 api.example.com 证书
acme.sh --issue --dns dns_cf \
  -d api.example.com \
  --challenge-alias challenge-dns.io

acme.sh 会将 TXT 记录写入 _acme-challenge.challenge-dns.io,而主域名通过 CNAME 指向该记录,CA 验证时会跟随 CNAME 找到正确的 TXT 值。


9.5 Standalone 独立服务器模式进阶

9.5.1 在 Docker 容器中运行 Standalone

如果在 Docker 容器内运行 acme.sh,使用 Standalone 模式时需要映射端口:

docker run --rm \
  -v /docker/acme.sh:/acme.sh \
  -p 80:80 \
  neilpang/acme.sh --issue \
    --standalone \
    -d example.com

9.5.2 反向代理后的 Standalone 模式

在使用 Nginx 作为反向代理的环境中,将 ACME 验证路径转发到 acme.sh 的 Standalone 服务:

server {
    listen 80;
    server_name example.com;

    # 将 ACME 验证请求转发给本地 8888 端口
    location /.well-known/acme-challenge/ {
        proxy_pass http://127.0.0.1:8888;
    }

    # 其他请求正常处理或重定向
    location / {
        return 301 https://$host$request_uri;
    }
}
# 使用 8888 端口申请证书
acme.sh --issue -d example.com --standalone --httpport 8888

9.6 调试与诊断

9.6.1 启用调试输出

# 基础调试(级别 1)
acme.sh --issue --dns dns_cf -d example.com --debug

# 详细调试(级别 2,输出所有 API 请求和响应)
acme.sh --issue --dns dns_cf -d example.com --debug 2

# 最详细调试(级别 3)
acme.sh --issue --dns dns_cf -d example.com --debug 3

9.6.2 使用测试 CA 验证配置

在生产环境申请之前,先用测试 CA 验证整个流程是否正常:

# 使用 Let's Encrypt 测试服务器(颁发的证书不受信任,但不消耗配额)
acme.sh --issue --dns dns_cf -d example.com -d *.example.com \
  --server letsencrypt_test

# 或使用 --test 参数
acme.sh --issue --dns dns_cf -d example.com \
  --test

9.6.3 常见诊断命令

# 检查证书文件完整性
openssl x509 -in ~/.acme.sh/example.com/fullchain.cer -noout -text

# 验证私钥与证书匹配
# 两个命令输出的 MD5 值应相同
openssl x509 -noout -modulus -in ~/.acme.sh/example.com/example.com.cer | openssl md5
openssl rsa  -noout -modulus -in ~/.acme.sh/example.com/example.com.key | openssl md5

# 检查已安装证书的 SSL 握手
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates

# 检查证书链完整性
openssl verify -CAfile ~/.acme.sh/example.com/ca.cer ~/.acme.sh/example.com/example.com.cer

# 用 curl 测试 HTTPS
curl -vI https://example.com 2>&1 | grep -E "SSL|expire|issuer"

9.6.4 DNS TXT 记录验证

在 DNS API 模式出现问题时,手动验证 DNS 记录:

# 检查 TXT 记录(使用 Google DNS)
dig @8.8.8.8 _acme-challenge.example.com TXT

# 检查 TXT 记录(使用阿里 DNS)
dig @223.5.5.5 _acme-challenge.example.com TXT

# 检查 CNAME(DNS 别名模式)
dig _acme-challenge.example.com CNAME

# 使用 nslookup
nslookup -type=TXT _acme-challenge.example.com 8.8.8.8

9.7 证书格式与转换

9.7.1 PEM 到 PKCS12(PFX)

# 生成 PFX(Windows IIS、Java 应用服务器)
acme.sh --toPkcs -d example.com --password "your_password"

# 生成的文件位置:~/.acme.sh/example.com/example.com.pfx

# ECC 证书转 PFX
acme.sh --toPkcs -d example.com --ecc --password "your_password"

9.7.2 PEM 到 PKCS8(Java 应用)

# 将私钥转换为 PKCS8 格式(某些 Java 应用需要)
openssl pkcs8 -topk8 -inform PEM -outform PEM \
  -in ~/.acme.sh/example.com/example.com.key \
  -out /path/to/privkey-pkcs8.pem \
  -nocrypt

9.7.3 手动 PEM 转 PFX(不使用 acme.sh 命令)

openssl pkcs12 -export \
  -in ~/.acme.sh/example.com/fullchain.cer \
  -inkey ~/.acme.sh/example.com/example.com.key \
  -out /path/to/example.com.pfx \
  -passout pass:"your_password"

9.8 IPv6 服务器支持

对于仅有 IPv6 地址的服务器(IPv6 only):

# Standalone 模式(IPv6)
acme.sh --issue -d example.com --standalone --listen-v6

# 确保服务器有 IPv6 可达性
ping6 acme-v02.api.letsencrypt.org

9.9 证书透明度日志(CT Logs)

acme.sh 申请的证书会自动记录到证书透明度日志(CT Logs)中。这是 CA 颁发证书时的要求,无法关闭。可以通过以下网站查询你域名的所有历史证书:


9.10 非 root 用户运行 acme.sh

acme.sh 设计为无需 root 权限即可运行,这是它的重要安全特性之一。

# 以普通用户安装
su - myuser
curl https://get.acme.sh | sh -s email=myuser@email.com
source ~/.bashrc

# 以普通用户申请证书(使用 DNS API,无需 80 端口权限)
acme.sh --issue --dns dns_cf -d example.com

# 如果需要安装到系统目录,使用 sudo 执行 reloadcmd
acme.sh --install-cert -d example.com \
  --key-file       /home/myuser/ssl/privkey.pem \
  --fullchain-file /home/myuser/ssl/fullchain.pem \
  --reloadcmd      "sudo systemctl reload nginx"

配置 sudoers 允许无密码执行:

# /etc/sudoers.d/acme-nginx
myuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx

9.11 使用自定义 CSR 申请证书

在某些特殊场景(如需要指定 CSR 中的特定字段),可以使用预先生成的 CSR:

# 生成私钥和 CSR
openssl req -new -newkey rsa:2048 -nodes \
  -keyout /path/to/my.key \
  -out /path/to/my.csr \
  -subj "/CN=example.com"

# 使用自定义 CSR 申请证书
acme.sh --signcsr --csr /path/to/my.csr --dns dns_cf

9.12 批量操作技巧

9.12.1 批量申请多个域名的证书

#!/bin/bash
# batch_issue.sh - 批量申请证书

export CF_Token="your_cf_token"
export CF_Account_ID="your_account_id"

DOMAINS=(
    "site1.com *.site1.com"
    "site2.com *.site2.com"
    "site3.com www.site3.com api.site3.com"
)

for domain_group in "${DOMAINS[@]}"; do
    primary=$(echo $domain_group | awk '{print $1}')
    echo "Issuing certificate for: $domain_group"

    # 构建 acme.sh 命令
    CMD="acme.sh --issue --dns dns_cf"
    for domain in $domain_group; do
        CMD="$CMD -d $domain"
    done

    eval "$CMD"
    echo "Done: $primary"
    echo "---"
done

9.12.2 批量更新已有证书的安装路径

#!/bin/bash
# 如果服务器迁移,批量更新证书安装配置

acme.sh --list | tail -n +2 | awk '{print $1}' | while read domain; do
    echo "Reinstalling cert for: $domain"
    acme.sh --install-cert -d "$domain" \
      --key-file       "/etc/nginx/ssl/$domain/privkey.pem" \
      --fullchain-file "/etc/nginx/ssl/$domain/fullchain.pem" \
      --reloadcmd      "systemctl reload nginx"
done

9.13 小结

本章介绍了 acme.sh 的高级功能:

  • ECC 证书--keylength ec-256ec-384,性能更好,推荐现代环境
  • 通配符证书:必须用 DNS-01 验证,使用 -d *.example.com
  • 多域名 SAN:一张证书最多 100 个域名
  • DNS 别名模式:主域名 CNAME 到辅助域名,集中管理验证
  • 调试模式--debug--debug 2 获取详细日志
  • 格式转换--toPkcs 生成 PFX,支持 Windows/Java 环境
  • 非 root 运行:使用 DNS API 模式完全无需 root 权限

下一章将介绍常见问题的诊断和解决方案。