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 = $true)] [string]$KryptonCorrelationId, [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 -KryptonCorrelationId $KryptonCorrelationId 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 = $true)] [string]$KryptonCorrelationId ) $requestHeaders = @{ 'x-krypton-correlation-id' = $KryptonCorrelationId 'Content-Type' = 'application/json; charset=utf-8' } return $requestHeaders } function Invoke-EnableAvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $true)] [string]$KryptonCorrelationId, [Parameter(Mandatory = $true)] [string]$LiftrApiVersion ) if ($null -ne $env:SddcResourceId) { $payload = @{ 'sddcResourceId' = $env:SddcResourceId } } else { throw "No SDDC resource ID found in the environment" } $requestHeaders = Get-RequestHeader -KryptonCorrelationId $KryptonCorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/enableAvsConnection?api-version=$LiftrApiVersion" $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 -KryptonCorrelationId $KryptonCorrelationId } function Get-AvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $true)] [string]$KryptonCorrelationId, [Parameter(Mandatory = $true)] [string]$LiftrApiVersion ) $requestHeaders = Get-RequestHeader -KryptonCorrelationId $KryptonCorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/getAvsConnection?api-version=$LiftrApiVersion" 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 = $true)] [string]$KryptonCorrelationId, [Parameter(Mandatory = $true)] [string]$LiftrApiVersion ) $payload = @{ 'serviceInitializationDataEnc' = "" 'serviceInitializationData' = $ServiceInitializationData } $requestHeaders = Get-RequestHeader -KryptonCorrelationId $KryptonCorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/finalizeAvsConnection?api-version=$LiftrApiVersion" $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 -KryptonCorrelationId $KryptonCorrelationId } function Invoke-DisableAvsConnection { param ( [Parameter(Mandatory = $true)] [string]$StoragePoolResourceId, [Parameter(Mandatory = $true)] [string]$KryptonCorrelationId, [Parameter(Mandatory = $true)] [string]$LiftrApiVersion ) $requestHeaders = Get-RequestHeader -KryptonCorrelationId $KryptonCorrelationId $uri = $DefaultManagementHostUri + $StoragePoolResourceId + "/disableAvsConnection?api-version=$LiftrApiVersion" $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 -KryptonCorrelationId $KryptonCorrelationId } # SIG # Begin signature block # MIIugwYJKoZIhvcNAQcCoIIudDCCLnACAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAH/xdsY7F/GQCS # El83Tm9AHysGlndbyZZfFZ45CVVwAKCCE2gwggVyMIIDWqADAgECAhB2U/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 # S7IxghpxMIIabQIBATBpMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxT # aWduIG52LXNhMS8wLQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25p # bmcgQ0EgMjAyMAIMXLlutZE5L/uLfM4EMA0GCWCGSAFlAwQCAQUAoIGcMBkGCSqG # SIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3 # AgEVMC8GCSqGSIb3DQEJBDEiBCDxnO01DSj5DdtKLCuC5eslNzivSpKTCjLakP0l # jCydfzAwBgorBgEEAYI3AgEMMSIwIKACgAChGoAYaHR0cHM6Ly9wdXJlc3RvcmFn # ZS5jb20gMA0GCSqGSIb3DQEBAQUABIICALzipw2/e2RYl1nBmSMU2ZryeAtdnBSE # gUGlxeL8+z9NHG0zAs/RLQtVmEvMVSRFjOP1Twii1HNRod6UiGn+S4wBnJ6+uKFX # ZQdGj3F03dIBvUUOD5yRlDavnLKPXC9dSUWA/k3ITqbvm2liyofn+2Kp0rBKrJ25 # Zpml/wi4oVvaGNLz54bqjjLYQLKlOrEewaaK9i59J3+kwBInt4Wjiotj1hJikbEf # TlsaNNQxhxA8d1CF0OI/p1f+aGnihG1ujLGmVI9vwBkouiEs8ywvRd5QygKAO9HM # 7e7woGDD7XczMVd0GdDbRnvOFE6+fKYD1BF4mUck09nXKaiBJTBsr/Zgvy8g11sN # nhZA7X9hO4KzfNRavKRamKoVLBgfwCiMnTcZmvydOWxRJOgQyl8ozZs6sZMgeiec # 6AkxkwQFMRH3oJnsapBTxuyOpaNXt1fd2iDZB5b8VVFVrVr1JYeFsgLumZYa2G8m # BJ/YH3nwIve9x9BQz4wVS2K6Bvgg4nflfcMhwIc1A7QuUjhJu0KdBQ9xhEP5SG4p # DkweoWgA5g/i7m2bwE0Tt8N/uALRA+2e4PR5pnh73Oua+P0ZPcZlUZ6IvX5Z6gmS # gWLcDNa59IWtxRsRtJT6VJrG7WjKFnuzyp2Hztx8oZ7HrFYJmUZy5JRWS/qAp7Ng # N8vjDu3B+MqfoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJKoZIhvcNAQcC # oIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0BCRABBKBpBGcw # ZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIPz0C7oaPIn+0b5d7ibh # UiUzKx9MWoRotSPtsJgRgGD1AhEA5ZUuWQeHMxUk5PC3wButOhgPMjAyNTA1MzAy # MTU4MzBaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTjMwQwDQYJKoZI # hvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRp # bWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUyMzU5NTlaMEIx # CzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UEAxMXRGlnaUNl # cnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+CMR0Rne/i+ut # MeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+hDu6ObS7rJcXa # /UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh926SxMe6We2r1Z # 6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjWfISDmHuI5e/6 # +NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496OVh4R1TvjQYp # AztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0rqViTbLVZIqi6 # viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY+V4j1XbJ+Z9d # I8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnAalNEeJPvIeoG # JXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb3dWnqUnjXkRF # wLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgfta8b2ypi6n2P # zP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQABo4IBizCCAYcw # DgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYB # BQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQY # MBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywDdw4oFZBmpWNe # 7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0Eu # Y3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au # ZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy # dC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5n # Q0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZQ5H2EdubTggd # 0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B76k9NJxUl4Jl # KwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc1Mt+FWqz57yF # q6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7iSpU5wlWjNlH # lFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl7YFUMYgZU1WM # 6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLISZi2yemW0P8Z # Zfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtbu3CKldMnn+Lm # mRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGoCwwc6ZpPddOF # kM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGOzISkcqwXu7nM # pFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXqBzR0/Zd2QwQ/ # l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHhDXNkoPIdynhV # Aku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZI # hvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1 # c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE # aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD # QTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVccl # A8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9Q # Ewsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDW # VtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0 # UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huo # wWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZw # mCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rn # H1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC # 3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jz # RWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEm # CPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4w # Er1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/ # AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs # 1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYI # KwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz # cC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2 # oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290 # RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG # 9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3i # Syn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKo # Fr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9 # jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JE # rpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOA # CcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9r # p/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvE # lXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2 # uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRi # CQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlH # K+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwwggWN # MIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJ # BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k # aWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBD # QTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK # 2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/G # nhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJ # IB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4M # K7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN # 2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I # 11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KIS # G2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9 # HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4 # pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpy # FiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS31 # 2amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs # 1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd # 823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzAB # hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9j # YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQw # RQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZI # hvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4 # hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3 # rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs # 9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K # 2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0n # ftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIBATB3MGMxCzAJ # BgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGln # aUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EC # EAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTA1MzAyMTU4MzBaMCsG # CyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTVhltFMC8GCSqG # SIb3DQEJBDEiBCAQ5akpuKtwwMww4OCvwPQLq89AR6ME1Wrc0TZCWDJkZjA3Bgsq # hkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7Xc9sF1mW5BZOY # Q4+a2zANBgkqhkiG9w0BAQEFAASCAgCJjSoQRpL7c6zTu9N1lmNB4YwIdd76locU # 2CkhEd8RGZ+qNkVrGN/nzAs0nk4iTXr1PkuUT5YO2LgfExr8NvTMKTCycFEhnQhH # JV0nQAXVet6QAGvsbdvYqIsyhQp0l2KeheiXMdU21xFiDwU5CuK4+zFcAmMKEqIz # QXjR0IjaAj5+v8d/31724htnlRk2mesN//PgZb5XLneu2ifizX7EEdYNmvBZE3FY # aHvDhIenLbA++TXQgDGrVid7BTab4jB02un+gL+uulfvcGiVE4YhbzVG95mKzLcx # JvKygHDoXqYeoc6JY5ftbzkIhGKgdW/zU89WkIJJqylD73iFoggwuoUZp5jAFaDK # /MGnze++rWnLRx3Z6bMn6FwvWZpP+H4HC0+t6XP7z4Sy7OaIPh5kpdAu6M4o6QnE # g71ut8XcPI0byKoDUahKjL+YuT2kaMF1Eoa62uW/CZKmE2zLTFxketsPTGlk4hrp # 7hGG5Pzv49Ltd+a9YFwjyHAYSm7kCcwwNHQLYuARC6AS/Moy0Rq2SUOJdI2lJG1a # FRiQm/0/F1N3wRRxMNYbWmOcCDtelMVQX9Q/JDzSWv84mVE8AWl9xBkW8ouC5SKg # 9NJ3EVuo1/3DxSfkTHW6ZuvJFTrv1WAhgcZa1jT3VcHXkuUz3I1pLlBEBmU/8osr # 2ovLHC6pVQ== # SIG # End signature block |