CertificateValidation/Microsoft.AzureStack.PublicCertificateRequest.Internal.psm1

function Set-CertificateSubject {
    <#
    .SYNOPSIS
        Set Distinguished name for certificate request
    .DESCRIPTION
        Checks if Common Name is present and honours CN in distinguished name.
        If common name is not present, it preprends the target RP hostname to the distinguished name as a common name value.
    #>

    param (
            [System.Security.Cryptography.X509Certificates.X500DistinguishedName]$distinguishedName,
            [System.Security.Cryptography.X509Certificates.X500DistinguishedNameFlags]$distinguishedNameFlag = 'UseUTF8Encoding',
            [string]$commonName
            )
    try {
        $thisFunction = $MyInvocation.MyCommand.Name
        if ($distinguishedName) {
            $distinguishedNameExpanded = $distinguishedName.Format($true)
            $distinguishedNameString = $distinguishedName.Format($false)
    
            Write-AzsReadinessLog -Message ("User DN String is: {0}, flag: {1}" -f $distinguishedNameString,$distinguishedNameFlag) -Type Info -Function $thisFunction
            $commonNamePair = $distinguishedNameExpanded.trim() -split "`n" | Where-Object { $PSITEM -imatch 'cn='}
        }
        
        if ($commonNamePair) {
            Write-AzsReadinessLog -Message ("User DN String contains common name, no change: {0}" -f $distinguishedNameString) -Type Info -Function $thisFunction
        }
        else {
            $distinguishedNameString = "CN={0}" -f (($commonName,$distinguishedName.Name) -join ',')
            Write-AzsReadinessLog -Message ("Calculated DN String is: {0}, flag: {1}." -f $distinguishedNameString,$distinguishedNameFlag) -Type Info -Function $thisFunction
        }
        return [System.Security.Cryptography.X509Certificates.X500DistinguishedName]::new($distinguishedNameString,[System.Security.Cryptography.X509Certificates.X500DistinguishedNameFlags]::UseUTF8Encoding)
    }
    catch {
        Write-AzsReadinessLog -Message ("Setting Certificate Subject failed with exception: {0}" -f $_.exception.message) -Type Error -Function $thisFunction -toScreen
        break
    }
}

