CertificateValidation/Microsoft.AzureStack.PublicCertificateRequest.psm1

function Write-AzsCertificateRequestFileInternal
{
        <#
    .SYNOPSIS
        Internal function to call CSR using the standard Azure Stack request template.
    .DESCRIPTION
        Imports the standard Azure Stack certificate request template
        replaces deployment specific data such as subject and SAN.
        Detects if commas are used in the subject name and thus requiring a different X500NameFlag
    .EXAMPLE
        Write-AzsCertificateRequestFileInternal -subjectAltNames $completeSANs -subject $subject -OutputRequestPath $OutputRequestPath
    .INPUTS
        subjectAltNames - string - SubjectAlternativeNames
        subject - ordereddictionary - hashtable of deployment subject
        OutputRequestPath - string - path (parent must be valid) to where the CSRs should land.
    .OUTPUTS
        Encoded CSR file - This is the file or content that should be presented to a CA to request certificates
        Clear Text INF file - this is a reference file to help debugging.
        Certreq log file - the output of the certreq.exe command, later streamed into overall AzsCertificateRequest.log
    .NOTES
    #>

    [cmdletbinding()]
    param (
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [System.Collections.Specialized.OrderedDictionary]$subject, 
            
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [string]$subjectAltNames, 
            
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [ValidateScript({Test-Path $_ -PathType Container})]
            $OutputRequestPath
          )
    
    $thisFunction = $MyInvocation.MyCommand.Name
    try 
    {
        #parse subject for comma and set appropropriate name flag to avoid error, and build subjectname
        $subjectStringArray = $subject.GetEnumerator()  | ForEach-Object { if ($_.Value){"$($_.Name)=$($_.Value)"} }
        if ($subjectStringArray -notcontains ',')
        {
            Write-AzsReadinessLog -Message ("Subject {0} doesnt contain comma" -f $subjectStringArray) -Type Info -Function $thisFunction
            $stringString = $subjectStringArray -join ','
        }
        else
        {
            Write-AzsReadinessLog -Message ("Subject contains comma" -f $subjectStringArray) -Type Info -Function $thisFunction
            $stringString = $subjectStringArray -join ';'
            $X500NameFlags = "X500NameFlags = 0x40000000`n`r"
        }

        #Get INF Template and replace placeholders with user data
        $data = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.PublicCertificateRequestData.psd1
        $infTemplate = $data.requestINF
        $requestINF = $infTemplate.Replace('[[SubjectString]]',$stringString)
        $requestINF = $requestINF.Replace('[[X500]]',$X500NameFlags)
        $requestINF = $requestINF.Replace('[[SANStrings]]',$subjectAltNames)
        #Create filepaths for inf, req and log as [host|wildcard].region.external.fqdn-CertRequest-
        $infFilePath = "{0}\{1}_CertRequest_{2}_ClearTextDoNotUse.inf" -f $OutputRequestPath,$subjectAltNames.split('&')[0].Replace('dns=','').Replace('.','_').Replace('*','wildcard'),(Get-Date -f yyyyMMddHHmmss)
        Write-AzsReadinessLog -Message ("Setting infFilePath to {0}" -f $infFilePath) -Type Info -Function $thisFunction
        $csrFilePath = $infFilePath.Replace('_ClearTextDoNotUse','').Replace('inf','req')
        $certReqLogPath = $csrFilePath.Replace('.req','.log')

        #Create and print Inf template
        $requestINF | Out-File $infFilePath -Force
        #Create encoded cert request
        Write-AzsReadinessLog -Message ("Setting csrFilePath to {0}" -f $csrFilePath) -Type Info -Function $thisFunction

        $cmd = "-new $infFilePath $csrFilePath"
        $null = Start-Process -FilePath certreq.exe `
                    -ArgumentList $cmd `
                    -WindowStyle Hidden `
                    -PassThru `
                    -Wait `
                    -RedirectStandardOutput $certReqLogPath
        Write-AzsReadinessLog -Message ("CSR generating for following SAN(s): {0}" -f $subjectAltNames) -Type Info -Function $thisFunction -toScreen
        Write-AzsReadinessLog -Message ("Present this CSR to your Certificate Authority for Certificate Generation: {0}" -f $csrFilePath) -Type Info -Function $thisFunction -toScreen
    }
    catch 
    {
        Write-AzsReadinessLog -Message ("CSR generation failed with: {0}" -f $_.exception) -Type Error -Function $thisFunction
    }
    finally
    {
        # Move inf file to child directory called inf
        $infDest = ("{0}\{1}" -f (Split-Path $infFilePath -parent),'Inf')
        if (-not (Test-Path $infDest)){$null = New-Item -path $infDest  -ItemType Directory -Force}
        Move-Item -Path $infFilePath -Destination $infDest -Force -ErrorAction SilentlyContinue
        # Move log content to parent log and clean up log file.
        $certReqLogContent = Get-Content $certReqLogPath -ErrorAction SilentlyContinue | ForEach-Object {if($_){$_}}
        if (-not $certReqLogContent){$certReqLogContent = '[missing]'}
        Write-AzsReadinessLog -Message ("Certreq.exe output: {0}" -f ($certReqLogContent -join '. ')) -Type Info -Function $thisFunction -toScreen
        Remove-item $certReqLogPath -Force
    }
}

function Write-AzsCertificateRequestFile
{
    <#
    .SYNOPSIS
        Wrapper function to build SANs and call internal certreq.exe function for multiSAN CSR or SingleSAN CSR
    .DESCRIPTION
        Imports the standard Azure Stack SANs and builds the SAN list including PaaS if neccessary
        Calls internal function for once for multiSAN or multiple time for singleSAN (depending on the user input)
    .EXAMPLE
        $certificateRequestParams = @{
            'regionName' = $regionName
            'externalFQDN' = $fqdn
            'subject' = $subject
            'requestType' = $RequestType
            'includePaaS' = $IncludePaaS
            'OutputRequestPath' = $OutputRequestPath
        }
        Write-AzsCertificateRequestFile @certificateRequestParams
    .INPUTS
        regionName - string - deployment's regionName
        externalFQDN - string - deployment's FQDN
        subject - ordereddictionary - hashtable of deployment subject
        requestType - string - Valid values: MultipleSAN, SingleSAN to indicate
                               if the user wants one certificate with all SANs defined,
                               or seperate certificates with single SANs
        includePaaS - switch - appends PaaS SANs to list of SANs that certificate request(s) should be created for
        OutputRequestPath - string - path (parent must be valid) to where the CSRs should land.
    .OUTPUTS
        None - This is a wrapper function that calls an internal function to generate the encoded CSR
    .NOTES
    #>

    [cmdletbinding()]
    param (
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [string]$regionName, 
            
            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [string]$externalFQDN, 

            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [System.Collections.Specialized.OrderedDictionary]$subject, 
            
            [Parameter(Mandatory=$true)]
            [ValidateSet('MultipleCSR','SingleCSR')]
            [string]$requestType,

            [Parameter(Mandatory=$false)]
            [switch]$includePaaS,

            [ValidateSet('AAD','ADFS')]
            [string]$IdentitySystem,

            [Parameter(Mandatory=$true)]
            [ValidateNotNullOrEmpty()]
            [ValidateScript({Test-Path $_ -PathType Container})]
            $OutputRequestPath = $PSScriptRoot

          )
    
    $thisFunction = $MyInvocation.MyCommand.Name
    
    # Get template Data for SANs
    $data = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.PublicCertificateRequestData.psd1
    
    # Generate environment specific Deployment SANs, and force region and fqdn to lowercase
    $deploySANs = $data.deploySANs -f $regionName.ToLower(),$externalFQDN.ToLower()
    if ($IdentitySystem -eq 'ADFS')
    {
        $deploySANs += $data.adfsSANs -f $regionName.ToLower(),$externalFQDN.ToLower()
    }
    Write-AzsReadinessLog -Message ("Set Deployment SANs to: {0}" -f $deploySANs) -Type Info -Function $thisFunction

    # If applicable, Generate environment specific Deployment SANs, and force region and fqdn to lowercase
    if ($includePaaS)
    {
        $paasSANs = $data.paasSANs -f $regionName.ToLower(),$externalFQDN.ToLower()
        Write-AzsReadinessLog -Message ("Set PaaS SANs to: {0}" -f $paasSANs) -Type Info -Function $thisFunction
        $completeSANs = $deploySANs + $paasSANs
    }
    else
    {
        $completeSANs = $deploySANs
    }

    Write-AzsReadinessLog -Message ("Complete SAN list is: {0}" -f $completeSANs) -Type Info -Function $thisFunction

    # If the request type is for multiple SANs generate one request file with all SANs, else generate individual single SAN requests
    Write-AzsReadinessLog -Message ("CSR RequestType is: {0}" -f $requestType) -Type Info -Function $thisFunction
    if ($requestType -eq 'SingleCSR')
    {
        $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $completeSANs -SubjectToChange $subjectHash
        Write-AzsCertificateRequestFileInternal -subjectAltNames $completeSANs -subject $subject -OutputRequestPath $OutputRequestPath
    }
    elseif ($requestType -eq 'MultipleCSR')
    {
        foreach ($individualSAN in $completeSANs.split('&'))
        {
            if ($individualSAN -match '\*.appservice.|\*.scm.appservice.|\*.sso.appservice.')
            {
                Write-AzsReadinessLog -Message ("Skipping {0} as part of multi SAN request later" -f $individualSAN) -Type Warning -Function $thisFunction
            }
            else
            {
                $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $individualSAN -SubjectToChange $subjectHash
                Write-AzsCertificateRequestFileInternal -subjectAltNames $individualSAN -subject $subject -OutputRequestPath $OutputRequestPath
            }
        }
        # special case for app service web default ssl is a multi SAN request
        $appSvcWebDefaultSSLSANs = $data.AppSvcWebTrafficDefault -f $regionName.ToLower(),$externalFQDN.ToLower()
        $subject = Set-AzsCertificateCommonName -SubjectAlternativeNames $appSvcWebDefaultSSLSANs -SubjectToChange $subjectHash
        Write-AzsCertificateRequestFileInternal -subjectAltNames $appSvcWebDefaultSSLSANs -subject $subject -OutputRequestPath $OutputRequestPath
    }
    else
    {
        Write-AzsReadinessLog -Message ("Unexpected RequestType: {0}" -f $requestType) -Type Error -Function $thisFunction
        throw ("Unexpected RequestType: $requestType" -f $thisFunction)
    }
}

function Set-AzsCertificateCommonName
{
    <#
    .SYNOPSIS
        Set's the CN of the subject to the first DNS name in the SAN
    .DESCRIPTION
        To avoid potential client problems the common name will be made consistent with
        the first DNS Name in the SubjectAlternativeNames
    .EXAMPLE
        Set-AzsCertificateCommonName -subjectAlternativeNames $SANs -SubjectToChange $subject
        This will set the common regardless of it's previous state or value.
    .INPUTS
        SubjectAlternativeNames - string - SANs to parse common from.
        SubjectToChange - OrderedDictionary - may or may not contain a common name.
    .OUTPUTS
        SubjectChanged - OrderedDictionary - with the new or replaced common name
    .NOTES
        General notes
    #>

    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
    [OutputType([System.Collections.Specialized.OrderedDictionary])]
    param ([string]$SubjectAlternativeNames,
           [System.Collections.Specialized.OrderedDictionary]$SubjectToChange
    )

    $thisFunction = $MyInvocation.MyCommand.Name
    
    # Write CN as first SAN, overwrite as needed.
    $commonName = $SubjectAlternativeNames.split('&')[0].Replace('dns=','')
    if ($SubjectToChange.CN)
    {
        Write-AzsReadinessLog -Message ("Found CN = {0} and will remove it" -f $SubjectToChange.CN) -Type Info -Function $thisFunction
        $SubjectToChange.Remove('CN')
    }
    $SubjectToChange.Insert(0, 'CN',$commonName)
    Write-AzsReadinessLog -Message ("Inserted CN = {0}" -f $commonName) -Type Info -Function $thisFunction
    $SubjectChanged = $SubjectToChange
    $SubjectChanged
}
# SIG # Begin signature block
# MIIdqAYJKoZIhvcNAQcCoIIdmTCCHZUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUDqIglDgqQfjUIUAKvH4bQf6d
# IVqgghhUMIIEwjCCA6qgAwIBAgITMwAAAL+RbPt8GiTgIgAAAAAAvzANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ5
# WhcNMTgwOTA3MTc1ODQ5WjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046
# NTdDOC0yRDE1LTFDOEIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt7X+GwPaidVcV
# TRT2yohV/L1dpTMCvf4DHlCY0GUmhEzD4Yn22q/qnqZTHDd8IlI/OHvKhWC9ksKE
# F+BgBHtUQPSg7s6+ZXy69qX64r6m7X/NYizeK31DsScLsDHnqsbnwJaNZ2C2u5hh
# cKsHvc8BaSsv/nKlr6+eg2iX2y9ai1uB1ySNeunEtdfchAr1U6Qb7AJHrXMTdKl8
# ptLov67aFU0rRRMwQJOWHR+o/gQa9v4z/f43RY2PnMRoF7Dztn6ditoQ9CgTiMdS
# MtsqFWMAQNMt5bZ8oY1hmgkSDN6FwTjVyUEE6t3KJtgX2hMHjOVqtHXQlud0GR3Z
# LtAOMbS7AgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQU5GwaORrHk1i0RjZlB8QAt3kX
# nBEwHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ
# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB
# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAQEAjt62jcZ+2YBqm7RKit827DRU9OKioi6HEERT0X0bL+JjUTu3
# 7k4piPcK3J/0cfktWuPjrYSuySa/NbkmlvAhQV4VpoWxipx3cZplF9HK9IH4t8AD
# YDxUI5u1xb2r24aExGIzWY+1uH92bzTKbAjuwNzTMQ1z10Kca4XXPI4HFZalXxgL
# fbjCkV3IKNspU1TILV0Dzk0tdKAwx/MoeZN1HFcB9WjzbpFnCVH+Oy/NyeJOyiNE
# 4uT/6iyHz1+XCqf2nIrV/DXXsJYKwifVlOvSJ4ZrV40MYucq3lWQuKERfXivLFXl
# dKyXQrS4eeToRPSevRisc0GBYuZczpkdeN5faDCCBgEwggPpoAMCAQICEzMAAADE
# 6Yn4eoFQ6f8AAAAAAMQwDQYJKoZIhvcNAQELBQAwfjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2ln
# bmluZyBQQ0EgMjAxMTAeFw0xNzA4MTEyMDIwMjRaFw0xODA4MTEyMDIwMjRaMHQx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xHjAcBgNVBAMTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAIiKuCTDB4+agHkV/CZg/HKILPr0o5eIlka3o8tfiS86My4ekXj6fKkfggG1
# essavAPKRuvFmff7BB3yhQr/Im6h8mc9xScY5Sgf9QSUQWPs47oVjO0TmjXeOHBU
# bzvsrUUJMEnBvo8wmQzLdsn3c5UWd9GLu5THCIUg7R6oNfFxwuB0AEuK0tyR69Z4
# /o36rWCIPb25H65il7/FhLGQrtavK9NU+zXazXGS5h7/7HFry38IdnTgEFFI1PEA
# yEhMowc15VkN/XycyOZa44X11poPH46m5IQXwdbKnx0Bx/1IpxOSM5chSDL4wiSi
# ALK+U8qDbilbge84boDzu+wTC+sCAwEAAaOCAYAwggF8MB8GA1UdJQQYMBYGCisG
# AQQBgjdMCAEGCCsGAQUFBwMDMB0GA1UdDgQWBBTL1mKEz2A56v9nwlzSyLurt8MT
# mDBSBgNVHREESzBJpEcwRTENMAsGA1UECxMETU9QUjE0MDIGA1UEBRMrMjMwMDEy
# K2M4MDRiNWVhLTQ5YjQtNDIzOC04MzYyLWQ4NTFmYTIyNTRmYzAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AAYWH9tXwlDII0+iUXjX7fj9zb3VwPH5G1btU8hpRwXVxMvs4vyZW5VfETgowAVF
# E+CaeYi8Zqvbu+sCVSO3PSN4QW2u+PEAWpSZihzMCZXQmhxEMKmlFse6R1v1KzSL
# n49YN8NOHK8iyhDN2IIQqTXwriLIjySmgYvfJxzkZh2JPi7/VwNNwW6DoDLrtLMv
# UFZdBrEVjMgdY7dzDOPWeiYPKpZFpzKDPpY+V0l3I4n+sRDHiuUIFVHFK1oxWzlq
# lqikiGuWKG/xxK7qvUUXzGJOgbVUGkeOmKVtwG4nxvgnH8jtIKkLsfHOC5qU4mqd
# aYOhNtdtIP6F1f/DuJc2Cf49FMGYFKnAhszvgsGrVSRDGLVIhXiG0PnSnT8Z2RSJ
# 542faCSIaDupx4BOJucIIUxj/ZyTFU0ztVZgT9dKuTiO/y7dsV+kQ2vJeM+xu2uP
# g2yHcqrqpfuf3RrWOfxkyW0+COV8g7GtvKO6e8+WVqR6WMsSR2LSIe/8PMQxC/cv
# PmSlN29gUD+3RJBPoAuLvn5Y9sdnh2HbnpjEyIzLb0fhwC6U7bH2sDBt7GpJqOmW
# dsi9CMT+O/WuczcGslbPGdS79ZTKhxzygGoBT7YbgXOz01siPzpYGN+I7mfESacv
# 3CWLPV7Q7DREkR28kQx2gj7vxNgtoQQCjkj5790CzwOiMIIGBzCCA++gAwIBAgIK
# YRZoNAAAAAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29t
# MRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEw
# NDAzMTMwMzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4k
# D+7Rp9FMrXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMk
# h53y9GccLPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDl
# KEYuJ6yGT1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gA
# SkdmEScpZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1U
# n68eeEExd8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIB
# pzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWz
# DzALBgNVHQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAU
# DqyCYEBWJ5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20x
# GTAXBgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBS
# b290IENlcnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNV
# HR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9w
# cm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQG
# CCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01p
# Y3Jvc29mdFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQS
# ooxtYrhXAstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBT
# Fd1Pq5Lk541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2Oawpylbih
# OZxnLcVRDupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfr
# Tot/xTUrXqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWG
# zFFW6J1wlGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H21
# 46SodDW4TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4i
# IdBD6Svpu/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2
# sWo9iaF2YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1
# sMpjtHhUBdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/J
# mu5J4PcBZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0wggd6
# MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDla
# Fw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS6
# 8rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15
# ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+er
# CFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVc
# eaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGM
# XeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/
# U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwj
# p6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwC
# gl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1J
# MKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3co
# KPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfe
# nk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAw
# HQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoA
# UwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQY
# MBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6
# Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1
# dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAC
# hkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1
# dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4D
# MIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBs
# AF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcN
# AQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjD
# ctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw
# /WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkF
# DJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3z
# Dq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEn
# Gn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1F
# p3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0Qax
# dR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AAp
# xbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//W
# syNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqx
# P/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIEvjCC
# BLoCAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo
# MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAMTp
# ifh6gVDp/wAAAAAAxDAJBgUrDgMCGgUAoIHSMBkGCSqGSIb3DQEJAzEMBgorBgEE
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ
# BDEWBBRsY0HaYb9mKQDolyhwNGxUd6GsUDByBgorBgEEAYI3AgEMMWQwYqBIgEYA
# TQBpAGMAcgBvAHMAbwBmAHQAIABBAHoAdQByAGUAUwB0AGEAYwBrACAAUABhAHIA
# dABuAGUAcgBUAG8AbwBsAGsAaQB0oRaAFGh0dHA6Ly9Db2RlU2lnbkluZm8gMA0G
# CSqGSIb3DQEBAQUABIIBADXeuWKrrefOyVCitgARc8rfAs8wroCJdyXxPzShsqzM
# S2b2s78+ZH/YinqjA3vky5Eih+AzRVnFdrjUM6PyMkIyNtQQiq9HzlQBdyeNZiEa
# InMKPDGSI4+zMq7GvNSszNJEqHRRzBxFFD3rFCsMd027C0h78NBDHX3FhwwLEebG
# OAnPmYjZxviT1ekf7qyylb+u3JVelfFs15wnX/aQlZTpkOrFzYJ7ZzxY+6Av0afV
# YY47e1RCsq1PLWkpFSx1wyb3+L4wjjXTKHU1IlF2502J2XrSz291XEjvWaYUfGXb
# hEu91/Lr0wBunA4wbS9o7K+9IaFhrGfvfjnK++DXMdShggIoMIICJAYJKoZIhvcN
# AQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBAhMzAAAA
# v5Fs+3waJOAiAAAAAAC/MAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZI
# hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODA0MjcwOTI5MzRaMCMGCSqGSIb3DQEJ
# BDEWBBQj/eBa5Q65lV0Z5sYZgumJPUkiizANBgkqhkiG9w0BAQUFAASCAQAa8K/n
# ZleqdpB/BXDXA0QTIIMyzbpjh2QBSLYjLAkxQBas1YtaY+R+2I4C+TP8/UrJyD6L
# aNQBB9zTCZkju6aoWh/SK2gG6gZOtDGz5vjsuYtjjpBe5Fh174dHW2nVywFW5eln
# MvHLn6OQWmxbhCEXv0R9ejnxN9zPhOCEzpm5OT/ZNFxim9FKNED7h3MIBGY8dQHf
# /wb30NY7BAFHy+X/b1NTzw4q2DhCQLK5an+mPNp50N3l/G3+sc0KKgLRcfXOR3Q+
# 9DwMhC5peQhPZJQUv0dxVC8JJQ/ia9O4wTPukO6fKLedKKyEt0VUjokZpfQbvIVW
# DXzcU2v6DiPpY46F
# SIG # End signature block