public/Get-IvantiWCRegistry.ps1
|
<#
.SYNOPSIS Reads and parses Ivanti Workspace Control registry sets from a Building Block XML file or a standalone .reg file. .DESCRIPTION Accepts either: - An Ivanti Workspace Control Building Block XML file: extracts every <registry type="registry"> node, decodes the hex-encoded registryfile, and parses it. Name and description are taken from the <name> and <description> XML elements. - A standalone .reg file: parses it directly. Name and description are read from the ;<PFNAME> and ;<PFDESC> comment tags if present. Each value entry is returned as a structured object with its registry hive, key, value name, value data, value type, and an optional per-value description captured from the ;<PF>...</PF> comment tag that may follow the value line. Supports all standard .reg value types: - REG_SZ (quoted string or hex(1):) - REG_EXPAND_SZ (hex(2):) - REG_BINARY (hex: / hex(3):) - REG_DWORD (dword: / hex(4):) - REG_MULTI_SZ (hex(7):) - REG_QWORD (qword: / hex(b):) .PARAMETER Path Path to an Ivanti Workspace Control Building Block XML file or a standalone .reg file. .PARAMETER ExportFor Target export format. 'AppVentiX' adds the AppVentiXParams property to each output object. .EXAMPLE # From a Building Block XML Get-IvantiWCRegistry -Path 'C:\temp\LAB-BB.xml' .EXAMPLE # From a standalone .reg file, exported for AppVentiX Get-IvantiWCRegistry -Path 'C:\temp\registry.reg' -ExportFor AppVentiX .LINK Import-IvantiWCRegistry .NOTES Function : Get-IvantiWCRegistry Author : John Billekens Copyright : (c) John Billekens Consultancy & AppVentiX Version : 2026.0309.1200 #> function Get-IvantiWCRegistry { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path $_ -PathType Leaf })] [string]$Path, [ValidateSet('AppVentiX', 'WEM')] [string]$ExportFor = 'AppVentiX' ) #region Internal helpers function Convert-HexToString { param([string]$HexString) if ([string]::IsNullOrEmpty($HexString)) { return '' } $bytes = for ($i = 0; $i -lt $HexString.Length; $i += 2) { [System.Convert]::ToByte($HexString.Substring($i, 2), 16) } return [System.Text.Encoding]::GetEncoding(1252).GetString($bytes) } function Convert-RegistryHexToValue { param([string]$hexString, [string]$type) $hexData = $hexString -replace '^hex\(\w+\):', '' -replace '[,\s]', '' if ([string]::IsNullOrEmpty($hexData)) { return $null } try { $bytes = for ($i = 0; $i -lt $hexData.Length; $i += 2) { [System.Convert]::ToByte($hexData.Substring($i, 2), 16) } switch ($type) { 'REG_SZ' { return [System.Text.Encoding]::Unicode.GetString($bytes).TrimEnd([char]0) } 'REG_EXPAND_SZ' { return [System.Text.Encoding]::Unicode.GetString($bytes).TrimEnd([char]0) } 'REG_BINARY' { return [System.BitConverter]::ToString($bytes) } 'REG_DWORD' { if ($bytes.Count -eq 4) { return [System.BitConverter]::ToUInt32($bytes, 0) } return $null } 'REG_MULTI_SZ' { $fullString = [System.Text.Encoding]::Unicode.GetString($bytes) return ($fullString -split '\0' | Where-Object { $_ }) } 'REG_QWORD' { if ($bytes.Count -eq 8) { return [System.BitConverter]::ToUInt64($bytes, 0) } return $null } default { return [System.BitConverter]::ToString($bytes) } } } catch { Write-Warning "Could not decode hex value for type $type. Error: $_" return $hexString } } function ConvertTo-RegistryEntries { param( [string]$Name, [string]$Description, [string]$RegContent, [string]$SourceFile, [bool]$Enabled = $true, [bool]$RunOnce = $false, [string]$ExportFor = 'WEM', [PSCustomObject[]]$Assignments = @() ) $lines = $RegContent.Split([string[]]@("`r`n", "`n", "`r"), [StringSplitOptions]::None) # If name/description not provided, fall back to PFNAME/PFDESC tags in the content $resolvedName = $Name $resolvedDescription = $Description if ([string]::IsNullOrEmpty($resolvedName) -or [string]::IsNullOrEmpty($resolvedDescription)) { foreach ($line in $lines) { if ([string]::IsNullOrEmpty($resolvedName) -and $line -match ';<PFNAME>(.+)</PFNAME>') { $resolvedName = $Matches[1].Trim() } if ([string]::IsNullOrEmpty($resolvedDescription) -and $line -match ';<PFDESC>(.+)</PFDESC>') { $resolvedDescription = $Matches[1].Trim() } if (-not [string]::IsNullOrEmpty($resolvedName) -and -not [string]::IsNullOrEmpty($resolvedDescription)) { break } } } Write-Verbose "Registry set: '$resolvedName' | Description: '$resolvedDescription' | Enabled: '$Enabled' | RunOnce: '$RunOnce'" $entries = [System.Collections.Generic.List[PSCustomObject]]::new() $currentHive = '' $currentKey = '' for ($i = 0; $i -lt $lines.Length; $i++) { $line = $lines[$i] # Section header: [HKEY_...] or ![HKEY_...] (delete entire key) if ($line -match '^(!?)\[((HKEY_[^\\]+)(\\(.+))?)\]$') { $isKeyDeletion = $Matches[1] -eq '!' $sectionHive = $Matches[3] $sectionKey = if ($Matches[5]) { $Matches[5] } else { '' } if ($isKeyDeletion) { # Emit a key-deletion entry; do not update current section context Write-Verbose " Delete key: $sectionHive\$sectionKey" $entries.Add([PSCustomObject]([ordered]@{ RegistryHive = $sectionHive RegistryKey = $sectionKey RegistryKeyPath = if ([string]::IsNullOrEmpty($sectionKey)) { $sectionHive } else { "$sectionHive\$sectionKey" } RegistryValueName = $null Value = $null ValueType = $null Description = '' Delete = $true })) } else { $currentHive = $sectionHive $currentKey = $sectionKey Write-Verbose " Section: $currentHive\$currentKey" } continue } # Skip comments, blank lines, and the editor header if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith(';') -or $line.StartsWith('Windows Registry Editor')) { continue } # Skip structural placeholder default values (@="") on keys with no meaningful path # These are Ivanti reg file artifacts — empty-string default values on intermediate keys if ($line -match '^@=""$' -and [string]::IsNullOrEmpty($currentKey)) { continue } # Deletion marker on value: !"ValueName"=... $isDeletion = $false $parsedLine = $line if ($parsedLine.TrimStart().StartsWith('!"')) { $isDeletion = $true $parsedLine = $parsedLine.TrimStart().Substring(1) } $valueName = $null $rawValue = $null if ($parsedLine -match '^"([^"]*)"=(.+)') { $valueName = $Matches[1] $rawValue = $Matches[2].Trim() } elseif ($parsedLine -match '^@=(.+)') { $valueName = '' $rawValue = $Matches[1].Trim() } else { continue } # Skip empty-string default values on intermediate keys (structural artifacts) if ($valueName -eq '' -and $rawValue -eq '""') { continue } # Peek at next line for optional ;<PF>...</PF> per-value description $valueDescription = '' if (($i + 1) -lt $lines.Length -and $lines[$i + 1] -match ';<PF>(.+)</PF>') { $valueDescription = $Matches[1].Trim() } # Decode value data and determine type $registryValue = $null $registryValueType = 'Unknown' if ($rawValue.StartsWith('dword:')) { $registryValueType = 'REG_DWORD' try { $registryValue = [uint64]("0x" + $rawValue.Substring(6)) } catch { $registryValue = $rawValue; $registryValueType = 'Unknown' } } elseif ($rawValue.StartsWith('qword:')) { $registryValueType = 'REG_QWORD' try { $registryValue = [uint64]("0x" + $rawValue.Substring(6)) } catch { $registryValue = $rawValue; $registryValueType = 'Unknown' } } elseif ($rawValue.StartsWith('"') -and $rawValue.EndsWith('"')) { $registryValueType = 'REG_SZ' $registryValue = $rawValue.Substring(1, $rawValue.Length - 2) } elseif ($rawValue.StartsWith('hex(1):')) { $registryValueType = 'REG_SZ' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_SZ' } elseif ($rawValue.StartsWith('hex(2):')) { $registryValueType = 'REG_EXPAND_SZ' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_EXPAND_SZ' } elseif ($rawValue.StartsWith('hex(3):') -or $rawValue.StartsWith('hex:')) { $registryValueType = 'REG_BINARY' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_BINARY' } elseif ($rawValue.StartsWith('hex(4):')) { $registryValueType = 'REG_DWORD' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_DWORD' } elseif ($rawValue.StartsWith('hex(7):')) { $registryValueType = 'REG_MULTI_SZ' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_MULTI_SZ' } elseif ($rawValue.StartsWith('hex(b):')) { $registryValueType = 'REG_QWORD' $registryValue = Convert-RegistryHexToValue -hexString $rawValue -type 'REG_QWORD' } else { $registryValueType = 'REG_SZ' $registryValue = $rawValue } $entries.Add( [PSCustomObject]( [PSCustomObject]@{ RegistryHive = $currentHive RegistryKey = $currentKey RegistryKeyPath = if ([string]::IsNullOrEmpty($currentKey)) { $currentHive } else { "$currentHive\$currentKey" } RegistryValueName = $valueName Value = $registryValue ValueType = $registryValueType Description = $valueDescription Delete = $isDeletion } ) ) } Write-Verbose " Parsed $($entries.Count) registry value(s)" $output = [ordered]@{ Name = $resolvedName Description = $resolvedDescription Enabled = $Enabled RunOnce = $RunOnce Entries = $entries.ToArray() SourceFile = $SourceFile } if ($ExportFor -eq 'AppVentiX') { $registryEntries = @( foreach ($entry in $entries) { $deleteKey = $false if ($entry.Delete -eq $true -and [string]::IsNullOrEmpty($entry.RegistryValueName)) { $deleteKey = $true } $valueType = $entry.ValueType if ([string]::IsNullOrEmpty(($valueType))) { $valueType = 'REG_SZ' } [PSCustomObject]@{ RootKey = $entry.RegistryHive KeyPath = $entry.RegistryKey ValueName = $entry.RegistryValueName ValueData = $entry.Value ValueType = $valueType Action = if ($entry.Delete) { 'Remove' } else { 'Set' } DeleteKey = $deleteKey } } ) $output.AppVentiXAssignments = @($Assignments | Select-Object Sid, Name, Type, DomainFQDN) $output.AppVentiXParams = ( [ordered]@{ FriendlyName = $resolvedName Description = $resolvedDescription RegistryEntries = @($registryEntries) } ) } elseif ($ExportFor -eq 'WEM') { Write-Warning "WEM export format is not implemented yet for registry sets!" } [PSCustomObject]$output } #endregion $absolutePath = (Resolve-Path -LiteralPath $Path).Path $extension = [System.IO.Path]::GetExtension($absolutePath).ToLower() if ($extension -eq '.xml') { # Building Block XML — extract all <registry type="registry"> nodes Write-Verbose "Loading Building Block XML: $absolutePath" try { $xmlContent = New-Object System.Xml.XmlDocument $xmlContent.Load($absolutePath) } catch { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( [System.Xml.XmlException]::new("Failed to parse XML file '$absolutePath': $($_.Exception.Message)"), 'XmlParseError', [System.Management.Automation.ErrorCategory]::InvalidData, $absolutePath ) ) } $registryNodes = @($xmlContent.GetElementsByTagName('registry') | Where-Object { $_.type -eq 'registry' }) Write-Verbose "Found $($registryNodes.Count) registry set node(s)" if ($registryNodes.Count -eq 0) { Write-Warning "No <registry type='registry'> nodes found in '$absolutePath'" return } foreach ($node in $registryNodes) { $nodeName = $node.name $nodeDescription = $node.description $decodedContent = (Convert-HexToString -HexString $node.registryfile).TrimStart([char]0xFEFF) if ($node.enabled -ieq "yes") { $enabled = $true } else { $enabled = $false } if ($node.runonce -ieq "yes") { $runOnce = $true } else { $runOnce = $false } Write-Verbose "Processing registry set: '$nodeName'" $Assignments = @(ConvertFrom-IvantiAccessControl -AccessControl $node.accesscontrol -IWCComponentName $nodeName -IWCComponent "Policy") ConvertTo-RegistryEntries -Name $nodeName -Description $nodeDescription -RunOnce $runOnce -RegContent $decodedContent -SourceFile $absolutePath -ExportFor $ExportFor -Assignments $Assignments } } else { # Standalone .reg file Write-Verbose "Loading .reg file: $absolutePath" try { $rawContent = Get-Content -LiteralPath $absolutePath -Raw -ErrorAction Stop } catch { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( [System.IO.IOException]::new("Failed to read file '$absolutePath': $($_.Exception.Message)"), 'RegFileReadError', [System.Management.Automation.ErrorCategory]::ReadError, $absolutePath ) ) } ConvertTo-RegistryEntries -Name '' -Description '' -RegContent $rawContent -SourceFile $absolutePath -ExportFor $ExportFor } } # SIG # Begin signature block # MIInigYJKoZIhvcNAQcCoIInezCCJ3cCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBGX6RZyGnwFjsk # bR0B0rKoCqvDIobKWgof6hAOYr46HqCCIR0wggZFMIIELaADAgECAhAIMk+dt9qR # b2Pk8qM8Xl1RMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQK # ExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2Rl # IFNpZ25pbmcgMjAyMSBDQTAeFw0yNDA0MDQxNDA0MjRaFw0yNzA0MDQxNDA0MjNa # MGsxCzAJBgNVBAYTAk5MMRIwEAYDVQQHDAlTY2hpam5kZWwxIzAhBgNVBAoMGkpv # aG4gQmlsbGVrZW5zIENvbnN1bHRhbmN5MSMwIQYDVQQDDBpKb2huIEJpbGxla2Vu # cyBDb25zdWx0YW5jeTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMsl # ntDbSQwHZXwFhmibivbnd0Qfn6sqe/6fos3pKzKxEsR907RkDMet2x6RRg3eJkiI # r3TFPwqBooyXXgK3zxxpyhGOcuIqyM9J28DVf4kUyZHsjGO/8HFjrr3K1hABNUsz # P0o7H3o6J31eqV1UmCXYhQlNoW9FOmRC1amlquBmh7w4EKYEytqdmdOBavAD5Xq4 # vLPxNP6kyA+B2YTtk/xM27TghtbwFGKnu9Vwnm7dFcpLxans4ONt2OxDQOMA5Nwg # cUv/YTpjhq9qoz6ivG55NRJGNvUXsM3w2o7dR6Xh4MuEGrTSrOWGg2A5EcLH1XqQ # tkF5cZnAPM8W/9HUp8ggornWnFVQ9/6Mga+ermy5wy5XrmQpN+x3u6tit7xlHk1H # c+4XY4a4ie3BPXG2PhJhmZAn4ebNSBwNHh8z7WTT9X9OFERepGSytZVeEP7hgypt # SLcuhpwWeR4QdBb7dV++4p3PsAUQVHFpwkSbrRTv4EiJ0Lcz9P1HPGFoHiFAQQID # AQABo4IBeDCCAXQwDAYDVR0TAQH/BAIwADA9BgNVHR8ENjA0MDKgMKAuhixodHRw # Oi8vY2NzY2EyMDIxLmNybC5jZXJ0dW0ucGwvY2NzY2EyMDIxLmNybDBzBggrBgEF # BQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9jY3NjYTIwMjEub2NzcC1jZXJ0 # dW0uY29tMDUGCCsGAQUFBzAChilodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv # Y2NzY2EyMDIxLmNlcjAfBgNVHSMEGDAWgBTddF1MANt7n6B0yrFu9zzAMsBwzTAd # BgNVHQ4EFgQUO6KtBpOBgmrlANVAnyiQC6W6lJwwSwYDVR0gBEQwQjAIBgZngQwB # BAEwNgYLKoRoAYb2dwIFAQQwJzAlBggrBgEFBQcCARYZaHR0cHM6Ly93d3cuY2Vy # dHVtLnBsL0NQUzATBgNVHSUEDDAKBggrBgEFBQcDAzAOBgNVHQ8BAf8EBAMCB4Aw # DQYJKoZIhvcNAQELBQADggIBAEQsN8wgPMdWVkwHPPTN+jKpdns5AKVFjcn00psf # 2NGVVgWWNQBIQc9lEuTBWb54IK6Ga3hxQRZfnPNo5HGl73YLmFgdFQrFzZ1lnaMd # Icyh8LTWv6+XNWfoyCM9wCp4zMIDPOs8LKSMQqA/wRgqiACWnOS4a6fyd5GUIAm4 # CuaptpFYr90l4Dn/wAdXOdY32UhgzmSuxpUbhD8gVJUaBNVmQaRqeU8y49MxiVrU # KJXde1BCrtR9awXbqembc7Nqvmi60tYKlD27hlpKtj6eGPjkht0hHEsgzU0Fxw7Z # JghYG2wXfpF2ziN893ak9Mi/1dmCNmorGOnybKYfT6ff6YTCDDNkod4egcMZdOSv # +/Qv+HAeIgEvrxE9QsGlzTwbRtbm6gwYYcVBs/SsVUdBn/TSB35MMxRhHE5iC3aU # TkDbceo/XP3uFhVL4g2JZHpFfCSu2TQrrzRn2sn07jfMvzeHArCOJgBW1gPqR3Wr # J4hUxL06Rbg1gs9tU5HGGz9KNQMfQFQ70Wz7UIhezGcFcRfkIfSkMmQYYpsc7rfz # j+z0ThfDVzzJr2dMOFsMlfj1T6l22GBq9XQx0A4lcc5Fl9pRxbOuHHWFqIBD/BCE # hwniOCySzqENd2N+oz8znKooSISStnkNaYXt6xblJF2dx9Dn89FK7d1IquNxOwt0 # tI5dMIIGgjCCBGqgAwIBAgIQNsKwvXwbOuejs902y8l1aDANBgkqhkiG9w0BAQwF # ADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcT # C0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAs # BgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN # MjEwMzIyMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYG # A1UEChMPU2VjdGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBU # aW1lIFN0YW1waW5nIFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAiJ3YuUVnnR3d6LkmgZpUVMB8SQWbzFoVD9mUEES0QUCBdxSZqdTkdizI # CFNeINCSJS+lV1ipnW5ihkQyC0cRLWXUJzodqpnMRs46npiJPHrfLBOifjfhpdXJ # 2aHHsPHggGsCi7uE0awqKggE/LkYw3sqaBia67h/3awoqNvGqiFRJ+OTWYmUCO2G # AXsePHi+/JUNAax3kpqstbl3vcTdOGhtKShvZIvjwulRH87rbukNyHGWX5tNK/WA # BKf+Gnoi4cmisS7oSimgHUI0Wn/4elNd40BFdSZ1EwpuddZ+Wr7+Dfo0lcHflm/F # DDrOJ3rWqauUP8hsokDoI7D/yUVI9DAE/WK3Jl3C4LKwIpn1mNzMyptRwsXKrop0 # 6m7NUNHdlTDEMovXAIDGAvYynPt5lutv8lZeI5w3MOlCybAZDpK3Dy1MKo+6aEtE # 9vtiTMzz/o2dYfdP0KWZwZIXbYsTIlg1YIetCpi5s14qiXOpRsKqFKqav9R1R5vj # 3NgevsAsvxsAnI8Oa5s2oy25qhsoBIGo/zi6GpxFj+mOdh35Xn91y72J4RGOJEoq # zEIbW3q0b2iPuWLA911cRxgY5SJYubvjay3nSMbBPPFsyl6mY4/WYucmyS9lo3l7 # jk27MAe145GWxK4O3m3gEFEIkv7kRmefDR7Oe2T1HxAnICQvr9sCAwEAAaOCARYw # ggESMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBT2 # d2rdP/0BE/8WoWyCAi/QCj0UJTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw # AwEB/zATBgNVHSUEDDAKBggrBgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYD # VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz # dFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMDUGCCsGAQUFBwEBBCkwJzAl # BggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0B # AQwFAAOCAgEADr5lQe1oRLjlocXUEYfktzsljOt+2sgXke3Y8UPEooU5y39rAARa # AdAxUeiX1ktLJ3+lgxtoLQhn5cFb3GF2SSZRX8ptQ6IvuD3wz/LNHKpQ5nX8hjsD # LRhsyeIiJsms9yAWnvdYOdEMq1W61KE9JlBkB20XBee6JaXx4UBErc+YuoSb1SxV # f7nkNtUjPfcxuFtrQdRMRi/fInV/AobE8Gw/8yBMQKKaHt5eia8ybT8Y/Ffa6HAJ # yz9gvEOcF1VWXG8OMeM7Vy7Bs6mSIkYeYtddU1ux1dQLbEGur18ut97wgGwDiGin # CwKPyFO7ApcmVJOtlw9FVJxw/mL1TbyBns4zOgkaXFnnfzg4qbSvnrwyj1NiurMp # 4pmAWjR+Pb/SIduPnmFzbSN/G8reZCL4fvGlvPFk4Uab/JVCSmj59+/mB2Gn6G/U # YOy8k60mKcmaAZsEVkhOFuoj4we8CYyaR9vd9PGZKSinaZIkvVjbH/3nlLb0a7SB # IkiRzfPfS9T+JesylbHa1LtRV9U/7m0q7Ma2CQ/t392ioOssXW7oKLdOmMBl14su # VFBmbzrt5V5cQPnwtd3UOTpS9oCG+ZZheiIvPgkDmA8FzPsnfXW5qHELB43ET7HH # FHeRPRYrMBKjkb8/IN7Po0d0hQoF4TeMM+zYAJzoKQnVKOLg8pZVPT8wgganMIIE # j6ADAgECAhEAkKwIciD9xafEa1zHDfc9BjANBgkqhkiG9w0BAQwFADBXMQswCQYD # VQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0 # aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFJvb3QgUjQ2MB4XDTI2MDMyNTAwMDAw # MFoXDTQxMDMyNDIzNTk1OVowVTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3Rp # Z28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFtcGlu # ZyBDQSBSNDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCu5EqiAa2C # HGL5Zi1bmgPM8NUXwYZJ+BtQqHps43GLTC+sjVLypsBh+8uv+TLkgtVGD//vSmA0 # qrzELf9YRCh2MTAA/aGaQZKGg0BRCmziR3pbCnvgWjtGXBDUyn3j3K2lZAO8KxgF # tlxwOYEAkL+CCqK4v9zzTl8ZwzDpPMiDIFa5THk8an1ieF5I09cXNrPQw+1ER1li # ThaG0z6FrOpqwxZWmPRZQBw2E32878UB1bL0Zp91vuWZgsMpNNiPCoBj0/1F+LE8 # +NRokfqacFI0F2tftrRB2W7HQClLR9zjxFbWb5be2rceIfNyHUUfKGIvMI2NzoxS # lxXnFqUG887D8W1Cj8DFok688JKxWvHR/9aQykSbd+9Vutj36ij2sgq/125wTpUZ # /AgC0ph50bRs7gFrUyaXE9wSsOqMvCCC+sEm7vd/BemSG0TSHNXSmyCba+FCzeke # WX03TRIcF3Laqd0Rw24OH7jpei4zaGhcI7nfdhBA4c8RScxNY6jeHLHHmSMMTk9W # qn7H4dLhUBP5YEwbgbN4uv1i9ltTnHli8t1xHV0StX9BFgrnmunTX19kUXY1H5OR # JbRZyZDdvm1oZyteDj0SnMozr+YSmdIleDUTXdfoY7b2taz8s2+QbOxLxcahEIYG # Wzqu6h955tKwcANHcZ4gTmAhT3btuOiQsQIDAQABo4IBbjCCAWowHwYDVR0jBBgw # FoAU9ndq3T/9ARP/FqFsggIv0Ao9FCUwHQYDVR0OBBYEFDp0pQxnxkJQwv21/Me7 # KTSC9Hq5MA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMCMGA1UdIAQcMBowCAYGZ4EMAQQCMA4GDCsGAQQBsjEB # AgEDCDBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLnNlY3RpZ28uY29tL1Nl # Y3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdSb290UjQ2LmNybDB8BggrBgEFBQcBAQRw # MG4wRwYIKwYBBQUHMAKGO2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY1RpbWVTdGFtcGluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8v # b2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMt5SR2bxngNm+N8o # c6Gq76Gx1c235fkX7jw8Ho9MAkJGADerHE7dhsBXttqmzgr/7ZZahZSykGRPhPY1 # crj028kB8KzO0dKC2qQBAwtfgqMLKkkX/6bYq2uT33eD6ByAp2/XKD0LcmZh0kKe # cvSBr6ln9ajX6u1dnx2fA7xEKy1M3qBhfQSUWLtjs2nFt0ELVLptzTlX9ID0cL+i # OPfdboZ3CelT+JXKVKR2Sge0d4YiFAtPZkfSo8z1Z1x7y/Z9mwMIlBAnyuWXs4Ys # NuxdrYIt/QxE31PDOJ9DesS4Bc7H9OTORlEV/AvfiF/VepKZpira1MzLYuCw+uoL # Zn/pkpvd+CvNTS+mEHjBJNa6WK1j8qXFu+jIq+sG9QILHiyB6p/xpHrkJu8zkw39 # 3+VqF9eKlTY2VjRxdycZLrVemZ4Yp3wi33b+W58CllH3HqjmowlZ7SOrgmx8YwYO # kgrHsXOQHyBp6O4FRb8In0+FzjT7ElGie9V7CfhL3IlVFZ4zjuKsZtH1iU3fGu4z # /JnOGT6sCb0BbTqe/uhvpFCQBdH5xPGIA/LrbQUXjU2tWJgHhTIqnN/HvHyOHi5t # M4zP3nhgh2rJ6Kqq2xsHBeNYs/R18xQ8DeIg+c90Eoaeh0YlN1KU8AyYol3K9M+q # Y5ez8syd/7ZlrRnoVewgH3P1pcswgga5MIIEoaADAgECAhEAmaOACiZVO2Wr3G6E # prPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVu # aXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y # ayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjELMAkGA1UE # BhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UE # AxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34vuTmflN4 # mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSoMcbvIKck # 6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+njvMk2xq # bNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHIohzuC8KN # qfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRdaCtVD2bSl # bfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837Q4QOZgYq # VWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8mFSkcSkAE # xzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI5XDYO962 # WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5boufUnq1UiY # PIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplKShBSND36 # E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n858JSqPEC # AwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10XUwA23uf # oHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbROg79MA4G # A1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8EKTAnMCWg # I6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsGAQUFBwEB # BGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVtLmNvbTAy # BggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0bmNhMi5j # ZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93d3cu # Y2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCiaU58Q7EP8 # 9DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L94C9LGF3v # jzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rGDxLUUAsO # 0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f8pXdd3x2 # mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0CdY9rNLqyA # 3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B7eMcZNhp # n8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloMU+vUBfSo # uCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1tVxTpV2Na # 3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2DaGgK2W1 # eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNjt2ywzOr+ # tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCrW602NCmv # O1nm+/80nLy5r0AZvCQxaQ4wggbiMIIEyqADAgECAhEA507yVbBQT/rbpt/3/Iuj # FTANBgkqhkiG9w0BAQwFADBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGln # byBMaW1pdGVkMSwwKgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5n # IENBIFI0MTAeFw0yNjAzMjUwMDAwMDBaFw0zNzA2MjQyMzU5NTlaMHIxCzAJBgNV # BAYTAkdCMRcwFQYDVQQIEw5HcmVhdGVyIExvbmRvbjEYMBYGA1UEChMPU2VjdGln # byBMaW1pdGVkMTAwLgYDVQQDEydTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5n # IFNpZ25lciBSMzcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCy/8Nt # S9xQ2UUtBRF32bj7VK3n4m50Uqjk/zTciSziYV40H1LKah0/oEklYG42E4VCP3Dv # sBUB6DmpCkDZ0jCnZBPIEevaH15ZJOQwFWP2ZXr5YjlJpb68Nlbs+ElNvKx32/1Y # Hde3qqUSLybjulxPLz6T85+HOIqK7M1Bep8LspyhEP/q6nw5kGxTSrGvufmeH+JF # 8CnVBcVMFA40FlIYh0cDJVFhhfTfdWgLy/vWuLMQoKkf3s/FvByf16r0rtbyHm/i # emwxSioJL9zyZDDKUNAbHXl0dhXo2VxUV2NcPXWXuoKsjL+6cfk6Vm2DHnxAlFdF # saBDIF1JOkSnC6PeLlBznZn2buF3vIIYJcq6N/zeFRCk4/HXDz7zgRsRRMdUB+rh # yk5FoZaBjw0nLq3GZ3fClLUx5es5pUAxzNODMBn7JkFYip2BAGBPER5eV0ROhk6t # GTG+fUiMiV+vgjg1YnP5FvnYWyEtWeQD/B2hp3vz0RvtdkM0p3igyadzrfpOBq5p # pVk/YsuhTQkP99ivneHAGfi5e7lmxJ+meoBPrRLuzMmb81rzzbESjJHMsn5RVtc6 # Ucs7rcMqQC13PUIO7BbGBETV2ufCmV6lPTp3P7XJOvmnUCRTPbVvMTpxP/z+SOHg # 4/OCBhiqs4FA9+4oQvlkk9w32NGASli9GWrm5wIDAQABo4IBjjCCAYowHwYDVR0j # BBgwFoAUOnSlDGfGQlDC/bX8x7spNIL0erkwHQYDVR0OBBYEFGEQ6XoSr1HEhdTy # z6R0D1DNIK/4MA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB # /wQMMAoGCCsGAQUFBwMIMEoGA1UdIARDMEEwCAYGZ4EMAQQCMDUGDCsGAQQBsjEB # AgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzBKBgNV # HR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJs # aWNUaW1lU3RhbXBpbmdDQVI0MS5jcmwwegYIKwYBBQUHAQEEbjBsMEUGCCsGAQUF # BzAChjlodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3Rh # bXBpbmdDQVI0MS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28u # Y29tMA0GCSqGSIb3DQEBDAUAA4ICAQAD6j2N0azN+hl6k6bKB5/U6VuSOs93ZBb3 # Pczy9VtBIKu4947Z5GwL0aFngIxl+GSuLFrJgPruBCRvKJEJsm7kv+LQ1COVCEG9 # tZ+IRtr4ocUoa53lgdFaENlS0N4wgkZkbQEPv+x+1lSjYh+T4JeL9mUznT7Erc6S # p5dWLka5sMP/m3GZi6oJPdPcsCKWagH7m2H2xDGIyHJC5PdH9phvi/KmhkktiSVT # NNqVeV5bWdX2zhRE6UTfz0IcMoCL996lFIydXxOCE4MNDHDM0as4lnTiT/KHMccO # 6l8c9TnUVgmpci9ar1IABZ2U1XUkYjGGSn9MC3EHDP9V39VuBVvZ33/BEV/EWSRr # f07T7jFplKX+gQr/UOqPGMlE7ZJ72UaUkNJy7bVl3bcLKzdpjIHzLkf/4MVa1V7w # 8wqCv5W4gOnRGTlud5UMARbRM8BPxR/CXYXoMmIOD8pmTk2axgRL4LG8XtuchISd # CHRmtacAmLGq5XSYSVTHTXADlO48iDKh3HM2r98LSF6f0sG12d8V9Jn7C3wDUieO # xuKj4MdWrW+hiJU2kF87v6eH00HgCFFc2V0+CvfOCMn7juzS41jLaINcBlKWQ/fK # b/uDLfWOW73z1I2lFY7Xj8tQ1XYtK5eREjWItM8jpl1cbQOc88btR+0XS2TmboE/ # 141+va2PWzGCBcMwggW/AgEBMGowVjELMAkGA1UEBhMCUEwxITAfBgNVBAoTGEFz # c2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMbQ2VydHVtIENvZGUgU2ln # bmluZyAyMDIxIENBAhAIMk+dt9qRb2Pk8qM8Xl1RMA0GCWCGSAFlAwQCAQUAoIGE # MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE # MSIEIJSyO2MLVEWyRTzTOk4SYbygcke5DgEym9hHaB2+fZNjMA0GCSqGSIb3DQEB # AQUABIIBgGshmM7VnsP7O17nskw/163pdO82pJF0vB4R6MfQ4+Av9E54FU19/S14 # vPxZfSBpMazo2/uvxviGHy3wi0zz7zI9vWnKtpnJSZIBjuzL4J+UDxD1tfyzxwqB # HNfCZaMls8R2NtL0hFtKozDEHjfVyRRkUobsaLe61CVft4QJjJEHhoNL3FBsTl96 # sNvsxiCeGvUsd6ZmXH6eFtbw1Qk0U6jihHvxLJ5c9N4APZmjfbZi3GdA4C/5UWwl # bqks/2A9+P9Kzq7x/eOabNz6cCwzTz80Z9LcWE8H/ZxdsU7VNnGiZ16ZMXSfvRRg # 721se2gejAQmm58pDIgtbXjyhhi/M7YvQb8Jlm68S8cCjf/Cji2YF7MznQY09jtq # zF25X+iBZSJfiO7ZNaymY03i1ZjuMQVjDQ7E+A/R/3kHQbdc5QWv+Lv8hgWbokA8 # TStzyDPXryNoa4YK+ZDfJHKYhgtQJ/CmXqyK6X9tpb5M9EDQBKVLPS5yGeXzTBYe # xLo+0R9d76GCAyMwggMfBgkqhkiG9w0BCQYxggMQMIIDDAIBATBqMFUxCzAJBgNV # BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3Rp # Z28gUHVibGljIFRpbWUgU3RhbXBpbmcgQ0EgUjQxAhEA507yVbBQT/rbpt/3/Iuj # FTANBglghkgBZQMEAgIFAKB5MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ # KoZIhvcNAQkFMQ8XDTI2MDcwNzE1Mjc0MlowPwYJKoZIhvcNAQkEMTIEMInkNN5u # OikRaw+plqVVRa2WB2Q6sFx6EvB5Niv3hK84J3lZXHtj5uN58SnzTDD2DzANBgkq # hkiG9w0BAQEFAASCAgCOrWvjhDA29y1wnDgLctWc/ZR367bNgC52T95ThM7MWCft # VhO3N9aLEqY66KbsCrVAfdVLcSg1CreNb68HDzrtCs78FxHJ7IZsgksFMx/H8jXd # ZLIEjQeeLo5d4CuuJ/Bs8eG5rHMXM3lRunRsldf59si8TEIJNmjg9MXLQlNLIbam # Ca2RWXN04kVB8+mwtplJ2npIPul+C9WIOvioP+QlrlzJY4HuUN9sWv1L54kppf4+ # IYB5Tm1ZlYwcdAd/aP1tGvHZZo/U0nV9DqylgLPc7t4Z+JG2StAiIFvLDCjQdv29 # iXBkVCVjXnZuRRvh/NRESNtAI1tWiYPId5VrB/jTi/UOsiQ+mnGeAnZ7Wk2kcIOG # ejZNOUC9STbEenAUtqAMGJ8Pa25qJQ07sqC11jBYtvoDqiA6ZN5lR2AVJtKOAln0 # teinazbq6EHllInApDZNgTaP1wYpukhufkgjquVDQ07tZytQKqYsu/q2MXcwKEak # 0heT75k3gwTmMmlmV6D6L1wA+qvkZrneCtSI1ebtY3lx63A3imho1GrLTQ4asHXu # HJMlVe4uP2wnezS6lLsWt0qalgRPh7FJaSzgo2gfRQ7zLcxaSuNG8S47MH4ObDLr # 33Z6J6hSdHn99mQnbD3QqqlV3h9r539aUWxlUpi33EH895ebdDk+v1Q8y0DKXw== # SIG # End signature block |