function Get-CSRFileName {
    param (
        [array]$commonName
    )
    $filename = "{0}_certrequest.req" -f $commonName[0].Replace('.', '_').Replace('*', 'wildcard')
    return $filename
}

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 -KeyLength $KeyLength -HashAlgorithm $HashAlgorithm -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.
        KeyLength - int - Defines the length of the public and private key
        HashAlgorithm - string - Hash Algorithm to be used for this request.
    .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()]
        [string]$subject,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$subjectAltNames,

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

        [Parameter(Mandatory = $false)]
        [ValidateSet(2048, 4096, 8192)]
        [int]$KeyLength = 2048,

        [Parameter(Mandatory = $false)]
        [ValidateSet('SHA256', 'SHA384', 'SHA512')]
        [string]$HashAlgorithm = 'SHA256',

        [Parameter(Mandatory = $false)]
        [string[]]$KeyUsage = @('DigitalSignature','KeyEncipherment'),

        [Parameter(Mandatory = $false)]
        $KeyUsageEKUExtension = @('Server Authentication','Client Authentication'),

        [Parameter(Mandatory = $false)]
        [System.Security.Cryptography.X509Certificates.X500DistinguishedNameFlags]$DistinguishedNameFlag = $null
    )

    $thisFunction = $MyInvocation.MyCommand.Name
    try {

        # translate DistinguishNameFlag
        $x500flagValue = switch ($DistinguishedNameFlag) {
            'DoNotUsePlusSign' {'0x20000000'}
            'DoNotUseQuotes'   {'0x10000000'}
            'ForceUTF8Encoding'{'0x80000'}
            'None'             {'0'}
            'Reversed'         {'0x2000000'}
            'UseCommas'        {'0x4000000'}
            'UseNewLines'      {'0x8000000'}
            'UseSemicolons'    {'0x40000000'}
            'UseT61Encoding'   {'0x20000'}
            'UseUTF8Encoding'  {'0x40000'}
            'Default'          {'0'}
        }

        # if name flag isn't 'none' write the flag in the inf file, otherwise write nothing.
        if ($x500flagValue -ne '0') {
            $X500NameFlags = "X500NameFlags = $x500flagValue`n`r"
        }

        # Get Key Usages Strings
        $keyUsageStrings = Format-KeyUsage -KeyUsage $KeyUsage

        # Get Enhanced Key Usage strings
        $EKUInfStrings = Format-EnhancedKeyUsage -EKUExtension $KeyUsageEKUExtension

        # Get SAN DNS names formatted
        $sanString = Format-SubjectAlternativeNames -subjectAltNames $subjectAltNames

        # Get INF Template and replace placeholders with user data
        $data = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.PublicCertificateRequestData.psd1
        $infTemplate = $data.requestINF
        $requestINF = $infTemplate.Replace('[[SubjectString]]', $subject.replace('"',''))
        $requestINF = $requestINF.Replace('[[KeyUsage]]',$keyUsageStrings)
        $requestINF = $requestINF.Replace('[[X500]]', $X500NameFlags)
        $requestINF = $requestINF.Replace('[[SANStrings]]', $sanString)
        $requestINF = $requestINF.Replace('[[KeyLength]]', $KeyLength)
        $requestINF = $requestINF.Replace('[[HashAlgorithm]]', $HashAlgorithm)
        $requestINF = $requestINF.Replace('[[EnhancedKeyStrings]]', $EKUInfStrings.Strings)
        $requestINF = $requestINF.Replace('[[EnhancedKeyExtension]]', $EKUInfStrings.Extensions)

        $CertReqFilePaths = Get-CertReqFilePaths -filePath $OutputRequestPath
        $infFilePath = $CertReqFilePaths.infFilePath
        $csrFilePath = $CertReqFilePaths.csrFilePath
        $certReqLogPath = $CertReqFilePaths.certReqLogPath

        # 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"
        $process = Start-Process -FilePath certreq.exe `
            -ArgumentList $cmd `
            -WindowStyle Hidden `
            -PassThru `
            -Wait `
            -RedirectStandardOutput $certReqLogPath
        if ($process.ExitCode -ne 0) {
            $certReqLogContent = Get-Content $certReqLogPath
            throw $certReqLogContent
        }
        Write-AzsReadinessLog -Message ("CSR generating for following SAN(s): {0}" -f ($subjectAltNames -join ',')) -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
        throw ("CSR generation failed with: {0}" -f $_.exception)
    }
    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 Format-KeyUsage {
    param([string[]]$KeyUsage)
    $thisFunction = $MyInvocation.MyCommand.Name
    try {
        Write-AzsReadinessLog -Message ("Formating key {0} usage(s)." -f $KeyUsage.Length) -Type Info -Function $thisFunction
        $KeyUsageOutput = for ($i = 0; $i -lt $KeyUsage.Length; $i++) {
            # convert the value
            Write-AzsReadinessLog -Message ("Formating Key usage: {0}" -f $KeyUsage[$i]) -Type Info -Function $thisFunction
            $keyUsageValue = switch ($KeyUsage[$i]) {
                digitalSignature {'CERT_DIGITAL_SIGNATURE_KEY_USAGE'}
                nonRepudiation {'CERT_NON_REPUDIATION_KEY_USAGE'}
                keyEncipherment {'CERT_KEY_ENCIPHERMENT_KEY_USAGE'}
                dataEncipherment {'CERT_DATA_ENCIPHERMENT_KEY_USAGE'}
                keyAgreement {'CERT_KEY_AGREEMENT_KEY_USAGE'}
                keyCertSign {'CERT_KEY_CERT_SIGN_KEY_USAGE'}
                cRLSign {'CERT_CRL_SIGN_KEY_USAGE'}
                encipherOnly {'CERT_ENCIPHER_ONLY_KEY_USAGE'}
                decipherOnly {'CERT_DECIPHER_ONLY_KEY_USAGE'}
            }
            "`"$keyUsageValue`""
        }
        $keyUsageString = $KeyUsageOutput -join '|'
        $keyUsageString
    }
    catch {
        Write-AzsReadinessLog -Message ("Formatting key usage failed with exception: {0}" -f $_.exception) -Type Error -Function $thisFunction
        break
    }

}

