AzureValidation/AzureADConfiguration.psm1
<#############################################################
# # # Copyright (C) Microsoft Corporation. All rights reserved. # # # #############################################################> $ErrorActionPreference = 'Stop' Import-LocalizedData LocalizedData -BaseDirectory $PSScriptRoot -Filename AzureADConfiguration.Strings.psd1 $AzurePowerShellClientID = "1950a258-227b-4e31-a9cf-717495945fc2" $AzurePowerShellReturnUri = "urn:ietf:wg:oauth:2.0:oob" function Get-Endpoints([string] $CloudARMEndpoint) { <# .Synopsis Builds graph and login endpoints for a given CloudARMEndpoint #> $fullUri = $CloudARMEndpoint.TrimEnd('/')+"/metadata/endpoints?api-version=2015-01-01" Write-AzsReadinessLog -message "Getting Azure Endpoints from $fullUri" -type Info $response = Invoke-RestMethod -Uri $fullUri -ErrorAction Stop -UseBasicParsing -TimeoutSec 30 $EndpointProperties = @{ GraphUri = $response.graphEndpoint LoginUri = $response.authentication.loginEndpoint ManagementServiceUri = $response.authentication.audiences[0] ARMUri = $CloudARMEndpoint } return $EndpointProperties } function Get-AzToken { [CmdletBinding(DefaultParameterSetName = 'default')] param ( # The Azure PowerShell context representing the context of a token to be resolved. [Parameter()] [ValidateNotNull()] [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context = (Get-AzContext -ErrorAction Stop), # The target resource for which a token should be resolved. [Parameter()] [ValidateNotNullOrEmpty()] [string] $Resource = ($Context.Environment.ActiveDirectoryServiceEndpointResourceId), # The target tenantId in which a token should be resolved. [Parameter()] [ValidateNotNullOrEmpty()] [string] $TenantId = ($t = if ($Context.Tenant) { $Context.Tenant } else { $Context.Subscription.TenantId }), # The account for which a token should be resolved. [Parameter()] [ValidateNotNullOrEmpty()] [string] $AccountId = ($Context.Account.Id), # Indicates that target token should be resolved from existing cache data (including a refresh token, if one is available). [Parameter(Mandatory = $true, ParameterSetName = 'FromCache')] [switch] $FromCache, # Indicates that all token cache data should be returned. [Parameter(ParameterSetName = 'FromCache')] [switch] $Raw ) $originalErrorActionPreference = $ErrorActionPreference try { $ErrorActionPreference = 'Stop' Write-AzsReadinessLog -message "Attempting to retrieve a token for account '$AccountId' in tenant '$TenantId' for resource '$Resource'..." -type Info if (-not $FromCache) { $token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate( ($account = $Context.Account), ($environment = $Context.Environment), ($tenant = $TenantId), ($password = $null), ($promptBehavior = 'Never'), ($promptAction = $null), ($tokenCache = $null), ($resourceIdEndpoint = $Resource)) return [pscustomobject]@{ AccessToken = ConvertTo-SecureString $token.AccessToken -AsPlainText -Force } | Add-Member -MemberType ScriptMethod -Name 'GetAccessToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.AccessToken).Password } -PassThru } else { Write-AzsReadinessLog -message "Attempting to find a refresh token and an access token from the existing token cache data..." -type Info } # # Resolve token cache data # [Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory]$authenticationClientFactory = $null if (-not ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.TryGetComponent( [Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory]::AuthenticationClientFactoryKey, [ref]$authenticationClientFactory))) { $m = 'Please ensure you have authenticated with Az Accounts module!' $m += ' Unable to resolve authentication client factory from Az Accounts module runtime' $m += ' ([Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory])' Write-AzsReadinessLog -message $m -type Error -toscreen return } $client = $authenticationClientFactory.CreatePublicClient( ($clientId = '1950a258-227b-4e31-a9cf-717495945fc2'), ($TenantId), ($authority = if ($Context.Environment.EnableAdfsAuthentication) { $Context.Environment.ActiveDirectoryAuthority } else { '{0}/{1}' -f $Context.Environment.ActiveDirectoryAuthority.TrimEnd('/'), $TenantId }), ($redirectUri = $null), ($useAdfs = $Context.Environment.EnableAdfsAuthentication)) $authenticationClientFactory.RegisterCache($client) $accounts = $client.GetAccountsAsync().ConfigureAwait($true).GetAwaiter().GetResult() $bytes = ([Microsoft.Identity.Client.ITokenCacheSerializer]$client.UserTokenCache).SerializeMsalV3() $json = [System.Text.Encoding]::UTF8.GetString($bytes) $data = ConvertFrom-Json $json Write-AzsReadinessLog -message "MSAL token cache deserialized ($($bytes.Length) bytes); Looking for target tokens..." -type Info foreach ($name in 'AccessToken', 'Account', 'AppMetadata', 'IdToken', 'RefreshToken') { $data | Add-Member -NotePropertyName "${name}s" -NotePropertyValue ((Get-Member -MemberType NoteProperty -InputObject $data."$name").Name | ForEach-Object { $data."$name"."$_" }) } if ($Raw) { Write-AzsReadinessLog -message "Returning raw token cache data!" -type Warning Write-Output $data return } # # Resolve target account # $targetAccount = $accounts | Where-Object Username -EQ $AccountId if (-not $targetAccount -or $targetAccount.Count -gt 1) { Write-AzsReadinessLog -message "Unable to resolve acccount for identity '$identityId'; available accounts: $(ConvertTo-Json $accounts.Username -Compress)" -type Error -ToScreen return } Write-AzsReadinessLog -message "Target account resolved to: $(ConvertTo-Json $targetAccount -Compress)" -type Info # # Resolve target token(s) # $resolvedRefreshToken = $data.RefreshToken."$(Get-Member -InputObject $data.RefreshToken -MemberType NoteProperty | Where-Object { "$($_.Name)".StartsWith($targetAccount.HomeAccountId.Identifier, [System.StringComparison]::OrdinalIgnoreCase) } | Select-Object -ExpandProperty Name)".secret $resolvedAccessToken = Get-Member -InputObject $data.AccessToken -MemberType NoteProperty | ForEach-Object { $data.AccessToken."$($_.Name)" } | Where-Object home_account_id -EQ $targetAccount.HomeAccountId.Identifier | Where-Object { (-not $_.realm) -or ($_.realm -eq $TenantId) } | Where-Object target -Like "*$Resource*" | Sort-Object expires_on -Descending | Select-Object -First 1 -ExpandProperty secret if (-not $resolvedAccessToken -and -not $resolvedRefreshToken) { Write-AzsReadinessLog -message "Unable to resolve an access token or refresh token for identity '$identityId' with the specified properties..." -type Error -ToScreen return } elseif (-not $resolvedAccessToken) { Write-AzsReadinessLog -message "Unable to resolve an access token for identity '$identityId' with the specified properties..." -type Warning } elseif (-not $resolvedRefreshToken) { Write-AzsReadinessLog -message "Unable to resolve a refresh token for identity '$identityId' with the specified properties..." -type Warning } $result = [pscustomobject]@{ AccessToken = if ($resolvedAccessToken) { ConvertTo-SecureString $resolvedAccessToken -AsPlainText -Force } else { $null } RefreshToken = if ($resolvedRefreshToken) { ConvertTo-SecureString $resolvedRefreshToken -AsPlainText -Force } else { $null } } return $result | Add-Member -MemberType ScriptMethod -Name 'GetAccessToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.AccessToken).Password } -PassThru | Add-Member -MemberType ScriptMethod -Name 'GetRefreshToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.RefreshToken).Password } -PassThru } catch [Microsoft.Identity.Client.MsalUiRequiredException] { $msg = "Failed to retrieve a token. Login using Connect-AzAccount and specify the target tenant. Exception: $($_.exception.message)" Write-AzsReadinessLog -message $msg -type Error throw $msg } catch { $msg = "Failed to retrieve a token. Exception: $($_.exception.message)" Write-AzsReadinessLog -message $msg -type Error throw $msg } finally { $ErrorActionPreference = $originalErrorActionPreference } } function Get-AADTenantIds { [OutputType([Hashtable])] [CmdletBinding()] param() $token = Get-AzToken $cToken = ConvertFrom-JwtToken $token.GetAccessToken() $UserInfo = @{ FirstName = $cToken.claims.given_name LastName = $cToken.claims.family_name Account = $cToken.claims.unique_name } Write-AzsReadinessLog -message "Retrieving tenantIds..." -type Info $tenantsResponse = Invoke-RestMethod -Method Get -Uri "$($AzureURIs.ARMUri.TrimEnd('/'))/tenants?api-version=2016-02-01" -Headers @{Authorization = "Bearer $($token.GetAccessToken())"} -UseBasicParsing -TimeoutSec 30 $tenantIds = [Array]$tenantsResponse.Value.tenantId return @{ UserInfo = $UserInfo TenantIds = $tenantIds } } function Invoke-Graph($method, $uri, $authorization, $body) { $params = @{ Method = $method Uri = "$($uri)?api-version=1.6" Headers = @{ Authorization = $authorization } UseBasicParsing = $true TimeoutSec = 30 ContentType = 'application/json' } if ($body) { $params += @{ Body = $body } } try { return (Invoke-WebRequest @params -ErrorAction Stop).Content | ConvertFrom-Json } catch { if ($_.Exception.Response.StatusCode -eq 'Internal Server Error') { Write-AzsReadinessLog -message ("$LocalizedData.GraphEndpointError" -f $graphUri) -type Error throw ($LocalizedData.GraphEndpointError -f $graphUri) } if ($_.Exception.Response.StatusCode -eq 'Forbidden') { Write-AzsReadinessLog -message ("{0} StatusCode: Forbidden" -f $graphUri) -type Error throw "Forbidden: Login using Connect-AzAccount and specify the target tenant." } else { Write-AzsReadinessLog -message ("{0} throw an exception: {1}" -f $graphUri, $_) -type Error throw $_ } } } function Get-AADTenantDetail { [OutputType([Hashtable])] [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string] $TenantId, [Parameter(Mandatory=$true)] [string] $UserId, [Parameter(Mandatory=$true)] [Hashtable] $AzureURIs ) try { $token = Get-AzToken -resource (Get-AzContext).Environment.GraphEndpointResourceId -TenantId $TenantId } catch { throw $_ } # Validating if the user has required permission $authorization = "Bearer $($token.GetAccessToken())" $accessTokenObj = ConvertFrom-JwtToken -JwtToken $token.GetAccessToken() $postUri = $azureURIs.GraphUri.TrimEnd('/')+"/$($TenantId)/directoryObjects/$($accessTokenObj.Claims.oid)/getMemberObjects" Write-AzsReadinessLog -message "Retrieving member objects for user." -type Info $roles = Invoke-Graph -method Post -uri $postUri -authorization $authorization -body ( ConvertTo-Json -Depth 1 @{ securityEnabledOnly = $false } ) | Select-Object -ExpandProperty value if( $roles -eq $null ){ return $null } $getUri = $azureURIs.GraphUri.TrimEnd('/')+"/$($TenantId)/directoryRoles" Write-AzsReadinessLog -message "Retrieving administrator role in directory." -type Info $roleOid = Invoke-Graph -method Get -uri $getUri -authorization $authorization | Select-Object -ExpandProperty Value | Where displayName -EQ 'Company Administrator' | Select-Object -ExpandProperty objectId if ($roleOid -notin $roles) { Write-AzsReadinessLog -message "Administrator role not present in user roles." -type Error return $null } Write-AzsReadinessLog -message "Success. Administrator role present in user roles." -type Info $tenantResponse = Invoke-RestMethod -Method Get -Uri "$($AzureURIs.GraphUri.TrimEnd('/'))/myOrganization/tenantDetails?api-version=1.5" -Headers @{Authorization = "Bearer $($token.GetAccessToken())"} -UseBasicParsing -TimeoutSec 30 return @{ Id = $tenantResponse.Value.objectId DisplayName = $tenantResponse.Value.DisplayName DomainName = ($tenantResponse.Value.VerifiedDomains | Where {$_.default}).name Token = $token.GetAccessToken() } } function Get-AADTenantDetails { <# .SYNOPSIS Resolve Azure Tenant Details .DESCRIPTION Resolve Azure Tenant Details .EXAMPLE Get-AADTenantDetails -AADDirectoryTenantName azurestack.contoso.com .OUTPUTS Hashtable of tenant details .NOTES General notes #> [OutputType([Hashtable])] [CmdletBinding()] param( [Parameter(Mandatory=$false)] [string] $AADDirectoryTenantName ) Write-Progress -Activity $LocalizedData.InfoSigningIn $azureURIs = Get-Endpoints (Get-AzContext).Environment.ResourceManagerUrl $TenantsInfo = Get-AADTenantIds $TenantIds = [Array] $TenantsInfo.TenantIds $tenantDetails = @() # Workaround to add the target tenant Id if specified to the list of "resolved" tenant memberships if ($AADDirectoryTenantName) { Write-AzsReadinessLog -message "Retrieving tenant detail from target tenant $AADDirectoryTenantName" -type Info $targetTenantId = Get-TenantIdFromName -tenantName $AADDirectoryTenantName $tenantDetail = Get-AADTenantDetail -TenantId $targetTenantId -UserId $TenantsInfo.UserInfo.Account -AzureURIs $azureURIs if ($tenantDetail -ne $null) { Write-AzsReadinessLog -message ("Success. Target directory ({0}...{1}) returned result." -f $targetTenantId.split('-')[0],$targetTenantId.split('-')[-1]) -type Info $tenantDetails = $tenantDetail } else { for ($i = 1; $i -le $TenantIds.Count; $i++) { Write-Progress -Activity $LocalizedData.InfoSigningIn -PercentComplete ($i * 100 / ($TenantIds.Count + 1)) $tid = $TenantIds[$i-1] Write-AzsReadinessLog -message ("Retrieving tenant detail from tenant {0}...{1}" -f $tid.split('-')[0],$tid.split('-')[-1]) -type Info $tenantDetail = Get-AADTenantDetail -TenantId $tid -UserId $TenantsInfo.UserInfo.Account -AzureURIs $azureURIs if ($tenantDetail -ne $null) { Write-AzsReadinessLog -message ("Success. Directory ({0}...{1}) returned result." -f $tid.split('-')[0],$tid.split('-')[-1]) -type Info $tenantDetails += $tenantDetail } } } } Write-Progress -Activity $LocalizedData.InfoSigningIn -Completed return @{ UserInfo = $TenantsInfo.UserInfo TenantDetails = $tenantDetails } } <# .Synopsis Decodes a base64-encoded string. #> function ConvertFrom-Base64String { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNull()] [string] $Base64String ) process { $data = $Base64String.Replace('-','+').Replace('_','/') switch ($data.Length % 4) { 0 { break } 2 { $data += '==' } 3 { $data += '=' } default { Write-Error "Invalid data: '$data'" } } $bytes = [System.Convert]::FromBase64String($data) Write-Output $bytes } } <# .Synopsis Converts a Jwt Token into an object. #> function ConvertFrom-JwtToken { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $JwtToken ) function ConvertFrom-RawData([String]$Data) { [System.Text.Encoding]::UTF8.GetString((ConvertFrom-Base64String $Data)) | ConvertFrom-Json | Write-Output } $parts = $JwtToken.Split('.') [pscustomobject]@{ Headers = ConvertFrom-RawData $parts[0] Claims = ConvertFrom-RawData $parts[1] Signature = $parts[2] } | Write-Output } function Get-AzureADTenantDetails { <# .SYNOPSIS Resolve Azure AD Tenant Details .DESCRIPTION Resolve Azure AD Tenant Details .EXAMPLE Get-AzureADTenantDetails -AADDirectoryTenantName $AADDirectoryTenantName .INPUTS AzureEnvironment - string - should be AzureCloud, AzureChinaCloud, AzureGermanCloud or CustomCloud AADAdminCredential - PSCredential - AAD Admin Credential AADDirectoryTenantName - string - AAD tenant Name .OUTPUTS Hashtable of Azure AD tenant details .NOTES General notes #> [OutputType([Hashtable])] [CmdletBinding()] param( [Parameter(Mandatory=$false)] [string] $AADDirectoryTenantName ) $claimsProvider = (Get-AzContext).Environment.Name $azureResult = Get-AADTenantDetails -AADDirectoryTenantName $AADDirectoryTenantName $azureToken = $null #$azureRefreshToken = $null $tenantId = $null $tenantName = $null if ($azureResult.TenantDetails -eq $null -or @($azureResult.TenantDetails).Count -eq 0) { Write-AzsReadinessLog -message ($LocalizedData.AADAccountNotAdmin -f $($azureResult.UserInfo.Account)) -type Error -toScreen } elseif (@($azureResult.TenantDetails).Count -gt 1 -and -not $AADDirectoryTenantName) { Write-AzsReadinessLog -message ($LocalizedData.MoreThanOneTenant -f @($($azureResult.UserInfo.Account), $($azureResult.TenantDetails.DomainName -join ', '))) -type Error -toScreen } elseif (@($azureResult.TenantDetails).Count -ige 1 -and $AADDirectoryTenantName) { $tenantDetail = $azureResult.TenantDetails | Where-Object DomainName -eq $AADDirectoryTenantName if ($tenantDetail) { $azureToken = $tenantDetail.Token #$azureRefreshToken = $tenantDetail.RefreshToken $tenantName = $tenantDetail.DomainName $tenantID = $tenantDetail.ID } else { Write-AzsReadinessLog -message ($LocalizedData.NotAdminOfTenant -f @($($azureResult.UserInfo.Account), $AADDirectoryTenantName, $($azureResult.TenantDetails.DomainName -join ', '))) -type Error -toScreen } } else { $azureToken = $azureResult.TenantDetails.Token #$azureRefreshToken = $azureResult.TenantDetails.RefreshToken $tenantName = $azureResult.TenantDetails.DomainName $tenantID = $azureResult.TenantDetails.ID } $adminSubscriptionOwner = (ConvertFrom-JwtToken $azureToken).Claims.unique_name return @{ UserName = $azureResult.UserInfo.Account Token = $azureToken #RefreshToken = $azureRefreshToken AdminSubscriptionOwner = $adminSubscriptionOwner TenantDirectoryName = $tenantName TenantDirectoryID = $tenantID ClaimsProvider = $claimsProvider } } <# .SYNOPSIS Returns Azure AD directory tenant ID given the login endpoint and the directory tenant name .DESCRIPTION Makes an unauthenticated REST call to the given Azure environment's login endpoint to retrieve directory tenant id .EXAMPLE $tenantId = Get-TenantIdFromName -azureEnvironment "Public Azure" -tenantName "msazurestack.onmicrosoft.com" #> function Get-TenantIdFromName { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNull()] [string] $tenantName ) $azureURIs = Get-Endpoints (Get-AzContext).Environment.ResourceManagerUrl $uri = "{0}/{1}/.well-known/openid-configuration" -f ($azureURIs.LoginUri).TrimEnd('/'), $tenantName $response = Invoke-RestMethod -Uri $uri -Method Get -UseBasicParsing -TimeoutSec 30 Write-AzsReadinessLog -message "using token_endpoint $($response.token_endpoint) to parse tenant id" -type Info $tenantId = $response.token_endpoint.Split('/')[3] $tenantIdGuid = [guid]::NewGuid() $result = [guid]::TryParse($tenantId, [ref] $tenantIdGuid) if(-not $result) { Write-AzsReadinessLog -message "Error obtaining tenant id from tenant name" -type Error } else { Write-AzsReadinessLog -message "Success. Tenant Name: $tenantName Tenant id: $tenantId" -type Info return $tenantId } } Export-ModuleMember -Function Get-AzureADTenantDetails Export-ModuleMember -Function Get-Endpoints Export-ModuleMember -Function Get-TenantIdFromName Export-ModuleMember -Function Get-AzToken Export-ModuleMember -Function ConvertFrom-JwtToken # SIG # Begin signature block # MIIjlgYJKoZIhvcNAQcCoIIjhzCCI4MCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDT9ThdDokaV3lX # v/SFPs8ySNwd6jEX2fDpJNCmL3qqDaCCDYUwggYDMIID66ADAgECAhMzAAABiK9S # 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0 # t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs # 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd # vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv # V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W # MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q # qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X # 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P # mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM # i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT # GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz # LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM # SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa # 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV # Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+ # r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFWcwghVjAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA # AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOcB # tgqWh/bXhH6OaTsxL/LktFfSlv9Bo801hvH4isdfMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAKrGayCWFWDpQ8tCFHMphosbXPJliMV15S0d4 # 6Xi5ZszHlboBNkozk+u4eQ1RqETY016o6KZ568IthF827edpUQdnD3joovzFc9Ho # zlMXjpu1mmbWPaVDMcC1xpT5tyeCm54FgxJmVvcSzh3Ax142EnsexODzjs4EEIj6 # X8CjTIRI36/0D/ZzvGeVXRLvtgupG2gTmzS7j3UDjBujBd9KTVR0mVtX6DUEce6r # woeWZnkOc8x3jU+Yz6Oumdx5Yqls/9EJpy2s4ertfXjX1FVIJvtTbFFyyVcfhDKK # 33H8Nr3JaXnodn7TGczMUn0Grosk9nGvNsihLHEaTWII+NzqSKGCEvEwghLtBgor # BgEEAYI3AwMBMYIS3TCCEtkGCSqGSIb3DQEHAqCCEsowghLGAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCDF+71QWj6AlruTDqfNm1xtW0Q7Y5OXU/ru # vgW6c1opLgIGXtVJxX8+GBMyMDIwMDYyMjIxNDMzOC41OTVaMASAAgH0oIHUpIHR # MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL # EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh # bGVzIFRTUyBFU046MzJCRC1FM0Q1LTNCMUQxJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2Wggg5EMIIE9TCCA92gAwIBAgITMwAAAS6o0hkHk/Rr # 6AAAAAABLjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMDAeFw0xOTEyMTkwMTE1MDVaFw0yMTAzMTcwMTE1MDVaMIHOMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg # T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046 # MzJCRC1FM0Q1LTNCMUQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl # cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCu00yiUVNfeixS # IzEPav1I6T4c+N5uUZx4BwFadmBp4u+PpbbGKDtZ2efK02yYQkPBmB8vAeHXr4G6 # qmD0DVN4vna6RLRHDS27DK+DBKt0jPrzt2ukDBXdZQ0cuyECzyTSyr3DOvTbe5Lt # WMjOTZzzQ3ESbJ1G//GMf83FuXFo56I/LPX0PdwT3/ye+KjNapdgnc0Fe+DwYrDD # /7aaQJ0rjcKPCO/rlq+r36Qcp0lg7yXjA63LGutl7Juw2wdTZ0rDxgBrd0c5QVfS # lYnIs3uFN36OHIXaAdvkC7tez2hJo9O6qu9NAhuVyZKRGDo3nTRYgcgKRe2Eb0Kz # mUNO0GIjAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUhpqJJk4LiTe7qkk0NVIS8LdU # TmwwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBL # oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr # BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAK # BggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAjeAEaqcwrrrTamNp1iQw149+ # YmonZYLSf27KWLAF/GrSZdfSH1CiM07rklVN3Gl2ev2LzDGDHG7DWv+CdvLrNtyu # j1mQJYyI9BvELcAzJUB46yp63K+BQQM+marwekqzd/1m29EEivzpcbFNJnw+8/O0 # diTOIMCibx+hCC7gBLY8FBkgdTSpt08K5udXwg9I8b1sc2lEtnrbzD64fAza6xyy # QUd1B74PzV0tiM3UJ9tMY47FtAVAIk3/jQkacVL7v4/NDW9fvr53o4m36UCdiJV4 # iXSIvh2/ji91zq/DGgw0mDmkRQCH3KKt1T4uAZL0gvEBq74YwGWn5BO0ei+RSTCC # BnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m # dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1 # NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/ # aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxh # MFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhH # hjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tk # iVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox # 8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJN # AgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIox # kPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P # BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9 # lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQu # Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3Js # MFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3Nv # ZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAG # A1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAG # CCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEA # dABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXED # PZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgr # UYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c # 8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFw # nzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFt # w5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk # 7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9d # dJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zG # y9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3 # yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7c # RDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wkn # HNWzfjUeCLraNtvTX4/edIhJEqGCAtIwggI7AgEBMIH8oYHUpIHRMIHOMQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv # ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBF # U046MzJCRC1FM0Q1LTNCMUQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w # IFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAPtfwjegSC3lNcgxqPm+lpKS6EKboIGD # MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEF # BQACBQDim3dNMCIYDzIwMjAwNjIyMjIzMDM3WhgPMjAyMDA2MjMyMjMwMzdaMHcw # PQYKKwYBBAGEWQoEATEvMC0wCgIFAOKbd00CAQAwCgIBAAICJN0CAf8wBwIBAAIC # EjwwCgIFAOKcyM0CAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAK # MAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQBRUSf//Pn/ # kIXKBAnFEZRzoA6A7KEA7K+ViWlc/4+N75OJY4YAXVdRIxc3fCzgNwMMTray4tff # 7sq9rRHQBOCl2lH2FOLjfVUN7CJMOIQEsPNLlwrOTACK/jQUmxEHeOA1bEVdiPh5 # hd/CDNqpNZAs94GAAa8g6AxY/rSi9F3iujGCAw0wggMJAgEBMIGTMHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABLqjSGQeT9GvoAAAAAAEuMA0GCWCG # SAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZI # hvcNAQkEMSIEIDEEq5PRlhW/jO4PUeyh699yeFt6LnXIc1IQP7RwoeaKMIH6Bgsq # hkiG9w0BCRACLzGB6jCB5zCB5DCBvQQg2v7NzvGIVoqqVTdJE3ivsdptEQIdeyaU # eg4haEyCySkwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAIT # MwAAAS6o0hkHk/Rr6AAAAAABLjAiBCAlu7rK5fxL0FJsem1krbjxA7YXKvQhOTbl # cH+kGpHa4DANBgkqhkiG9w0BAQsFAASCAQCKDzc2o48KFtPINUy2jBQQOO/CYoV1 # LVpaT4H76J5ecbrPEO7ub5vYzLJ2lRP3koRM4ldkaCnckGNnc/nCCc6tlZlIXBFK # A+57vR+4WpgSIMIHdbc140LctOA7J0WxuYlIAEhIZNQjU5nNNxsTmB4pSTLa+Bga # G3S7BakTI10XvnHHYjXY1pr7Dv36ddxi8CHxyKY72PJOd2kpxA0qRtCweXwvlLdb # 2Zv/ySBdfXIePbBl0YmmhsodAhomLWwetW2npMjzCQg/brcUDiVg/s6Ng58O/yph # KIRIeAIKBBJ7uSEPshNeFw6GKY7aIg0HLrEGpDRzqGb6FRDeX3G5qe2B # SIG # End signature block |