windows在cygwin64下使用acme.sh批量签发Let's Encrypt的ssl证书,并用powershell重新分配iis证书
使用前提
本脚本是在使用阿里云Windows服务器的前提,如果使用其他dns服务,请参看acme.sh的dns相关文档
配置好cygwin64、acme.sh并配置好阿里云账户,openssl最好也安装上
cygwin64配置
如果windows server 08R2启动安装程序失败,请使用cmd运行
setup-x86_64.exe --allow-unsupported-windows --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215 --no-verify
其他老旧系统请参考cygwin64官网网页的How can I install the last Cygwin version for an old, unsupported Windows回答
acme.sh配置
openssl参考,添加-certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -nomac 是为了应对pfx输入密钥不正确
最终路径就是项目路径
以下是batch脚本,请保存在autoacme.bat文件中
@echo off & setlocal EnableDelayedExpansion
:: 数组长度,不用管,会自己加值
set objLength=0
:: 公共证书备份路径
set commonPath=E:\cert
:: cygwin64用户路径
set cygwinPath=E:\Cygwin64\home\Administrator
:: cygwin64内部用户路径
set cygwinUserPath=/home/Administrator
:: pfx文件密钥,这里的密钥必须和powershell里的pfx密钥一致
set pfxPassword=dgfdgsdfg
:: 如果公共路径不存在,那么创建,如果路径已存在,不影响命令继续执行
md %commonPath%
:: 证书在以下列表中添加即可
:: 指定域名
set obj[%objLength%]*domain=www.test.com
:: 最终路径
set obj[%objLength%]*path=D:\Web\Main
set /a objLength+=1
:: 指定域名
set obj[%objLength%]*domain=buy.test.com
:: 最终路径
set obj[%objLength%]*path=D:\Web\buy
set /a objLength+=1
:: 指定域名
set obj[%objLength%]*domain=go.test.com
:: 最终路径
set obj[%objLength%]*path=D:\Web\Go
:: 初始索引
set objIndex=0
:: 重试次数
set retryCnt=0
::循环
:loopStart
::判断索引值是否大于数组长度,大于的话跳到结束,不大于的话继续循环
if %objIndex% gtr %objLength% goto end
::初始化当前变量
set curr.domain=0
set curr.path=0
::重置重试次数
set /a retryCnt=0
:: delims==*表示使用=和*分割字符串,tokens=1-3是取切割后字符串的前1到3个,循环对象的每个属性,%%i 是如 obj[0] 标识是第几个对象, %%j 标识是对象的那个属性,%%k 是指定对象属性的值
for /f "usebackq delims==* tokens=1-3" %%i in (`set obj[%objIndex%]`) do (
:: 赋值变量
set curr.%%j=%%k
)
echo domain=%curr.domain%
echo path=%curr.path%
:: 登录到cygwin使用acme.sh签发证书,并将文件拷贝到公共证书目录,并转成pfx格式,密码统一使用%pfxPassword%
echo 签发%curr.domain%证书
:: 设置执行命令后缀,这里是acme.sh相关命令,修改dns api就在这里
set issueCmd=--issue --dns dns_ali -d %curr.domain% --fullchain-file %cygwinUserPath%/.acme.sh/%curr.domain%_ecc/%curr.domain%.pem --key-file %cygwinUserPath%/.acme.sh/%curr.domain%_ecc/%curr.domain%.key
bash --login -i -c "acme.sh %issueCmd%"
echo 检查%curr.domain%key文件大小和backup目录是否存在文件
set byte=0
for %%A in ("%cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.key") do (
set /a byte=%%~zA
)
echo %curr.domain%.key大小:%byte%字节
:: 如果key文件大小为零
if %byte% equ 0 (
if EXIST %cygwinPath%\.acme.sh\%curr.domain%_ecc\backup\key.bak (
echo 检查key.bak的大小
for %%A in ("%cygwinPath%\.acme.sh\%curr.domain%_ecc\backup\key.bak") do (
set /a byte=%%~zA
)
:: bak文件不为零,那么拷贝覆盖
echo /backup/key.bak大小:%byte%字节
if %byte% equ 0 (
echo 拷贝key.bak到根目录
copy %cygwinPath%\.acme.sh\%curr.domain%_ecc\backup\key.bak %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.key /y
) else (
:: 如果全部失败,那么直接重新申请
:forceIssue
echo 尝试重新申请%curr.domain%证书第%retryCnt%次
bash --login -i -c "acme.sh %issueCmd% --force"
:: 重试一次,加一次次数
set /a retryCnt+=1
)
)
)
echo 拷贝key文件到公共目录
copy %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.key %commonPath%\%curr.domain%.key /y
echo 赋予权限
bash --login -i -c "chmod -R g+rw %cygwinUserPath%/.acme.sh/%curr.domain%_ecc"
echo 第一次检查%curr.domain%.pfx文件是否存在
IF NOT EXIST %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pfx (
echo openssl转换pfx,因为acme.sh转换失败
openssl pkcs12 -export -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -nomac -out %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pfx -inkey %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.key -in %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.cer -password pass:"%pfxPassword%"
)
echo 第二次检查%curr.domain%.pfx文件是否存在
IF NOT EXIST %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pfx (
IF %retryCnt% gtr 3 goto skipCurr
else goto forceIssue
)
echo 拷贝pfx文件到公共目录
copy %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pfx %commonPath%\%curr.domain%.pfx /y
:: 如果pem格式文件不存在,那么使用openssl转换成pem格式
IF NOT EXIST %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pem (
echo openssl转换pem
openssl pkcs12 -in %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pfx -out %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pem -nodes -password pass:"%pfxPassword%"
)
:: 拷贝pem文件到公共目录
echo 拷贝pem到公共目录
copy %cygwinPath%\.acme.sh\%curr.domain%_ecc\%curr.domain%.pem %commonPath%\%curr.domain%.pem /y
:: 拷贝证书到最终路径,如果路径相等会直接拷贝失败,如果最终路径不存在,也会拷贝失败
echo 拷贝%curr.domain%证书到项目目录
copy %commonPath%\%curr.domain%.pem %curr.path%\%curr.domain%.pem /y & copy %commonPath%\%curr.domain%.key %curr.path%\%curr.domain%.key /y & copy %commonPath%\%curr.domain%.pfx %curr.path%\%curr.domain%.pfx /y
:skipCurr
:: 索引+1
set /a objIndex=%objIndex% + 1
:: 继续循环
goto loopStart
:end
:: 暂停看结果
:: pause
:: 执行完后退出
exit
PowerShell 脚本,使用前,更改执行策略
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
将以下脚本保存为reissueIISCert.ps1文件
# 使用前先将策略设置为不严格 Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
# 保证证书有效的情况下再运行次脚本,将证书名称、证书目录、密钥放入以下数组
# 公共证书密钥
$pfxpassword = "dgfdgsdfg"
# 公共证书路径
$pfxCommandDir= "E:\cert"
# 域名
$domain="test.com"
# 服务器上的证书与端口映射关系
$data = @(
[pscustomobject]@{subDomain = 'www';port=443}
[pscustomobject]@{subDomain = 'buy';port=8443}
[pscustomobject]@{subDomain = 'go';port=7443}
)
# 开始循环数组操作
foreach ($element in $data) {
$pfxPath="$($pfxCommandDir)\$($element.subDomain).$($domain).pfx"
Write-Host $pfxPath
$securePfxKey = ConvertTo-SecureString -String $pfxpassword -AsPlainText -Force
Import-PfxCertificate -FilePath $pfxPath -CertStoreLocation Cert:\LocalMachine\My -Password $securePfxKey
$pfxData = Get-PfxData -FilePath $pfxPath -Password $securePfxKey
$newThumbprint = $pfxData.EndEntityCertificates.Thumbprint
$guid=New-Guid
$applicationID = "{$($guid)}" # hardcode it once
$addr="0.0.0.0:$($element.port)"
netsh http delete sslcert ipport=$addr
netsh http add sslcert ipport=$addr certhash=$newThumbprint appid=$applicationID
}
# 执行完后退出
exit
创建任务计划程序参考
将启动程序设置为autoacme.bat即可,设置完成后,右键对应任务计划程序->属性->操作,在这一对话框中,将reissueIISCert.ps1加入到队列中。在常规页面中,勾选“不管用户是否登录都要运行”以及“使用最高权限”,保存即可
任务计划程序是按照顺序执行的。