简介:win-acme.v2.1.18-x64 是一款面向 Windows 平台网站运维者与个人站长的免费 SSL 证书申请与管理工具,基于 ACME 协议对接 Let's Encrypt,帮助用户在不付费的前提下完成证书申请、安装、自动续订与多域名(含通配符)管理,适合希望低成本启用 HTTPS 的中小项目与入门运维人员。资源包共 27 个文件,以 15 个 ps1 脚本、4 个 dll 运行库、2 个 json 配置、2 个 bat 批处理为主,另含 exe 主程序、xml 配置与 dat 数据文件,压缩包约 29.58MB,脚本与配置分离,便于按需调用与二次调整。目前已有 336 人学习下载。借助其中的命令行与图形化组件,读者可快速搭建证书自动化流程,理解 HTTP/DNS 验证、续期调度与日志排查思路,减少证书过期带来的安全隐患。
1. 免费域名证书的自动化续签:win-acme 到底能省掉多少手工活
很多团队把 HTTPS 证书当成一次性任务:上线前手动申请一张,导进 IIS,配好绑定,然后就把这事忘了。三个月后浏览器开始报红,运维半夜被叫起来重新走一遍 CSR、验证、导出 PFX 的流程。win-acme 就是冲着这个场景来的——它是一个跑在 Windows 上的 ACME 客户端,用命令行或交互式向导完成证书申请、安装到 IIS、并注册计划任务自动续期。标题里的 v2.1.18-x64 是它的一个 x64 发行版本,解压即用,不依赖额外运行时。适合谁?手上有一批 Windows Server、IIS 站点多、又不想为每张证书单独付费或手工维护的运维和全栈开发者。它解决的不是“证书怎么加密”这种理论问题,而是“几十个站点怎么在过期前自动换掉”这种落地问题。
2. 先搞懂 ACME 与验证方式:为什么它能在 Windows 上自动签发
2.1 ACME 协议在 win-acme 里的落地路径
ACME 是一套“客户端向证书颁发机构证明域名控制权、然后换取证书”的协议。win-acme 把这条链路拆成几个可配置的环节:账户注册(生成或复用账户密钥)、订单创建(提交要签发的域名列表)、挑战应答(证明你控制这个域名)、证书下载(拿到 PEM/DER 再转成 Windows 能用的 PFX)、安装(绑定到 IIS 站点或写入指定路径)、续期(注册计划任务定期重跑)。理解这条链路的意义在于:出问题时你能定位到是哪一环断了,而不是对着一个“申请失败”干瞪眼。
账户密钥默认落在程序目录下的一个存储文件里,第一次运行会引导你创建。这里有个容易被忽略的点:账户密钥丢了不影响已签发证书的使用,但会影响后续续期时对账户的识别,所以程序目录不要随手删。常见做法是把整个 win-acme 目录放在一个固定路径(比如C:\tools\win-acme),而不是解压到桌面或临时目录。
2.2 三种验证方式的取舍:HTTP-01、DNS-01、TLS-ALPN-01
验证方式是整个流程里最需要提前决策的部分,选错了后面全是返工。
| 验证方式 | 原理 | 适用场景 | 主要限制 |
|---|---|---|---|
| HTTP-01 | 在站点根路径下放置指定文件,CA 回源访问 | 站点已对外可访问、80 端口通 | 需要 80 端口可达,通配符不支持 |
| DNS-01 | 在 DNS 里添加 TXT 记录 | 内网站点、需要通配符证书 | 需要 DNS 服务商 API 或手工加记录 |
| TLS-ALPN-01 | 在 443 端口完成 TLS 层挑战 | 只有 443 可用、不想开 80 | 对中间设备敏感,配置复杂 |
对多数 IIS 场景,HTTP-01 是最省事的:win-acme 会自动把验证文件写到站点的.well-known\acme-challenge目录,CA 访问通过后即完成。但如果你要签*.example.com这种通配符,HTTP-01 直接不支持,必须走 DNS-01。DNS-01 又分两种:有 API 的走自动加记录,没 API 的只能手工加 TXT 再回来继续——后者在续期时会变成半自动,需要人盯着,这一点在选型时就要想清楚。
提示:如果站点前面有 CDN 或反向代理,HTTP-01 的回源路径可能被改写,验证文件访问不到。这种情况要么临时绕过代理,要么改用 DNS-01。
2.3 交互式向导与命令行参数:两条入口怎么选
win-acme 提供交互式菜单和纯命令行两种用法。交互式适合第一次配置、边看边选;命令行适合写进脚本、批量执行、放进 CI。两者底层是同一套逻辑,交互式最终也会把选择落成参数。
先看交互式的启动方式,解压后在程序目录执行:
# 进入解压后的 win-acme 目录,启动交互式向导 cd C:\tools\win-acme .\wacs.exe启动后会出现菜单,依次选择“创建新证书”“手动输入域名”“选择验证方式”“选择安装目标(IIS 站点或 PEM/PFX 文件)”。每一步都有默认值,回车即接受。逻辑说明:向导本质是把你的选择拼成一条命令行再执行,所以走完一遍后,程序会打印出等价的命令,这条命令可以直接抄去写脚本。参数含义上,域名列表支持逗号分隔多个,安装目标选 IIS 时会自动匹配站点绑定,选文件时则要指定输出路径和 PFX 密码。
命令行方式更适合可复现的部署。下面是一条典型的 HTTP-01 + IIS 安装示例:
# 为指定域名申请证书并自动绑定到 IIS 站点 .\wacs.exe --target iis --host example.com --validation selfhosting --store certificatestore --installation iis --accepttos --emailaddress ops@example.com逐段说明:--target iis表示从 IIS 站点读取域名和绑定信息;--host显式指定域名(也可省略让程序自动枚举);--validation selfhosting即 HTTP-01 自托管验证,程序临时在站点下放验证文件;--store certificatestore把证书存进 Windows 证书存储;--installation iis申请成功后自动更新 IIS 绑定;--accepttos表示接受服务条款,脚本化时必须带上,否则会卡在交互确认;--emailaddress用于到期提醒和账户找回。这条命令跑通一次,后面续期就是同一套参数加计划任务。
3. 从申请到续期:把一条命令变成可重复的流水线
3.1 首次申请:域名、验证、安装三件事一次配齐
首次申请的目标不是“拿到一张证书”,而是“拿到一张能被自动续期的证书”。这两者的差别在于:续期时程序要能重新走完验证和安装,所以首次配置里的每一个选择都必须可重复。手工加 DNS 记录这种不可重复的动作,在首次配置时就要意识到它会在续期时变成人工介入点。
一个稳妥的首次流程是:先用交互式向导跑通,确认证书确实签发并绑定成功,再从输出里抄出等价命令行,存成一个.cmd或.ps1脚本。脚本里把域名、验证方式、安装目标、邮箱全部写死,避免依赖运行时交互。下面是一个把首次申请固化成脚本的例子:
@echo off REM 固定路径,避免相对路径在不同计划任务上下文里失效 cd /d C:\tools\win-acme REM 申请并安装,日志重定向便于事后排查 .\wacs.exe ^ --target iis ^ --host example.com,www.example.com ^ --validation selfhosting ^ --store certificatestore ^ --installation iis ^ --accepttos ^ --emailaddress ops@example.com ^ --verbose >> C:\tools\win-acme\logs\renew.log 2>&1逻辑说明:cd /d切到绝对路径,是因为计划任务的工作目录默认是system32,相对路径会找不到程序;--verbose把详细过程写进日志,续期失败时这是唯一的黑匣子;>>追加而非覆盖,保留历史记录。参数上,--host里多个域名用逗号分隔,主域名放第一个;如果站点绑定里已经包含全部域名,可以去掉--host让程序自动读取,但自动读取在多站点共用绑定时可能选错,显式指定更稳。
3.2 续期任务:计划任务怎么注册、怎么验证它真的在跑
win-acme 在申请成功后会询问是否注册续期任务,选是则创建一个 Windows 计划任务,默认在证书到期前一段时间触发。这里的关键不是“有没有注册”,而是“注册的任务在真实环境下能不能跑起来”。计划任务以某个账户身份运行,如果该账户对程序目录、证书存储、IIS 配置没有足够权限,任务会静默失败。
验证方法很直接:在计划任务里找到 win-acme 创建的任务,右键“运行”,然后看日志目录里有没有新的续期记录。更稳妥的做法是手动触发一次续期命令,观察输出:
# 手动触发续期,--force 强制重签便于验证流程 .\wacs.exe --renew --force --verbose--renew表示对所有已配置的续期执行一遍;--force忽略“还没到期”的判断强制重签,用于验证链路是否通畅。注意--force会真实消耗签发配额,不要频繁跑。逻辑上,续期和首次申请走的是同一套验证与安装逻辑,所以首次能跑通、续期通常也能跑通,差异主要在权限和网络环境——计划任务运行时可能没有交互式登录时的网络代理配置或用户级证书访问权限。
注意:如果续期任务配置为“仅在用户登录时运行”,服务器重启后没人登录就不会触发。应改为“无论用户是否登录都运行”,并确认运行账户有“作为批处理作业登录”的权限。
3.3 证书存储与 IIS 绑定:PFX 密码和绑定更新
证书最终要落到两个地方之一:Windows 证书存储,或磁盘上的 PFX 文件。选证书存储的好处是 IIS 绑定可以直接引用,续期后绑定自动指向新证书;选 PFX 文件则适合需要把证书分发给其他服务(比如反向代理、邮件服务)的场景,但绑定更新要自己处理。
PFX 密码是个高频翻车点。用--store pemfiles或--store pfxfile时,密码通过参数传入,如果密码里有特殊字符,在命令行和脚本里的转义规则不同,容易出现“申请成功但导出失败”。常见做法是把密码放在一个只有运维可读的配置文件里,用--settings引用,而不是直接写在命令行。另外,证书存储方式下,私钥权限默认只给运行账户,如果 IIS 应用池身份和运行账户不一致,会出现“证书已安装但站点报私钥不可用”,这时要在证书管理里给对应应用池身份补私钥读取权限。
4. 避坑与排查:五条真实会遇到的翻车记录
4.1 验证文件 404:站点根路径与虚拟目录的错位
现象:HTTP-01 验证阶段报“挑战文件无法访问”,日志里能看到 CA 回源请求返回 404。原因:win-acme 默认把验证文件写到站点物理根路径下的.well-known\acme-challenge,但站点如果配置了 URL 重写规则、或该路径被某个虚拟目录覆盖,回源请求会被导向别处。解决:先在浏览器里直接访问http://域名/.well-known/acme-challenge/测试文件确认可达;检查 web.config 里的 rewrite 规则是否把.well-known也重写了,加一条排除规则;确认站点物理路径和程序写入路径一致。
4.2 续期任务成功但证书没换:绑定指向了旧指纹
现象:日志显示续期成功,但浏览器里证书到期时间没变。原因:证书存储里新旧证书并存,IIS 绑定仍指向旧证书的指纹,程序更新绑定时匹配失败或跳过了。解决:在 IIS 绑定里核对证书指纹,与证书存储里最新证书比对;检查是否有多个同名站点导致程序匹配到错误站点;必要时用--installation iis显式重跑一次安装步骤,而不是只跑续期。
4.3 计划任务报 0x1:工作目录与权限的双重问题
现象:手动运行脚本正常,计划任务里返回 0x1。原因:计划任务的工作目录不是程序目录,相对路径失效;或运行账户对程序目录没有写权限,日志和账户存储写不进去。解决:脚本里用cd /d切绝对路径;给运行账户授予程序目录的修改权限;把日志输出到该账户可写的路径,先确认日志能写出来,再谈业务逻辑。
4.4 通配符证书申请失败:验证方式没切到 DNS
现象:提交*.example.com后立即报验证方式不支持。原因:HTTP-01 不支持通配符,程序沿用了之前的自托管验证配置。解决:改用 DNS-01,配置对应 DNS 服务商的凭据;没有 API 的情况下只能手工加 TXT 记录,此时要接受续期需要人工介入,或者换用支持 API 的解析服务。
4.5 端口 80 被占用:自托管验证起不来
现象:验证阶段报端口绑定失败。原因:站点本身没监听 80,或 80 被其他进程占用,win-acme 的自托管验证需要临时接管 80 端口。解决:确认 80 端口空闲或由目标站点监听;如果 80 被前置服务占用,改用 DNS-01;不要为了验证去停生产服务,先评估验证方式再动手。
5. 进阶:把续期做成可观测的流水线,而不是祈祷它别出事
到这一步,证书能自动续期了,但“自动”不等于“可靠”。真正让运维睡得着觉的,是续期过程可观测、失败可告警。我的做法是把 win-acme 的日志纳入统一采集,并在每次续期后做一次独立校验,而不是只信程序自己的退出码。
具体做法分三层。第一层是日志:把--verbose的输出按日期切分,保留至少两个续期周期,出问题时能回溯上一次成功和这一次失败的差异。第二层是独立校验:写一个小脚本,在续期任务之后运行,直接连接站点读取证书到期时间,与当前日期比较,低于阈值就触发告警。这一步的价值在于它不依赖 win-acme 的自我报告,是端到端的验证。
# 独立校验:读取站点实际下发的证书到期时间 $hostName = "example.com" $port = 443 $client = New-Object System.Net.Sockets.TcpClient($hostName, $port) $sslStream = New-Object System.Net.Security.SslStream($client.GetStream(), $false, {$true}) $sslStream.AuthenticateAsClient($hostName) $cert = $sslStream.RemoteCertificate $expire = [datetime]$cert.GetExpirationDateString() $daysLeft = ($expire - (Get-Date)).Days Write-Output "证书到期: $expire, 剩余天数: $daysLeft" if ($daysLeft -lt 15) { Write-Output "ALERT: 证书即将到期" } $sslStream.Close(); $client.Close()逻辑说明:这段脚本模拟一次 TLS 握手,拿到服务端实际下发的证书,而不是读本地存储——两者可能不一致,这正是 4.2 那类问题的检测手段。参数上,$hostName换成实际域名,$daysLeft的阈值按续期周期设定,续期提前量是 30 天的话,阈值设 15 天能留出排查窗口。第三层是告警通道:把校验脚本的输出接到现有的监控或邮件通道,失败时有人收到,而不是等用户发现。
还有一个容易被忽略的细节:续期任务的触发时间要避开业务高峰,且不要和站点部署、IIS 重启撞车。我一般把续期安排在凌晨,并在部署流水线里加一条检查——如果当天有部署,部署完成后再手动触发一次续期校验,避免部署过程覆盖了绑定配置。
从那以后我每次接手一批 Windows 站点,第一件事不是看证书还有多久到期,而是先确认续期任务真的在跑、日志真的在写、独立校验真的在告警,这三件事缺一件,自动续期就只是心理安慰。希望帮到你。
本文还有配套的精品资源,点击获取