Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>Ma-CertNew to Visual Studio Code? Get it now.
Ma-Cert

Ma-Cert

majj

| (0) | Free
在 VS Code 内一键申请/续期 Let's Encrypt 证书(阿里云 DNS 验证)
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

ma-cert

基于 ACME v2 (RFC 8555) 的 Let's Encrypt 免费证书自动申请脚本,使用 阿里云 DNS (dns-01) 完成域名所有权验证。

  • 零第三方依赖,仅使用 Node.js 内置模块(crypto / https / fs / dns)
  • 支持多域名与泛域名(*.example.com)
  • CSR (PKCS#10 DER) 由脚本手工构造,无需安装 OpenSSL

两种运行方式

方式一:命令行(Node 脚本)

cp .env.example .env   # 填写 ALI_AK / ALI_SK / DOMAINS 等
node certd_acme.js

详见下文「配置说明」。

方式二:VS Code 插件(Ma-Cert)

本项目同时是一个 VS Code 扩展,在编辑器内一键签发/续期,并查看证书状态。

安装 / 调试:

  1. 用 VS Code 打开本目录(作为工作区)
  2. 安装依赖:npm install(仅需 @types/vscode 用于类型提示,运行时无需)
  3. 按 F5 启动「Extension Development Host」新窗口
  4. 在新窗口中按 Ctrl+Shift+P,输入 Ma-Cert 即可看到所有命令

命令: | 命令 | 说明 | |------|------| | Ma-Cert: 申请/续期证书 | 按有效期策略自动跳过或签发 | | Ma-Cert: 强制重新签发 | 忽略有效期检查,强制签发 | | Ma-Cert: 查看证书状态 | 在状态栏/输出面板显示剩余天数与域名 | | Ma-Cert: 打开配置 | 打开 VS Code 设置中的 Ma-Cert 段 |

配置(VS Code 设置,替代 .env): UI 路径:设置 → 扩展 → Ma-Cert,或 settings.json 中:

{
  "maCert.aliAk": "你的AccessKeyId",
  "maCert.aliSk": "你的AccessKeySecret",
  "maCert.domains": ["example.com", "*.example.com"],
  "maCert.email": "you@domain.com",
  "maCert.outDir": "./certs/local",
  "maCert.nginxCertDir": "/etc/nginx/certs",
  "maCert.staging": true,
  "maCert.renewBeforeDays": 15,
  "maCert.skipIfValid": true
}

AK/SK 以明文存于本地 settings.json,请勿在公共机器使用。

打包发布:

npx @vscode/vsce package   # 生成 ma-cert-x.x.x.vsix

目录结构

ma-cert/
├── certd_acme.js          # 命令行入口:参数校验 + 调用 issue()
├── src/                   # 源代码目录
│   ├── extension.js       # VS Code 插件入口(命令/树视图/状态栏)
│   └── lib/
│       ├── config.js      # 配置加载(.env + 环境变量),集中所有常量
│       ├── utils.js       # base64url 编解码、日志、sleep、HTTPS 请求
│       ├── asn1.js        # DER 编码原语(SEQUENCE/SET/INTEGER/BIT STRING...)
│       ├── csr.js         # 域名密钥生成 + PKCS#10 CSR 构造(含 SAN)
│       ├── alidns.js      # 阿里云 DNS API + DNS 传播检测
│       ├── acme.js        # ACME v2 客户端(账户/JWS/订单/finalize)
│       ├── certinfo.js    # 现有证书解析与续期判断
│       └── issue.js       # 签发主流程编排
├── .vscode/launch.json    # F5 调试配置
├── .env                   # 配置(需自行创建,勿提交)
├── .env.example           # 配置示例(可提交)
├── account_prod.key       # 正式环境账户密钥(自动生成,勿提交)
├── account_staging.key    # 测试环境账户密钥(自动生成,勿提交)
└── certs/local/           # 证书输出目录(OUT_DIR)
    ├── 2026-08-06_143012/ # 每次签发独立归档,按时间戳命名
    │   ├── fullchain.pem  # 服务器证书 + 中间链(Nginx 用这个)
    │   ├── privkey.pem    # 域名私钥(权限 0600)
    │   ├── cert.pem       # 仅服务器证书
    │   └── chain.pem      # 仅中间链
    ├── 2026-10-05_030114/ # 下次续期again新建一个目录
    └── latest/            # always 指向最近一次结果,部署脚本引用这里
        └── ...            # 同上 4 个文件

证书输出规则

每次签发会:

  1. 新建 OUT_DIR/yyyy-mm-dd_HHMMSS/(本地时间),写入 4 个 pem 文件
  2. 把同样 4 个文件同步一份到 OUT_DIR/latest/

这样既保留了完整历史版本(便于回滚、排查),又给部署脚本提供了一个固定不变的引用路径 latest/。

模块职责

模块 职责 对外导出
lib/config.js 读取 .env、计算派生配置(ACME 端点、账户密钥路径) config, validate()
lib/utils.js 无业务依赖的纯工具 b64u, unb64u, log, sleep, httpRequest
lib/asn1.js 只负责 DER 字节编码,不含业务语义 derSeq, derSet, derInt, derBitString 等
lib/csr.js 把域名列表变成 {privPem, csrDer} makeDomainKeyAndCsr()
lib/alidns.js TXT 记录增删查 + 公共 DNS 传播确认 addDnsTxt, delDnsTxtAll, waitDnsPropagation
lib/acme.js ACME 协议细节(JWS 签名、nonce、POST-as-GET) Acme 类
lib/issue.js 串联以上模块,实现完整签发流程 issue()

依赖方向是单向的:issue → acme/csr/alidns → config/utils/asn1,无循环依赖。


快速开始

1. 环境要求

  • Node.js >= 14
  • 域名 DNS 托管在阿里云云解析
  • 阿里云 AccessKey,需具备权限:
    • alidns:DescribeDomainRecords
    • alidns:AddDomainRecord
    • alidns:DeleteDomainRecord

2. 创建 .env

在项目根目录创建 .env:

# 阿里云 AccessKey
ALI_AK=你的AccessKeyId
ALI_SK=你的AccessKeySecret

# 需要签发的域名,逗号分隔;泛域名与主域名需分别列出
DOMAINS=example.com,*.example.com

# 注册邮箱(用于接收到期提醒)
EMAIL=you@example.com

# 输出目录,默认 ./certs
OUT_DIR=./certs

# true = 测试环境(staging),false = 正式环境
STAGING=true

# true 时只跑到"添加 DNS 记录"为止,用于验证协议层是否通
DRY_RUN=false

环境变量优先级高于 .env,可用 STAGING=false node certd_acme.js 临时覆盖。

3. 运行

# 第一步:先用测试环境跑通(STAGING=true)
node certd_acme.js

# 第二步:确认无误后,把 .env 里 STAGING 改为 false,再跑正式环境
node certd_acme.js

成功后在 certs/ 下得到 4 个文件。


配置项说明

变量 必填 默认值 说明
ALI_AK 是 — 阿里云 AccessKeyId
ALI_SK 是 — 阿里云 AccessKeySecret
DOMAINS 是 — 逗号分隔的域名列表,支持 *. 泛域名
EMAIL 否 admin@example.com ACME 账户联系邮箱
OUT_DIR 否 ./certs 证书输出根目录,实际写入其下的时间戳子目录 + latest/
STAGING 否 true true 用测试环境,默认是测试环境
DRY_RUN 否 false true 时不真正签发,仅验证协议链路
SKIP_IF_VALID 否 true 现有证书仍有效时跳过签发
RENEW_BEFORE_DAYS 否 15 到期前多少天开始续期
FORCE_RENEW 否 false true 时忽略有效期检查,强制重新签发

续期检查(幂等运行)

脚本在任何网络请求之前会先检查 OUT_DIR/latest/fullchain.pem,满足条件就直接退出。 这让脚本可以安全地被定时任务反复调用,不会浪费 Let's Encrypt 限额。

判定顺序:

条件 结果
FORCE_RENEW=true 签发(忽略后续所有检查)
SKIP_IF_VALID=false 签发
latest/fullchain.pem 不存在或损坏 签发
现有证书未覆盖全部 DOMAINS 签发
剩余天数 <= RENEW_BEFORE_DAYS 签发
剩余天数 > RENEW_BEFORE_DAYS 跳过

跳过时的输出示例:

现有证书: CN=R3
  覆盖域名: example.com, *.example.com
  到期时间: 2026-10-06T12:00:00.000Z(剩余 60 天)
✔ 证书仍然有效(剩余 60 天 > 阈值 15 天),跳过本次签发
  如需强制重新签发,使用 FORCE_RENEW=true 运行

临时强制签发(不改 .env):

# PowerShell
$env:FORCE_RENEW='true'; node certd_acme.js
# Linux / macOS
FORCE_RENEW=true node certd_acme.js

域名覆盖检查是逐字匹配的:若 DOMAINS 从 example.com 改成 example.com,*.example.com,即使旧证书还剩很久,也会自动触发重新签发。


工作流程

0. 检查现有证书有效期,充足则直接退出(见上节)
1. 读取/生成账户密钥  →  account_{prod|staging}.key
2. 获取 ACME directory
3. 注册(或复用)Let's Encrypt 账户
4. 生成域名 RSA 2048 密钥 + 构造 CSR(含 SAN 扩展)
5. 创建订单 newOrder
6. 【阶段一】为每个域名计算 dns-01 值,写入阿里云 TXT 记录
7. 【阶段二】通过公共 DNS(223.5.5.5 / 8.8.8.8 / 1.1.1.1) 轮询确认记录已传播
8. 【阶段三】通知 CA 校验 → 轮询 authorization 直到 valid
9. finalize 提交 CSR → 轮询订单直到 valid 拿到 certificate URL
10. POST-as-GET 下载证书 → 拆分写出 4 个 pem 文件
11. 清理本次添加的 TXT 记录

部署示例(Nginx)

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

    # 引用 latest 目录,续期后无需改配置,reload 即可
    ssl_certificate     /path/to/certs/local/latest/fullchain.pem;
    ssl_certificate_key /path/to/certs/local/latest/privkey.pem;

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

更新证书后需 reload:nginx -s reload


⚠️ 注意事项

一、务必先跑测试环境

STAGING=true 时使用 acme-staging-v02.api.letsencrypt.org:

  • 签出的证书浏览器不信任,只能用于验证流程
  • 限额极宽松(每周 30000 张重复证书),可随意重试
  • 任何脚本改动、新域名首次申请,都应先在 staging 验证通过再切正式环境

二、正式环境限流(Let's Encrypt Rate Limits)

正式环境限流很严格,触发后需等待才能恢复:

限制项 额度
同一组域名重复证书 5 张 / 周
每个注册域名签发证书 50 张 / 周
每个 IP 新建账户 10 个 / 3 小时
每个账户新建订单 300 个 / 3 小时
每个账户失败验证 5 次 / 小时 / 域名

最容易踩的坑:反复调试正式环境,一周内跑满 5 次就被锁到下周。所以调试一律用 staging。

三、账户密钥必须妥善保存

  • account_prod.key / account_staging.key 由脚本自动生成并复用
  • 删除后会重新注册新账户,可能触发「每 IP 每 3 小时 10 个账户」限制
  • 文件权限已设为 0600,绝不能提交到 Git

四、.gitignore 必须包含

.env
account_*.key
certs/
*.pem
*.key

私钥一旦泄漏,证书即失去意义,必须立即吊销并重签。

五、DNS 相关

  • 脚本会先清空 _acme-challenge 下的历史 TXT 记录(每个根域只清一次),若你手工加过同名 TXT 记录会被删除
  • example.com 与 *.example.com 的挑战记录同名(都是 _acme-challenge.example.com),必须共存两条 TXT,脚本已处理,不要手工干预
  • DNS 传播等待上限 180 秒,超时会继续尝试通知 CA;若你的 DNS TTL 很长,可能需要调大
  • 签发完成或失败时脚本会自动清理本次添加的 TXT 记录

六、证书有效期与续期

  • Let's Encrypt 证书有效期 90 天
  • 建议到期前 30 天续期(即每 60 天跑一次)
  • 每次运行都会生成新的域名私钥,且写入新的时间戳目录;latest/ 会同步更新,Nginx 引用 latest/ 时只需 reload
  • 历史目录会不断累积,可定期清理旧的(保留最近 2~3 次即可)

由于开启 SKIP_IF_VALID 后脚本是幂等的(有效期充足会自动跳过), 可以放心地每天跑一次,到期前自动续期,无需精确计算周期。

Linux 下用 crontab(每天凌晨 3 点检查):

0 3 * * * cd /path/to/ma-cert && /usr/bin/node certd_acme.js >> /var/log/ma-cert.log 2>&1

续期成功后需 reload Nginx,可追加:

0 3 * * * cd /path/to/ma-cert && /usr/bin/node certd_acme.js >> /var/log/ma-cert.log 2>&1 && nginx -t && nginx -s reload

Windows 可用「任务计划程序」创建每日任务。

七、其他

  • Let's Encrypt 没有网页控制台,账户由密钥标识,无用户名密码
  • 想查看已签发的证书,可到 crt.sh 搜索你的域名
  • 泛域名 *.example.com 不覆盖裸域名 example.com,两者都需要时必须同时写进 DOMAINS
  • 泛域名只能用 dns-01 验证,不支持 http-01

常见错误排查

报错 原因 处理
缺少 DOMAINS 环境变量 .env 未配置或未被读取 确认 .env 与脚本同目录
阿里云 DNS 调用 ... 失败 AK/SK 错误或权限不足 检查 RAM 用户的 alidns 权限
DNS 传播等待超时 TTL 过长 / 解析未生效 手工 nslookup -type=TXT _acme-challenge.example.com 确认
Incorrect TXT record TXT 值不匹配 确认没有残留旧记录干扰
校验失败: ... 5 failed authorizations 触发失败验证限流 等 1 小时后重试,或先用 staging 调试
too many certificates already issued 触发重复证书限流 等到下周,或改用 staging
finalize 失败: 400 ... malformed CSR 结构问题 脚本已修复;如自行改过 CSR 代码需用 OpenSSL 校验

许可

内部自用脚本,按需自取。

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft