PureStorage.AzureNative.Util.ps1
$PluginPrivileges = @( 'Datastore.AllocateSpace', 'Datastore.Browse', 'Datastore.Config', 'Datastore.Delete', 'Datastore.DeleteFile', 'Datastore.FileManagement', 'Datastore.Move', 'Datastore.Rename', 'Datastore.UpdateVirtualMachineFiles', 'Datastore.UpdateVirtualMachineMetadata', 'Extension.Register', 'Extension.Unregister', 'Extension.Update', 'Folder.Create', 'Folder.Delete', 'Folder.Move', 'Folder.Rename', 'Global.CancelTask', 'Global.ManageCustomFields', 'Global.SetCustomField', 'Host.Config.Storage', 'ScheduledTask.Create', 'ScheduledTask.Delete', 'ScheduledTask.Edit', 'ScheduledTask.Run', 'Sessions.ValidateSession', 'StorageProfile.Update', 'StorageProfile.View', 'StorageViews.View', 'StorageViews.ConfigureService', 'Task.Create', 'Task.Update', 'VirtualMachine.Config.AddExistingDisk', 'VirtualMachine.Config.AddNewDisk', 'VirtualMachine.Config.AddRemoveDevice', 'VirtualMachine.Config.RemoveDisk', 'VirtualMachine.Interact.PowerOff', 'VirtualMachine.Interact.PowerOn', 'VirtualMachine.Inventory.Create', 'VirtualMachine.Inventory.CreateFromExisting', 'VirtualMachine.Inventory.Delete', 'VirtualMachine.Inventory.Move', 'VirtualMachine.Inventory.Register', 'VirtualMachine.Inventory.Unregister', 'VirtualMachine.Provisioning.Clone', 'VirtualMachine.Provisioning.CloneTemplate', 'VirtualMachine.Provisioning.CreateTemplateFromVM', 'VirtualMachine.Provisioning.GetVmFiles', 'VirtualMachine.State.CreateSnapshot', 'VirtualMachine.State.RemoveSnapshot', 'VirtualMachine.State.RenameSnapshot', 'VirtualMachine.State.RevertToSnapshot' ) # Service account name prefix $AccountNamePrefix = "psserviceaccount" # Service account role name $RoleName = "PureStorageService" # Timeout for successful Rest call $WaitTimeSeconds = 3600 $DefaultManagementHostUri = "https://management.azure.com" # .SYNOPSIS # Write a message to console with added timestamp. function PrintLog { param ( [Parameter(Mandatory = $true)] [string]$Message, [Parameter()] [ValidateNotNullOrEmpty()] [ValidateSet('INFO', 'DEBUG', 'WARNING', 'ERROR', 'VERBOSE')] [string]$Severity = 'INFO' ) $dateTime = (Get-Date -f "yyyy-MM-dd HH:mm:ss") $msg = "$dateTime $Severity $Message" switch ($Severity) { WARNING { Write-Warning $msg } DEBUG { Write-Debug $msg } ERROR { Write-Error $msg } VERBOSE { Write-Verbose $msg } Default { Write-Host $msg } } } function New-RandomPassword { param ( [int]$length = 16, [int]$specialCharCount = 4 ) if ($length -lt 8) { throw "Password length should be at least 8 characters." } if ($specialCharCount -gt $length) { throw "Number of special characters cannot exceed total length of the password." } $upperCaseCount = [math]::Ceiling(($length - $specialCharCount) / 3) $lowerCaseCount = [math]::Ceiling(($length - $specialCharCount) / 3) $numberCount = ($length - $specialCharCount) - $upperCaseCount - $lowerCaseCount $upperCase = 1..$upperCaseCount | ForEach-Object { [char[]]([char]'A'..[char]'Z') | Get-SecureRandom } $lowerCase = 1..$lowerCaseCount | ForEach-Object { [char[]]([char]'a'..[char]'z') | Get-SecureRandom } $numbers = 1..$numberCount | ForEach-Object { [char[]]([char]'0'..[char]'9') | Get-SecureRandom } $special = 1..$specialCharCount | ForEach-Object { ([char[]]"!@#$%^&*()_+-=[]{}|;:',.<>?") | Get-SecureRandom } $passwordChars = $upperCase + $lowerCase + $numbers + $special $password = ($passwordChars | Sort-Object { Get-SecureRandom }) -join "" return $password } function Get-EncryptedSignature { param ( [Parameter(Mandatory = $true)] [string]$Text, [Parameter(Mandatory = $true)] [string]$PrivateKey ) $hashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256 # Convert the private key from PEM format to an RSA object $rsaKey = [System.Security.Cryptography.RSA]::Create() $rsaKey.ImportFromPem($PrivateKey) $bytesToEncrypt = [System.Text.Encoding]::UTF8.GetBytes($Text) $signature = $rsaKey.SignData($bytesToEncrypt, $hashAlgorithm, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) # Convert the encrypted byte array to a base64 string $Signature64 = [Convert]::ToBase64String($signature) return $Signature64 } function Test-TextSignarure { param ( [Parameter(Mandatory = $true)] [string]$Text, [Parameter(Mandatory = $true)] [string]$Signature, [Parameter(Mandatory = $true)] [string]$PublicKey ) $key = $PublicKey -replace "-----BEGIN PUBLIC KEY-----", "" -replace "-----END PUBLIC KEY-----", "" $key = [Convert]::FromBase64String($key) $hashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256 # Convert the private key from PEM format to an RSA object $rsaKey = [System.Security.Cryptography.RSA]::Create() $rsaKey.ImportSubjectPublicKeyInfo($key, [ref]0) # Convert the base64-encoded encrypted text to a byte array $SignatureBytes = [Convert]::FromBase64String($Signature) $TextBytes = [System.Text.Encoding]::UTF8.GetBytes($Text) $IsValid = $rsaKey.VerifyData($TextBytes, $SignatureBytes, $hashAlgorithm, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) return $IsValid } function Test-RequestDatetimeInUTC { param ( [Parameter(Mandatory = $true)] [string]$RequestDatetime, [Parameter(Mandatory = $true)] [int]$TimeWindowInMinutes ) # Check time window to validate the request $requestDatetimeUTC = Get-Date -Date $RequestDatetime $currentDatetimeUTC = Get-Date -AsUTC if ($requestDatetimeUTC.AddMinutes($TimeWindowInMinutes) -lt $currentDatetimeUTC) { throw "Request is outside the time window: $TimeWindowInMinutes minutes" } } function ConvertTo-EncryptedText { param ( [Parameter(Mandatory = $true)] [string]$Text, [Parameter(Mandatory = $true)] [string]$PublicKey ) # Convert the private key from PEM format to an RSA object $rsaKey = [System.Security.Cryptography.RSA]::Create() $rsaKey.ImportFromPem($PublicKey) # Convert the text to a byte array $bytesToEncrypt = [System.Text.Encoding]::UTF8.GetBytes($Text) # Encrypt the byte array using the key $encryptedBytes = $rsaKey.Encrypt($bytesToEncrypt, [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1) # Convert the encrypted byte array to a base64 string $encryptedText = [Convert]::ToBase64String($encryptedBytes) return $encryptedText } function ConvertFrom-EncryptedText { param ( [Parameter(Mandatory = $true)] [string]$EncryptedText, [Parameter(Mandatory = $true)] [string]$PrivateKey ) # Convert the private key from PEM format to an RSA object $rsaKey = [System.Security.Cryptography.RSA]::Create() $rsaKey.ImportFromPem($PrivateKey) # Convert the base64-encoded encrypted text to a byte array $encryptedBytes = [Convert]::FromBase64String($EncryptedText) # Decrypt the byte array using the key $decryptedBytes = $rsaKey.Decrypt($encryptedBytes, [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1) # Convert the decrypted byte array back to a string $decryptedText = [System.Text.Encoding]::UTF8.GetString($decryptedBytes) return $decryptedText } function ConvertFrom-Base64 { param ( [Parameter(Mandatory = $true)] [string]$Base64Text ) $bytes = [Convert]::FromBase64String($Base64Text) $text = [System.Text.Encoding]::UTF8.GetString($bytes) return $text } function ConvertTo-Base64 { param ( [Parameter(Mandatory = $true)] [string]$Text ) $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text) $base64Text = [Convert]::ToBase64String($bytes) return $base64Text } function CompareAndUpdate-Privileges { param ( [Parameter(Mandatory = $true)] [object]$Role, [Parameter(Mandatory = $true)] [string[]]$RequiredPrivileges ) # These are the default privileges that are always required and cannot be removed $DefaultPrivileges = @( 'System.Anonymous', 'System.Read', 'System.View' ) $RequiredPrivileges += $DefaultPrivileges $RoleName = $Role.Name $RolePrivileges = $Role.PrivilegeList # Identify missing and extra privileges # Find missing privileges (those in the required list but not in the role) $MissingPrivileges = $RequiredPrivileges | Where-Object { $_ -notin $RolePrivileges } # Find extra privileges (those in the role but not in the required list) $ExtraPrivileges = $RolePrivileges | Where-Object { $_ -notin $RequiredPrivileges } $PrivilegesToAdd = @() if ($MissingPrivileges) { PrintLog "Missing Privileges: $($MissingPrivileges -join ', ')" INFO $PrivilegesToAdd += $MissingPrivileges | ForEach-Object { Get-VIPrivilege -Id $_ } } else { PrintLog "No missing privileges" INFO } $PrivilegesToRemove = @() if ($ExtraPrivileges) { PrintLog "Extra Privileges: $($ExtraPrivileges -join ', ')" INFO $PrivilegesToRemove += $ExtraPrivileges | ForEach-Object { Get-VIPrivilege -Id $_ } } else { PrintLog "No extra privileges" INFO } # Update the role with the missing and extra privileges # Apply updates only if needed $IsUpdated = $false if ($PrivilegesToAdd.Count -gt 0) { PrintLog "Adding missing privileges to role '$RoleName'" INFO $null = Set-VIRole -Role $Role -AddPrivilege $PrivilegesToAdd $IsUpdated = $true } if ($PrivilegesToRemove.Count -gt 0) { PrintLog "Removing extra privileges from role '$RoleName'" INFO $null = Set-VIRole -Role $Role -RemovePrivilege $PrivilegesToRemove $IsUpdated = $true } if (-not $IsUpdated) { PrintLog "No changes needed for the role: $RoleName" INFO } } <# .SYNOPSIS Creates a new service account and assigns it a role with specific privileges. #> function _New-AvsServiceAccount { param( [Parameter(Mandatory = $true)] [string]$InitializationHandle ) # Convert the InitializationHandle JSON string to a JSON object $DecodedData = $InitializationHandle | ConvertFrom-Json # The DecodedData is a JSON object with the following structure: # { # "sddcResourceId": "string", # "serviceAccountUsername": "string" # } $AccountName = $DecodedData.serviceAccountUsername # Validate the prefix of the account name if (-not $AccountName.StartsWith($AccountNamePrefix)) { throw "The account name must start with '$AccountNamePrefix'" } # Generate a random password for the service account $AccountPassword = New-RandomPassword # If the user already exists, update the password $User = Get-SsoPersonUser -Domain 'vsphere.local' | Where-Object { $_.Name -eq $AccountName } if ($User) { PrintLog "User $AccountName already exists, updating the password" WARNING $null = Set-SsoPersonUser -User $User -NewPassword $AccountPassword -ErrorAction Stop } else { $User = New-SsoPersonUser -UserName $AccountName -Password $AccountPassword -Description "Pure Storage Service Account" -ErrorAction Stop } # Create Role and assign Role to user $Role = Get-VIRole -Name $RoleName -ErrorAction SilentlyContinue if ($Role) { PrintLog "Role $RoleName already exists, checking the role privileges against the required privileges" WARNING CompareAndUpdate-Privileges -Role $Role -RequiredPrivileges $PluginPrivileges } else { $Privileges = @() foreach ($priv in $PluginPrivileges) { PrintLog "Adding privilege: $priv" DEBUG $Privileges += Get-VIPrivilege -Id $priv } $Role = New-VIRole -Name $RoleName -Privilege $Privileges } $Account = Get-VIAccount -Domain $User.Domain | Where-Object { $_.Id -eq $AccountName } if (-not $Account) { throw "Failed to create account for user $User" } $RootFolder = Get-Folder -NoRecursion if (-not $RootFolder) { throw "Failed to retrieve root folder" } $RootFolder = $RootFolder | Select-Object -Last 1 PrintLog "Adding permissions for Account $AccountName on $($RootFolder.Name) with role $RoleName" INFO $null = New-VIPermission -Entity $RootFolder -Principal $Account -Role $Role -Propagate $true PrintLog "Avs Service Account $AccountName created successfully with role $RoleName" INFO # Return the initialization data for the service account $vSphereIp = $Account.Server.ServiceUri.Host $InitializationData= @{ "serviceAccountUsername" = $AccountName "serviceAccountPassword" = $AccountPassword "vSphereIp" = $vSphereIp } return $InitializationData } <# .SYNOPSIS Removes Pure Storage AVS service account(s) and the role assigned to them. #> function _Remove-AvsServiceAccount { param( [Parameter(Mandatory = $false)] [string]$Suffix, [switch]$DryRun ) # If we want to remove all service accounts, we can set the suffix to "*" $AccountName = $AccountNamePrefix + $Suffix $users = Get-SsoPersonUser -Domain 'vsphere.local' | Where-Object { $_.Name -like $AccountName } if (-not $users) { PrintLog "User '$AccountName' not found." WARNING return } PrintLog "Found $($users.Count) user(s) in total" INFO foreach ($user in $users) { $userName = $user.Name PrintLog "Found user: $userName" INFO $name = "VSPHERE.LOCAL\" + $user.Name $accountPermissions = Get-VIPermission -Principal $name # Attempt to remove any permissions the user might have if ($accountPermissions) { if ($DryRun) { PrintLog "Dry run: Removing permissions '$($accountPermissions.Role)' from user $userName" INFO } else { PrintLog "Removing permissions '$($accountPermissions.Role)' from user $userName" INFO try { Remove-VIPermission -Permission $accountPermissions -Confirm:$false PrintLog "Successfully removed permissions for user $userName" INFO } catch { PrintLog "Failed to remove permissions for user $userName with error: $_" WARNING } } } # Remove the user regardless of role if ($DryRun) { PrintLog "Dry run: Removing user $userName" INFO } else { PrintLog "Removing user $userName" INFO try { Remove-SsoPersonUser -User $user PrintLog "Successfully removed user $userName." INFO } catch { throw "Failed to remove user $userName with error: $_" } } # Check and clean up role if it's unused $role = Get-VIRole -Name $RoleName if ($role) { $remainingRoleAssignments = Get-VIPermission | Where-Object { $_.Role -eq $RoleName } if ($remainingRoleAssignments.Count -eq 0) { if ($DryRun) { PrintLog "Dry run: Removing unused role $RoleName" INFO } else { PrintLog "Removing unused role $RoleName" INFO try { Remove-VIRole -Role $role -Force -Confirm:$false PrintLog "Successfully removed role $RoleName" INFO } catch { throw "Failed to remove role $RoleName with error: $_" } } } else { PrintLog "Role $RoleName still has other accounts assigned" WARNING } } else { PrintLog "Role $RoleName not found" WARNING } } } function ConvertTo-HttpHeaders { param ( [Parameter(Mandatory = $true)] [System.Collections.Generic.Dictionary[string, System.Collections.Generic.IEnumerable[string]]]$Headers ) # Create a new HttpRequestMessage to construct HttpHeaders $httpRequestMessage = [System.Net.Http.HttpRequestMessage]::new() foreach ($key in $Headers.Keys) { foreach ($value in $Headers[$key]) { $null = $httpRequestMessage.Headers.TryAddWithoutValidation($key, $value) } } $result = $httpRequestMessage.Headers Write-Output -NoEnumerate $result } <# .SYNOPSIS Invoke an arm request to Azure REST API and convert the response to an AzRest response object. #> function Invoke-ARM{ param( [Parameter(Mandatory = $true)] [string]$Uri, [Parameter(Mandatory = $true)] [string]$Method, [Parameter(Mandatory = $false)] [string]$Body, [Parameter(Mandatory = $false)] [hashtable]$RequestHeaders = @{} ) $content = Invoke-ARMRequest -Uri $Uri -Method $Method -Body $Body -Headers $RequestHeaders ` -StatusCodeVariable "statusCode" -ResponseHeadersVariable "headers" $httpHeaders = ConvertTo-HttpHeaders -Headers $headers # Structure the output to match Invoke-AzRest response $responseObject = [pscustomobject]@{ Content = $content Headers = $httpHeaders StatusCode = $statusCode } Write-Output -NoEnumerate $responseObject } function Invoke-AzureRestCallWithVerificationAndRetries { param ( [Parameter(Mandatory = $true)] [ValidateSet("GET", "POST", "PUT", "DELETE")] [string]$Method, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Uri, [Parameter(Mandatory = $false)] [hashtable]$PayLoad = @{}, [Parameter(Mandatory = $false)] [hashtable]$RequestHeaders = @{}, [Parameter(Mandatory = $false)] [int]$MaxRetries = 3, [int]$RetryInterval = 1 ) if ($Payload.Count -eq 0) { # When payload is empty, set it to an empty string to avoid the Azure GET request failure. $jsonPayload = "" } else { $jsonPayLoad = $PayLoad | ConvertTo-Json -Depth 10 } PrintLog "Request URI: $Uri" DEBUG PrintLog "Request BODY: $jsonPayLoad" DEBUG $attempt = 0 while ($attempt -lt $MaxRetries) { try { $attempt++ $res = Invoke-ARM -Uri $Uri -Method $Method -Body $jsonPayLoad -RequestHeaders $RequestHeaders if ($res.StatusCode -ge 200 -and $res.StatusCode -lt 300) { return $res } $errorMsg = "Request failed with status code: $( $res.StatusCode ) and content: $( $res.Content )" PrintLog $errorMsg ERROR throw $errorMsg } catch { $errorMsg = $_.Exception.Message if ($attempt -lt $MaxRetries) { PrintLog "Attempt $attempt failed. Retrying in $RetryInterval seconds..." DEBUG Start-Sleep -Seconds $RetryInterval } else { $msg = "Maximum retry attempts reached: $errorMsg" PrintLog $msg ERROR throw $msg } } } } function WaitForSuccessStatus { param( [Parameter(Mandatory = $true)] [System.Net.Http.Headers.HttpHeaders]$Headers, [Parameter(Mandatory = $false)] [string]$CorrelationId, [Parameter(Mandatory = $false)] [int]$WaitTimeoutSeconds, [Parameter(Mandatory = $false)] [int]$CheckIntervalSeconds = 30 ) $asyncOpUrl = $Headers.GetValues("Azure-AsyncOperation") $correlationId = $Headers.GetValues("x-ms-correlation-request-id") $requestHeaders = Get-RequestHeader -CorrelationId $CorrelationId PrintLog "'x-ms-correlation-request-id' of the request: '$correlationId'" DEBUG PrintLog "Op URL: $asyncOpUrl" DEBUG PrintLog "Querying the operation status for up to $WaitTimeoutSeconds seconds to complete" $endTime = (Get-Date).AddSeconds($WaitTimeoutSeconds) while ((Get-Date) -lt $endTime) { $content = (Invoke-AzureRestCallWithVerificationAndRetries -Method 'GET' -Uri "$asyncOpUrl" -RequestHeaders $requestHeaders).Content $opStatus = $content.status PrintLog "Operation status: '$opStatus'" DEBUG if ($opStatus -eq "Succeeded") { return "" } elseif ($opStatus -ne "Accepted" -And $opStatus -ne "Deleting" -And $opStatus -ne "Pending" -And $opStatus -ne "Running") { $msg = "OP status for '$correlationId' correlation ID: '$opStatus'" PrintLog $msg ERROR if ($opStatus -eq "Failed") { $errorMessage = "{0}" -f $content.error.message PrintLog "Error message: $errorMessage" ERROR return $errorMessage } $msg = "Unexpected OP status" PrintLog $msg ERROR return $msg } Start-Sleep -Seconds $CheckIntervalSeconds } $msg = "Timed out waiting for successful operation status, correlation ID: '$correlationId'" PrintLog $msg ERROR return $msg } function Get-RequestHeader { param ( [Parameter(Mandatory = $false)] [string]$CorrelationId ) $requestHeaders = @{ 'Content-Type' = 'application/json; charset=utf-8' } if ($CorrelationId -and $CorrelationId -ne '') { $requestHeaders['x-krypton-correlation-id'] = $CorrelationId } return $requestHeaders } function Invoke-EnableAvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $false)] [string]$CorrelationId, [Parameter(Mandatory = $true)] [string]$PureStorageBlockApiVersion ) if ($null -ne $SddcResourceId) { PrintLog "SDDC resource ID: $SddcResourceId" DEBUG $payload = @{ 'sddcResourceId' = $SddcResourceId } } else { throw "SDDC resource ID is empty" } $requestHeaders = Get-RequestHeader -CorrelationId $CorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/enableAvsConnection?api-version=$PureStorageBlockApiVersion" $res = Invoke-AzureRestCallWithVerificationAndRetries -Method 'POST' -Uri $uri -PayLoad $payload -RequestHeaders $requestHeaders if ($null -eq $res) { $msg = "Azure rest call failed. No response received." PrintLog $msg ERROR return $msg } return WaitForSuccessStatus -Headers $res.Headers -WaitTimeoutSeconds $WaitTimeSeconds -CorrelationId $CorrelationId } function Get-AvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $false)] [string]$CorrelationId, [Parameter(Mandatory = $true)] [string]$PureStorageBlockApiVersion ) $requestHeaders = Get-RequestHeader -CorrelationId $CorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/getAvsConnection?api-version=$PureStorageBlockApiVersion" return Invoke-AzureRestCallWithVerificationAndRetries -Method 'POST' -Uri $uri -RequestHeaders $requestHeaders ` | Select-Object -ExpandProperty Content } function Invoke-FinalizeAvsConnection { param ( [Parameter(Mandatory = $true)] [hashtable]$ServiceInitializationData, [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $false)] [string]$CorrelationId, [Parameter(Mandatory = $true)] [string]$PureStorageBlockApiVersion ) $payload = @{ 'serviceInitializationDataEnc' = "" 'serviceInitializationData' = $ServiceInitializationData } $requestHeaders = Get-RequestHeader -CorrelationId $CorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/finalizeAvsConnection?api-version=$PureStorageBlockApiVersion" $res = Invoke-AzureRestCallWithVerificationAndRetries -Method 'POST' -Uri $uri -PayLoad $payload -RequestHeaders $requestHeaders if ($null -eq $res) { $msg = "Azure rest call failed. No response received." PrintLog $msg ERROR return $msg } return WaitForSuccessStatus -Headers $res.Headers -WaitTimeoutSeconds $WaitTimeSeconds -CorrelationId $CorrelationId } function Invoke-DisableAvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $false)] [string]$CorrelationId, [Parameter(Mandatory = $true)] [string]$PureStorageBlockApiVersion ) $requestHeaders = Get-RequestHeader -CorrelationId $CorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/disableAvsConnection?api-version=$PureStorageBlockApiVersion" $res = Invoke-AzureRestCallWithVerificationAndRetries -Method 'POST' -Uri $uri -RequestHeaders $requestHeaders if ($null -eq $res) { $msg = "Azure rest call failed. No response received." PrintLog $msg ERROR return $msg } return WaitForSuccessStatus -Headers $res.Headers -WaitTimeoutSeconds $WaitTimeSeconds -CorrelationId $CorrelationId } # SIG # Begin signature block # MIIuvwYJKoZIhvcNAQcCoIIusDCCLqwCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDryc9iQbjLm1ba # uMkTIBqvrW4yGJEdWypGRuk5UQPlNKCCE2gwggVyMIIDWqADAgECAhB2U/6sdUZI # k/Xl10pIOk74MA0GCSqGSIb3DQEBDAUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK # ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln # bmluZyBSb290IFI0NTAeFw0yMDAzMTgwMDAwMDBaFw00NTAzMTgwMDAwMDBaMFMx # CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQD # EyBHbG9iYWxTaWduIENvZGUgU2lnbmluZyBSb290IFI0NTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBALYtxTDdeuirkD0DcrA6S5kWYbLl/6VnHTcc5X7s # k4OqhPWjQ5uYRYq4Y1ddmwCIBCXp+GiSS4LYS8lKA/Oof2qPimEnvaFE0P31PyLC # o0+RjbMFsiiCkV37WYgFC5cGwpj4LKczJO5QOkHM8KCwex1N0qhYOJbp3/kbkbuL # ECzSx0Mdogl0oYCve+YzCgxZa4689Ktal3t/rlX7hPCA/oRM1+K6vcR1oW+9YRB0 # RLKYB+J0q/9o3GwmPukf5eAEh60w0wyNA3xVuBZwXCR4ICXrZ2eIq7pONJhrcBHe # OMrUvqHAnOHfHgIB2DvhZ0OEts/8dLcvhKO/ugk3PWdssUVcGWGrQYP1rB3rdw1G # R3POv72Vle2dK4gQ/vpY6KdX4bPPqFrpByWbEsSegHI9k9yMlN87ROYmgPzSwwPw # jAzSRdYu54+YnuYE7kJuZ35CFnFi5wT5YMZkobacgSFOK8ZtaJSGxpl0c2cxepHy # 1Ix5bnymu35Gb03FhRIrz5oiRAiohTfOB2FXBhcSJMDEMXOhmDVXR34QOkXZLaRR # kJipoAc3xGUaqhxrFnf3p5fsPxkwmW8x++pAsufSxPrJ0PBQdnRZ+o1tFzK++Ol+ # A/Tnh3Wa1EqRLIUDEwIrQoDyiWo2z8hMoM6e+MuNrRan097VmxinxpI68YJj8S4O # JGTfAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBQfAL9GgAr8eDm3pbRD2VZQu86WOzANBgkqhkiG9w0BAQwFAAOCAgEA # Xiu6dJc0RF92SChAhJPuAW7pobPWgCXme+S8CZE9D/x2rdfUMCC7j2DQkdYc8pzv # eBorlDICwSSWUlIC0PPR/PKbOW6Z4R+OQ0F9mh5byV2ahPwm5ofzdHImraQb2T07 # alKgPAkeLx57szO0Rcf3rLGvk2Ctdq64shV464Nq6//bRqsk5e4C+pAfWcAvXda3 # XaRcELdyU/hBTsz6eBolSsr+hWJDYcO0N6qB0vTWOg+9jVl+MEfeK2vnIVAzX9Rn # m9S4Z588J5kD/4VDjnMSyiDN6GHVsWbcF9Y5bQ/bzyM3oYKJThxrP9agzaoHnT5C # JqrXDO76R78aUn7RdYHTyYpiF21PiKAhoCY+r23ZYjAf6Zgorm6N1Y5McmaTgI0q # 41XHYGeQQlZcIlEPs9xOOe5N3dkdeBBUO27Ql28DtR6yI3PGErKaZND8lYUkqP/f # obDckUCu3wkzq7ndkrfxzJF0O2nrZ5cbkL/nx6BvcbtXv7ePWu16QGoWzYCELS/h # AtQklEOzFfwMKxv9cW/8y7x1Fzpeg9LJsy8b1ZyNf1T+fn7kVqOHp53hWVKUQY9t # W76GlZr/GnbdQNJRSnC0HzNjI3c/7CceWeQIh+00gkoPP/6gHcH1Z3NFhnj0qinp # J4fGGdvGExTDOUmHTaCX4GUT9Z13Vunas1jHOvLAzYIwggbmMIIEzqADAgECAhB3 # vQ4DobcI+FSrBnIQ2QRHMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkw # FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENv # ZGUgU2lnbmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAw # MDBaMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS8w # LQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0EgMjAyMDCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANZCTfnjT8Yj9GwdgaYw90g9 # z9DljeUgIpYHRDVdBs8PHXBg5iZU+lMjYAKoXwIC947Jbj2peAW9jvVPGSSZfM8R # Fpsfe2vSo3toZXer2LEsP9NyBjJcW6xQZywlTVYGNvzBYkx9fYYWlZpdVLpQ0LB/ # okQZ6dZubD4Twp8R1F80W1FoMWMK+FvQ3rpZXzGviWg4QD4I6FNnTmO2IY7v3Y2F # QVWeHLw33JWgxHGnHxulSW4KIFl+iaNYFZcAJWnf3sJqUGVOU/troZ8YHooOX1Re # veBbz/IMBNLeCKEQJvey83ouwo6WwT/Opdr0WSiMN2WhMZYLjqR2dxVJhGaCJedD # CndSsZlRQv+hst2c0twY2cGGqUAdQZdihryo/6LHYxcG/WZ6NpQBIIl4H5D0e6lS # TmpPVAYqgK+ex1BC+mUK4wH0sW6sDqjjgRmoOMieAyiGpHSnR5V+cloqexVqHMRp # 5rC+QBmZy9J9VU4inBDgoVvDsy56i8Te8UsfjCh5MEV/bBO2PSz/LUqKKuwoDy3K # 1JyYikptWjYsL9+6y+JBSgh3GIitNWGUEvOkcuvuNp6nUSeRPPeiGsz8h+WX4VGH # aekizIPAtw9FbAfhQ0/UjErOz2OxtaQQevkNDCiwazT+IWgnb+z4+iaEW3VCzYkm # eVmda6tjcWKQJQ0IIPH/AgMBAAGjggGuMIIBqjAOBgNVHQ8BAf8EBAMCAYYwEwYD # VR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU # 2rONwCSQo2t30wygWd0hZ2R2C3gwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0Q9lW # ULvOljswgZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8vb2Nz # cC5nbG9iYWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUHMAKG # Omh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWduaW5n # cm9vdHI0NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9iYWxz # aWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFYGA1UdIARPME0wQQYJKwYB # BAGgMgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29t # L3JlcG9zaXRvcnkvMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEACIhyJsav # +qxfBsCqjJDa0LLAopf/bhMyFlT9PvQwEZ+PmPmbUt3yohbu2XiVppp8YbgEtfjr # y/RhETP2ZSW3EUKL2Glux/+VtIFDqX6uv4LWTcwRo4NxahBeGQWn52x/VvSoXMNO # Ca1Za7j5fqUuuPzeDsKg+7AE1BMbxyepuaotMTvPRkyd60zsvC6c8YejfzhpX0FA # Z/ZTfepB7449+6nUEThG3zzr9s0ivRPN8OHm5TOgvjzkeNUbzCDyMHOwIhz2hNab # XAAC4ShSS/8SS0Dq7rAaBgaehObn8NuERvtz2StCtslXNMcWwKbrIbmqDvf+28rr # vBfLuGfr4z5P26mUhmRVyQkKwNkEcUoRS1pkw7x4eK1MRyZlB5nVzTZgoTNTs/Z7 # KtWJQDxxpav4mVn945uSS90FvQsMeAYrz1PYvRKaWyeGhT+RvuB4gHNU36cdZytq # tq5NiYAkCFJwUPMB/0SuL5rg4UkI4eFb1zjRngqKnZQnm8qjudviNmrjb7lYYuA2 # eDYB+sGniXomU6Ncu9Ky64rLYwgv/h7zViniNZvY/+mlvW1LWSyJLC9Su7UpkNpD # R7xy3bzZv4DB3LCrtEsdWDY3ZOub4YUXmimi/eYI0pL/oPh84emn0TCOXyZQK8ei # 4pd3iu/YTT4m65lAYPM8Zwy2CHIpNVOBNNwwggcEMIIE7KADAgECAgxcuW61kTkv # +4t8zgQwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEds # b2JhbFNpZ24gbnYtc2ExLzAtBgNVBAMTJkdsb2JhbFNpZ24gR0NDIFI0NSBDb2Rl # U2lnbmluZyBDQSAyMDIwMB4XDTI0MDMxMTE0MDQxMloXDTI3MDMxMjE0MDQxMlow # cjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1Nh # bnRhIENsYXJhMRswGQYDVQQKExJQdXJlIFN0b3JhZ2UsIEluYy4xGzAZBgNVBAMT # ElB1cmUgU3RvcmFnZSwgSW5jLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAMCQrioSn48IvHpTg5dofsUYj/pNTDidwjYUrcxVu78NoyhSweG8FhcxDi/S # I40+8Fccl3D5ZoqpjkFnGhzSwmpxU3J4AP7+fdTZht9eWD1I5qKY07esYwdPDV4y # g+csPfdGPqI2XjRfT5UC3YkXQeUrX8KQZldD4KqvgxzpYcuBwsgHbTb/eArpi68Y # gFR2jgZGyZigfy8RuJMrL1thcBOe/VWjUyK21wVT8cuunBYFaStLHhsRBRMDcZBD # uTSGC4evE6oaCqlQbdMl9YFJ64mDQsKlCxrr7rmLVtcVzKGwmjp4b2xRwE+RmTh6 # JtrUL9Wx/3a3UzgAnDNimfwp85zoL48kyLtHqQ3FI8tVKGm+aBOgBZfmURoy7fbp # 4zKhGgqFbpOmILO16i4f999YsEEJQgIF3CtyH1R60/ZZWlDmoeeEgjAGrnd14muU # 5Hk3Cksr43uPUAg+fV78Y0fDV85ibm42ZwwPuz6MI4HhYNUlGzRwIQ31vjaGuAMW # HNqFKkcO0JuIeHQ/gFKPnYIxnGC9H9R4Kw/uMezqtnYJwGU2epB/ABl/w7U4NgU2 # ZOxWB5BFy4frZ3f+hNgbjFUjMaXnVFotOJxXntzjdSl4znw8DaKiC5ooChteZMIT # G9p078p/TUsOJQbUtFADSY1hsfCfB7t+gJSNt5peS9GOZIMVAgMBAAGjggGxMIIB # rTAOBgNVHQ8BAf8EBAMCB4AwgZsGCCsGAQUFBwEBBIGOMIGLMEoGCCsGAQUFBzAC # hj5odHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2djY3I0NWNv # ZGVzaWduY2EyMDIwLmNydDA9BggrBgEFBQcwAYYxaHR0cDovL29jc3AuZ2xvYmFs # c2lnbi5jb20vZ3NnY2NyNDVjb2Rlc2lnbmNhMjAyMDBWBgNVHSAETzBNMEEGCSsG # AQQBoDIBMjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNv # bS9yZXBvc2l0b3J5LzAIBgZngQwBBAEwCQYDVR0TBAIwADBFBgNVHR8EPjA8MDqg # OKA2hjRodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1Y29kZXNpZ25j # YTIwMjAuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNqzjcAk # kKNrd9MMoFndIWdkdgt4MB0GA1UdDgQWBBSzJ9KiDCa3UBiAajy+Iioj5kQjzDAN # BgkqhkiG9w0BAQsFAAOCAgEAHsFQixeQEcoHurq9NWSUt4S39Q+UGP6crmVq3Wwy # 9g23YbdWg+SgMxoLUqdoDfA4k4B6Dyoo0jEQzn2kxnsnT9lNHKrcZHH88dv0hjfi # H2qAiQWazPjS3LhK2J6nhpyipJPpyRaSQG4x4aG0NB2D4WUfUz9CGAYsERJGww/w # kTaaxMipttKDTaI1C49u1igDfRzIO+Q8vuyyBFLiYTno/df97xtjNC+KxxFhDhl/ # 4tawK6kwxaVzCMAfj48I67Wbo4DMH6pM1s19as7c3qp92i3MylGKsB6+u+o7UkbS # dLNkS4ALI33CJOUc+GoK3Nt5IXXCFJTQFHBXkBdAur3gmlXEm8vlNG/1Sbxr0H7T # 1e7ABGH/48o/+PeMLuCc72EeK5dJ4cX9NEQ3QnTsZHwGnYzjEOvOvP0s1c7yNsDb # cUHoIqQvb5xS5aqMU5G+8sdPQ1nwpPf7gGaEEbAVW4w51Pam42qeN9HIPa+ZinXn # sN02Kk1Qw0QwUqzaQy9W/gIquI0KOjw0LmoW9M/8S0lrjpEq2eEeUw9WQLhhUEIi # rFxGPtjqiCLiiS9CZ+kf2vWLJKUspkYv+OHT3q805Zg1dJsBFAzEYUFLb1mhmigD # EO9bsMorjECIL2ijE5zHtbGkalrrsPWu8tiDT/B7P9GSYzKfOOy4PoOIfWSK0Ixl # S7IxghqtMIIaqQIBATBpMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxT # aWduIG52LXNhMS8wLQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25p # bmcgQ0EgMjAyMAIMXLlutZE5L/uLfM4EMA0GCWCGSAFlAwQCAQUAoIGcMBkGCSqG # SIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3 # AgEVMC8GCSqGSIb3DQEJBDEiBCDdFp5B5Ft5IO0iveLTaBPaTJ9Tt49YtVXLRBu7 # sufjXTAwBgorBgEEAYI3AgEMMSIwIKACgAChGoAYaHR0cHM6Ly9wdXJlc3RvcmFn # ZS5jb20gMA0GCSqGSIb3DQEBAQUABIICAJ9kk/CzJZ0ADhxE8CF6xWgGSiRiUBKn # pKzY2iXxFXbzLPjbVP0Eh3OG+guwwF6xl3Wys9fVemSGQZ5TDjEZG+1sEgMn9SQD # 7708cx7gqr4ua3oX1CrAyXQY5AHlvhlrNgUCOIIGO8HWdoyVdALrnlPVXjvcSUMy # 7wsMLtZoiKU6ZTuoZLgM2ZUV8WeBbBMWNWlE4I+QXShf4OqqjOLV5BHLHt8Z4d7e # UnY3bkWIvm4q0rdWEfPy9r5Auqd/ihIy6cKWYvKfAEulV7PHmKF2Lxc/fdFNDbvF # faZGtYlVn5UfsgJIY9vBR80BwVLzRkOICQn3r0CSzL3TcIckt1Buq34buEHgwztN # wg+ykGX+W+iBKDme+RfrLUpQMMbZMHefqyV5YgonVubJ2OPL0Tc8PbFGvlCpUFIe # lMHFRyQqw0TrzypqGGkRjQNuOfszlxkwZpZiPlKyMfi4HT6UJ1z2wBXKiJyzskQS # 4NUyAJxrTSaJeVAQrZEF2Miyvu0/vMBkzWJv33Yk2xr4rrgFgXwhtM5odzR8n+8I # tjy+dNFVx20XjZF2eDDs61GFTgUIxc1Os94sT8BuyxZrd6iw+59joySM1yVlEiji # Uq/gqDTq0za9+9Cb7FJlcp/Yo9o4CxUIL9VMHAN0/596PNLW+FYMWQ7c4YUSL2R4 # xVPws8knPa3soYIXdjCCF3IGCisGAQQBgjcDAwExghdiMIIXXgYJKoZIhvcNAQcC # oIIXTzCCF0sCAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0BCRABBKBoBGYw # ZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIH9/exe2t3pS3p95HaT1 # 09R5JxKy2UTvBJHjerr0jpLDAhA1uw7DC5/VccrJPTwqGXJsGA8yMDI1MDYxMjE5 # MjkyOVqgghM6MIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG # 9w0BAQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x # QTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQw # OTYgU0hBMjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1 # OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD # VQQDEzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVy # IDIwMjUgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q # 6q5gVrMrV7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPn # Z8ZN+vo8dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSss # p3qome7MrxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09 # ldQ//nBZZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98ok # souTMYFOnHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+ # 3qU8DD+nigNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsn # qcnpJeItK/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQ # PdP51ho1zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbS # LZ49kPmk8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojT # dS5PWPsWeupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoK # RR7YufAkprxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8E # AjAAMB0GA1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTv # b1NK6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAww # CgYIKwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQw # OTZTSEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0 # MDk2U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI # AYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk # 9iJP2zWLpQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tsh # gb4O6Lgjg8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9m # zskgiC3QYIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQ # BHMUBaB5bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+ # YkDFkxUGtMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0c # Ksb+zJNEsuEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY # 7T/lwd6UArb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcboj # BcxlRcGG0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05o # xYy2zRWVFjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskK # PIJgbaP5t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjd # NXOCIUjsarfNZzCCBrQwggScoAMCAQICEA3HrFcF/yGZLkBDIgw6SYYwDQYJKoZI # hvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1 # c3RlZCBSb290IEc0MB4XDTI1MDUwNzAwMDAwMFoXDTM4MDExNDIzNTk1OVowaTEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhE # aWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAy # MDI1IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALR4MdMKmEFy # vjxGwBysddujRmh0tFEXnU2tjQ2UtZmWgyxU7UNqEY81FzJsQqr5G7A6c+Gh/qm8 # Xi4aPCOo2N8S9SLrC6Kbltqn7SWCWgzbNfiR+2fkHUiljNOqnIVD/gG3SYDEAd4d # g2dDGpeZGKe+42DFUF0mR/vtLa4+gKPsYfwEu7EEbkC9+0F2w4QJLVSTEG8yAR2C # QWIM1iI5PHg62IVwxKSpO0XaF9DPfNBKS7Zazch8NF5vp7eaZ2CVNxpqumzTCNSO # xm+SAWSuIr21Qomb+zzQWKhxKTVVgtmUPAW35xUUFREmDrMxSNlr/NsJyUXzdtFU # Ut4aS4CEeIY8y9IaaGBpPNXKFifinT7zL2gdFpBP9qh8SdLnEut/GcalNeJQ55Iu # wnKCgs+nrpuQNfVmUB5KlCX3ZA4x5HHKS+rqBvKWxdCyQEEGcbLe1b8Aw4wJkhU1 # JrPsFfxW1gaou30yZ46t4Y9F20HHfIY4/6vHespYMQmUiote8ladjS/nJ0+k6Mvq # zfpzPDOy5y6gqztiT96Fv/9bH7mQyogxG9QEPHrPV6/7umw052AkyiLA6tQbZl1K # hBtTasySkuJDpsZGKdlsjg4u70EwgWbVRSX1Wd4+zoFpp4Ra+MlKM2baoD6x0VR4 # RjSpWM8o5a6D8bpfm4CLKczsG7ZrIGNTAgMBAAGjggFdMIIBWTASBgNVHRMBAf8E # CDAGAQH/AgEAMB0GA1UdDgQWBBTvb1NK6eQGfHrK4pBW9i/USezLTjAfBgNVHSME # GDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0l # BAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8 # MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0 # ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAN # BgkqhkiG9w0BAQsFAAOCAgEAF877FoAc/gc9EXZxML2+C8i1NKZ/zdCHxYgaMH9P # w5tcBnPw6O6FTGNpoV2V4wzSUGvI9NAzaoQk97frPBtIj+ZLzdp+yXdhOP4hCFAT # uNT+ReOPK0mCefSG+tXqGpYZ3essBS3q8nL2UwM+NMvEuBd/2vmdYxDCvwzJv2sR # UoKEfJ+nN57mQfQXwcAEGCvRR2qKtntujB71WPYAgwPyWLKu6RnaID/B0ba2H3LU # iwDRAXx1Neq9ydOal95CHfmTnM4I+ZI2rVQfjXQA1WSjjf4J2a7jLzWGNqNX+DF0 # SQzHU0pTi4dBwp9nEC8EAqoxW6q17r0z0noDjs6+BFo+z7bKSBwZXTRNivYuve3L # 2oiKNqetRHdqfMTCW/NmKLJ9M+MtucVGyOxiDf06VXxyKkOirv6o02OoXN4bFzK0 # vlNMsvhlqgF2puE6FndlENSmE+9JGYxOGLS/D284NHNboDGcmWXfwXRy4kbu4QFh # Om0xJuF2EZAOk5eCkhSxZON3rGlHqhpB/8MluDezooIs8CVnrpHMiD2wL40mm53+ # /j7tFaxYKIqL0Q4ssd8xHZnIn/7GELH3IdvG2XlM9q7WP/UwgOkw/HQtyRN62JK4 # S1C8uw3PdBunvAZapsiI5YKdvlarEvf8EA+8hcpSM9LHJmyrxaFtoza2zNaQ9k+5 # t1wwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUA # MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT # EHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQg # Um9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIw # DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqcl # LskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YF # PFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceIt # DBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZX # V59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1 # ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2Tox # RJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdp # ekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF # 30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9 # t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQ # UOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXk # aS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud # DgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEt # UYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsG # AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0 # dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAw # DQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyF # XqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76 # LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8L # punyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2 # CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si # /xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN8MIIDeAIBATB9 # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEy # NTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCggdEw # GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTA2 # MTIxOTI5MjlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFN1iMKyGCi0wa9o4sWh5 # UjAH+0F+MC8GCSqGSIb3DQEJBDEiBCAUgMt2DPCXd7Mt5fVvbp4Y8OA1dS4ZY7NL # ahM8RCGkWjA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCBKoD+iLNdchMVck4+Cjmdr # nK7Ksz/jbSaaozTxRhEKMzANBgkqhkiG9w0BAQEFAASCAgCNCHI/Q9HC2Q9ALw0d # POBzjNqr+xr/yUQIYlnATUfk7biWWK0oOzBYoRbKp6U+TzmIHpcXEsRNUtU0/XAT # w/WaM2s1e6Eo5b2L9PDm2tcxsp1BsXARQd58C+HGVAvHVRaJ7kVQZVyHMNfAeIYe # NGTl66jzwycvZJpQ3BAFFfh7dXuxd+7HETn+bNgOexMZJz/b2D2gwfJrrBdYNOqE # 6GlgIv8GA+apcg4ze9pj4AIuuUxaO64061nxoAFOOjVEST74ufd15hj+88N5zubt # upFBlXKC4mzsjdqyMoJ9vEntz3qtnOG0xcXDlSVzwYytdnARLIT221Y3kAj6kmuR # wUSZHOkv/QVW0I3LOLLe83RLfGKVlzASwjud5k6ILCCW3Q/eqZRrXDIAQj9yv/MS # 2wEsKFhqTA+O6tch3Z9cM8fu+We8MmPqx4dNYpHCuCqphaSnkDfI+nggte5b87Hp # B2Y+/lXI8t0CCZE7J4h0qhP/WYhbLiI+rKeD+hV+HxC/JQHqL3qSHZg+zxrBFVzX # EoteCOCJ8H/+5FZCFBcKPThZRoIAyluuUgiGHfwAQaMl4lWhXx/hat5hkKpVwWxb # Q3lo3qUDsJ8zIQWk9MX8kbcXioWq3wWURodKfz94TRyxSmrxGnInmfIyLMh95LXM # xFfjspumAe3/sB8zN1JXPJ+rog== # SIG # End signature block |