# TLS 证书简介

TLS 证书在网站用户之间建立信任。Web 服务器上安装 TLS 证书,可以创建受 TLS 保护的网站。受 TLS 保护的网页的特征如下:

  • Web 浏览器中的挂锁图标和绿色地址栏
  • 浏览器网站地址中的 https 前缀
  • 有效的 TLS 证书。您可以通过单击并展开 URL 地址栏中的挂锁图标来检查 TLS 证书是否有效
  • 建立加密连接后,只有客户端和对应 Web 服务器才能解析数据。

# TLS 证书的工作原理

  • 握手阶段
    • 客户端尝试连接到受 TLS 证书保护的服务器。
    • 服务器将自身的证书发送给客户端。
    • 客户端验证证书的有效性,包括检查证书是否由受信任的证书颁发机构 (CA) 颁发、证书是否过期以及证书上的域名是否与服务器的域名匹配。
  • 生成会话密钥
    • 如果证书验证通过,客户端会生成一个对称密钥,并使用服务器证书中的公钥对其进行加密,然后发送给服务器。
    • 服务器使用其私钥解密客户端发送的密钥,从而获得对称密钥。
  • 加密通信
    • 客户端和服务器使用该对称密钥对后续通信进行加密和解密。

# TLS 证书颁发途径

# 受信任的证书颁发机构(CA)

这些机构如 Let's Encrypt、DigiCert、GlobalSign 等,通过验证域名所有权或组织身份来颁发证书。受信任的 CA 颁发的证书在主流浏览器和操作系统中都被认可,确保用户访问时不会出现警告信息。

# 自签名证书

自签名证书是由服务器自己生成的证书,不经过 CA 的验证。这种证书适合内部测试和开发环境,但不推荐用于生产环境,因为它不会被主流浏览器和操作系统信任。

与 CA 颁发的证书相比,自签名证书的主要区别在于:

  • 颁发者不同: 普通证书由受信任的第三方机构(CA)颁发,而自签名证书由证书的所有者自己创建和签名。
  • 信任度不同: 浏览器和操作系统默认信任由知名 CA 颁发的证书,而对自签名证书则不信任,会发出安全警告。

自签名证书由于缺乏第三方认证,浏览器不信任,容易遭受中间人攻击,无法验证网站身份,且影响 SEO。这些安全隐患和用户体验问题使得自签名证书并不适合用于保护网站。相比之下,由受信任的证书颁发机构签发的 SSL 证书能够提供更强的安全性保障和更好的用户体验。

# acme.sh 简介

为了避免自签名证书带来的额外风险,可以使用 acme.sh 申请受信任的 TLS 证书。
acme.sh 是一款功能强大且易于使用的 ACME 协议客户端,专门用于申请和管理第三方机构 Let's Encrypt 颁发的免费 TLS 证书。它以其简洁、高效和高度自动化等特点,成为了广大用户申请 TLS 证书的首选工具之一。

# 使用 acme.sh

# 安装并设置其自动更新

首先使用自动化脚本安装 acme.sh 并使命令生效。
最后通过指令设置其为自动更新。

h
wget -O -  https://get.acme.sh | sh
. .bashrc
acme.sh --upgrade --auto-upgrade

# 测试证书申请

在正式申请证书之前,我们先用测试命令 (--issue --test) 来验证指令配置是否正确,防止反复申请证书失败,超过 Let's Encrypt 的验证频率上限。
在操作过程中,需要将指令中的 Your.Domain.Here 替换为域名,并将路径 /Path/to/Webpage/Root/Dir 替换为网站的根目录。

本文将采用 ECC 证书,相较于 RSA 证书,其在相同长度下的性能更佳。
但如需兼容部分古董设备,请依旧考虑 RSA 证书。

h
acme.sh --issue --server letsencrypt --test \
    -d Your.Domain.Here \
    -w /Path/to/Webpage/Root/Dir \
    --keylength ec-256

