четверг, 16 августа 2018 г.

Получение сертификата от Let's Encrypt для Windows

Ниже приводится скрипт для получении сертификата от LE и загрузка его в раздел сертификатов локальной машины. Данный скрипт проверялся на Windows Server 2016, раздел будет дополнится, когда разберусь с автоматическим добавлением dns записи в NIC.RU
Скрипт обновлён 17-08-2018



#old manual here https://github.com/ebekker/ACMESharp/wiki/Quick-Start
#new manual here https://pkisharp.github.io/ACMESharp-docs/Quick-Start
function Resolve-ACMEDnsRecord ([string]$dnsAddressName) {
    $res=Resolve-DnsName $dnsAddressName -Type TXT `
        -Server '1.1.1.1' `
        -ErrorAction SilentlyContinue
    if ($res) {
Write-Host ("Найдена запись "+$res.Name+" со значением "+$res.Strings) -ForegroundColor Cyan
        return $res.Strings[0]
    } else {
        return ''
    }
}

function RunCMD ([string]$arg) {
 cmd /c $arg
}

function Add-DnsRecerodToNic () {
    $curlPath=$env:windir+'\system32\curl.exe'
    if (!(Test-Path $curlPath)) {return $false}
    #Получить логин и пароль можно по ссылке 
    #https://www.nic.ru/help/oauth-server_3642.html
    #раздел будет допилен позже
    return $false
}

if (!(Get-Module ACMESharp)) {
    Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
    Install-Module ACMESharp -Force
}
if (!(Get-Module ACMESharp.Providers.Windows)) {
    Install-Module ACMESharp.Providers.Windows -Force
}
Import-Module ACMESharp
$domain='example.com'
$email='admin@'+$domain
$serverName=($env:COMPUTERNAME).ToLower()
$fqdn=$serverName+'.'+$domain
$ChallengeType='dns-01'
$certName=$serverName+'-cert'
$KeyPemPath=($env:USERPROFILE+'\'+$serverName+".key.pem")
$certPemPath=($env:USERPROFILE+'\'+$serverName+".crt.pem")
$certDerPath=($env:USERPROFILE+'\'+$serverName+".crt")
$CertPFXPath=($env:USERPROFILE+'\'+$serverName+".pfx")
$password='Zz'+(Get-date -Format yyyyMMdd)+'%'
$HandleMessageFile=($env:TMP+'\'+$serverName+'.txt')
$RequestTimeout=120

if (Test-Path $KeyPemPath) {rm $KeyPemPath -Force}
if (Test-Path $certPemPath) {rm $certPemPath -Force}
if (Test-Path $certDerPath) {rm $certDerPath -Force}
if (Test-Path $CertPFXPath) {rm $CertPFXPath -Force}
if (Test-Path $HandleMessageFile) {rm $HandleMessageFile -Force}

#Шлём запрос на регистрацию
Initialize-ACMEVault -Force
New-ACMERegistration -Contacts "mailto:$email" -AcceptTos
$debugUri=(New-ACMEIdentifier -Dns $fqdn -Alias $serverName).Uri
(Invoke-WebRequest -Uri $debugUri).Content
#Записываем в файл ответ от сервера, чтобы удобнее было парсить его
$CompleteACMEChallenge=Complete-ACMEChallenge $serverName -ChallengeType $ChallengeType -Handler manual -Force
$CompleteACMEChallenge.Challenges.HandlerHandleMessage > $HandleMessageFile
#Извлекаем имя dns записи для внесение в DNS серевер
if (Test-Path $HandleMessageFile) {
    $RRNameString=cat $HandleMessageFile | sls Name
    if ($RRNameString) {
        $startRRSearch=([string]$RRNameString).IndexOF('[')
        $endRRSearch=([string]$RRNameString).IndexOF(']')
        $HandleMessageRR=([string]$RRNameString).Substring($startRRSearch+1,($endRRSearch-$startRRSearch-1))
        #Извлекаем значение dns записи для внесение в DNS серевер
        $TXTNameString=cat $HandleMessageFile | sls Value
        $startRRSearch=([string]$TXTNameString).IndexOF('[')
        $endRRSearch=([string]$TXTNameString).IndexOF(']')
        $HandleMessageRRValue=([string]$TXTNameString).Substring($startRRSearch+1,($endRRSearch-$startRRSearch-1))
    } else {
        "В файле нет $HandleMessageFile нет нужного значения"
        pause
        exit
    } 
} else {
    "Файла $HandleMessageFile не существует"
    pause
    exit
}
#добавить руками во внешний DNS запись
if (!(Add-DnsRecerodToNic)) {
    Write-Host "Добавьте руками во внешний DNS запись" -ForegroundColor Cyan
    cat $HandleMessageFile
    pause
}
###

Submit-ACMEChallenge $serverName -ChallengeType $ChallengeType
#ждём пока не обновится dns
while ((Resolve-ACMEDnsRecord $HandleMessageRR) -ne $HandleMessageRRValue) {
    $msg="DNS запись $HandleMessageRR со значением "
    $msg+="$HandleMessageRRValue не зарегистированна "
    $msg+="на "+(get-date -Format yyyy-MM-ddThh:mm)
    Write-Host $msg -ForegroundColor Yellow
    Start-Sleep -Seconds $RequestTimeout
}
Start-Sleep -Seconds $RequestTimeout
#ждём пока не обновится статус запроса
do {
$UpdateRequest=Update-ACMEIdentifier $serverName
$msg="Текущий статус запроса "+$UpdateRequest.Status
    $msg+=" на "+(get-date -Format yyyy-MM-ddThh:mm)
    Write-Host $msg -ForegroundColor Yellow
$(Invoke-WebRequest -Uri $UpdateRequest.Uri).Content
    Start-Sleep -Seconds $RequestTimeout
} while ($UpdateRequest.Status -ne 'valid')
while  {
    
}
#получаем сертификаты и скачиваем их
New-ACMECertificate $serverName -Generate -Alias $certName
Submit-ACMECertificate $certName
Get-ACMECertificate $certName -ExportKeyPEM $KeyPemPath
Get-ACMECertificate $certName -ExportCertificatePEM $certPemPath -ExportCertificateDER $certDerPath
Update-ACMECertificate $certName
Get-ACMECertificate $certName -ExportPkcs12 $CertPFXPath -CertificatePassword $password
#добавляем сертификат в хранилище и запоминаем его отпечаток
$secPass=ConvertTo-SecureString -AsPlainText $password -Force
$ImportCertPFX=Import-PfxCertificate $CertPFXPath -Password ($secPass) -CertStoreLocation 'Cert:\LocalMachine\My\'
if ($?) {
    #если необходимо что-то сделать дополнительно 
    #с скачанными ключом и сертификатами,
    #то вставить код здесь
    rm $KeyPemPath
    rm $certPemPath
    rm $certDerPath
    rm $certPFXPath
}

Комментариев нет:

Отправить комментарий