Appearance
CertiMan CLI 自动化实践:IIS 证书部署与自动续期
CertiMan CLI 生成 PFX 证书文件,部署脚本(Hook)将它导入服务器并替换目标网站的 HTTPS 证书。网站原绑定继续使用,无需重启整个 IIS。
本文接管已有订单和正常运行的 HTTPS 网站,按“接入已有订单 → 确认绑定 → 准备 PFX → 配置 Hook → 测试换证 → 开启计划任务”完成配置。尚未启用 HTTPS 时,请先完成IIS 首次部署指南。
方案是怎样的?
在 IIS 服务器上安装 CertiMan CLI,由 Windows 任务计划定期检查证书是否需要更新。需要更新时,CertiMan 从 CertCloud 获取新证书,再调用部署脚本,将证书导入 Windows 并更新指定网站的 HTTPS 绑定。
Windows 计划任务由 CLI 注册。需要写 DNS 验证记录时,CLI 调用 DNS Hook;持久验证记录按订单要求保留。证书交付与网站访客的 HTTPS 请求是两条不同的路径。
首次手动部署验收后再注册或启用 Windows 任务计划。任务定期扫描已开启自动续期的证书,满足续期条件才更新,不是每次扫描都重新签发。
网络放行
IIS 服务器需能通过 HTTPS(443)访问 certiman.trustasia.com、certcloud.cn 和 api.certcloud.cn。使用 DNS 验证脚本时,还需访问 DNS 服务商 API。CLI 安装成功后若连接异常,在同一运行账号下执行 certiman doctor,按诊断说明检查;自定义服务地址以实际配置为准。网站继续使用原 HTTPS 端口。
开始前准备什么?
- 一个已经能正常访问 HTTPS 的 IIS 网站,和 Windows 服务器管理员账号。
- 网站当前证书对应的 CertCloud 订单,支持“自动交付”。
- 该证书的 PEM 私钥;如果只有 PFX,按第一步链接的提取方法准备。
若使用 CDN、WAF 或负载均衡,请确认公网证书实际部署在哪个入口。
第一步:接入已有订单并确认材料
使用原订单和匹配私钥接入 CLI,不重新创建网站或订单。
1.1 确认订单并安装 CLI
在 CertCloud 打开已有订单,确认支持“自动交付”,无需重复购买。若页面没有对应入口,请联系支持确认。
从订单的“自动交付”进入接入引导,选择“命令行工具 CLI → Windows”。在 IIS 服务器上右键打开 Windows PowerShell,选择“以管理员身份运行”,执行安装命令:

powershell
irm https://certiman.trustasia.com/install.ps1 | iex安装完成后重新打开管理员 PowerShell,检查版本:
powershell
certiman version显示版本号后继续。后面的登录、部署和计划任务都使用这个 Windows 账号;详细安装方法见官方安装说明。
1.2 登录并接入订单
在运行 CLI 的环境中,以前述固定运行账号执行:
powershell
certiman login status缓存有效且账户正确时无需重新登录;未登录时执行 certiman login。在浏览器中核对账户及应用名称为 CertiMan 后完成授权。终端没有浏览器时,在自己的电脑上打开 CLI 输出的授权地址。