function Format-EnhancedKeyUsage {
    <#
    .SYNOPSIS
        Crafts neccessary data for certificate request file
    .DESCRIPTION
        Creates strings section and extensions section data e.g.
        [Strings]
        szOID_SUBJECT_ALT_NAME2 = "2.5.29.17"
        szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
        szOID_PKIX_KP_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"
        szOID_PKIX_KP_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"
        [Extensions]
        %szOID_SUBJECT_ALT_NAME2% = "{text}[[SANStrings]]"
        %szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_PKIX_KP_SERVER_AUTH%,%szOID_PKIX_KP_CLIENT_AUTH%"
    #>

    param ($EKUExtension)
    $thisFunction = $MyInvocation.MyCommand.Name
    try {
        $dataFile = Import-PowerShellDataFile $PSScriptRoot\Microsoft.AzureStack.CertificateConfig.psd1
        $EKUHashtable = $dataFile.CertificateAttributes.EnhancedKeyUsage

        Write-AzsReadinessLog -Message ("Formating Enhanced Key usages: {0}" -f ($EKUExtension -join ',')) -Type Info -Function $thisFunction
        if ($null -ne $EKUExtension) {

            $enhancedStrings = @()
            $enhancedExtensions = @()
            $enhancedStrings += "szOID_ENHANCED_KEY_USAGE = `"2.5.29.37`""

            foreach ($eku in $EKUExtension) {
                Write-AzsReadinessLog -Message ("Formating Key usage: {0}" -f $eku) -Type Info -Function $thisFunction
                $ekuData = $EKUHashtable[$eku]
                # if the eku isn't present in the config, treat it as user supplied
                if (-not $ekuData) {
                    $ekuData = @{}
                    $eku.keys | ForEach-Object {
                        $stringName = "XCN_OID_{0}" -f $_.replace(' ','_')
                        $ekuData.Add($stringName, $eku[$_])
                    }
                }
                # Construct string format
                $ekuString = "{0} = `"{1}`"" -f $ekuData.GetEnumerator().Name,$ekuData[$ekuData.GetEnumerator().Name]
                Write-AzsReadinessLog -Message ("EKU String: {0}" -f $ekuString) -Type Info -Function $thisFunction

                # Append string section with EKU and OID
                $enhancedStrings += $ekuString.replace('XCN_','sz')
                Write-AzsReadinessLog -Message ("Enhanced String: {0}" -f $enhancedStrings) -Type Info -Function $thisFunction

                # Append Extension section with EKU and OID
                $enhancedExtensions += $ekuData.GetEnumerator().Name.replace('XCN_','sz')
                Write-AzsReadinessLog -Message ("EKU Extension: {0}" -f $enhancedExtensions) -Type Info -Function $thisFunction
            }

            $enhancedExtensionString += "%szOID_ENHANCED_KEY_USAGE% = `"{{text}}%{0}%`"" -f ($enhancedExtensions -join '%,%')
        }
        @{Strings = ($enhancedStrings -join "`n"); Extensions = $enhancedExtensionString}
    }
    Catch {
        Write-AzsReadinessLog -Message ("Formatting key usage failed with exception: {0}" -f $_.exception) -Type Error -Function $thisFunction
        break
    }
}

