NetScalerToolkit.Common/Private/Request-NSACMECertificateDnsOrder.ps1

function Request-NSACMECertificateDnsOrder {
<#
    .SYNOPSIS
        Requests an ACME certificate using Posh-ACME DNS validation.
 
    .DESCRIPTION
        Uses Posh-ACME New-PACertificate for DNS plugin validation. When
        UseNetScalerDNS is enabled, it publishes DNS-01 TXT records directly on the
        NetScaler and drives the ACME order manually.
 
    .PARAMETER Request
        Certificate request object.
 
    .PARAMETER Domains
        Domains for the certificate order.
 
    .PARAMETER PfxSecret
        PFX password as SecureString.
 
    .PARAMETER AcmeOptions
        Normalized ACME options hashtable.
 
    .PARAMETER Force
        Forces Posh-ACME to renew the DNS validated certificate.
 
    .NOTES
        Function : Request-NSACMECertificateDnsOrder
        Author : John Billekens
        Copyright : Copyright (c) John Billekens Consultancy
        Version : 2026.0525.2218
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][object]$Request,
        [Parameter(Mandatory)][string[]]$Domains,
        [Parameter(Mandatory)][securestring]$PfxSecret,
        [Parameter(Mandatory)][hashtable]$AcmeOptions,
        [Parameter()][psobject]$Session,
        [Parameter()][object]$Account,
        [switch]$Force
    )

    if ($Request.UseNetScalerDNS) {
        if (-not $Session) { throw 'A NetScaler session is required when UseNetScalerDNS is enabled.' }
        if (-not $Account) { $Account = Get-PAAccount -ErrorAction Stop }

        $orderParams = @{
            Domain        = $Domains
            KeyLength     = [string]$Request.KeyLength
            FriendlyName  = $Request.FriendlyName
            PfxPassSecure = $PfxSecret
            Force         = $true
            ErrorAction   = 'Stop'
        }
        if ($AcmeOptions.UseModernPfxEncryption) { $orderParams.UseModernPfxEncryption = $true }
        if ($AcmeOptions.PreferredChain) { $orderParams.PreferredChain = $AcmeOptions.PreferredChain }
        if ($AcmeOptions.Profile) { $orderParams.Profile = $AcmeOptions.Profile }
        if ($AcmeOptions.LifetimeDays) { $orderParams.LifetimeDays = [int]$AcmeOptions.LifetimeDays }
        if ($AcmeOptions.ValidationTimeout) { $orderParams.ValidationTimeout = [int]$AcmeOptions.ValidationTimeout }
        if ($AcmeOptions.DnsAlias) { $orderParams.DnsAlias = $AcmeOptions.DnsAlias }
        if ($AcmeOptions.AlwaysNewKey -or [bool]$Force -or [bool]$Request.ForceCertRenew) { $orderParams.AlwaysNewKey = $true }
        if ([bool]$Force -or [bool]$Request.ForceCertRenew) {
            try { Remove-PAOrder -MainDomain $Request.CN -Force -ErrorAction SilentlyContinue | Out-Null } catch {}
        }

        $validationTimeout = if ($AcmeOptions.ValidationTimeout) { [int]$AcmeOptions.ValidationTimeout } else { 240 }

        Write-NSACMECertificateLog Info 'ACME' "Creating ACME DNS order for $($Domains -join ', ') using NetScaler DNS."
        $order = New-PAOrder @orderParams
        $publishedRecords = @()

        try {
            $authzs = $order | Posh-ACME\Get-PAOrder -Refresh | Posh-ACME\Get-PAAuthorizations
            $pendingAuthzs = @($authzs | Where-Object { $_.status -ne 'valid' -and $_.DNS01Status -ne 'valid' })
            foreach ($authz in $pendingAuthzs) {
                $domainName = [string]$authz.fqdn
                $sanitizedDomain = $domainName -replace '^\*\.', ''
                $recordName = if ($authz.DnsAlias) {
                    [string]$authz.DnsAlias
                } elseif ($AcmeOptions.DnsAlias) {
                    $index = [array]::IndexOf([string[]]$Domains, $domainName)
                    if ($index -ge 0 -and @($AcmeOptions.DnsAlias).Count -gt $index) { [string]@($AcmeOptions.DnsAlias)[$index] } else { [string]@($AcmeOptions.DnsAlias)[0] }
                } else {
                    "_acme-challenge.$sanitizedDomain"
                }
                $recordName = $recordName.TrimEnd('.')
                $txtValue = Posh-ACME\Get-KeyAuthorization -Token $authz.DNS01Token -Account $Account -ForDNS
                Add-NSACMECertificateSensitiveValue -Value $txtValue -Placeholder '<ACMEDnsTxtValue>'

                Write-NSACMECertificateLog Info 'NetScalerDNS' "Adding DNS-01 TXT record '$recordName' for $domainName."
                $existingRecords = @(Invoke-NSGetDNSTXTRecord -Session $Session -Domain $recordName -ReturnNullOnNotFound -ErrorAction SilentlyContinue)
                $existing = @($existingRecords | Where-Object {
                        $values = @($_.String) + @($_.string)
                        $values -contains $txtValue
                    })
                if (-not $existing) {
                    Invoke-NSAddDNSTXTRecord -Session $Session -Domain $recordName -String $txtValue -Ttl 300 | Out-Null
                } else {
                    Write-NSACMECertificateLog Debug 'NetScalerDNS' "DNS-01 TXT record '$recordName' already exists."
                }
                $publishedRecords += [pscustomobject]@{ Domain = $recordName; String = $txtValue; Fqdn = $domainName }
            }

            $dnsWaitTime = if ($Request.DNSWaitTime) { [int]$Request.DNSWaitTime } else { [int]$AcmeOptions.DNSWaitTime }
            if ($dnsWaitTime -gt 0) {
                Write-NSACMECertificateLog Info 'NetScalerDNS' "Waiting $dnsWaitTime seconds for DNS records to settle."
                Start-Sleep -Seconds $dnsWaitTime
            }

            foreach ($authz in $pendingAuthzs) {
                Write-NSACMECertificateLog Info 'ACME' "Acknowledging DNS-01 challenge for $($authz.fqdn)."
                Posh-ACME\Send-ChallengeAck -ChallengeUrl $authz.DNS01Url -Account $Account -ErrorAction Stop
            }

            Wait-NSACMECertificateAuthorization -MainDomain $Request.CN -TimeoutSeconds $validationTimeout | Out-Null
            Write-NSACMECertificateLog Info 'ACME' "Finalizing ACME order for $($Request.CN)."
            $null = Submit-OrderFinalize -Order (Get-PAOrder -MainDomain $Request.CN -Refresh) -ErrorAction Stop
            Wait-NSACMECertificateOrderFinal -MainDomain $Request.CN -TimeoutSeconds $validationTimeout | Out-Null
            Write-NSACMECertificateLog Info 'ACME' "Downloading ACME certificate artifacts for $($Request.CN)."
            $null = Complete-PAOrder -Order (Get-PAOrder -MainDomain $Request.CN -Refresh) -ErrorAction Stop
            return Get-PACertificate -MainDomain $Request.CN -ErrorAction Stop
        } finally {
            foreach ($record in $publishedRecords) {
                try {
                    Write-NSACMECertificateLog Info 'NetScalerDNS' "Removing DNS-01 TXT record '$($record.Domain)' for $($record.Fqdn)."
                    Invoke-NSDeleteDNSTXTRecord -Session $Session -Domain $record.Domain -String $record.String -IgnoreNotFound -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
                } catch {
                    Write-NSACMECertificateLog Warning 'NetScalerDNS' "Could not remove DNS-01 TXT record '$($record.Domain)': $($_.Exception.Message)"
                }
            }
        }
    }

    $plugin = if ($Request.DNSPlugin) { $Request.DNSPlugin } elseif ($AcmeOptions.DNSPlugin) { $AcmeOptions.DNSPlugin } else { 'Manual' }
    $pluginValues = @($plugin)
    if ($pluginValues.Count -eq 1 -and @($Domains).Count -gt 1) {
        $expandedPlugin = @()
        for ($index = 0; $index -lt @($Domains).Count; $index++) {
            $expandedPlugin += [string]$pluginValues[0]
        }
        $plugin = $expandedPlugin
    }
    $pluginArgs = if ($Request.DNSParams) { ConvertTo-NSACMECertificateHashtable -InputObject $Request.DNSParams } else { ConvertTo-NSACMECertificateHashtable -InputObject $AcmeOptions.DNSParams }
    $dnsSleep = if ($Request.DNSWaitTime -and [int]$Request.DNSWaitTime -gt 0) {
        [int]$Request.DNSWaitTime
    } elseif ($AcmeOptions.DNSWaitTime -and [int]$AcmeOptions.DNSWaitTime -gt 0) {
        [int]$AcmeOptions.DNSWaitTime
    } else {
        120
    }

    $params = @{
        Domain          = $Domains
        Contact         = $Request.EmailAddress
        AcceptTOS       = $true
        Plugin          = $plugin
        PluginArgs      = $pluginArgs
        DnsSleep        = $dnsSleep
        PfxPassSecure   = $PfxSecret
        FriendlyName    = $Request.FriendlyName
        Force           = ([bool]$Force -or [bool]$Request.ForceCertRenew)
        ErrorAction     = 'Stop'
    }
    if ($Request.KeyLength) { $params.CertKeyLength = [string]$Request.KeyLength }
    if ($AcmeOptions.UseModernPfxEncryption) { $params.UseModernPfxEncryption = $true }
    if ($AcmeOptions.PreferredChain) { $params.PreferredChain = $AcmeOptions.PreferredChain }
    if ($AcmeOptions.Profile) { $params.Profile = $AcmeOptions.Profile }
    if ($AcmeOptions.LifetimeDays) { $params.LifetimeDays = [int]$AcmeOptions.LifetimeDays }
    if ($AcmeOptions.ValidationTimeout) { $params.ValidationTimeout = [int]$AcmeOptions.ValidationTimeout }
    if ($AcmeOptions.DnsAlias) { $params.DnsAlias = $AcmeOptions.DnsAlias }
    if ($AcmeOptions.AlwaysNewKey) { $params.AlwaysNewKey = $true }

    Write-NSACMECertificateLog Info 'ACME' "Requesting DNS certificate for $($Domains -join ', ') using plugin '$($pluginValues[0])'."
    Write-NSACMECertificateLog Debug 'ACME' "DNS propagation wait is $dnsSleep seconds." -Data ([ordered]@{
            Plugin    = $pluginValues[0]
            DnsSleep  = $dnsSleep
            Domains   = ($Domains -join ', ')
            DnsAlias  = if ($AcmeOptions.DnsAlias) { @($AcmeOptions.DnsAlias) -join ', ' } else { $null }
        }) -ConsoleDataKeys @('DnsSleep', 'Plugin')
    try {
        New-PACertificate @params
    } catch {
        if ($_.Exception.Message -match 'Incorrect TXT record') {
            Write-NSACMECertificateLog Warning 'ACME' 'DNS-01 validation saw stale or missing TXT records.' -Data ([ordered]@{
                    Plugin   = $pluginValues[0]
                    DnsSleep = $dnsSleep
                    Domains  = ($Domains -join ', ')
                    Action   = 'Remove old _acme-challenge TXT values, wait for public DNS, or increase DNSWaitTime.'
                }) -ConsoleDataKeys @('DnsSleep', 'Action')
        }
        throw
    }
}