这里以 CF 的 DNS API 为例申请通配符证书,下同,请逐一对应。
其他服务商的验证方式可见官方提供的 API 详细用法

h
export CF_Token="Your CF Token"
export CF_Zone_ID="Your Zone ID"
export CF_Account_ID="Your Account ID"
acme.sh --issue --server letsencrypt --test --dns dns_cf \
    -d Your.Domain.Here \
    -d '*.Your.Domain.Here' \
    --keylength ec-256

在验证成功后,命令行将返回如下内容,表示已经成功申请的测试证书,之后就可以执行正式 TLS 的申请了。

h
Your cert is in  ~/.acme.sh/Your.Domain.Here_ecc/Your.Domain.Here.cer
Your cert key is in  ~/.acme.sh/Your.Domain.Here_ecc/Your.Domain.Here.key
The intermediate CA cert is in  ~/.acme.sh/Your.Domain.Here_ecc/ca.cer
And the full chain certs is there:  ~/.acme.sh/Your.Domain.Here_ecc/fullchain.cer

# 正式证书申请

# 指定证书申请服务器

将 Let's Encrypt 指定为证书申请的服务器。

h
acme.sh --set-default-ca --server letsencrypt

# 更新正式证书

采用指令将测试证书更新为正式证书。
其中 --force 指令代表强制执行证书更新。因为测试版证书刚刚申请还未到期,若不添加该指令,证书将不会被更新。

h
acme.sh --issue -d Your.Domain.Here \
    -w /Path/to/Webpage/Root/Dir \
    --keylength ec-256 \
    --force
h
export CF_Token="Your CF Token"
export CF_Zone_ID="Your Zone ID"
export CF_Account_ID="Your Account ID"
acme.sh --issue --dns dns_cf \
    -d Your.Domain.Here \
    -d '*.Your.Domain.Here' \
    --keylength ec-256 \
    --force

# 安装证书并配置 Reload 指令

该指令中 --ecc 表示安装的是 ecc 证书。
/Path/to/Install/Cert/ 需要被替换为安装证书文件的目录,以便后续配置用。
/Path/to/ReloadCmd/Reload.sh 需要配置更新证书后执行的指令,如常规的 service nginx force-reload 等,请确保正在执行该指令的用户有权限执行 Reload.sh 中的内容。如果需要执行的指令只有一条,可以将 bash /Path/to/ReloadCmd/Reload.sh 替换为对应的指令。

执行完该指令后,每个域名的证书会每隔一段时间自动更新,并重新执行最后一次安装证书的命令。

h
acme.sh --installcert -d Your.Domain.Here --ecc \
    --cert-file /Path/to/Install/Cert/cert.crt \
    --key-file /Path/to/Install/Cert/cert.key \
    --fullchain-file /Path/to/Install/Cert/fullchain.crt \
    --reloadcmd "bash /Path/to/ReloadCmd/Reload.sh"
h
acme.sh --installcert --ecc \
    -d Your.Domain.Here \
    -d '*.Your.Domain.Here' \
    --cert-file /Path/to/Install/Cert/cert.crt \
    --key-file /Path/to/Install/Cert/cert.key \
    --fullchain-file /Path/to/Install/Cert/fullchain.crt \
    --reloadcmd "bash /Path/to/ReloadCmd/Reload.sh"

# 移除证书

移除自动更新的证书,并移除保存证书的对应文件夹。

h
acme.sh --remove Your.Domain.Here #事实上这一步可以忽略
rm -rf ~/.acme.sh/Your.Domain.Here_ecc/

# 卸载 acme.sh

可以通过如下指令完全卸载 acme.sh ,如果之前通过直接删除文件夹方式尝试过卸载可以先安装一遍再执行。

h
acme.sh --uninstall
rm -rf ~/.acme.sh/

# 参考文献

什么是 SSL 认证?- SSL/TLS 认证简介 - AWS
acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol | Github.com