下面使用 C:\CertiMan 保存私钥和部署脚本。先在当前管理员 PowerShell 中创建目录:
powershell
New-Item -ItemType Directory -Path 'C:\CertiMan' -Force | Out-Null可通过目录“属性 → 安全”核对访问权限,仅允许运行账号、必要管理员及系统所需账号访问;目录创建命令本身不设置这些权限。后续示例使用此目录,已有其他工作目录时可统一替换。
将网站证书对应的 PEM 私钥放到上述目录。只有 PFX 时,先按文末从 PFX 提取私钥操作,再回到这里。
将 YOUR_ORDER_ID 换成 CertCloud 接入引导中的订单号,将 C:\CertiMan\original-key.pem 换成准备好的私钥路径,执行:
powershell
certiman issue --reuse-order YOUR_ORDER_ID --private-key 'C:\CertiMan\original-key.pem'已由本机 CLI 管理且材料齐全时不重复导入。确实无法取得匹配私钥时,参见文末找不到原始私钥时重新签发。
1.3 确认订单状态和本地材料
将下面的 trustasia.com 换成订单主域名,查看状态和本地文件。只有 CLI 提示待验证或审核时,才按订单提示完成;需要继续处理时执行 certiman issue -d 订单主域名。
powershell
certiman status -d trustasia.com
Get-ChildItem "$HOME/.certiman/cc/certs/trustasia.com/"将示例域名替换为订单实际主域名。确认已签发,且目录中已有 fullchain.pem、privkey.pem 和 cert.yaml。目录名由订单主域名决定,请以 CLI 输出为准。
1.4 确认后续验证可自动完成
自动续期时,CertCloud 仍需要验证域名归属。根据订单使用的验证方式,按下面对应的一种处理:
持久验证: 使用 dns-persist 时,按订单提示添加 TXT 记录;使用 dns-proxy 时,添加指向平台的 CNAME 记录。授权有效期间通常可以继续使用这些记录,无需每次续期修改。已经配置好的记录继续保留,不要在首次签发成功后删除,也不要配置会删除它们的清理操作。
普通 DNS 验证: 如果每次签发都需要新的验证记录,就需要让 CertiMan 自动更新 DNS。先按 DNS 服务商配置 DNS 验证 Hook(自动写入验证记录的脚本)和 API 凭据;CertiMan 会将本次记录名、类型和值交给脚本,由脚本调用服务商 API 写入 DNS。具体配置见官方 DNS Hook 示例。确认自动写入可用后,再开启自动续期;仅安装 CertiMan 或配置 IIS 部署脚本,不会自动完成 DNS 验证配置。
第二步:确认目标网站和 HTTPS 绑定
先在浏览器确认网站 HTTPS 正常,再在 IIS 管理器中打开“网站 → 目标网站 → 绑定”,选中要更新的 https 条目并点击“编辑”。只查看和记录,暂不修改:
| 从哪里取得 | 第四步脚本填写 |
|---|---|
| IIS 左侧列表中的网站名称 | SiteName |
| HTTPS 条目的“IP 地址” | BindingIP;“全部未分配”填 * |
| HTTPS 条目的“端口” | BindingPort |
| HTTPS 条目的“主机名” | BindingHost,填写域名,不带 https:// |
本例更新一个已填写主机名、已勾选“需要服务器名称指示(SNI)”的 HTTPS 条目。脚本会检查是否找到唯一目标;配置不符时先按实际绑定确认适用方式,不为了套用脚本改动其他网站绑定。
第三步:配置 PFX 输出与续期选项
本步让 CLI 为已有证书生成 PFX,第四步脚本会把它导入 IIS。
3.1 配置证书输出格式与续期选项
打开证书目录中的 cert.yaml,路径示例:
text
C:\Users\<Windows用户名>\.certiman\cc\certs\trustasia.com\cert.yaml修改前将本证书的 renew.auto_cert 设为 false,确认没有更新任务正在运行,不影响其他证书的任务。
修改已有字段,保留订单和域名等其他信息,不要重复添加同名节点:
yaml
output:
formats: [pfx]
password: '替换为 PFX 密码'
pfx_file: 'TrustAsia.pfx'
deploy: null
renew:
auto_cert: false # 验收通过后再开启
auto_order: false # 不自动创建付费续订订单password:填写本次生成 PFX 使用的密码,第四步PfxPassword填相同值。pfx_file:生成的文件名,示例为TrustAsia.pfx,第四步PfxFileName填相同值。deploy: null和auto_cert: false:准备期间先不执行部署和自动续期。
密码含单引号时,在 YAML 的单引号内写成两个单引号;
保存配置后,在 PowerShell 中执行:
powershell
certiman download -d trustasia.com -f将 trustasia.com 替换为实际订单主域名。下载完成后,生成的 TrustAsia.pfx 保存在该证书目录下的 converted 文件夹中,路径示例:
text
C:\Users\<Windows用户名>\.certiman\cc\certs\trustasia.com\converted\TrustAsia.pfx第四步:保存并配置部署脚本
4.1 准备 PowerShell 部署脚本
在同一运行账号的管理员 PowerShell 中,将以下脚本保存为 C:\CertiMan\deploy-iis.ps1,使用第一步已准备的目录和访问权限。修改顶部“用户修改这里”的参数,与第二步记录的网站及绑定一致。Windows PowerShell 5.1 下请以 UTF-8 带 BOM 保存。
脚本从 CLI 生成的目录读取 PFX,导入原绑定使用的“个人”或“Web Hosting”证书存储,再替换第二步选中的网站证书。
powershell
# ================= 用户修改这里 =================
$SiteName = 'Default Web Site' # IIS 中的网站名称,不一定是域名
$BindingHost = 'trustasia.com' # HTTPS 绑定中的实际主机名,不能为空
$BindingPort = 443 # HTTPS 绑定端口
$BindingIP = '*' # “全部未分配”填 *;指定 IP 则填写实际 IP
$PfxFileName = 'TrustAsia.pfx' # 与 cert.yaml 的 output.pfx_file 一致
$PfxPassword = '替换为 PFX 密码' # 与 output.password 一致
# ================================================
$ErrorActionPreference = 'Stop'
try {
Import-Module WebAdministration -ErrorAction Stop
# 1. 精确查找已有 HTTPS 绑定
$bindingInfo = '{0}:{1}:{2}' -f $BindingIP, $BindingPort, $BindingHost
$bindings = @(
Get-WebBinding -Name $SiteName -Protocol 'https' |
Where-Object { $_.bindingInformation -eq $bindingInfo }
)
if ($bindings.Count -ne 1) {
throw "未找到唯一的 HTTPS 绑定:站点 [$SiteName],绑定 [$bindingInfo]。请核对顶部参数。"
}
$binding = $bindings[0]
# 仅支持主机名非空的普通 SNI 绑定
if ([string]::IsNullOrWhiteSpace($BindingHost) -or [int]$binding.sslFlags -ne 1) {
throw '本例仅支持主机名非空且 sslFlags=1 的 SNI 绑定。'
}
# 同一主机名和端口的其他绑定可能共享 TLS 关联,本例直接拒绝
$shared = @(Get-WebBinding -Protocol 'https' | Where-Object {
$_.bindingInformation -like ('*:{0}:{1}' -f $BindingPort, $BindingHost)
})
if ($shared.Count -ne 1) {
throw '该主机名和端口存在其他 HTTPS 绑定,需另行适配。'
}
# 沿用原绑定的证书存储,并输出旧状态供人工恢复
$storeName = [string]$binding.certificateStoreName
$oldHash = $binding.certificateHash
$oldThumbprint = if ($oldHash -is [byte[]]) {
[BitConverter]::ToString($oldHash).Replace('-', '')
} else { ([string]$oldHash).Replace(' ', '') }
if ($storeName -notin @('My', 'WebHosting') -or [string]::IsNullOrWhiteSpace($oldThumbprint)) {
throw '无法确认原证书和存储,未执行部署。'
}
Write-Output "原绑定:$SiteName / $bindingInfo;存储:$storeName;指纹:$oldThumbprint"
# 2. 从 CertiMan 传入的目录获取本次 PFX
if ([string]::IsNullOrWhiteSpace($env:CERTIMAN_CONVERTED_DIR)) {
throw '缺少 CertiMan 环境变量,请通过 certiman deploy 调用此脚本。'
}
$pfxPath = Join-Path $env:CERTIMAN_CONVERTED_DIR $PfxFileName
if (-not (Test-Path -LiteralPath $pfxPath -PathType Leaf)) {
throw "找不到 PFX:$pfxPath。请检查输出配置并重新下载生成。"
}
# 3. 使用生成 PFX 时设置的密码
if ([string]::IsNullOrWhiteSpace($PfxPassword)) {
throw '请填写 PFX 密码。'
}
$securePassword = ConvertTo-SecureString $PfxPassword -AsPlainText -Force
# 4. 导入前核对 PFX 中的服务器证书及 SAN 域名
$pfxData = Get-PfxData -FilePath $pfxPath -Password $securePassword
$leafCertificates = @($pfxData.EndEntityCertificates)
if ($leafCertificates.Count -ne 1) {
throw 'PFX 必须包含且仅包含一张服务器证书。'
}
$leaf = $leafCertificates[0]
$sanExtensions = @($leaf.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.17' })
if ($sanExtensions.Count -ne 1) {
throw '证书缺少 SAN 扩展,未执行部署。'
}
# 直接解码 SAN,只读取 DNS 项,不回退到 Subject CN
$san = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$san.InitializeDecode(1, [Convert]::ToBase64String($sanExtensions[0].RawData))
$idn = New-Object System.Globalization.IdnMapping
$targetName = $idn.GetAscii($BindingHost.TrimEnd('.')).ToLowerInvariant()
if ($targetName.Contains('*')) { throw '绑定主机名必须为具体域名。' }
$matched = $false
foreach ($dnsName in $san.AlternativeNames) {
if ([int]$dnsName.Type -ne 3) { continue }
$name = [string]$dnsName.StrValue
if ([string]::IsNullOrWhiteSpace($name)) { continue }
$name = $name.TrimEnd('.').ToLowerInvariant()
if ($name.StartsWith('*.')) {
$name = '*.' + $idn.GetAscii($name.Substring(2)).ToLowerInvariant()
} else { $name = $idn.GetAscii($name).ToLowerInvariant() }
if ($name.StartsWith('*.')) {
# 通配符只匹配一个标签,不匹配根域名或多级子域名
$suffix = $name.Substring(2)
$labels = $targetName.Split('.')
if ($labels.Count -eq $suffix.Split('.').Count + 1 -and
$targetName.EndsWith('.' + $suffix)) { $matched = $true }
} elseif ($targetName -eq $name) { $matched = $true }
}
if (-not $matched) { throw '证书 SAN 不覆盖目标主机名,未执行部署。' }
$now = Get-Date
if ($leaf.NotBefore -gt $now -or $leaf.NotAfter -le $now) {
throw '证书尚未生效或已经过期,未执行部署。'
}
# 5. 导入原绑定使用的本机证书存储
$certificates = @(
Import-PfxCertificate -FilePath $pfxPath `
-CertStoreLocation ("Cert:\LocalMachine\{0}" -f $storeName) `
-Password $securePassword |
Where-Object { $_.HasPrivateKey }
)
if ($certificates.Count -ne 1) {
throw 'PFX 中应包含且仅包含一张带私钥的服务器证书。'
}
$certificate = $certificates[0]
$now = Get-Date
if ($certificate.NotBefore -gt $now -or $certificate.NotAfter -le $now) {
throw '证书尚未生效或已经过期,未更新 IIS 绑定。'
}
# 核对导入结果与刚验证的证书一致,再更新原绑定
if ($certificate.Thumbprint -ne $leaf.Thumbprint) {
throw '导入结果与已验证证书不一致,未更新绑定。'
}
$binding.AddSslCertificate($certificate.Thumbprint, $storeName)
Write-Output (
'部署成功:站点 [{0}],绑定 [{1}],指纹 [{2}],有效期至 [{3}]' -f
$SiteName, $bindingInfo, $certificate.Thumbprint, $certificate.NotAfter
)
}
catch {
[Console]::Error.WriteLine('部署失败:' + $_.Exception.Message)
exit 1
}SiteName、BindingIP、BindingPort、BindingHost:填写第二步表格记录的值。PfxFileName、PfxPassword:填写第三步pfx_file和password的值。密码含单引号时,在 PowerShell 单引号字符串内写成两个单引号。脚本也使用第一步目录的访问权限。
4.2 配置部署 Hook
确认脚本参数后,将 cert.yaml 中的 deploy: null 替换为以下配置并保存:
yaml
deploy: 'powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\CertiMan\deploy-iis.ps1'-File 后填写刚保存的脚本完整路径,路径包含空格时用双引号括起来;使用示例路径时可直接保留。执行部署和注册计划任务仍需使用同一账号的管理员 PowerShell。保存配置后进入第五步。
第五步:在已有绑定上试部署并检查网站
记下第二步目标绑定当前选中的“SSL 证书”,保留该证书,以便异常时重新选回。在同一账号的管理员 PowerShell 中执行,把 trustasia.com 换成第一步的订单主域名:
powershell
certiman deploy -d trustasia.com若提示已部署而跳过,可加 -f 再次部署本地证书:
powershell
certiman deploy -d trustasia.com -f看到“部署成功”且命令没有报错后,回到第二步的绑定窗口,点击所选证书旁的“查看”,确认其域名和有效期与本次部署的证书一致,IP、端口和主机名仍是原值。使用同一张证书接入时,有效期不会变化。
再在自己的电脑上用浏览器打开网站,例如 https://trustasia.com/。域名换成第二步的“主机名”;端口不是 443 时加上原端口,例如 https://trustasia.com:8443/。
- 确认打开的是自己的网站,浏览器没有“连接不安全”或证书错误提示。
- 点击地址栏左侧的网站信息按钮查看证书,确认域名正确、有效期与 CertCloud 订单中本次部署的证书一致。
- 浏览常用页面,确认图片、样式和登录等功能正常。
使用 CDN 等代理或怀疑仍显示旧证书时,按证书检查说明核对 IIS 上的证书。

检查通过后继续第六步;有错误时先按本文异常恢复处理,脚本不会自动选回旧证书。
第六步:按需开启自动续期
第五步验收通过后,修改原 renew 节点,保留已验证的 Hook:
yaml
renew:
auto_cert: true
auto_order: false在安装和授权时的同一账号下,以管理员身份打开 PowerShell。尚未注册任务时执行 install;已注册时无需重复安装:
powershell
certiman cron install
certiman cron status在 Windows“任务计划程序”中确认 certiman-cron 的运行账号、触发计划和权限,确认用户注销后仍能运行。计划任务由系统定期执行,不需要长期打开终端;已有企业任务时保留一个执行来源。详见计划任务注册说明。

任务已启用不等于自动续期已验证。未到续期时间时不会立即换证;首次真实续期后,按第五步确认网站已使用新证书。
从 PFX 提取私钥
只有 PFX、没有单独 PEM 私钥时使用本节;已经有私钥的读者跳过。Windows 默认不自带 OpenSSL,没有该工具时可以从原 KeyManager 环境导出 PEM 私钥。
在能运行 openssl 的管理员 PowerShell 中,修改下面两个变量并执行。输出目录使用第一步已准备的 C:\CertiMan,改用其他目录时需先准备好:
powershell
$SourcePfx = 'C:\CertificateFiles\TrustAsia.pfx'
$OutputKey = 'C:\CertiMan\original-key.pem'
& {
$ErrorActionPreference = 'Stop'
if (Test-Path -LiteralPath $OutputKey) { throw '输出文件已存在,请另选路径,避免覆盖原私钥。' }
if (-not (Test-Path -LiteralPath (Split-Path -Parent $OutputKey) -PathType Container)) {
throw '请先准备仅运行账号及必要管理员可访问的输出目录。'
}
openssl pkcs12 -in $SourcePfx -nocerts -nodes -out $OutputKey
if ($LASTEXITCODE -ne 0) { throw '私钥提取失败,请检查 PFX 文件及密码;不要导入失败产物。' }
openssl pkey -in $OutputKey -check -noout
if ($LASTEXITCODE -ne 0) { throw '私钥检查失败,停止导入。' }
Write-Output "私钥已保存到:$OutputKey"
}SourcePfx:现有 PFX 的完整路径。OutputKey:提取后 PEM 私钥的保存路径,不能覆盖已有文件。
按提示输入 PFX 密码,看到“私钥已保存到……”后,回到 1.2,将该路径填入 --private-key 导入。输出为未加密 PEM 私钥;确实无法取得匹配私钥时再看下一节。
找不到原始私钥时重新签发
仅在无法找回匹配私钥时使用。重颁发可能使旧证书失效,操作前安排换证时间,准备及时生成新 PFX、更新绑定并验收。
首次接管订单时执行:
powershell
certiman issue --reuse-order YOUR_ORDER_ID --on-existing-cert reissue已由本机 CLI 管理时,改用 certiman issue -d trustasia.com --on-existing-cert reissue,域名替换为订单主域名。两种方式按接入状态选择一种。签发后返回第三至第五步,生成 PFX、部署并验收。
后续维护与异常恢复
定期查看任务状态:
powershell
certiman cron status遇到问题时,可检查运行账号下 .certiman\logs 中的日志。若使用 Hook 且证书已下载、但 IIS 部署失败,修复原因后重新执行 certiman deploy -d trustasia.com,无需重新下单。
auto_order: false 不会自动创建付费续订订单,请在服务期结束前安排续费,并及时处理审核或域名验证提示。
升级方法参阅 CertiMan 官方文档。
异常恢复
先将对应 cert.yaml 的 renew.auto_cert 改为 false,确认更新任务已结束。若错误发生在更新绑定之前,原绑定未改变,修正参数、密码或材料后重新部署即可。
如果绑定已更新但访问异常,在 IIS 管理器中选择原网站,打开“绑定”,编辑同一个 HTTPS 条目,将“SSL 证书”重新选择为更新前仍有效且含私钥的旧证书。保持 IP、端口、主机名及 SNI 不变;旧证书及存储可按 Hook 输出的原绑定记录核对。不要删除原证书或重建网站。
旧证书不在计算机存储中时,先通过服务器节点的“服务器证书 → 导入”导入保留的旧 PFX,选择与原绑定相同的“个人(My)”或“Web Hosting”存储,再恢复绑定。随后按第五步验证实际域名及业务。旧证书已过期或已失效时不能用于恢复,应及时部署有效的新证书。问题解决并重新部署验收通过后,再开启自动续期。
停用自动化
只停用此证书时,将 renew.auto_cert 改为 false,删除 deploy 字段,保留其他配置。其他证书仍需自动更新时,不卸载共用任务;当前账号全部证书都不再需要任务时,在管理员 PowerShell 中执行 certiman cron uninstall,再用 certiman cron status 确认。停用不会删除 IIS 已部署证书或网站绑定,后续换证需自行安排。
卸载 CertiMan(不再使用时)
如不再使用 CertiMan,请以安装和配置任务时的账号打开管理员 PowerShell,执行官方卸载脚本,并按提示确认:
powershell
irm https://certiman.trustasia.com/uninstall.ps1 | iex卸载脚本会移除 CertiMan 注册的计划任务和程序,默认保留 %USERPROFILE%\.certiman 中的证书、私钥及授权数据。卸载后不再由该任务自动续期,请自行安排后续证书更新。详见官方卸载指引。