function ConvertTo-EnhancedKeyUsage {
    <#
    .SYNOPSIS
        Converts custom input from user to Oid collection of Enhanced Key Usage
    .DESCRIPTION
        Converts custom input from user to Oid collection of Enhanced Key Usage
    .EXAMPLE
        PS C:\> ConvertTo-EnhancedKeyUsage @('Server Authentication','Client Authentication',@{'My_Custom_Name' = '1.3.6.1.5.15.7.3.2'})
        Converts custom input from user to Oid collection of Enhanced Key Usage
    #>

    param ($EnhancedKeyUsages)
    $thisFunction = $MyInvocation.MyCommand.Name
    try {
        $EKUCollection = [System.Security.Cryptography.OidCollection]::new()
        $EKUCollection += foreach ($EnhancedKeyUsage in $EnhancedKeyUsages) {
                if ($EnhancedKeyUsage -is [HashTable]) {
                    $EnhancedKeyUsage.keys | ForEach-Object {
                        $oid = [System.Security.Cryptography.Oid]::new($EnhancedKeyUsage[$_], $_)
                        [void]$EKUCollection.Add($oid)
                        Write-AzsReadinessLog -Message ('Added OID {0} ({1})' -f $oid.Value,$oid.FriendlyName) -Type Info -Function $thisFunction 
                    }
                }
                else {
                    $oid = [System.Security.Cryptography.Oid]::new($EnhancedKeyUsage)
                    [void]$EKUCollection.Add($oid)
                    Write-AzsReadinessLog -Message ('Added OID {0} ({1})' -f $oid.Value,$oid.FriendlyName) -Type Info -Function $thisFunction
                }
        }
        $EKUCollection | Foreach-Object {
            if (!$_.FriendlyName -or !$_.Value) {
                $errorMsg = ("Invalid Oid data. `nName: '{0}' `nValue: '{1}' `nEnsure input is valid. For example, the following will request Client and Server Auth key usage and the custom usage: `n@('Client Authentication','Server Authentication',@{{'Custom Usage' = '1.3.6.1.5.7.8.2.1'}})" -f $_.friendlyName, $_.Value)
                Write-AzsReadinessLog -Message $errorMsg -Type Error -Function $thisFunction
                throw $errorMsg
            }
        }
    }
    catch {
        throw $_
    }
}

function Format-SubjectAlternativeNames {
    <#
    .SYNOPSIS
        Formats array of DNS names for SAN
    .DESCRIPTION
        Creates neccessary string for SAN in certificate request inf config.
        %szOID_SUBJECT_ALT_NAME2% = "{text}dns=*.queue.east.azurestack.contoso.com"
    #>

    param ([string[]]$subjectAltNames)
    $thisFunction = $MyInvocation.MyCommand.Name
    try {
        $sanStrings = @()
        foreach ($san in $subjectAltNames) {
            Write-AzsReadinessLog -Message ("Formatting Subject Alternative DNS Names : {0}" -f $san) -Type Info -Function $thisFunction
            $sanStrings += "dns={0}" -f $san
        }
        $sanStrings -join "&"
    }
    catch {
        Write-AzsReadinessLog -Message ("Formatting Subject Alternative DNS Names failed with exception: {0}" -f $_.exception) -Type Error -Function $thisFunction
        break
    }
}

function Test-DistinguishedName {
    <#
    .SYNOPSIS
        Ensure user specified distinguished name is valid.
    .DESCRIPTION
        Ensure the distringuished name has the rght number of rdn when it is formatted
    .NOTES
        General notes
    #>

    param (
        [System.Security.Cryptography.X509Certificates.X500DistinguishedName]$DistinguishedName,
        [System.Security.Cryptography.X509Certificates.X500DistinguishedNameFlags]$DistinguishedNameFlag
    )
    $thisFunction = $MyInvocation.MyCommand.Name
    try {
        $dn = [Security.Cryptography.X509Certificates.X500DistinguishedName]::new($distinguishedName.Name,$DistinguishedNameFlag)
        $rdnCount = $distinguishedName.Name.ToCharArray() | Group-Object -NoElement | Where-Object Name -eq '=' | Select-Object -ExpandProperty Count
        $dnFormat = $dn.Format($true)
        $formatLineCount = $dnFormat | Measure-Object -Line | Select-Object -ExpandProperty Lines

        # Checking for common issues
        if ($dnFormat -match ',' -and $DistinguishedNameFlag -ne 'UseSemicolons') {
            throw ("One or more RDNs appear to contain commas, the correct format for the distinguished name is to seperate RDNs with semi-colons and pass the UseSemiColons DistinguishedNameFlag")
        }

        # if the number of rdns doesn't match the number of '=' characters, the format probably isn't what the user intended.
        if ($rdnCount -ne $formatLineCount) {
            throw ("Error validating the distinguish name provided: {0}" -f $dnFormat)
        }
        
        Write-AzsReadinessLog ("Validated the distinguished name: {0}" -f $dnFormat) -Type Info -Function $thisFunction
        return $true
    }
    catch {
        Write-AzsReadinessLog ("Check the input of the distinguished name. Error {0}" -f $_.exception.message) -type Error -Function $thisFunction -toScreen
        throw $_.exception.message
    }
    
}

