
简介win-acme.v2.1.18-x64 是一款面向 Windows 站点管理员、个人站长与小型项目运维者的免费 SSL 证书自动化工具重点解决 Lets Encrypt 证书申请、安装与续订流程繁琐的问题。压缩包共 27 个文件、约 29.58MB包含 15 个 ps1 脚本、4 个 dll 运行库、2 个 json 配置文件、2 个 bat 批处理文件以及 exe 主程序、xml 配置模板、dat 公共后缀列表和 txt 版本说明等兼顾图形化操作与命令行脚本自动化。工具可自动完成域名所有权验证、证书签发、IIS 等 Web 服务器部署及到期续订并支持多域名与通配符证书管理配合日志与报告便于排查验证失败、续订异常等问题对 HTTP-01 与 DNS-01 验证、证书计划任务和状态检查也有相应支持。已有 334 人学习下载适合希望以较低成本为站点启用 HTTPS、减少手工维护证书负担并借助脚本与配置样例搭建自动化续期流程的读者参考使用。1. 免费域名搜进来先说清楚 win-acme 到底给什么搜「免费域名」的人多半会被 win-acme 这个名字带偏它不发域名发的是 90 天一轮的免费 TLS 证书把 HTTPS 那笔每年几千块的开销直接抹掉。v2.1.18 的 x64 自包含包解开就是一个wacs.exe不需要预装 .NET 运行时Windows Server 2012 R2 以上丢上去双击就能跑。它管的是证书从签发、验证、部署到续期的整条链路对接 ACME 协议、跑 HTTP-01 或 DNS-01 验证、把结果写进 IIS 或 PEM/PFX 文件、再自己挂一个计划任务做无人值守续期。适合自己维护一两台 Windows 机器的运维、给内网测试站配证书的后端以及不想天天敲 OpenSSL 的人。它不管域名本身也不替你做解析这两件事必须先自己搞定。2. 装到签发第一张证书x64 自包含包的启动姿势与向导映射2.1 解压后的目录结构为什么优先选自包含包这个包最大的价值是「零依赖启动」。解压出来你会看到wacs.exe加一批 dll、一个web子目录和若干插件目录全部自包含机器上没装 .NET 运行时也能直接跑。同一版本系列里还有依赖框架的变体体积小一些但换来的代价是每台目标机器都得先补运行时一旦版本对不上双击就是一句「缺少运行时」的弹窗连菜单都进不去。生产环境里我一般无条件选自包含版硬盘多占几十兆换的是排障时少一个变量。解压路径也有讲究别放桌面或者带中文的目录放到C:\tools\win-acme这类纯英文短路径下因为后面计划任务的工作目录会引用这个位置路径里带空格或者中文很容易在任务计划里被截断。第一次运行必须以管理员身份开命令行不是双击 exe——双击虽然也能进交互菜单但后续写证书存储、改 IIS 绑定都需要提权中途失败会让你以为证书签发失败其实是权限不够。先做一件事确认这版支持哪些参数别照抄三年前的教程。# 管理员身份打开 cmd进到解压目录 cd /d C:\tools\win-acme # 打印完整参数列表重点看 target / validation / store / installation 四段 wacs.exe --help--help的输出很长但只需要盯四组参数--target决定主机名从哪来手动输入还是扫 IIS--validation决定用哪种验证方式--store决定证书存成什么格式--installation决定要不要顺手装进 IIS。这四个决定了后面所有命令的形状。不同小版本之间参数名会有微调比如早期版本某些开关叫法不一样抄旧命令最常见的报错就是「未知参数」而这条命令能在三十秒内告诉你正确答案。养成习惯换一台机器、换一个版本先跑一次--help比出问题再回头查强得多。2.2 交互向导走一遍从主菜单到证书落地第一次用建议走交互模式把流程在脑子里建立一遍映像之后再用命令行固化。运行wacs.exe管理员 cmd 里主菜单是几个字母键新建证书、续期、管理续期、列出已有续期。选新建之后向导会依次问你五件事这五件事和后面命令行的参数是一一对应的理解了这层映射参数就不用背了。第一步问主机名输入www.example.com多个域名用逗号分隔注意这里填的必须是能通过公网解析到本机的域名填错后面验证必然失败。第二步问验证方式列表里会同时出现文件系统验证、自托管验证、DNS 手动验证、DNS 脚本验证等选项每一项前面标着[http-01]、[dns-01]或[tls-alpn-01]这就是协议类型。第三步问证书存到哪可选 PEM 文件、PFX 归档、Windows 证书存储选完还会追问具体路径或存储位置。第四步问要不要安装到 IIS如果这台机器上有 IIS 站点选安装会自动更新绑定。第五步是接受协议条款和填邮箱邮箱必须真实可用证书快到期时 CA 会往这个地址发提醒很多「证书突然过期」的事故就是当初随手填了个不存在的邮箱。向导跑完屏幕上会打印一段汇总包含证书路径、指纹、到期时间和下次续期时间。这一段务必截图或者复制到记事本因为它是后面写脚本、核对绑定关系的依据。整个过程中最容易出错的是第二步和第三步的路径验证路径填的必须是网站真实根目录证书输出路径必须是空目录或者你有写权限的目录两者填反了会得到两种完全不同的报错前者是验证 403后者是写文件失败。2.3 用命令行把向导固化一条能重复执行的签发命令交互模式适合第一次第二次开始就该上命令行了。命令行最大的好处是可复现换机器、换域名改几个变量就能重跑而且能直接塞进自动化脚本里。下面这条是我常用的最小可用组合做的是「手动指定域名 文件系统验证 输出 PEM」wacs.exe ^ --target manual ^ --host www.example.com ^ --validation filesystem ^ --webroot C:\inetpub\wwwroot ^ --store pemfiles ^ --pemfilespath C:\certs\www.example.com ^ --accepttos ^ --emailaddress opsexample.com ^ --verbose逐段说明这几个参数在干什么以及改哪里参数作用改动时的注意点--target manual域名从参数里读不扫 IIS想让它自动发现 IIS 站点改成--target iis--host要签的域名逗号分隔多个通配符写*.example.com且必须配 DNS 验证--validation filesystem把验证文件写进网站根目录需要--webroot配套路径必须是站点真实根--webroot验证文件的落地目录写错就是 403用浏览器能访问到该目录再继续--store pemfiles输出 PEM 格式证书需要--pemfilespath目录不存在会自动建--pemfilespathPEM 输出目录建议一域名一目录便于脚本按目录 reload--accepttos自动接受 CA 条款不加会卡在交互确认自动化场景必加--emailaddress到期提醒邮箱别填 noreply 之类的假地址--verbose打印详细过程首次签发必开出问题就看它这条命令跑完最快十几秒就能出结果。如果第一次就失败把--verbose的输出从头看到尾它会明确告诉你卡在哪一步是 DNS 解析不到、是 80 端口连不上、还是文件写不进去。这比在浏览器里反复刷新猜原因高效得多。还有一点调试阶段不要拿生产环境反复试ACME 服务端对同一组域名有每周签发次数限制反复失败重试很容易把自己锁住正确做法是把 baseuri 指向 CA 提供的测试目录先跑通再切回来。测试证书浏览器不认这是正常的它只用来验证流程通不通。3. 验证方式怎么选HTTP-01、DNS-01 与产物落地位置3.1 文件系统验证和自托管验证差在一个 80 端口两种方式都属于 HTTP-01本质都是让 CA 通过http://域名/.well-known/acme-challenge/随机串访问到一个指定内容。差别在于谁来提供这个 HTTP 服务。文件系统验证是「借」你已有的 web 服务器win-acme 把验证文件写进网站根目录的.well-known/acme-challenge/下然后由你现有的 IIS 或 nginx 把它吐出去。这条路适合已经有站点在跑、80 端口本来就被占用的机器缺点是对 web 服务器的路由配置有依赖最常见的失败原因是站点做了全站 301 跳 HTTPS或者 nginx 里写了try_files把所有请求都转给后端应用导致.well-known这个路径根本落不到真实文件上。自托管验证是 win-acme 自己临时起一个极简 web 服务器抢占 80 端口验完就关。它适合这台机器上根本没有 web 服务、或者 web 服务不在这台机器上的场景。代价是运行期间 80 端口必须是空闲的如果 IIS 正在跑就会直接报端口占用。判断方法很简单netstat -ano | findstr :80看有没有进程在监听有就说明自托管模式走不通乖乖用文件系统模式或者先停服务再签。下面这段是 nginx 环境下最容易踩的配置坑放行规则要写在跳转规则之前server { listen 80; server_name www.example.com; # 验证路径必须优先匹配且不能跳转、不能交给后端 location ^~ /.well-known/acme-challenge/ { root /var/www/html; default_type text/plain; try_files $uri 404; } # 其余请求才走 HTTPS 跳转 location / { return 301 https://$host$request_uri; } }^~前缀匹配的优先级高于正则能保证验证请求不被后面的 rewrite 规则截走try_files $uri 404是为了在文件真的不存在时返回 404 而不是被兜底到首页这样出问题时你能立刻从状态码判断是「文件没写进来」还是「路由被拦了」。改完 nginx 一定要nginx -t再 reload配置语法错误会导致整个站点挂掉那时候你面对的就是双重故障。IIS 那边同理检查 URL 重写模块里有没有全站规则有的话给.well-known加一条排除。3.2 DNS-01 与通配符手动模式和脚本钩子的边界通配符证书*.example.com只能走 DNS-01这是硬约束因为 HTTP-01 无法证明你控制所有子域。DNS-01 的原理是 CA 让你在_acme-challenge.example.com加一条指定内容的 TXT 记录它去权威 DNS 查询这条记录查到就算通过。手动模式下win-acme 会把该加的记录名和记录值打印在屏幕上你复制到 DNS 控制台等生效后回车继续签完再手动删掉。这条路适合一年只签一次、域名数量个位数的场景虽然笨但绝不会因为 API 权限出问题而失败。脚本模式是把「加记录 / 删记录」两个动作交给脚本win-acme 在验证前后各调一次。真正的坑不在脚本本身而在 DNS 传播延迟记录提交成功不代表权威 DNS 立刻对外可见客户端如果查得太早会拿到 NXDOMAIN 直接判失败。稳妥做法是在脚本里加主动探测用公共 DNS 反复查这条 TXT 直到查到为止再返回给 win-acme而不是靠固定 sleep 硬等。另一个坑是 TXT 记录同名多条同一主域下同时签*.example.com和example.com时两条验证会往同一个记录名写不同值如果脚本是「覆盖写」而不是「追加写」第二条必然覆盖第一条导致验证失败。脚本里必须实现「读现有值 → 追加 → 验证完删掉自己那条」的逻辑。提示脚本模式的参数名在不同小版本间有过调整写脚本之前先跑wacs.exe --help找到 validation 段的实际写法别直接套网上的旧示例。3.3 产物清单PEM、PFX、证书存储分别喂给谁同一个证书不同消费者要不同格式这一步选错后面会得到「证书明明签下来了服务就是不认」的玄学现象。下面这张表是我这几年反复对照过的对应关系存储类型落地产物典型消费者备注PEM 文件fullchain.pem、privkey.pem、chain.pemnginx、Apache、Go 服务私钥权限要收紧Linux 侧 600PFX 归档.pfx单文件IIS 导入、Tomcat、Java keystore需要设密码密码要能传给部署脚本Windows 证书存储本机「个人」或「Web 托管」存储IIS 绑定、SMTP、远程桌面私钥 ACL 丢了会报密钥集不存在IIS 安装中央证书存储 站点绑定直接对外的 IIS 站点会自动改绑定签之前先备份配置PEM 那一行要特别说一句fullchain.pem是「站点证书 中间证书」拼起来的完整链chain.pem只有中间证书privkey.pem是私钥。nginx 的ssl_certificate要指向 fullchain 而不是单独的站点证书只配站点证书会出现在部分客户端上「证书链不完整」的警告桌面浏览器因为会自动补链所以看不出来手机和部分 API 客户端就直接报错了。这个问题藏得很深属于典型的「本机测试一切正常、上线被投诉」类型。证书存储那条路的坑在私钥权限。证书导进「Web 托管」存储后IIS 应用池账户需要能读到私钥如果这台机器是从别的机器迁移过来的、或者你手工用 certutil 导入过私钥 ACL 很容易丢症状是绑定站点时报0x80070520。遇到这个别急着删证书重来先用certutil -store WebHosting看证书是否在、私钥是否标记为可导出再检查私钥文件的 ACL。4. 自动续期闭环计划任务、钩子脚本与日志三件套4.1 续期任务是怎么被写进任务计划的证书只有 90 天能不能自动续上决定了这套方案是省事还是添乱。win-acme 在创建续期条目时默认会往 Windows 任务计划里注册一个名字带win-acme renew的任务触发器设成每天跑一次实际执行时它自己判断「离到期还有多久」没到续期窗口就直接退出所以天天跑并不会真的天天签发。这个设计比「算好日期定一个 60 天后的单次任务」稳得多因为机器可能关机、可能休眠、可能临时没网单次任务错过就是错过每日轮询则有很多次补救机会。注册任务时会问你用哪个账户运行这一步别随手选当前登录用户。选当前用户意味着任务依赖这个账户的密码密码一改、账户一禁用任务就静默失败而且失败得很安静——任务计划里只显示一个错误码没有任何弹窗。我一般选 SYSTEM它不受登录状态和密码策略影响缺点是脚本里如果用到了用户级的环境变量或者映射网络驱动器会读不到。如果确实需要访问网络路径就用一个有固定密码的服务账户并且把「不管用户是否登录都运行」勾上。:: 列出当前所有续期条目看下一次到期时间和上次执行结果 wacs.exe --list :: 强制立刻走一次续期流程用于验证钩子脚本是否生效 wacs.exe --renew --force :: 测试阶段不想让它自动注册计划任务加上这个开关 wacs.exe --renew --notaskscheduler--list这条命令的价值被严重低估。它输出的不只是域名还有每条续期的 ID、绑定的验证方式、证书路径和下次续期时间出问题时先跑它能立刻区分是「续期条目压根没建」还是「条目在但执行失败」。--renew --force是调试利器但它会真的去 CA 申请新证书所以在生产域名上别连着跑——一周内重复签发同一组域名是有次数上限的超了就得等窗口重置。想反复测流程就把 baseuri 指到测试目录随便折腾。注意--force绕过的是「是否在续期窗口内」的判断不是绕过 CA 的限制。调试和生产的边界要在这里划清楚。4.2 续期后的钩子导出 PEM 并让服务热加载证书续下来只是第一步让正在跑的服务用上新证书才是闭环。win-acme 支持在续期成功后执行一个脚本把续期 ID、证书指纹之类的信息作为参数传进去脚本里根据这些信息做后续动作。典型场景是win-acme 只负责把新 PEM 写到固定目录脚本负责把文件同步到 nginx 目录并 reload。# reload-nginx.ps1 # 由 win-acme 在续期成功后调用参数由 --scriptparameters 传入 param( [string]$RenewalId, [string]$Thumbprint ) $log C:\tools\win-acme\hooks\reload.log $(Get-Date -Format s) renewal$RenewalId thumbprint$Thumbprint | Out-File $log -Append # 先做语法检查检查不过就不 reload避免把站点搞挂 C:\nginx\nginx.exe -t if ($LASTEXITCODE -ne 0) { nginx config test failed, skip reload | Out-File $log -Append exit 1 } # 平滑重载不中断现有连接 C:\nginx\nginx.exe -s reload reload done, exit$LASTEXITCODE | Out-File $log -Append这段脚本的关键设计有三点。第一它自己写日志因为 win-acme 的日志只记录到「脚本已执行」脚本内部的成败它看不到没有独立日志你就只能靠猜。第二reload 之前先nginx -t配置有问题时宁可继续用旧证书也不要让服务起不来这个顺序反了就是血泪经验。第三用-s reload而不是重启服务reload 是平滑的重启会掐断正在处理的请求。参数$RenewalId和$Thumbprint来自 win-acme 的占位符替换具体可用哪些占位符以--help里 script 段的说明为准不同版本支持的不完全一样写之前对一遍别想当然。4.3 用日志和状态文件判断这次续期到底成没成续期失败最怕的是「静默」计划任务照常跑日志里没有明显红色报错等到某天浏览器弹证书过期才发现已经断了两个月。所以要有固定的巡检动作。日志落在程序数据目录下路径形如C:\ProgramData\win-acme\acme-v02.api.letsencrypt.org\Log文件名按日期组织一次续期一个文件。判断成败不要只看最后一行要看有没有出现明确的成功字样和证书指纹中间出现的重试记录不算失败只有最终没有拿到新证书才算。除了日志续期条目的配置文件也值得看一眼。每个续期条目在Renewals子目录下有一份自己的配置文件记录了域名、验证方式、存储路径、上次执行时间。这份文件是「当前生效配置」的唯一真相改了它等于改了续期行为所以我一般先备份再动。有些场景下你会需要手工改它比如换了 webroot 路径、换了输出目录与其删掉重建不如直接改配置里的路径字段保留原来的续期 ID 和计划任务关联。巡检脚本可以很简单每天读一次最新日志找关键字没有成功标记就发告警。真正麻烦的不是写脚本而是坚持看。我见过太多「配好就不管」的案例包括我自己早期配完三个月后证书过期排查半天才发现是某次系统更新后计划任务的运行账户被改成了需要交互登录。从那以后我给自己定了规矩任何自动续期上线后第二周必须人为跑一次--list确认状态之后再按月抽查。5. 五个翻车现场从 403 到 0x80070520 的排查顺序5.1 验证返回 403 或 Invalid response现象命令跑完报验证失败提示从http://域名/.well-known/acme-challenge/xxx取文件失败返回 403 或 404。原因基本只有三类webroot 路径填的不是站点真实根目录站点有全站跳转或重写规则把.well-known拦截了文件写进去了但 web 服务器进程没有读权限。解决顺序是先用浏览器或curl -I直接访问那个路径看返回什么状态码403 就是权限或规则问题404 就是路径问题。确认路径无误后检查 nginx 的location顺序和 IIS 的 URL 重写规则把.well-known的放行规则提到跳转规则之前。5.2 自托管模式报端口占用现象选自托管验证后立刻失败日志里出现地址已被占用的字样。原因很直接80 端口上已经有别的进程在监听最常见的是 IIS 的默认站点其次是某些系统服务。排查用netstat -ano | findstr :80拿到 PID再tasklist | findstr PID对上是哪个进程。解决方式不是硬停服务而是换验证方式——既然 80 端口已经被 web 服务器占着直接改用文件系统验证把验证文件写进那个站点的根目录反而更省事。硬停 IIS 再签签完还得启回来中间站点是断的。5.3 IIS 绑定更新了但浏览器还是旧证书现象证书续期成功路径里的文件时间也是新的但浏览器访问还是显示旧证书或者证书链不完整。原因通常是 IIS 站点绑定还指向旧证书的指纹或者用了中央证书存储但站点没有正确引用。先用netsh http show sslcert看 443 端口当前绑定的指纹和--list输出的新指纹比对不一致就是绑定没更新。再检查 IIS 站点绑定的 SNI 设置多站点共用 443 时靠 SNI 区分SNI 没开会导致所有域名都拿到同一个证书。改完绑定记得iisreset或者回收应用池某些情况下 IIS 会缓存证书句柄。5.4 计划任务天天跑但证书始终不续现象任务计划里能看到任务上次运行结果是一个非零的错误码证书到期时间纹丝不动。原因有三个高频项任务运行账户的密码过期或被改任务被设成「仅在用户登录时运行」而服务器没人登录机器在续期窗口期长期断网。排查先看任务的「上次运行结果」和「历史记录」再手动用同一账户执行一次--renew看报错。解决方式是把运行账户改成 SYSTEM勾选「不管用户是否登录都运行」并在脚本里加网络可达性检查不通时提前退出并写日志而不是等到验证阶段才失败。5.5 报 too many certificates 被限流现象调试阶段反复重试突然所有签发都失败提示同一组域名的证书签发次数超限。原因是公共 CA 对「同一组域名集合」的单位时间签发数量有上限--force反复跑正好踩中。这个坑没有后悔药只能等窗口重置通常是一周。正确的应对是提前把调试流量导到测试目录签发命令里把 baseuri 指向 CA 提供的测试入口测试证书不被浏览器信任但流程完全一致等流程确认无误再切回生产目录跑一次。这个习惯我从第一次被限流之后就固定下来了此后再没在生产目录上做过任何调试。6. 多站点批量把续期配置当代码管起来机器上站点一多一条条敲命令就不现实了。批量模式的核心思路是先用一条命令把多个域名塞进同一个证书再用--list和续期配置文件统一管理而不是为每个域名建一个计划任务。6.1 多域名合并与配置复用同一个站点的主域、www、几个子域完全可以签进同一张证书--host支持逗号分隔多个域名验证只需做一次续期窗口也只有一个管理成本直接降一个量级。但要注意证书里的域名数量不是越多越好一个域名验证失败会导致整张证书签发失败所以「按业务分组、每组一张证书」比「全站一张大证书」更稳。:: 三个域名合并一张证书输出到按主域命名的目录 wacs.exe ^ --target manual ^ --host example.com,www.example.com,api.example.com ^ --validation filesystem ^ --webroot C:\inetpub\wwwroot ^ --store pemfiles ^ --pemfilespath C:\certs\example.com ^ --accepttos ^ --emailaddress opsexample.com这条命令生成的续期条目会作为一个整体存在续期时三个域名一起验、一起签、一起更新文件。改配置时改的也是这一个条目不用满机器找散落的计划任务。如果确实需要通配符把--host换成*.example.com,example.com同时把验证方式换成 DNS-01因为通配符不支持 HTTP 验证。6.2 用一条命令验收证书链签完之后别只信日志直接验证证书本身。Windows 上可以用 OpenSSL也可以用 PowerShell 的Get-PfxCertificate但我更习惯用 OpenSSL 看完整的链和有效期信息最全# 连上目标站点打印证书链和有效期 openssl s_client -connect www.example.com:443 -servername www.example.com /dev/null 2/dev/null \ | openssl x509 -noout -subject -issuer -dates -fingerprint -sha256输出里重点看三项subject里的域名是否和预期一致、dates的到期时间是否是刚续期后的新日期、issuer是否是中间证书而不是根证书。如果s_client输出里出现verify error: unable to get local issuer certificate说明服务端没把中间证书一起发出来回到 3.3 节检查是不是只配了站点证书没配 fullchain。这条命令我一般放在续期脚本的最后一步自动跑一遍输出写进日志比事后靠用户投诉发现要早得多。证书这东西配好那天是最可靠的之后可靠性只会一路下降。我现在的习惯是每次动过 web 服务器配置、改过防火墙、换过 DNS 服务商都强制跑一遍--list加一次 openssl 验证确认链完整、日期正确、续期条目还在。多花三分钟省掉的是某天早上被电话叫醒。这份 x64 自包含包解压即用配置、日志、续期条目全部落在 ProgramData 下按照第 2 章的向导走一遍就能出第一张证书先把单域名的闭环跑通再往上叠批量和钩子希望帮到你。本文还有配套的精品资源点击获取