# SIG # Begin signature block
# MIImdwYJKoZIhvcNAQcCoIImaDCCJmQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBsIpjcwxLAxVQB
# RJaBQ40r9xziOQI9FOVkYXjDoTj75KCCIAowggYUMIID/KADAgECAhB6I67aU2mW
# D5HIPlz0x+M/MA0GCSqGSIb3DQEBDAUAMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQK
# Ew9TZWN0aWdvIExpbWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIFRpbWUg
# U3RhbXBpbmcgUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5
# WjBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSwwKgYD
# VQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNjCCAaIwDQYJ
# KoZIhvcNAQEBBQADggGPADCCAYoCggGBAM2Y2ENBq26CK+z2M34mNOSJjNPvIhKA
# VD7vJq+MDoGD46IiM+b83+3ecLvBhStSVjeYXIjfa3ajoW3cS3ElcJzkyZlBnwDE
# JuHlzpbN4kMH2qRBVrjrGJgSlzzUqcGQBaCxpectRGhhnOSwcjPMI3G0hedv2eNm
# GiUbD12OeORN0ADzdpsQ4dDi6M4YhoGE9cbY11XxM2AVZn0GiOUC9+XE0wI7CQKf
# OUfigLDn7i/WeyxZ43XLj5GVo7LDBExSLnh+va8WxTlA+uBvq1KO8RSHUQLgzb1g
# bL9Ihgzxmkdp2ZWNuLc+XyEmJNbD2OIIq/fWlwBp6KNL19zpHsODLIsgZ+WZ1AzC
# s1HEK6VWrxmnKyJJg2Lv23DlEdZlQSGdF+z+Gyn9/CRezKe7WNyxRf4e4bwUtrYE
# 2F5Q+05yDD68clwnweckKtxRaF0VzN/w76kOLIaFVhf5sMM/caEZLtOYqYadtn03
# 4ykSFaZuIBU9uCSrKRKTPJhWvXk4CllgrwIDAQABo4IBXDCCAVgwHwYDVR0jBBgw
# FoAU9ndq3T/9ARP/FqFsggIv0Ao9FCUwHQYDVR0OBBYEFF9Y7UwxeqJhQo1SgLqz
# YZcZojKbMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYEVR0gADBMBgNVHR8ERTBDMEGg
# P6A9hjtodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3Rh
# bXBpbmdSb290UjQ2LmNybDB8BggrBgEFBQcBAQRwMG4wRwYIKwYBBQUHMAKGO2h0
# dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ1Jv
# b3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAN
# BgkqhkiG9w0BAQwFAAOCAgEAEtd7IK0ONVgMnoEdJVj9TC1ndK/HYiYh9lVUacah
# RoZ2W2hfiEOyQExnHk1jkvpIJzAMxmEc6ZvIyHI5UkPCbXKspioYMdbOnBWQUn73
# 3qMooBfIghpR/klUqNxx6/fDXqY0hSU1OSkkSivt51UlmJElUICZYBodzD3M/SFj
# eCP59anwxs6hwj1mfvzG+b1coYGnqsSz2wSKr+nDO+Db8qNcTbJZRAiSazr7KyUJ
# Go1c+MScGfG5QHV+bps8BX5Oyv9Ct36Y4Il6ajTqV2ifikkVtB3RNBUgwu/mSiSU
# ice/Jp/q8BMk/gN8+0rNIE+QqU63JoVMCMPY2752LmESsRVVoypJVt8/N3qQ1c6F
# ibbcRabo3azZkcIdWGVSAdoLgAIxEKBeNh9AQO1gQrnh1TA8ldXuJzPSuALOz1Uj
# b0PCyNVkWk7hkhVHfcvBfI8NtgWQupiaAeNHe0pWSGH2opXZYKYG4Lbukg7HpNi/
# KqJhue2Keak6qH9A8CeEOB7Eob0Zf+fU+CCQaL0cJqlmnx9HCDxF+3BLbUufrV64
# EbTI40zqegPZdA+sXCmbcZy6okx/SjwsusWRItFA3DE8MORZeFb6BmzBtqKJ7l93
# 9bbKBy2jvxcJI98Va95Q5JnlKor3m0E7xpMeYRriWklUPsetMSf2NvUQa/E5vVye
# fQIwggZFMIIELaADAgECAhAIMk+dt9qRb2Pk8qM8Xl1RMA0GCSqGSIb3DQEBCwUA
# MFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMu
# QS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBDQTAeFw0yNDA0
# MDQxNDA0MjRaFw0yNzA0MDQxNDA0MjNaMGsxCzAJBgNVBAYTAk5MMRIwEAYDVQQH
# DAlTY2hpam5kZWwxIzAhBgNVBAoMGkpvaG4gQmlsbGVrZW5zIENvbnN1bHRhbmN5
# MSMwIQYDVQQDDBpKb2huIEJpbGxla2VucyBDb25zdWx0YW5jeTCCAaIwDQYJKoZI
# hvcNAQEBBQADggGPADCCAYoCggGBAMslntDbSQwHZXwFhmibivbnd0Qfn6sqe/6f
# os3pKzKxEsR907RkDMet2x6RRg3eJkiIr3TFPwqBooyXXgK3zxxpyhGOcuIqyM9J
# 28DVf4kUyZHsjGO/8HFjrr3K1hABNUszP0o7H3o6J31eqV1UmCXYhQlNoW9FOmRC
# 1amlquBmh7w4EKYEytqdmdOBavAD5Xq4vLPxNP6kyA+B2YTtk/xM27TghtbwFGKn
# u9Vwnm7dFcpLxans4ONt2OxDQOMA5NwgcUv/YTpjhq9qoz6ivG55NRJGNvUXsM3w
# 2o7dR6Xh4MuEGrTSrOWGg2A5EcLH1XqQtkF5cZnAPM8W/9HUp8ggornWnFVQ9/6M
# ga+ermy5wy5XrmQpN+x3u6tit7xlHk1Hc+4XY4a4ie3BPXG2PhJhmZAn4ebNSBwN
# Hh8z7WTT9X9OFERepGSytZVeEP7hgyptSLcuhpwWeR4QdBb7dV++4p3PsAUQVHFp
# wkSbrRTv4EiJ0Lcz9P1HPGFoHiFAQQIDAQABo4IBeDCCAXQwDAYDVR0TAQH/BAIw
# ADA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY2NzY2EyMDIxLmNybC5jZXJ0dW0u
# cGwvY2NzY2EyMDIxLmNybDBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0
# dHA6Ly9jY3NjYTIwMjEub2NzcC1jZXJ0dW0uY29tMDUGCCsGAQUFBzAChilodHRw
# Oi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY2NzY2EyMDIxLmNlcjAfBgNVHSMEGDAW
# gBTddF1MANt7n6B0yrFu9zzAMsBwzTAdBgNVHQ4EFgQUO6KtBpOBgmrlANVAnyiQ
# C6W6lJwwSwYDVR0gBEQwQjAIBgZngQwBBAEwNgYLKoRoAYb2dwIFAQQwJzAlBggr
# BgEFBQcCARYZaHR0cHM6Ly93d3cuY2VydHVtLnBsL0NQUzATBgNVHSUEDDAKBggr
# BgEFBQcDAzAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAEQsN8wg
# PMdWVkwHPPTN+jKpdns5AKVFjcn00psf2NGVVgWWNQBIQc9lEuTBWb54IK6Ga3hx
# QRZfnPNo5HGl73YLmFgdFQrFzZ1lnaMdIcyh8LTWv6+XNWfoyCM9wCp4zMIDPOs8
# LKSMQqA/wRgqiACWnOS4a6fyd5GUIAm4CuaptpFYr90l4Dn/wAdXOdY32UhgzmSu
# xpUbhD8gVJUaBNVmQaRqeU8y49MxiVrUKJXde1BCrtR9awXbqembc7Nqvmi60tYK
# lD27hlpKtj6eGPjkht0hHEsgzU0Fxw7ZJghYG2wXfpF2ziN893ak9Mi/1dmCNmor
# GOnybKYfT6ff6YTCDDNkod4egcMZdOSv+/Qv+HAeIgEvrxE9QsGlzTwbRtbm6gwY
# YcVBs/SsVUdBn/TSB35MMxRhHE5iC3aUTkDbceo/XP3uFhVL4g2JZHpFfCSu2TQr
# rzRn2sn07jfMvzeHArCOJgBW1gPqR3WrJ4hUxL06Rbg1gs9tU5HGGz9KNQMfQFQ7
# 0Wz7UIhezGcFcRfkIfSkMmQYYpsc7rfzj+z0ThfDVzzJr2dMOFsMlfj1T6l22GBq
# 9XQx0A4lcc5Fl9pRxbOuHHWFqIBD/BCEhwniOCySzqENd2N+oz8znKooSISStnkN
# aYXt6xblJF2dx9Dn89FK7d1IquNxOwt0tI5dMIIGYjCCBMqgAwIBAgIRAKQpO24e
# 3denNAiHrXpOtyQwDQYJKoZIhvcNAQEMBQAwVTELMAkGA1UEBhMCR0IxGDAWBgNV
# BAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJsaWMgVGlt
# ZSBTdGFtcGluZyBDQSBSMzYwHhcNMjUwMzI3MDAwMDAwWhcNMzYwMzIxMjM1OTU5
# WjByMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxGDAWBgNV
# BAoTD1NlY3RpZ28gTGltaXRlZDEwMC4GA1UEAxMnU2VjdGlnbyBQdWJsaWMgVGlt
# ZSBTdGFtcGluZyBTaWduZXIgUjM2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEA04SV9G6kU3jyPRBLeBIHPNyUgVNnYayfsGOyYEXrn3+SkDYTLs1crcw/
# ol2swE1TzB2aR/5JIjKNf75QBha2Ddj+4NEPKDxHEd4dEn7RTWMcTIfm492TW22I
# 8LfH+A7Ehz0/safc6BbsNBzjHTt7FngNfhfJoYOrkugSaT8F0IzUh6VUwoHdYDpi
# ln9dh0n0m545d5A5tJD92iFAIbKHQWGbCQNYplqpAFasHBn77OqW37P9BhOASdmj
# p3IijYiFdcA0WQIe60vzvrk0HG+iVcwVZjz+t5OcXGTcxqOAzk1frDNZ1aw8nFhG
# EvG0ktJQknnJZE3D40GofV7O8WzgaAnZmoUn4PCpvH36vD4XaAF2CjiPsJWiY/j2
# xLsJuqx3JtuI4akH0MmGzlBUylhXvdNVXcjAuIEcEQKtOBR9lU4wXQpISrbOT8ux
# +96GzBq8TdbhoFcmYaOBZKlwPP7pOp5Mzx/UMhyBA93PQhiCdPfIVOCINsUY4U23
# p4KJ3F1HqP3H6Slw3lHACnLilGETXRg5X/Fp8G8qlG5Y+M49ZEGUp2bneRLZoyHT
# yynHvFISpefhBCV0KdRZHPcuSL5OAGWnBjAlRtHvsMBrI3AAA0Tu1oGvPa/4yeei
# Ayu+9y3SLC98gDVbySnXnkujjhIh+oaatsk/oyf5R2vcxHahajMCAwEAAaOCAY4w
# ggGKMB8GA1UdIwQYMBaAFF9Y7UwxeqJhQo1SgLqzYZcZojKbMB0GA1UdDgQWBBSI
# YYyhKjdkgShgoZsx0Iz9LALOTzAOBgNVHQ8BAf8EBAMCBsAwDAYDVR0TAQH/BAIw
# ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDBKBgNVHSAEQzBBMDUGDCsGAQQBsjEB
# AgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZn
# gQwBBAIwSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2NybC5zZWN0aWdvLmNvbS9T
# ZWN0aWdvUHVibGljVGltZVN0YW1waW5nQ0FSMzYuY3JsMHoGCCsGAQUFBwEBBG4w
# bDBFBggrBgEFBQcwAoY5aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVi
# bGljVGltZVN0YW1waW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz
# cC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAYEAAoE+pIZyUSH5ZakuPVKK
# 4eWbzEsTRJOEjbIu6r7vmzXXLpJx4FyGmcqnFZoa1dzx3JrUCrdG5b//LfAxOGy9
# Ph9JtrYChJaVHrusDh9NgYwiGDOhyyJ2zRy3+kdqhwtUlLCdNjFjakTSE+hkC9F5
# ty1uxOoQ2ZkfI5WM4WXA3ZHcNHB4V42zi7Jk3ktEnkSdViVxM6rduXW0jmmiu71Z
# pBFZDh7Kdens+PQXPgMqvzodgQJEkxaION5XRCoBxAwWwiMm2thPDuZTzWp/gUFz
# i7izCmEt4pE3Kf0MOt3ccgwn4Kl2FIcQaV55nkjv1gODcHcD9+ZVjYZoyKTVWb4V
# qMQy/j8Q3aaYd/jOQ66Fhk3NWbg2tYl5jhQCuIsE55Vg4N0DUbEWvXJxtxQQaVR5
# xzhEI+BjJKzh3TQ026JxHhr2fuJ0mV68AluFr9qshgwS5SpN5FFtaSEnAwqZv3IS
# +mlG50rK7W3qXbWwi4hmpylUfygtYLEdLQukNEX1jiOKMIIGgjCCBGqgAwIBAgIQ
# NsKwvXwbOuejs902y8l1aDANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYD
# VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBS
# U0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjEwMzIyMDAwMDAwWhcNMzgw
# MTE4MjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1p
# dGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFJvb3Qg
# UjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAiJ3YuUVnnR3d6Lkm
# gZpUVMB8SQWbzFoVD9mUEES0QUCBdxSZqdTkdizICFNeINCSJS+lV1ipnW5ihkQy
# C0cRLWXUJzodqpnMRs46npiJPHrfLBOifjfhpdXJ2aHHsPHggGsCi7uE0awqKggE
# /LkYw3sqaBia67h/3awoqNvGqiFRJ+OTWYmUCO2GAXsePHi+/JUNAax3kpqstbl3
# vcTdOGhtKShvZIvjwulRH87rbukNyHGWX5tNK/WABKf+Gnoi4cmisS7oSimgHUI0
# Wn/4elNd40BFdSZ1EwpuddZ+Wr7+Dfo0lcHflm/FDDrOJ3rWqauUP8hsokDoI7D/
# yUVI9DAE/WK3Jl3C4LKwIpn1mNzMyptRwsXKrop06m7NUNHdlTDEMovXAIDGAvYy
# nPt5lutv8lZeI5w3MOlCybAZDpK3Dy1MKo+6aEtE9vtiTMzz/o2dYfdP0KWZwZIX
# bYsTIlg1YIetCpi5s14qiXOpRsKqFKqav9R1R5vj3NgevsAsvxsAnI8Oa5s2oy25
# qhsoBIGo/zi6GpxFj+mOdh35Xn91y72J4RGOJEoqzEIbW3q0b2iPuWLA911cRxgY
# 5SJYubvjay3nSMbBPPFsyl6mY4/WYucmyS9lo3l7jk27MAe145GWxK4O3m3gEFEI
# kv7kRmefDR7Oe2T1HxAnICQvr9sCAwEAAaOCARYwggESMB8GA1UdIwQYMBaAFFN5
# v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBT2d2rdP/0BE/8WoWyCAi/QCj0U
# JTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUEDDAKBggr
# BgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/aHR0
# cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUNlcnRpZmljYXRpb25B
# dXRob3JpdHkuY3JsMDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDov
# L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEADr5lQe1oRLjl
# ocXUEYfktzsljOt+2sgXke3Y8UPEooU5y39rAARaAdAxUeiX1ktLJ3+lgxtoLQhn
# 5cFb3GF2SSZRX8ptQ6IvuD3wz/LNHKpQ5nX8hjsDLRhsyeIiJsms9yAWnvdYOdEM
# q1W61KE9JlBkB20XBee6JaXx4UBErc+YuoSb1SxVf7nkNtUjPfcxuFtrQdRMRi/f
# InV/AobE8Gw/8yBMQKKaHt5eia8ybT8Y/Ffa6HAJyz9gvEOcF1VWXG8OMeM7Vy7B
# s6mSIkYeYtddU1ux1dQLbEGur18ut97wgGwDiGinCwKPyFO7ApcmVJOtlw9FVJxw
# /mL1TbyBns4zOgkaXFnnfzg4qbSvnrwyj1NiurMp4pmAWjR+Pb/SIduPnmFzbSN/
# G8reZCL4fvGlvPFk4Uab/JVCSmj59+/mB2Gn6G/UYOy8k60mKcmaAZsEVkhOFuoj
# 4we8CYyaR9vd9PGZKSinaZIkvVjbH/3nlLb0a7SBIkiRzfPfS9T+JesylbHa1LtR
# V9U/7m0q7Ma2CQ/t392ioOssXW7oKLdOmMBl14suVFBmbzrt5V5cQPnwtd3UOTpS
# 9oCG+ZZheiIvPgkDmA8FzPsnfXW5qHELB43ET7HHFHeRPRYrMBKjkb8/IN7Po0d0
# hQoF4TeMM+zYAJzoKQnVKOLg8pZVPT8wgga5MIIEoaADAgECAhEAmaOACiZVO2Wr
# 3G6EprPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoT
# GVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0
# aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0
# d29yayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjELMAkG
# A1UEBhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIG
# A1UEAxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34vuTm
# flN4mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSoMcbv
# IKck6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+njvM
# k2xqbNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHIohzu
# C8KNqfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRdaCtVD
# 2bSlbfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837Q4QO
# ZgYqVWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8mFSkc
# SkAExzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI5XDY
# O962WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5boufUnq
# 1UiYPIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplKShBS
# ND36E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n858JS
# qPECAwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10XUwA
# 23ufoHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbROg79
# MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8EKTAn
# MCWgI6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsGAQUF
# BwEBBGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVtLmNv
# bTAyBggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0bmNh
# Mi5jZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93
# d3cuY2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCiaU58Q
# 7EP89DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L94C9L
# GF3vjzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rGDxLU
# UAsO0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f8pXd
# d3x2mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0CdY9rN
# LqyA3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B7eMc
# ZNhpn8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloMU+vU
# BfSouCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1tVxTp
# V2Na3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2DaGg
# K2W1eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNjt2yw
# zOr+tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCrW602
# NCmvO1nm+/80nLy5r0AZvCQxaQ4xggXDMIIFvwIBATBqMFYxCzAJBgNVBAYTAlBM
# MSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0Nl
# cnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBDQQIQCDJPnbfakW9j5PKjPF5dUTANBglg
# hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCCfp8ATmmHfV8JbML1PTj2X3DzdQBFPR129voENaXsP
# OzANBgkqhkiG9w0BAQEFAASCAYCJAGtOTBXqqkqMnd+axkofdtTGFTyFvfejpzCh
# zu0q7zU1uFOq3TN2uE8rK4RL4ElTU+xRTA/gcq+m71scM/ceQo5jhdvhK2S+bdy5
# E18L66Z5c+rCzkhV/fhnygVXAu6KQAfVOA073gmedjaFWTY68HxKuqMQlkMOsW+n
# /XQNm6xpCGHV7Fi5Y65C7DxgGDfU9mIHOKqLZL5DYxPJsBlVDWuo+6Wuzyqz6gM8
# 6VLF4SrC76fY3RCnZ/bYnDXRpaMXwHwsJ6OzFofS91ZFRBsTka0Ek9QVVR2YbGCg
# jjcHKy0d6THSfMMrGVgqGlM4QXp4BUWB0KCjxvqOVSMss0inlM1RSG3Pm5PYZFMS
# WnL0up7Fjiyyt6mP4fUk00/c2eaArtLhmZAccpAI/X7W33z7nE9qWWEcW8Ebg1LO
# iTASH8skJgMZU9WfcYFFFIJGTILeobRGd7Ls8unkVZVcALTvlpbfhnmpisan05qI
# mel1KVIBnoOxmi53ptQNgK3nJPehggMjMIIDHwYJKoZIhvcNAQkGMYIDEDCCAwwC
# AQEwajBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSww
# KgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNgIRAKQp
# O24e3denNAiHrXpOtyQwDQYJYIZIAWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJ
# KoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjA2MDQxMDE0MTRaMD8GCSqGSIb3
# DQEJBDEyBDD3o476c/FnLL5wtly1Gj6dSQ6oCG34d9NuaAlkX3AxIHNLycRQFx1w
# WW7a2JjnXYwwDQYJKoZIhvcNAQEBBQAEggIANeyEHhLFcDjpcvOmuXso7Rz9trIC
# eKJLW6rKSSSk6TXzgutx5RNLTQGMECCzo1XZwthHOZJbgt79NajQmajtyeTkfAp5
# +0o9EsMtvGeH4/FBbKKhTylGBhihV8le7IkWu5p4019TgkgLyb5XNl0Kdt+3QFWI
# ROFun/RmPW8eWxXWI8ANjNWUVQhWBHtGeicDhoSzG94407GGzXZm6TG7felzT0nl
# vmNjgIBe/O2I0MUhlT9L96ZyVas1r3lHnkmilRjlqziPGtr4sPCI6nsKgDg3tXuF
# oodMzooh3afQmueYlkem9qaC4zEYJDW2PSXJP0gFv7V3H9Pybjk3UzJMl6cRHYUt
# //5E7e7mh0x18yPFnpme6d44IlkyPnGfCbwrafr+i2lsHWanYnJkNOTcXjRrxLS+
# yN7t/SRkCQV//Hg+3lZ1KrEWYV2iTHwzHG0ku5hmC9ew3UOqAAH9Og0PBBWvYnn6
# TgaKcaEZ6v01t0PtgzBv0EQ6Q+JWDWmb+IdEuWPH3j1tynauGF5KtTsJOAWCB5vb
# MRwtU/KZYtyg438muvgq7IpXRtqj3gioXyrvBaAuE7vpj3q1xsbLbc1vSkIs4BAO
# spLvTXoIxnFwFQDTErCq3HPBr2wYhlfZR+mguRYp6vTkTU3Rj8S574cQ6VBGspNp
# ctd9ra5BAmSubhw=
# SIG # End signature block