function Get-CertReqFilePaths {
    <#
    .SYNOPSIS
        Short description
    .DESCRIPTION
        Long description
    .EXAMPLE
        PS C:\> $FilePath = Join-Path -Path $OutputRequestPath -ChildPath $subjectAltNames[0].Replace('.', '_').Replace('*', 'wildcard')
        Get-CertReqFilePaths -filePath $FilePath
        outputs inf path, req path and log path.
    #>

    param ($filePath)
    $thisFunction = $MyInvocation.MyCommand.Name

    $directory = Split-Path $filePath -Parent
    $fileSeed = Split-Path $filePath -Leaf

    #Create filepaths for inf, req and log as [host|wildcard].region.external.fqdn-CertRequest-
    $infFilePath = "{0}\{1}_CertRequest_{2}_ClearTextDoNotUse.inf" -f $directory, $fileSeed, (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')
    @{infFilePath = $infFilePath; csrFilePath = $csrFilePath; certReqLogPath = $certReqLogPath}
}
# SIG # Begin signature block
# MIIjkwYJKoZIhvcNAQcCoIIjhDCCI4ACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAGpr7UkJk+Xw7O
# Znn4ghutPevmZs8qPnjDK+QZvxonb6CCDYUwggYDMIID66ADAgECAhMzAAABiK9S
# 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0
# t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs
# 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd
# vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv
# V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W
# MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q
# qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X
# 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P
# mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM
# i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT
# GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz
# LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM
# SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa
# 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV
# Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+
# r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFWQwghVgAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA
# AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPUm
# EjvFWMCv2WvzfN5pO8ff7woBow35kVDy1XnzBJl1MEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAapWybRieBsEBnC1QnYRFDYi+lGImoTH49Pcd
# ERTBA/E0bYCmHsxU906JQXMk+379UWuJ1JWA2Hinhgf/2Wp6IFDLDYqrjvZkpnKP
# 18FGNHQYfVNIXv0h2ncMUtrftjDfOklKRtKT/PR07d+QJJnpCH8kj5lZQs4y1YJM
# lVV64h276evCCpBhs5tZKoh5SzzCOPIyKz5hJHdHf5A1nHAfTicb8vzXZ+OEI/UE
# Jkvw9LWDHJsJE2HqgiLtyvYR+NBKknc2jy8cF6+bPLIy6LEauEiHfS8gaF5wwtX6
# BjcMuCAqtAa+LPJ2lltQYtJEpP+0Cn8EWbzvzNWAydOcwsplnKGCEu4wghLqBgor
# BgEEAYI3AwMBMYIS2jCCEtYGCSqGSIb3DQEHAqCCEscwghLDAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCBwBcPaUtWabEmNLf/fvTCf3FysVQJ3px40
# UB096lKm9QIGXtUFW83UGBMyMDIwMDYyMjIxNDM0MC4zMzJaMASAAgH0oIHUpIHR
# MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL
# EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh
# bGVzIFRTUyBFU046RjdBNi1FMjUxLTE1MEExJTAjBgNVBAMTHE1pY3Jvc29mdCBU
# aW1lLVN0YW1wIFNlcnZpY2Wggg5BMIIE9TCCA92gAwIBAgITMwAAASWL3otsciYx
# 3QAAAAABJTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
# MjAxMDAeFw0xOTEyMTkwMTE0NThaFw0yMTAzMTcwMTE0NThaMIHOMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg
# T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046
# RjdBNi1FMjUxLTE1MEExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQex9jdmBb7OHJ
# wSYmMUorZNwAcv8Vy36TlJuzcVx7G+lFqt2zjWOMlSOMkm1XoAuJ8VZ5ShBedADX
# DGDKxHNZhLu3EW8x5ot/IOk6izLTlAFtvIXOgzXs/HaOM72XHKykMZHAdL/fpZtA
# SM5PalmsXX4Ol8lXkm9jR55K56C7q9+hDU+2tjGHaE1ZWlablNUXBhaZgtCJCd60
# UyZvgI7/uNzcafj0/Vw2bait9nDAVd24yt/XCZnHY3yX7ZsHjIuHpsl+PpDXai1D
# we9p0ryCZsl9SOMHextIHe9qlTbtWYJ8WtWLoH9dEMQxVLnmPPDOVmBj7LZhSji3
# 8N9Vpz/FAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQU86rK5Qcm+QE5NBXGCPIiCBdD
# JPgwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAK
# BggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAkxxZPGEgIgAhsqZNTZk58V1v
# QiJ5ja2xHl5TqGA6Hwj5SioLg3FSLiTmGV+BtFlpYUtkneB4jrZsuNpMtfbTMdG7
# p/xAyIVtwvXnTXqKlCD1T9Lcr94pVedzHGJzL1TYNQyZJBouCfzkgkzccOuFOfeW
# PfnMTiI5UBW5OdmoyHPQWDSGHoboW1dTKqXeJtuVDTYbHTKs4zjfCBMFjmylRu52
# Zpiz+9MBeRj4iAeou0F/3xvIzepoIKgUWCZ9mmViWEkVwCtTGbV8eK73KeEE0tfM
# U/YY2UmoGPc8YwburDEfelegLW+YHkfrcGAGlftCmqtOdOLeghLoG0Ubx/B7sTCC
# BnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m
# dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1
# NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/
# aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxh
# MFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhH
# hjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tk
# iVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox
# 8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJN
# AgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIox
# kPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P
# BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9
# lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQu
# Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3Js
# MFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAG
# A1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAG
# CCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEA
# dABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXED
# PZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgr
# UYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c
# 8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFw
# nzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFt
# w5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk
# 7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9d
# dJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zG
# y9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3
# yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7c
# RDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wkn
# HNWzfjUeCLraNtvTX4/edIhJEqGCAs8wggI4AgEBMIH8oYHUpIHRMIHOMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv
# ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBF
# U046RjdBNi1FMjUxLTE1MEExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAEXTL+FQbc2G+3MXXvIRKVr2oXCnoIGD
# MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEF
# BQACBQDimzFOMCIYDzIwMjAwNjIyMTczMTU4WhgPMjAyMDA2MjMxNzMxNThaMHQw
# OgYKKwYBBAGEWQoEATEsMCowCgIFAOKbMU4CAQAwBwIBAAICJ4AwBwIBAAICEhUw
# CgIFAOKcgs4CAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgC
# AQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQBzAR3+iKfMoznC
# ujJhI6OWQiL/3Gn4PLiDxSVdKD79gYuiaY0dfoXgra+c/q/4k5cgnE7KlmEsNSvS
# JQxUdoV1dqsvHs/E8D/YvTQ/+i5IPNB7j7TV2FSv5ZH2ofUVI8dcLzY8v0hX2Ryf
# ckehIM7fIrJzA+QXPwxKGEr8/ux43TGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBU
# aW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABJYvei2xyJjHdAAAAAAElMA0GCWCGSAFl
# AwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcN
# AQkEMSIEIAOJBUV8Y04IYpKCqqV2uhy1P7rPL/c0I49ZqUBToPfsMIH6BgsqhkiG
# 9w0BCRACLzGB6jCB5zCB5DCBvQQgXd/Gsi5vMF/6iX2CDh+VfmL5RvqaFkFwluiy
# je9B9w4wgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA
# ASWL3otsciYx3QAAAAABJTAiBCDgdvR+tpp+ZG60wSvwlQx7tW/67hC7ggOCMftd
# b8+MmzANBgkqhkiG9w0BAQsFAASCAQClOp5KggmGXziKHGMuTO9HUEVSHIjFb89j
# AVuop31cltN34byhThYJmRdUCBDSUCaLEjWBa6kebwS+DRJveHZYb0jelI7dnbmi
# BE5f7MyhwR1b0jQrpk3iUFguP1wj49LyliUDQS48U+6x6eV8u427YFe7SsgpAwnF
# yIDaDdrDm+smipq6yUilNHP2gtvrhjgMHt72PiPQUxaJoYxJJs6WtzhBkRA7SZq+
# lob4295ZC1711m0rvOJaocTLSW5J3lbdaTIRbsSrMMvSyRKc3PNhVcDkhsr+YbOk
# WmY9QPdEl/vOdVttW0yzRYRhm3/Un/bvLZnWvkZm38AK9IZg7iy2
# SIG # End signature block