scripts/helpers.ps1
# HPE Nimble PowerShell Toolkit. # File: helpers.ps1 # Description: This file contains common helper routines. These functions are called by generated SDK Cmdlet functions. # # © Copyright 2017 Hewlett Packard Enterprise Development LP. function Connect-NSGroup { <# .SYNOPSIS Connects to a Nimble Storage group. .DESCRIPTION Connect-NSGroup is an advanced function that provides the initial connection to a Nimble Storage array so that other subsequent commands can be run without having to authenticate individually. It is recommended to ignore the server certificate validation (-IgnoreServerCertificate param) since Nimble uses an untrusted SSL certificate. .PARAMETER Group The DNS name or IP address of the Nimble group. .PARAMETER Credential Specifies a user account that has permission to perform this action. Type a user name, such as User01 or enter a PSCredential object, such as one generated by the Get-Credential cmdlet. If you type a user name, this function prompts you for a password. .PARAMETER IgnoreServerCertificate Ignore the server SSL certificate. .EXAMPLE Connect-NSGroup -Group nimblegroup.yourdns.local -Credential admin -IgnoreServerCertificate *Note: IgnoreServerCertificate parameter is not available with PowerShell Core .EXAMPLE Connect-NSGroup -Group 192.168.1.50 -Credential admin -IgnoreServerCertificate *Note: IgnoreServerCertificate parameter is not available with PowerShell Core .EXAMPLE Connect-NSGroup -Group nimblegroup.yourdns.local -Credential admin -ImportServerCertificate .EXAMPLE Connect-NSGroup -Group 192.168.1.50 -Credential admin -ImportServerCertificate .INPUTS None .OUTPUTS None .NOTES #> [cmdletbinding(DefaultParameterSetName='IgnoreServerCertificate')] param ( [Parameter(Mandatory,position=0)] [string]$Group, [Parameter(Mandatory,position=1)] $Credential=$null, [Parameter(ParameterSetName='ImportServerCertificate')] [switch]$ImportServerCertificate ) DynamicParam { if ($PSEdition -ne 'Core'){ $IgnoreServerCertificateAttribute = New-Object System.Management.Automation.ParameterAttribute $IgnoreServerCertificateAttribute.Mandatory = $false #$IgnoreServerCertificateAttribute.Position = 3 $IgnoreServerCertificateAttribute.ParameterSetName = 'IgnoreServerCertificate' $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($IgnoreServerCertificateAttribute) $IgnoreServerCertificateParam = New-Object System.Management.Automation.RuntimeDefinedParameter('IgnoreServerCertificate', [Switch],$attributeCollection) $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('IgnoreServerCertificate', $IgnoreServerCertificateParam) return $paramDictionary } } Process{ if ($Credential -is [String]) { $Credential = Get-Credential $Credential } $global:Group=$Group if ($PSBoundParameters.IgnoreServerCertificate) { $Global:NimbleStorageIgnoreServerCertificate = $true; IgnoreServerCertificate} else { $Global:NimbleStorageIgnoreServerCertificate = $false $Global:GlobalImportServerCertificate = $ImportServerCertificate ValidateServerCertificate $group } Import-LocalizedData -BaseDirectory (Split-Path $PSScriptRoot -parent) -FileName "HPENimblePowerShellToolkit.psd1" -BindingVariable "ModuleData" $PSTKVersion = $ModuleData.moduleversion $Global:NimbleAppName = "HPENimblePowerShellToolkitV" + $PSTKVersion $Global:NimbleStoragePort = 5392 $Global:BaseUri = "https://$($global:Group):$($NimbleStoragePort)" try{ $Global:NimbleStorageTokenData = Invoke-RestMethod -Uri "$BaseUri/v1/tokens" -Method Post -Body ((@{data = @{username = $Credential.UserName;password = $Credential.GetNetworkCredential().password;app_name = $NimbleAppName}}) | ConvertTo-Json) Write-Host "Successfully connected to array $group `n`n" } catch{ Write-error "Failed to connect with array $group `n`n $_.Exception.Message" -ErrorAction Stop } $Global:RestVersion = (Invoke-RestMethod -Uri "$BaseUri/versions").data.name $Global:NimbleStorageSession_token = $NimbleStorageTokenData.data.session_token $Global:NimbleStorageArray = $group $Global:NimbleStorageCommonPSParams=@('Debug','Verbose','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutBuffer','OutVariable','PipelineVariable','Verbose','WarningAction','WarningVariable','WhatIf','Confim','ItemType') } } function Disconnect-NSGroup { <# .SYNOPSIS Disconnects from a Nimble SAN. .DESCRIPTION Disconnect-NSGroup is an advanced function that disconnects the established connection with Nimble Storage array. .EXAMPLE Disconnect-NSGroup .INPUTS None .OUTPUTS None .NOTES #> [CmdletBinding()] param ( ) if (Get-Variable NimbleStorageTokenData -Scope Global -ErrorAction SilentlyContinue) { Remove-NSToken -id $NimbleStorageTokenData.data.id remove-Variable -Scope "Global" NimbleStoragePort Remove-Variable -Scope "Global" BaseUri Remove-Variable -Scope "Global" NimbleStorageTokenData Remove-Variable -Scope "Global" RestVersion Remove-Variable -Scope "Global" NimbleStorageSession_token Remove-Variable -Scope "Global" NimbleStorageArray Remove-Variable -Scope "Global" NimbleStorageCommonPSParams } } function IgnoreServerCertificate { [CmdletBinding()] param() <# [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 add-type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class IDontCarePolicy : ICertificatePolicy { public IDontCarePolicy() {} public bool CheckValidationResult( ServicePoint sPoint, X509Certificate cert, WebRequest wRequest, int certProb) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy write-verbose 'Server certificate ignored' #> if (-not ([System.Management.Automation.PSTypeName]'CustomCertificateValidationCallback').Type) { add-type @" using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public static class CustomCertificateValidationCallback { public static void Install() { ServicePointManager.ServerCertificateValidationCallback += CustomCertificateValidationCallback.CheckValidationResult; } public static bool CheckValidationResult( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { // please don't do this. do some real validation with explicit exceptions. return true; } } "@ } [CustomCertificateValidationCallback]::Install() } function Invoke-NimbleStorageRestAPI () { param( [Parameter(Mandatory=$true)][string] $ResourcePath, [string] $APIVersion = 'v1', [string] $Method = 'GET', [hashtable] $RequestParams = @{} ) if (!(Get-Variable NimbleStorageTokenData -Scope Global -ErrorAction SilentlyContinue)) { Write-Error -Message "Authentication Info missing. Please use Connect-NSGroup to login." -ErrorAction Stop } if ( $NimbleStorageIgnoreServerCertificate ) { IgnoreServerCertificate } # Form the parameters to Invoke-RestMethod call. $WebRequestParams = @{ Uri = "$BaseUri/$APIVersion/$ResourcePath" Header = @{'X-Auth-Token' = $NimbleStorageSession_token} Method = $Method } # Copy request params to different variable. We may need to specifically process few of them. $RequestData = @{} foreach ($key in $RequestParams.keys) { # PowerShell serializes Booleans in JSON as True/False. We need all lowercase for Nimble Array's REST Server. if ($RequestParams.$key.getType() -eq [bool]) { $RequestData.Add($key, $RequestParams.$key.ToString().ToLower()) } elseif ($key -eq 'fields' -and $Method -eq 'GET') { # Array of fields. Convert to comma separated list. $RequestData.Add('fields', ($RequestParams['fields'] | Select-Object -unique) -join ',') } else { $RequestData.Add($key, $RequestParams.$key); } } switch($Method) { 'GET' { # Hashmap supplied in Body for GET request gets converted to query params automatically. $WebRequestParams.Add('Body',$RequestData) } 'POST' { # Encapsulate request payload in 'data'.. $RequestDataForNimbleAPI = @{ data = $RequestData } $WebRequestParams.Add('Body',($RequestDataForNimbleAPI | ConvertTo-Json -Depth 10)) } 'PUT'{ $RequestDataForNimbleAPI = @{ data = $RequestData } $WebRequestParams.Add('Body',($RequestDataForNimbleAPI | ConvertTo-Json -Depth 10)) } 'DELETE' { # Do nothing. No Body expected/required for delete request. } } Write-Verbose ($WebRequestParams | ConvertTo-Json -Depth 50) $max_retry_count = 5 $retry_count = 0 do { try { if ($retry_count -ne 0) { Start-Sleep -Milliseconds 30 } $JsonResponse = (Invoke-RestMethod @WebRequestParams | ConvertTo-Json -Depth 50) Write-Verbose "Server Response: $JsonResponse" # # The Invoke-Restmethod was successful we should exit the retry loop. # To do that we will force the retry count max surpass the max. # $retry_count = $max_retry_count + 1 } catch { if ($_.Exception.Response -ne $null) { APIExceptionHandler } else { # if the Error response buffer is null then we will go for retries. # if we exhaust the retries we will thrown and error. $retry_count = $retry_count + 1 if ($retry_count -gt $max_retry_count) { Write-Verbose $_.exception Write-Error "Error occoured while invoking restapi method, Please retry" -ErrorAction Stop } } } }until ($retry_count -gt $max_retry_count) return ($JsonResponse | ConvertFrom-Json) } function Get-NimbleStorageAPIObject() { param( [Parameter(Mandatory=$true)][string] $ObjectName, [Parameter(Mandatory=$true)][string] $APIPath, [Parameter(Mandatory=$true)][string] $Id, [System.Collections.ArrayList] $Fields ) $Params = @{ ResourcePath = $APIPath + "/$Id" Method = 'GET' } if ($Fields) { $Params.Add('RequestParams', @{ fields = ($Fields | Select-Object) -join ','}) } $APIObject = (Invoke-NimbleStorageRestAPI @Params).data $DataSetType = "NimbleStorage.$ObjectName" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) return $APIObject } function Get-NimbleStorageAPIObjectList() { param( [Parameter(Mandatory=$true)][string] $ObjectName, [Parameter(Mandatory=$true)][string] $APIPath, [hashtable] $Filters, [System.Collections.ArrayList] $Fields ) # First fetch all the objects (only id and name) matching the given filter. # Then for each of the objects, retrieve either all the details or given fields. $Params = @{ ResourcePath = $APIPath Method = 'GET' RequestParams = $Filters } # Get the list of objects matching given criteria $JSONResponseObject = (Invoke-NimbleStorageRestAPI @Params) [System.Collections.ArrayList] $APIObjects = $JSONResponseObject.data # We are expecting a list. If total items/objects on the array for this query are more than 1024, # array will send back only first 1024 objects along with total count of objects in 'totalRows'. if ($JSONResponseObject.endRow -and $JSONResponseObject.totalRows -and ($JSONResponseObject.endRow -lt $JSONResponseObject.totalRows)) { # There are more objects. Keep getting those until we reach the end. while ($JSONResponseObject.endRow -lt $JSONResponseObject.totalRows) { $Params.RequestParams.startRow = $JSONResponseObject.endRow $JSONResponseObject = Invoke-NimbleStorageRestAPI @Params $APIObjects.AddRange($JSONResponseObject.data) | out-null } } [System.Collections.ArrayList] $APIObjectsDetailed = @() # Fetch needed detailes of all the objects. foreach ($APIObject in $APIObjects) { $Params = @{ ObjectName = $ObjectName APIPath = $APIPath Id = $APIObject.id } if ($Fields) { $Params.Add('Fields', $Fields) } $APIObject = (Get-NimbleStorageAPIObject @Params) $DataSetType = "NimbleStorage.$ObjectName" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) $APIObjectsDetailed.Add($APIObject) | out-null } Write-Verbose ("Found " + $APIObjectsDetailed.Count + " objects.") return ,$APIObjectsDetailed } function New-NimbleStorageAPIObject() { param( [Parameter(Mandatory=$true)][string] $ObjectName, [Parameter(Mandatory=$true)][string] $APIPath, [Parameter(Mandatory=$true)][hashtable] $Properties ) $Params = @{ ResourcePath = $APIPath Method = 'POST' RequestParams = $Properties } $ResponseObject = (Invoke-NimbleStorageRestAPI @Params) $APIObject = $ResponseObject.data if ($APIObject) {$DataSetType = "NimbleStorage.$ObjectName" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) } else { $APIObject = $ResponseObject $DataSetType = "NimbleStorage.Messages" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) } return $APIObject } function Set-NimbleStorageAPIObject() { param( [Parameter(Mandatory=$true)][string] $ObjectName, [Parameter(Mandatory=$true)][string] $APIPath, [Parameter(Mandatory=$true)][string] $Id, [Parameter(Mandatory=$true)][hashtable] $Properties ) $Params = @{ ResourcePath = $APIPath + "/$Id" Method = 'PUT' RequestParams = $Properties } $ResponseObject = (Invoke-NimbleStorageRestAPI @Params) $APIObject = $ResponseObject.data if ($APIObject) {$DataSetType = "NimbleStorage.$ObjectName" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) } else { $APIObject = $ResponseObject $DataSetType = "NimbleStorage.Messages" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) } return $APIObject } function Remove-NimbleStorageAPIObject() { param( [Parameter(Mandatory=$true)][string] $ObjectName, [Parameter(Mandatory=$true)][string] $APIPath, [Parameter(Mandatory=$true)][string] $Id ) $Params = @{ ResourcePath = $APIPath + "/$Id" Method = 'DELETE' } $APIObject = (Invoke-NimbleStorageRestAPI @Params).data } function Invoke-NimbleStorageAPIAction() { param( [Parameter(Mandatory=$true)][string] $APIPath, [Parameter(Mandatory=$true)][string] $Action, [Parameter(Mandatory=$true)][hashtable] $Arguments, [Parameter(Mandatory=$true)][string] $ReturnType ) $Params = @{ ResourcePath = $APIPath + "/actions/$Action" Method = 'POST' RequestParams = $Arguments } if ($Arguments.id) { $id = $($Arguments.id) $Params.ResourcePath = $APIPath + "/$id/actions/$Action" $Arguments.Remove('id') } $ResponseObject = (Invoke-NimbleStorageRestAPI @Params) if ($ReturnType -eq "void") { # Return empty object return $ResponseObject } $APIObject = $ResponseObject.data $DataSetType = "NimbleStorage.$ReturnType" $APIObject.PSTypeNames.Insert(0,$DataSetType) $DataSetType = $DataSetType + ".TypeName" $APIObject.PSObject.TypeNames.Insert(0,$DataSetType) return $APIObject } function ValidateServerCertificate() { param( [Parameter(Mandatory,Position=0)] [string]$Group ) $Code = @' using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace CertificateCapture { public class Utility { public static Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,Boolean> ValidationCallback = (message, cert, chain, errors) => { var newCert = new X509Certificate2(cert); var newChain = new X509Chain(); newChain.Build(newCert); CapturedCertificates.Add(new CapturedCertificate(){ Certificate = newCert, CertificateChain = newChain, PolicyErrors = errors, URI = message.RequestUri }); return true; }; public static List<CapturedCertificate> CapturedCertificates = new List<CapturedCertificate>(); } public class CapturedCertificate { public X509Certificate2 Certificate { get; set; } public X509Chain CertificateChain { get; set; } public SslPolicyErrors PolicyErrors { get; set; } public Uri URI { get; set; } } } '@ if ($PSEdition -ne 'Core'){ $webrequest=[net.webrequest]::Create("https://$Group") try { $response=$webrequest.getresponse() } catch {} $cert=$webrequest.servicepoint.certificate if($cert -ne $null){ $Thumbprint = $webrequest.ServicePoint.Certificate.GetCertHashString() $bytes=$cert.export([security.cryptography.x509certificates.x509contenttype]::cert) $tfile=[system.io.path]::getTempFileName() set-content -value $bytes -encoding byte -path $tfile $certdetails = $cert | select * | ft -AutoSize | Out-String if ($($GlobalImportServerCertificate)) { try{ $output =import-certificate -filepath $tfile -certStoreLocation 'Cert:\localmachine\Root' $certdetails = $output | select -Property Thumbprint,subject | ft -AutoSize | Out-String } catch{ Write-Error "Failed to import the server certificate `n`n $_.Exception.Message" -ErrorAction Stop } Write-Host "Successfully imported the server certificate `n $certdetails" } else{ if((Get-ChildItem -Path Cert:\LocalMachine\root | Where-Object {$_.Thumbprint -eq $Thumbprint})){ } else{ write-Error "The security certificate presented by host $Group was not issued by a trusted certificate authority. Please verify the certificate details shown below and use ImportServerCertificate command line parameter to proceed. `n $certdetails `n`n" -ErrorAction Stop } } ResolveIPtoHost $cert.subject $Group } else{ Write-Error "Failed to import the server certificate `n`n" -ErrorAction Stop } }else { Add-Type $Code $Certs = [CertificateCapture.Utility]::CapturedCertificates $Handler = [System.Net.Http.HttpClientHandler]::new() $Handler.ServerCertificateCustomValidationCallback = [CertificateCapture.Utility]::ValidationCallback $Client = [System.Net.Http.HttpClient]::new($Handler) $Url = "https://$Group" $Result = $Client.GetAsync($Url).Result $cert= $Certs[-1].Certificate if($certs -ne $null){ $certdetails = $cert | select -Property Thumbprint,subject | ft -AutoSize | Out-String if ($($GlobalImportServerCertificate)) { $bytes=$cert.export([security.cryptography.x509certificates.x509contenttype]::cert) $OpenFlags = [System.Security.Cryptography.X509Certificates.OpenFlags] $store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "Root","LocalMachine" try{ $Store.Open($OpenFlags::ReadWrite) $Store.Add($Cert) $Store.Close() Write-Host "Successfully imported the server certificate `n $certdetails" } catch{ Write-Error "Failed to import the server certificate `n`n $_.Exception.Message" -ErrorAction Stop } } else { if((Get-ChildItem -Path Cert:\LocalMachine\root | Where-Object {$_.Thumbprint -eq $cert.Thumbprint})){ } else{ write-Error "The security certificate presented by host $Group was not issued by a trusted certificate authority. Please verify the certificate details shown below and use ImportServerCertificate command line parameter to proceed. `n $certdetails `n`n" -ErrorAction Stop } } ResolveIPtoHost $cert.subject $Group } else{ Write-Error "Failed to import the server certificate `n`n" -ErrorAction Stop } } } function ResolveIPtoHost{ param( [Parameter(Mandatory)] [string]$CertSubject, [Parameter(Mandatory)] [string]$Group ) # we will check if the host name given as input matches the host name in the certificate. # if IP is given as input and the certificate has hostname (FQDN) then we will use the hostname name but # before that we will ensurelookup from hostname to IP works. $cert_hostname = ($CertSubject.Substring(($CertSubject.IndexOf("=")+1),($CertSubject.IndexOf(",")-3))).trim() # check if the input and cert hostname matches, if yes we are good to go # Else the input must have ben the IP address, we will if cert hostname can be resolved to match the input hostname (IP). if ($Group -ne $cert_hostname) { # we will look up the DNS to resolve the cert_hostname. # we could do this with either cert hostname or ibput hostname(IP), but cert hostname has better chance of getting resolved. try { $resolved_name = [System.Net.DNS]::GetHostEntry($cert_hostname).AddressList $resolved_name = $resolved_name | select -ExpandProperty IPAddressToString # $Group = $resolved_name # will come here if the host got resolved. Write-Verbose " $cert_hostname is host-name for provided input $Group IP address" if ($resolved_name -ne $Group) { # most probably this will not happen, just to be defensive adding this code. # this is the same host as the certificate hostname got resolved to the input hostname(IP) # we will start using the cert hostname for all the calls from this point in this session. Write-Error "Unable to resolve the certificate hostname to match the provided input hostname/IP. `n`n $_.Exception.Message" -ErrorAction Stop } else { # we are good to go. Write-Verbose " Resolved name and input name matches: $Group IP address" $global:Group = $cert_hostname } } catch { # unable to resolve the cert hostname. Host not reachable. Error out!. Write-Error "Unable to resolve the certificate hostname. Host not reachable. `n`n $_.Exception.Message" -ErrorAction Stop } } else { # we are good to go. Write-Verbose "Host-name given as input matches with certificate name" } } Function APIExceptionHandler { #Exception message handle differently for core and non core environment #GetResponseStream method does not work in core environment if(Get-Member -inputobject $_.Exception.Response -name "GetResponseStream" -Membertype Method) { $JsonResponse = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($JsonResponse) $reader.BaseStream.Position = 0 $reader.DiscardBufferedData() $responseBody = $reader.ReadToEnd(); if (($responseBody | ConvertFrom-Json).messages -ne $null) { foreach( $errorMsg in ($responseBody | ConvertFrom-Json).messages) { if($errorMsg.text -ne $null) {$exceptionString += $errorMsg.text + " "} } throw [System.Exception] $exceptionString } else { throw $_.Exception } } else { $responseBody = $_.ErrorDetails if (($responseBody | ConvertFrom-Json).messages -ne $null) { foreach( $errorMsg in ($responseBody | ConvertFrom-Json).messages) { if($errorMsg.text -ne $null) {$exceptionString += $errorMsg.text + " "} } throw [System.Exception] $exceptionString } else { throw $_.Exception } } } # SIG # Begin signature block # MIIiUgYJKoZIhvcNAQcCoIIiQzCCIj8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDidTDS8U60Ltza # 425ucKonwVu33bD4r6ZjtGmnBlXjaqCCEIwwggVRMIIEOaADAgECAhAqlTIY4QtL # e2/RSyqeww0mMA0GCSqGSIb3DQEBCwUAMIG9MQswCQYDVQQGEwJVUzEXMBUGA1UE # ChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv # cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv # cml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290 # IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE2MDUxMjAwMDAwMFoXDTI2MDUx # MTIzNTk1OVowgZExCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jw # b3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazFCMEAGA1UE # AxM5U3ltYW50ZWMgQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIENvZGUgU2ln # bmluZyBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApZCU # ss4Vva9QGeDEzgpo6PhoHDzlK84LS1feob0ads/HnzSxAG14+SblzJmsdMae4Gzh # LZEgk4drboRs1S5SF9CP2ers/Owg8g3aP22cJdHb+yDs8ND/SxC2uTkRjfYMokxP # uB33fdDlz/dcM4BmpNYib3vjeBcGBfTGxpS3/stWKn4P+hjzSANNp24WtxUbfdwv # 20MOKR7ReG9oGBu3gst+WI/Y0ph/kE27xws8cf7MxMv2o0IQrB3Kg/yRviyqgK+3 # mWlGcdOVIJnNUS6V+KwSHnzitCJpLgvAF3yg/e1cpi4iK2X/fc0xpGOs3yLlk3yi # ToLCEyy/HIsiPatm7QIDAQABo4IBdTCCAXEwLgYIKwYBBQUHAQEEIjAgMB4GCCsG # AQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBg # BgNVHSAEWTBXMFUGBWeBDAEDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1j # Yi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBh # MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2ZXJzYWwt # cm9vdC5jcmwwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgEG # MCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0yLTM4ODAdBgNV # HQ4EFgQUq4sRSQsqAmJ1SpvFAiCghNJL+N4wHwYDVR0jBBgwFoAUtnf6aUhHn1MS # 1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBADAxqBF+Ga4dNNPS18ywHMoB # oUoWX2jFyk0FWQH0/IUKp88fsBwgZAlBGBK0490yM3KZEytIXUmWESLnucRvBTBN # bKwzvk1PImLW8WcqFnvGjI4OuZYAnQ5kAZJk6jf4BH4RYm+MLJxdC7j/3X9AzOiI # 8p0mNEm0H735Wz94cy6B+mka85SPlTB+wrTYlSXSev6KOOiwb/ZvOfDO+oRWEH9W # p8UUYLRPWjmBSqzhMmBLDhSg9R6hSEOXWZG9HWhmmDAAi8JZ1H72hRt6f7rbZWgO # rW5ztlQ59GgDhXyhvFOzO6GCP3gkNQKkroWGwgXngHVcBZF57aqNh2kmiheB58Mw # ggWNMIIDdaADAgECAgphLSPLAAAAAAAhMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAMTIE1pY3Jvc29m # dCBDb2RlIFZlcmlmaWNhdGlvbiBSb290MB4XDTExMDIyMjE5NDYzOVoXDTIxMDIy # MjE5NTYzOVowgb0xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5j # LjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMp # IDIwMDggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE4 # MDYGA1UEAxMvVmVyaVNpZ24gVW5pdmVyc2FsIFJvb3QgQ2VydGlmaWNhdGlvbiBB # dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHYTdesQE0 # 22LXFZv/WFqMIyPWYI6R15CYg3rmWBk4jMX25WSFtKJx++29udrNTQC0yC1zpcdp # cZUfOTyyRAec6A76TUrEId8pYY8yImGCxYcfbox8XxYgUUTRcE9X6uMc48x57ljY # DsKzRZPALOeaFyt7ADd6QTN44TPi8xAaf4csvvb190Li5b+HYolfAEvfxd3kdUQy # QToecW5pywt1RgjRytIrldDP+7lAa2SMV038ExF5hO1eVPY0nwgB8xAlBhdK2vEd # emZrmGBmpNnv0i6C8fDvCepEyRVq4gNuM9Osn1UAx/YIapS5X9zgM/GEYPlbJxG0 # /Bbyu1ZqgCWNAgMBAAGjgcswgcgwEQYDVR0gBAowCDAGBgRVHSAAMA8GA1UdEwEB # /wQFMAMBAf8wCwYDVR0PBAQDAgGGMB0GA1UdDgQWBBS2d/ppSEefUxLVwuoHMnYH # 0ZcHGTAfBgNVHSMEGDAWgBRi+wohW39DbhHaCVRQa/XSlnHxnjBVBgNVHR8ETjBM # MEqgSKBGhkRodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0 # cy9NaWNyb3NvZnRDb2RlVmVyaWZSb290LmNybDANBgkqhkiG9w0BAQUFAAOCAgEA # WUdSmjRDPbATxDK4+DfZVVANKqRnZzOUyf/Y9QysjVo5g0htsrtDj3sL81uvNHpP # nrcI5/4BOBHeLzucPpYLslCC5rVVtGCWE0jtJy0lAnI4s/NlWYpiM3bVbPmV9J01 # TIOgwWf8g7V2k8boUhtZubqFlAo70SgFGxkM8yMXZUZ4DtlzSsC8PcBI4aT4h+UW # 4VaHZTjhABeiHkQr9k9aKTywG7hfZUtI62qYKV5X4vVi2ENHsy5qE0GnfTgjBg33 # XOZ/us1lBJJSUiZgqKM8Ig2tt7pMMm9S3qubccme6L6fXqatd9dtJZVWKhhDwszB # buHO30Xv/rdRKnyYtP5mg59rbOx01Z8yHyQ7QzrRBB0ASlul0m1mTMjBTezDATXB # uHn94Mb2qCmgko09Q3d4ph5MwjgEWOs6gzQExjROE0WyW3IvcmpP5Rp8GGtE48hY # H45xDIBrTit7PPhK45OSxLCavKZht9N8ynD1+v/NB6LZ6BHMbERQn3GwgtI2pllz # vbcLW2mGe6Ufx53B0grdLkzCMbaHKo0Qevqnfjxf8LCg9UUF3nkSegKF8R6K05mH # FylcvnVY1nkBBpVjOfsFFsc9SVGs+muYLcyXbqyzw2+CnACWfyQN6VlAYbNk27n5 # QaG57tlYjf8EV87Y/ur/Z1vOE/yc/PbhZs2joN13EbIwggWiMIIEiqADAgECAhAo # MknpQET0/8yu7uTbXTF2MA0GCSqGSIb3DQEBCwUAMIGRMQswCQYDVQQGEwJVUzEd # MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj # IFRydXN0IE5ldHdvcmsxQjBABgNVBAMTOVN5bWFudGVjIENsYXNzIDMgRXh0ZW5k # ZWQgVmFsaWRhdGlvbiBDb2RlIFNpZ25pbmcgQ0EgLSBHMzAeFw0xODExMTkwMDAw # MDBaFw0yMTExMTgyMzU5NTlaMIIBIzETMBEGCysGAQQBgjc8AgEDEwJVUzEZMBcG # CysGAQQBgjc8AgECDAhEZWxhd2FyZTEZMBcGCysGAQQBgjc8AgEBDAhTYW4gSm9z # ZTEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzU2OTky # NjUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhT # YW4gSm9zZTErMCkGA1UECgwiSGV3bGV0dCBQYWNrYXJkIEVudGVycHJpc2UgQ29t # cGFueTEWMBQGA1UECwwNTmltYmxlc3RvcmFnZTErMCkGA1UEAwwiSGV3bGV0dCBQ # YWNrYXJkIEVudGVycHJpc2UgQ29tcGFueTCCASIwDQYJKoZIhvcNAQEBBQADggEP # ADCCAQoCggEBAKWSqwdskNaMZ0X9Nqn4RKr2Bc131ZU5pS3FWyBGPRumS2sFtCTa # frdZ9i1xQL0dF3KcNHFFBAnkM/Ig4+UNOR/TIlAD7ssnDxNxfYG07GXxPmAlogoI # 2s6CWQ1mmLjZoJPHcGqcBsjKvn9sw/OZ1buOjB9SPBn02sh3S2L7dFnlQ1LuJDYa # sL0BlOzye9leSysvqyhDwMsYv3K7wNpyLoxi7Ee4z/ZBupMZY3LY8F7RMg6W2aHK # zzqDaP/X867mLqHedB6aK80aQY3C4MVFfOqiLHQFIG7/gVa16aO4U1q8HDUZswRx # IeiRMOaHje8VRdhtWUU2U+CQC4/RRx1GLsMCAwEAAaOCAV8wggFbMAkGA1UdEwQC # MAAwDgYDVR0PAQH/BAQDAgeAMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9yaC5z # eW1jYi5jb20vcmguY3JsMGAGA1UdIARZMFcwVQYFZ4EMAQMwTDAjBggrBgEFBQcC # ARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGQwXaHR0cHM6 # Ly9kLnN5bWNiLmNvbS9ycGEwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwHwYDVR0j # BBgwFoAUq4sRSQsqAmJ1SpvFAiCghNJL+N4wHQYDVR0OBBYEFCwOW0/Kegfs4BMM # 4OMor35vnOjTMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3Jo # LnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3JoLnN5bWNiLmNvbS9yaC5j # cnQwDQYJKoZIhvcNAQELBQADggEBAHFMScjMHXHXJv6cZj0Kwh1Uphw2DfoLL5rz # dOYd0vT8PH3WU3WwfJssqsU6LxvPIQlVj75z73PoegdDYLOH2NRfxMGYuw9Ru+4P # vBEjWpJOeV3j5rM8yubcgF6twn6hkDbI5GsfaK7MJgovXOglzKuV7mCkJMHb8LDT # qPE6mT9zDUR7YRaavhxV9YiiCnU7RLZd7HwtmJvygF/xkyJg+XYoTolQw7ZEtBVb # UScbXdDmJO0BWpWlkAF+gMmIBlcdoZ7Avwf4DdQdp+KiDKRlMFTgp0fJ7uzfTZDY # P+yEYst/1wNjCavw8EoALakact7/SR/x1WP0hR5qALp/jg4gdqIxghEcMIIRGAIB # ATCBpjCBkTELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0 # aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUIwQAYDVQQDEzlT # eW1hbnRlYyBDbGFzcyAzIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5n # IENBIC0gRzMCECgySelARPT/zK7u5NtdMXYwDQYJYIZIAWUDBAIBBQCgfDAQBgor # BgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEE # AYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg7TzXgKubcKaV # eQhqpQOj/d6q+StVfJonqS/zVwA2RbYwDQYJKoZIhvcNAQEBBQAEggEANL/puypC # 6uvp+NjkjhBVxpaW7DPDHsxPEWpuGjhvdMJrZidNnvNBmDKnoHGG/Lfu3LHQ184D # Xbu2EcA1Kf5IssjIYKcpy/h0xdf4IHowK0x7rj2pvpknLjopdmcnGz7PCRDkyKfd # SoyFhtUFRmJW0azJejH+sT76DBhbxoQGa3mX8VGh/Fmln8nNxSt1s/Mt9dFRAUze # yr9sauUxWqSZRPgGYc9GtUEYeg6hybOyjkQKHxNQF6lilTlkWn7rlIXVrJ2kT3KQ # iGLV0gBjdJHvVX9PyOGwW0ldDVzK0pGZhn91OQ8nwuET2SZaX3GV4Zo/hVvNzCTx # Ntn7jvq7DetQEKGCDsgwgg7EBgorBgEEAYI3AwMBMYIOtDCCDrAGCSqGSIb3DQEH # AqCCDqEwgg6dAgEDMQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARm # MGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCAH0bZOnUurzEIsQou0 # ULNnqSZk/Ww9jIVZhd6atv/jQQIQUBu3Bg5g+LpJ6glaIrwIlRgPMjAyMDA5MDMx # MDQ4NDdaoIILuzCCBoIwggVqoAMCAQICEATNP4VornbGG7D+cWDMp20wDQYJKoZI # hvcNAQELBQAwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hB # MiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQTAeFw0xOTEwMDEwMDAwMDBaFw0z # MDEwMTcwMDAwMDBaMEwxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjEkMCIGA1UEAxMbVElNRVNUQU1QLVNIQTI1Ni0yMDE5LTEwLTE1MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6WQ1nPqpmGVkG+QX3LgpNsxnCViF # TTDgyf/lOzwRKFCvBzHiXQkYwvaJjGkIBCPgdy2dFeW46KFqjv/UrtJ6Fu/4QbUd # OXXBzy+nrEV+lG2sAwGZPGI+fnr9RZcxtPq32UI+p1Wb31pPWAKoMmkiE76Lgi3G # mKtrm7TJ8mURDHQNsvAIlnTE6LJIoqEUpfj64YlwRDuN7/uk9MO5vRQs6wwoJyWA # qxBLFhJgC2kijE7NxtWyZVkh4HwsEo1wDo+KyuDT17M5d1DQQiwues6cZ3o4d1RA # /0+VBCDU68jOhxQI/h2A3dDnK3jqvx9wxu5CFlM2RZtTGUlinXoCm5UUowIDAQAB # o4IDODCCAzQwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/ # BAwwCgYIKwYBBQUHAwgwggG/BgNVHSAEggG2MIIBsjCCAaEGCWCGSAGG/WwHATCC # AZIwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwggFk # BggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBz # ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBz # ACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBD # AGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBp # AG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBo # ACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBl # ACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAg # AHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaAFPS2 # 4SAd/imu0uRhpbKiJbLIFzVuMB0GA1UdDgQWBBRWUw/BxgenTdfYbldygFBM5Oye # wTBxBgNVHR8EajBoMDKgMKAuhixodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hh # Mi1hc3N1cmVkLXRzLmNybDAyoDCgLoYsaHR0cDovL2NybDQuZGlnaWNlcnQuY29t # L3NoYTItYXNzdXJlZC10cy5jcmwwgYUGCCsGAQUFBwEBBHkwdzAkBggrBgEFBQcw # AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME8GCCsGAQUFBzAChkNodHRwOi8v # Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEVGltZXN0 # YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQAug6FEBUoE47kyUvrZgfAa # u/gJjSO5PdiSoeZGHEovbno8Y243F6Mav1gjskOclINOOQmwLOjH4eLM7ct5a87e # IwFH7ZVUgeCAexKxrwKGqTpzav74n8GN0SGM5CmCw4oLYAACnR9HxJ+0CmhTf1oQ # pvgi5vhTkjFf2IKDLW0TQq6DwRBOpCT0R5zeDyJyd1x/T+k5mCtXkkTX726T2UPH # BDNjUTdWnkcEEcOjWFQh2OKOVtdJP1f8Cp8jXnv0lI3dnRq733oqptJFplUMj/ZM # ivKWz4lG3DGykZCjXzMwYFX1/GswrKHt5EdOM55naii1TcLtW5eC+MupCGxTCbT3 # MIIFMTCCBBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBl # MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 # d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv # b3QgQ0EwHhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQG # EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl # cnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0 # YW1waW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvN # j3/dqbqCmcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfT # w+F+CNZqFAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V # +/OjuiI+GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR # 4aF5FMZNJCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfk # dT66mA+Ef58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/a # rBD13ays6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKi # JbLIFzVuMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB # /wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRo # dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu # Y3JsMDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 # cmVkSURSb290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsG # AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwH # ATANBgkqhkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ # ys8DAv3Fp8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg33akOpMP+LLR2HwZ # YuhegiUexLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OM # kVIsrymJ5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1 # t5ljhSKMYcp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJ # rhO24XxCQijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaOUjGCAk0wggJJAgEB # MIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV # BAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNz # dXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0ECEATNP4VornbGG7D+cWDMp20wDQYJYIZI # AWUDBAIBBQCggZgwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3 # DQEJBTEPFw0yMDA5MDMxMDQ4NDdaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFAMl # vVBe2pYwLcIvT6AeTCi+KDTFMC8GCSqGSIb3DQEJBDEiBCABXf3W+Q60xf21oTxf # zSKCTUbBaoOx7VDgdn1U0gQBRzANBgkqhkiG9w0BAQEFAASCAQDpXz7R0Xc+kAPQ # 0afs4tiaRHVFv3Yg2KmUDmz1KizBiKuPA9fEhc0LbM2xeWnsoTQkynOuakbGHxAs # CDzvDGvhMEykFsogB4yInJQQ9lYUfuZ3ClIK8UA5nNyByyST4JvYa1EdNJGSXBDR # qy+wJrxZXn7u/kytenM0U2KyiPhqv+bQTMiVLrIzGzIJxRVYdU2EAcSCzPyD+Ts5 # TB1OEAE/zVdUfuQSLnFN9OXeaJtC3j5Yc5RC4NruBCb5F7r4rUDfyfrpjXclo9Jk # cdxIjZryH/HTAleGZfC8/DX2LcwQXeVskG9ywm1MEdeHsfsYFR3X4SFFn36lJDnW # TGwHpnHi # SIG # End signature block |