Public/Set-ECMA2ConnectorEncryptedParameter.ps1

function Set-ECMA2ConnectorEncryptedParameter {
    <#
    .SYNOPSIS
        Sets an encrypted parameter value for an ECMA2 connector.
 
    .DESCRIPTION
        Updates an encrypted parameter (such as passwords) for an ECMA2 connector by encrypting
        the value using Windows DPAPI with LocalMachine scope and updating the Configuration.xml.
         
        IMPORTANT: This cmdlet MUST be executed locally on the ECMA2Host server where the connector
        is configured, or via Invoke-Command to that server. DPAPI encryption is machine-specific
        and encrypted values can only be used on the machine where they were created.
 
    .PARAMETER Name
        The name of the connector to update.
 
    .PARAMETER ParameterName
        The name of the encrypted parameter to set (e.g., "Password", "Password (auxiliary)", "SecretToken").
        Note: SecretToken is stored in ECMAConfig and will be created if it doesn't exist.
 
    .PARAMETER Value
        The plaintext value to encrypt and store. This will be encrypted using DPAPI LocalMachine scope.
        For SecretToken, use the same GUID value on all ECMA2Host servers that connect to the same Entra application.
 
    .PARAMETER ConfigurationPath
        Path to the Configuration.xml file. Defaults to the standard ECMA2Host location.
 
    .EXAMPLE
        Set-ECMA2ConnectorEncryptedParameter -Name "SQLConnector" -ParameterName "Password" -Value "SecureP@ssw0rd123"
        Sets the Password parameter for the SQLConnector on the local machine.
 
    .EXAMPLE
        # Remote execution via PowerShell remoting
        Invoke-Command -ComputerName "EcmaServer" -ScriptBlock {
            Import-Module ECMA2HostTools
            Set-ECMA2ConnectorEncryptedParameter -Name "SQLConnector" -ParameterName "Password" -Value "SecureP@ssw0rd123"
        }
 
    .EXAMPLE
        # Automated deployment with password from vault
        $password = Get-Secret -Name "SQL-Service-Account" -AsPlainText
        Invoke-Command -ComputerName "EcmaServer" -ScriptBlock {
            param($pwd)
            Import-Module ECMA2HostTools
            Set-ECMA2ConnectorEncryptedParameter -Name "SQLConnector" -ParameterName "Password" -Value $pwd
        } -ArgumentList $password
 
    .EXAMPLE
        # Pipeline from Get-ECMA2Connector
        Get-ECMA2Connector -Name "SQLConnector" |
            Set-ECMA2ConnectorEncryptedParameter -ParameterName "Password" -Value "SecureP@ssw0rd123"
 
    .EXAMPLE
        # Set SecretToken for a connector (use same value on all servers connecting to same Entra app)
        $secretToken = "12345678-abcd-ef12-3456-7890abcdef12"
        Set-ECMA2ConnectorEncryptedParameter -Name "SQLConnector" -ParameterName "SecretToken" -Value $secretToken
 
    .NOTES
        This cmdlet must run on Windows PowerShell 5.1 or be delegated to it from PowerShell 7+
        because DPAPI encryption requires the .NET Framework implementation.
         
        The cmdlet will automatically create a backup of Configuration.xml before making changes.
         
        After updating encrypted parameters, you should restart the ECMA2Host service for
        changes to take effect.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Name,

        [Parameter(Mandatory)]
        [string]$ParameterName,

        [Parameter(Mandatory)]
        [string]$Value,

        [Parameter()]
        [string]$ConfigurationPath = 'C:\Program Files\Microsoft ECMA2Host\Service\Configuration\Configuration.xml'
    )

    begin {
        Write-Verbose "Set-ECMA2ConnectorEncryptedParameter starting"
        
        # Check if running on Windows
        if (-not $IsWindows -and $null -ne $IsWindows) {
            throw "This cmdlet requires Windows operating system for DPAPI encryption"
        }
        
        # Verify we're running on the local machine (not via network path)
        if ($ConfigurationPath -match '^\\\\' -or $ConfigurationPath -match '^[A-Z]:' -and $ConfigurationPath -notmatch '^C:') {
            Write-Warning "Configuration path appears to be remote. DPAPI encryption is machine-specific and will only work on the local ECMA2Host server."
            Write-Warning "If you're trying to configure a remote server, use Invoke-Command to run this cmdlet ON that server."
        }
        
        # Check PowerShell version - we need to use Windows PowerShell 5.1 for ECMA2Host encryption
        if ($PSVersionTable.PSVersion.Major -ge 7) {
            Write-Verbose "Running in PowerShell 7+, will delegate to Windows PowerShell 5.1"
            $script:UseWindowsPowerShell = $true
        }
        else {
            Write-Verbose "Running in Windows PowerShell $($PSVersionTable.PSVersion)"
            $script:UseWindowsPowerShell = $false
            
            # Load the ECMA2Host encryption assembly
            $ecmaHostPath = 'C:\Program Files\Microsoft ECMA2Host\Service'
            $encryptedDll = Join-Path $ecmaHostPath 'Microsoft.ECMA2Host.Encrypted.dll'
            
            if (-not (Test-Path $encryptedDll)) {
                throw "Microsoft.ECMA2Host.Encrypted.dll not found at: $encryptedDll. Ensure ECMA2Host is installed."
            }

            try {
                # Load the assembly
                $script:Assembly = [System.Reflection.Assembly]::LoadFrom($encryptedDll)
                
                # Get all types in the assembly
                $types = $script:Assembly.GetTypes()
                Write-Verbose "Found types in assembly: $($types.FullName -join ', ')"
                
                # Look for types with encryption methods
                foreach ($type in $types) {
                    $encryptMethods = $type.GetMethods([System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::Static) | 
                        Where-Object { $_.Name -match 'Encrypt' -and -not ($_.Name -match 'Decrypt') }
                    if ($encryptMethods) {
                        Write-Verbose "Type $($type.Name) has encryption methods: $($encryptMethods.Name -join ', ')"
                        $script:EncryptType = $type
                        break
                    }
                }
                
                # Also keep EncryptTools for reference
                $script:EncryptToolsType = $script:Assembly.GetType('Microsoft.ECMA2Host.Encrypted.EncryptTools')
                
                Write-Verbose "Loaded ECMA2Host encryption assembly"
            }
            catch {
                throw "Failed to load ECMA2Host encryption assembly: $_"
            }
        }
    }

    process {
        try {
            # Get the connector
            Write-Verbose "Loading connector '$Name' from configuration"
            $connector = Get-ECMA2Connector -Name $Name -ConfigurationPath $ConfigurationPath -ErrorAction Stop
            
            if (-not $connector) {
                throw "Connector '$Name' not found in configuration"
            }

            # Special handling for SecretToken (it's in ECMAConfig, not Parameters)
            $isSecretToken = $ParameterName -eq 'SecretToken'
            
            if (-not $isSecretToken) {
                # Check if the parameter exists in the connector
                $paramExists = $connector.Parameters.PSObject.Properties.Name -contains $ParameterName
                if (-not $paramExists) {
                    throw "Parameter '$ParameterName' not found in connector '$Name'. Available parameters: $($connector.Parameters.PSObject.Properties.Name -join ', ')"
                }

                # Verify the parameter is supposed to be encrypted
                $param = $connector.Parameters.$ParameterName
                if ($param.Type -notin @('EncryptedString', 'Password')) {
                    Write-Warning "Parameter '$ParameterName' is of type '$($param.Type)'. This cmdlet is intended for encrypted parameters (EncryptedString/Password types)."
                }
            }
            else {
                Write-Verbose "Setting SecretToken (ECMAConfig element)"
                Write-Verbose "SecretToken value will be serialized and encrypted for this machine"
            }

            if ($PSCmdlet.ShouldProcess("$Name/$ParameterName", "Set encrypted parameter value")) {
                
                # Delegate to Windows PowerShell 5.1 if running in PowerShell 7+
                if ($script:UseWindowsPowerShell) {
                    Write-Verbose "Delegating encryption to Windows PowerShell 5.1"
                    
                    # Build command to run in Windows PowerShell 5.1
                    $modulePath = Split-Path -Parent $PSScriptRoot
                    $manifestPath = Join-Path $modulePath 'ECMA2HostTools.psd1'
                    
                    # Escape special characters in the value
                    $escapedValue = $Value -replace "'", "''"
                    
                    $ps51Command = @"
Import-Module '$manifestPath' -Force -WarningAction SilentlyContinue
Set-ECMA2ConnectorEncryptedParameter -Name '$Name' -ParameterName '$ParameterName' -Value '$escapedValue' -ConfigurationPath '$ConfigurationPath' -Verbose:`$$($VerbosePreference -eq 'Continue') | ConvertTo-Json -Depth 5 -Compress
"@

                    
                    $result = powershell.exe -NoProfile -NonInteractive -Command $ps51Command 2>&1
                    
                    # Filter out error messages to get only JSON
                    $jsonOutput = $result | Where-Object { $_ -is [string] -and $_ -match '^\{' }
                    
                    if (-not $jsonOutput) {
                        $errorMessages = $result | Where-Object { $_ -isnot [string] -or $_ -notmatch '^\{' }
                        throw "Failed to encrypt in Windows PowerShell 5.1. Errors: $($errorMessages -join '; ')"
                    }
                    
                    $returnObject = $jsonOutput | ConvertFrom-Json
                    return $returnObject
                }
                
                # Running in Windows PowerShell 5.1 - use DPAPI encryption with BinaryFormatter
                Write-Verbose "Encrypting value using DPAPI with BinaryFormatter serialization"
                
                try {
                    Add-Type -AssemblyName System.Security
                    
                    if ($isSecretToken) {
                        # SecretToken requires BinaryFormatter serialization
                        Write-Verbose "Using BinaryFormatter serialization for SecretToken"
                        
                        # Create a MemoryStream and BinaryFormatter
                        $memStream = New-Object System.IO.MemoryStream
                        $formatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
                        
                        # Serialize the string value
                        $formatter.Serialize($memStream, $Value)
                        $serializedBytes = $memStream.ToArray()
                        $memStream.Close()
                        
                        Write-Verbose "Serialized to $($serializedBytes.Length) bytes"
                        
                        # Encrypt using DPAPI LocalMachine scope
                        $encryptedBytes = [System.Security.Cryptography.ProtectedData]::Protect(
                            $serializedBytes,
                            $null,
                            [System.Security.Cryptography.DataProtectionScope]::LocalMachine
                        )
                        
                        $encryptedValue = [Convert]::ToBase64String($encryptedBytes)
                        Write-Verbose "Encrypted to $($encryptedBytes.Length) bytes, Base64 length: $($encryptedValue.Length)"
                    }
                    else {
                        # Regular parameters - just encrypt the UTF8 bytes
                        Write-Verbose "Using simple UTF8 encryption for password parameter"
                        $bytes = [System.Text.Encoding]::UTF8.GetBytes($Value)
                        $encryptedBytes = [System.Security.Cryptography.ProtectedData]::Protect(
                            $bytes,
                            $null,
                            [System.Security.Cryptography.DataProtectionScope]::LocalMachine
                        )
                        $encryptedValue = [Convert]::ToBase64String($encryptedBytes)
                    }
                    
                    Write-Verbose "Value encrypted successfully"
                }
                catch {
                    throw "Failed to encrypt value using DPAPI: $_"
                }
                
                # Load the XML configuration
                Write-Verbose "Loading Configuration.xml from: $ConfigurationPath"
                $config = New-Object System.Xml.XmlDocument
                $config.Load($ConfigurationPath)
                
                # Create backup
                $backupPath = "$ConfigurationPath.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
                Write-Verbose "Creating backup at: $backupPath"
                Copy-Item -Path $ConfigurationPath -Destination $backupPath -Force
                
                # Find the connector using XPath
                $connectorNode = $config.SelectSingleNode("//Connector[ECMAConfig/ProfileName='$Name']")
                if (-not $connectorNode) {
                    throw "Could not locate connector '$Name' in XML configuration"
                }
                
                if ($isSecretToken) {
                    # SecretToken is stored in ECMAConfig, not Parameters
                    Write-Verbose "Setting SecretToken in ECMAConfig"
                    $ecmaConfigNode = $connectorNode.SelectSingleNode('ECMAConfig')
                    $secretTokenNode = $ecmaConfigNode.SelectSingleNode('SecretToken')
                    
                    if ($secretTokenNode) {
                        Write-Verbose "Updating existing SecretToken"
                        $secretTokenNode.InnerText = $encryptedValue
                    }
                    else {
                        Write-Verbose "Creating new SecretToken node"
                        $secretTokenNode = $config.CreateElement('SecretToken')
                        $secretTokenNode.InnerText = $encryptedValue
                        # Insert after AutosyncTimer or ProfileName
                        $autoSyncNode = $ecmaConfigNode.SelectSingleNode('AutosyncTimer')
                        if ($autoSyncNode) {
                            $ecmaConfigNode.InsertAfter($secretTokenNode, $autoSyncNode) | Out-Null
                        }
                        else {
                            $ecmaConfigNode.AppendChild($secretTokenNode) | Out-Null
                        }
                    }
                }
                else {
                    # Regular parameter in ConnectorConfig/Parameters
                    Write-Verbose "Setting parameter '$ParameterName' in ConnectorConfig/Parameters"
                    $paramNode = $connectorNode.SelectSingleNode("ConnectorConfig/Parameters/Parameter[Name='$ParameterName']")
                    if (-not $paramNode) {
                        throw "Could not locate parameter '$ParameterName' in connector '$Name'"
                    }
                    
                    # Update the parameter value and ensure it's marked as encrypted
                    $valueNode = $paramNode.SelectSingleNode('Value')
                    $encryptedNode = $paramNode.SelectSingleNode('Encrypted')
                    
                    if ($valueNode) {
                        Write-Verbose "Updating parameter value"
                        $valueNode.InnerText = $encryptedValue
                    }
                    else {
                        Write-Verbose "Creating Value node"
                        $valueNode = $config.CreateElement('Value')
                        $valueNode.InnerText = $encryptedValue
                        $paramNode.AppendChild($valueNode) | Out-Null
                    }
                    
                    if ($encryptedNode) {
                        $encryptedNode.InnerText = 'true'
                    }
                    else {
                        Write-Verbose "Creating Encrypted node"
                        $encryptedNode = $config.CreateElement('Encrypted')
                        $encryptedNode.InnerText = 'true'
                        $paramNode.AppendChild($encryptedNode) | Out-Null
                    }
                }
                
                # Save the configuration
                Write-Verbose "Saving configuration to: $ConfigurationPath"
                $config.Save($ConfigurationPath)
                
                Write-Verbose "Parameter '$ParameterName' updated successfully"
                
                # Return summary
                [PSCustomObject]@{
                    PSTypeName = 'ECMA2Host.EncryptedParameterUpdate'
                    ConnectorName = $Name
                    ParameterName = $ParameterName
                    ConfigurationPath = $ConfigurationPath
                    BackupPath = $backupPath
                    Timestamp = Get-Date
                    ComputerName = $env:COMPUTERNAME
                    Success = $true
                }
            }
        }
        catch {
            Write-Error "Failed to set encrypted parameter '$ParameterName' for connector '$Name': $_"
            
            [PSCustomObject]@{
                PSTypeName = 'ECMA2Host.EncryptedParameterUpdate'
                ConnectorName = $Name
                ParameterName = $ParameterName
                ConfigurationPath = $ConfigurationPath
                BackupPath = $null
                Timestamp = Get-Date
                ComputerName = $env:COMPUTERNAME
                Success = $false
                Error = $_.Exception.Message
            }
        }
    }

    end {
        Write-Verbose "Set-ECMA2ConnectorEncryptedParameter completed"
    }
}

# SIG # Begin signature block
# MIIoYgYJKoZIhvcNAQcCoIIoUzCCKE8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBSBVP9w+V2dw+C
# 758D1SCw/yOgTmH7dJ/tIn9+PPgdnqCCIV8wggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTlaMGkx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQg
# MjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C0Cit
# eLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce2vnS
# 1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0daE6ZM
# swEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6TSXBC
# Mo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoAFdE3
# /hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7OhD26j
# q22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM1bL5
# OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z8ujo
# 7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05huzU
# tw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNYmtwm
# KwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP/2NP
# TLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0TAQH/
# BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYDVR0j
# BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E
# PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATANBgkq
# hkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95RysQDK
# r2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HLIvda
# qpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5BtfQ/g+
# lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnhOE7a
# brs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIhdXNS
# y0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV9zeK
# iwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/jwVYb
# KyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYHKi8Q
# xAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmCXBVm
# zGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l/aCn
# HwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZWeE4w
# gga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1
# c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphBcr48RsAcrHXbo0Zo
# dLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6pvF4uGjwjqNjfEvUi
# 6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHeHYNnQxqXmRinvuNg
# xVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEdgkFiDNYiOTx4OtiF
# cMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjUjsZvkgFkriK9tUKJ
# m/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bRVFLeGkuAhHiGPMvS
# GmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeSLsJygoLPp66bkDX1
# ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIVNSaz7BX8VtYGqLt9
# MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL6s36czwzsucuoKs7
# Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2ZdSoQbU2rMkpLiQ6bG
# RinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFUeEY0qVjPKOWug/G6
# X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAd
# BgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0jBBgwFoAU7NfjgtJx
# XWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
# BwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJo
# dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy
# bDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQEL
# BQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/T8ObXAZz8OjuhUxj
# aaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQE7jU/kXjjytJgnn0
# hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9rEVKChHyfpzee5kH0
# F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y1IsA0QF8dTXqvcnT
# mpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gxdEkMx1NKU4uHQcKf
# ZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3ty9qIijanrUR3anzE
# wlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcytL5TTLL4ZaoBdqbh
# OhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEBYTptMSbhdhGQDpOX
# gpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud/v4+7RWsWCiKi9EO
# LLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiSuEtQvLsNz3Qbp7wG
# WqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZPubdcMIIG7TCCBNWg
# AwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0Ex
# MB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVowYzELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBTSEEy
# NTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIwMjUgMTCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5gVrMrV7pvUf+GcAoB38o3
# zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN+vo8dE2/pPvOx/Vj8Tch
# TySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qome7MrxVyfQO9sMx6ZAWj
# FDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ//nBZZREr4h/GI6Dxb2Uo
# yrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouTMYFOnHoRh6+86Ltc5zjP
# KHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8DD+nigNJFmt6LAHvH3KS
# uNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnpJeItK/DhKbPxTTuGoX7w
# JNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP51ho1zBp+xUIZkpSFA8vW
# doUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49kPmk8iyyizNDIXj//cOg
# rY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5PWPsWeupWs7NpChUk555K
# 096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7YufAkprxMiXAJQ1XCmnCf
# gPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTkO/zy
# Me39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK6eQGfHrK4pBW9i/USezL
# TjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwgZUGCCsG
# AQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
# b20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNy
# dDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5j
# cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB
# CwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP2zWLpQq1b4URGnwWBdEZ
# D9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O6Lgjg8K8elC4+oWCqnU/
# ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskgiC3QYIUP2S3HQvHG1FDu
# +WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMUBaB5bdrPbF6MRYs03h4o
# bEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDFkxUGtMTaiLR9wjxUxu2h
# ECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+zJNEsuEB7O7/cuvTQasn
# M9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/lwd6UArb+BOVAkg2oOvol
# /DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxlRcGG0LIhp6GvReQGgMgY
# xQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2zRWVFjF7mcr4C34Mj3oc
# CVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJgbaP5t2nGj/ULLi49xTcB
# ZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOCIUjsarfNZzCCB20wggVV
# oAMCAQICEAnI7Fw0fQcgWcyoNeinb/gwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB
# MTAeFw0yMzAzMjkwMDAwMDBaFw0yNjA2MjIyMzU5NTlaMHUxCzAJBgNVBAYTAkFV
# MRgwFgYDVQQIEw9OZXcgU291dGggV2FsZXMxFDASBgNVBAcTC0NoZXJyeWJyb29r
# MRowGAYDVQQKExFEYXJyZW4gSiBSb2JpbnNvbjEaMBgGA1UEAxMRRGFycmVuIEog
# Um9iaW5zb24wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDHrKfntVGe
# XaDp6S/nqZuiKuhmIqivGTXM9VwXuzO3gV8FcuLWD+QciGujTkWBLHpVViPV5jtT
# PnD0uo0TK6WW/cbVB/jaSmTvnkrYYEwLZxDtXVmgCumOwB/2VY5oDk1mVwVYm4wB
# PyUCiH2cseB5uRTh+oat27JQPkVEKaNzUMTb9gLs3JCkMG1uwKFyDbnY9HbmAog2
# LIZ//Zh884C9FaTWEaZoBGu1loHNSR9e1fkmJWn+qjFqWKFrjg8Lg5bUh9qee6gC
# Nv+Ceq1GBL57O0GfbICFHRpVK+fen6dGOI7sqclRhO0a9GvD7Qci1lLqcle2eZCj
# 6/zEY3q1wJgZ3+gHYSN5GOho89+en2ZDwOPVLgiFxYMk2U/OAKOipcPtEaie9CQ7
# eOPVJMu4XWvofIdj4lHX+610Gplee5mOufpRwJnOPlIE7lrJ6cJ07jZZG2cUZwsN
# g/lt6raNmgYQ3m3Iimc4r34gFpVn03B7QqcveoDOS/jgeOXsw6VOigB9YcEUozkV
# JVucqBU11Gz1AUX5VNztm2dMHQCXslGGh1gGsjaMhX7ina5gi7SMe9ujtOnc/SoP
# nCX/tWXSeynFL2YEdnfBdfRVeRtQlTJzs4TGUdnZyHieYdBIHDijR5d4TChXVUce
# JYVvLXK0EDeGU9hIBnyPXwXNItxl0xQNMQIDAQABo4ICAzCCAf8wHwYDVR0jBBgw
# FoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFAUxVql07mJzafndN3rN
# ijPSXRlIMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYD
# VR0fBIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBT
# oFGgT4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0
# Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAz
# BgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20v
# Q1BTMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
# ZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy
# dC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQy
# MDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBYQAlozzK3
# Gn8A32eZnv51K5L+MmICIud+XHXTwJv9rMBg07s0lQVRyDAafC1i5s1zwNRm8QTp
# gOC/L7w4IxKUBfSPT4eTDWIGIYNMUqQCKffygKHODkJ8JckRjzfgo2smONMcU8+P
# 4R6IVoOK5yTCLlRI5DLSpzHU26Z6lPOcO/AEJXw+/b/4FkNnS9U959fBzhI07fFU
# rq8ZBIUOSN0h/Aq/WIVL/eDm1iFGzilLeUhu5v3fstpn5CkUjpkZbi0qGCz1m8d+
# aQK7GJGj6Y3+WJeY4iT2NxkMxFP0kVVtK68AwG7SkjdIClrWcYozw27PGkFGAoox
# X43ujlhheEZ5j0kIdBX/AMsz0HMfS40P/Fu4FBC7BOiBblz+W49ouoHi8uuS0XuO
# kGZWA6v2zGs1KGUE5Y3v4bOqZDi+H9Sr+7WyWZjBDVVVESTZng0Xo7zZYh2mhhAL
# /4hdGaO6ar4+MAgghht4/7DUeVkkWJ8X+cUOK/YvYGapOMo8JPwyQltq5ijQlKMT
# SGVodhCJTEg88NwzCpNspWXYmPywIuRpmwshi7erE8/yBNcNTWMK6f8+r+CPdZQ4
# HV4Pn05IYcbeO4VpozDg92WFUhc0JoPGpdYkP/ukWCoH7MMOuLSJMvCTjmV/97LP
# 7ocSlIzycWCZDsEMFMqAGM43LvwBOwctKzGCBlkwggZVAgEBMH0waTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENB
# MQIQCcjsXDR9ByBZzKg16Kdv+DANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3
# AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
# AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDYthSPpQxA
# E3Acr3STgcFfsusQ+5ncj7wq+T45wWXR9TANBgkqhkiG9w0BAQEFAASCAgBCu6Ef
# rTa0f1qZKGdKCuw6ioFHXawVws7EhSiNRJTzHfkzey44cuyyQCtv71MTP1hHIBWM
# CeHZT3k+GzJPXj+r+89Xz4Ji4MoqOgIqEyTnYuuOZRlhkk7HH3i0rnii8cjnN05v
# 4TsADKFTqK/wjO7D5/JT6w1RXUI0b2Jbnt8+Ra6kfKfUiUrYpTkGYGRrOv+BXDGn
# vZQIsq3m5MsDG07z5v1OX8q342RGVYCjhSi64GFckRa7RNIMgUvGr46kPXpppKAG
# ruRQSqUyf7oYD0/Ds+g2Cf7VD38nIPCaX29BPUV2fT0PhBL8ez/8l2bNHwCW3ZO5
# V06EYysSFwvDu/m6PRCmGpmeIFucdTT052JfdCRqirdrWiCdr5dXCs8L1TXGEj3W
# b/2EbuZp1N1fF553p9MQNBAaw650qi1wh3oIcUJbLoeSmQpZUG+Ro1WSx/kWbbqG
# KPFKerRzfuCa2BF5w49TuJDeMFCIfBjlfE+DkA+hHYEgjtbEjSVVQwZF4krGEvYV
# wtTr0fikHC4WHkNqQlCvCaVIwtkYp/Gh3wGLw0TK1wCS4s3Bl5HWTKRLNmnXeQe4
# iFOu6eKPa4aqGLjXuoet8doRr3pHgVc3V39p1dc+9+/6rrtSV5XZeIDRB/1q2lxJ
# snnlTWjnr/IkzXd8g/olugMTQTBro9kUtAwwSKGCAyYwggMiBgkqhkiG9w0BCQYx
# ggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg
# UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZI
# AWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
# BTEPFw0yNTEyMDkwMTEyNDhaMC8GCSqGSIb3DQEJBDEiBCBuKfUm3ZkqrMTjoJJ6
# 88zfm6vpQlLHjkoHT7/2zHB9GDANBgkqhkiG9w0BAQEFAASCAgBrWw+moghuUYKY
# tAPr+OIUyXPYZqTvBh0uwOFiGUfxwO3bPNyLpP6UsJCriw7TzcuHeMyEP2dWguNr
# DWems3b5jL4V/nR36QechUevfR43HDXYu67TXFrM+MaNsP+bhjCQ7HopKNNMvcNr
# 5IMecb1mGReCzeh0Gek7uaIzqfP30+YZadlNN/AurSUHTTbAlWOkgeqRvpOa6pjC
# s8mET3T6/c2ICsEEoAyT4AuBIYnMm23NK8Y/TrUE1VS42JHSSHDeFrrAtnMCJC9Z
# AYNOGQ0UzDulozTKOZZyksYQrZyZrBMQVDGTmEKwlRxiZggQPJGGccJfyTw04EDE
# IQR5bDeMyc1G3ie4hHuCX+YZklZbwW1JotIhlSgbCFYYfOLbzULKNmG4RrI2jbyA
# 2ljbqXJQCBOIP/foARLRqcYQY7enmaCLvKfhYzvGQlPcbd5KuCJjZUoPsZWbRj9v
# fDR9jK7pZzCAGBMHbZzySKnI1JZym2Q4cN2GU6FWkyNL3PiAQ8yJqD23XCu0TViZ
# ELTseggyW7u7HUQ5qPHho8b6iocFv+BLcLaMN+xEynJhQvk+W2CdmAlN8OtjrtFt
# z9cE0hC+5jpO55vEy4kdmQtgWHzVNT/mrSQkQxJUZkeMGzH9twD6DFxS2Zb7lkBP
# r/26yu4y6c+vDNZ2QyEnmM/ZJFEPkA==
# SIG # End signature block