AffectedKeyCredentials.psm1
################################################################################################ # This script scans all key credentials in all apps/serviceprincipals in the specified tenant # # for credentials with property `hasExtendedValue == true` by calling Microsoft Graph # # # # Default output file path is at: $PWD\KeyCredentialsWithExtendedValue.tsv # # See sample usage at the end of this script # ################################################################################################ Set-StrictMode -Version 2.0 $IncludeExtendedProperties = $false Function Get-MSGraphEndpoint { param( [string] $Env ) switch ($Env) { "AzureCloud" { return "https://graph.microsoft.com" } "AzureChinaCloud" { return "https://microsoftgraph.chinacloudapi.cn" } "AzureUSGovernment" { return "https://graph.microsoft.us" } default { throw "$($Env) is not a valid cloud environment." } } } # Validate page size to be below the maximum page size Function Validate-PageSize { param( [int] $PageSize, [int] $MaxPageSize ) if ($PageSize -le 0 -or $PageSize -gt $MaxPageSize) { throw "$($PageSize) must be an integer between 0 and $($MaxPageSize)" } } # Remove unnecessary new line characters and whitespace in url Function Trim-Url { param( [string] $Url ) return $Url -replace '`n','' -replace '\s+', '' } Function Generate-Url { param( [string] $ObjectClass, [string] $AppId, [string] $ObjectId, [string] $Resource, [int] $PageSize ) $selectValues = "displayName,id,appId,keyCredentials" if ($IncludeExtendedProperties) { $selectValues += ",createdDateTime,identifierUris,signInAudience,web&`$expand=owners(`$select=id,userPrincipalName)" } $filterValues = "" if ($AppId) { $filterValues = "&filter=appId eq '$($AppId)'" } elseif ($ObjectId) { $filterValues = "&filter=id eq '$($ObjectId)'" } $url = "$($Resource)/beta/myorganization/$($ObjectClass)s?`$select=$($selectValues)&`$top=$($PageSize)$($filterValues)" Write-Verbose "Url Generated: $($url)" return $url } Function Parse-Owners { param( [array] $Owners ) $result = @() foreach ($owner in $Owners) { $ownerStr = "$($owner.id),$($owner.userPrincipalName)" $result += $ownerStr } return $result -Join ";" } Function Get-TotalObjectCount { param( [string] $AccessToken, [string] $ObjectClass, [string] $Resource ) $authHeader = @{ "Authorization" = "Bearer " + $AccessToken "ConsistencyLevel" = "eventual" } $url = "$($Resource)/beta/myorganization/$($ObjectClass)s/`$count" Write-Verbose "GET $($url)" try { $totalObjectCount = Invoke-RestMethod -Uri $url -Headers $authHeader -Method "GET" -Verbose:$false -ErrorAction Stop } catch { throw } Write-Verbose "Total $($ObjectClass) count: $($totalObjectCount)" return $totalObjectCount } # Make MS Graph request with retry and exponential backoff Function Make-MSGraphRequest { param( [string] $Url, [string] $ObjectClass, [string] $AccessToken, [int] $MaxRetryLimit, [int] $flatMinSeconds = 10 ) $authHeader = @{ "Authorization" = "Bearer " + $AccessToken } for ($i=1; $i -le $MaxRetryLimit; $i+=1) { try { Write-Verbose "GET $($Url)" $result = Invoke-RestMethod -Uri $Url -Headers $authHeader -Method "GET" -Verbose:$false break } catch { if ($_.Exception.Response.StatusCode.value__ -eq 429) { # Sleep then retry (Exponential backoff) $sleepDuration = [Math]::Pow(2,$i) + $flatMinSeconds Write-Verbose "Retry after sleeping for $($sleepDuration) seconds" Start-Sleep -s $sleepDuration continue } if ($_.Exception.Response.StatusCode.value__ -eq 404) { if ($AppId) { throw "$($ObjectClass) with AppId: $($AppId) not found" } if ($ObjectId) { throw "$($ObjectClass) with ObjectId: $($ObjectId) not found" } } Write-Warning "Unexpected Error. Try again later with -SkipTokenUrl '$($Url)'" throw } } if ($i -gt $MaxRetryLimit) { $Url = Trim-Url -Url $Url throw "Max backoff retry limit reached. Try again later with -SkipTokenUrl '$($Url)'" } return $result } # Main: Get all affected key credentials for the given object class in tenant Function Get-AffectedKeyCredentials { [CmdletBinding(HelpURI="https://aka.ms/aad-key-cred-scanner")] param( # The tenant ID or a verified domain where the test should happen. [Parameter(Mandatory = $true, HelpMessage = "Tenant Id (Guid) to search")] [guid] $TenantId, # The cloud environment [ValidateSet("AzureCloud", "AzureChinaCloud", "AzureUSGovernment")] [Parameter(Mandatory = $false, HelpMessage = "Cloud environment name. 'AzureCloud' by default")] [string] $Env = "AzureCloud", # The directory object class [ValidateSet("application", "servicePrincipal")] [Parameter(Mandatory = $true, HelpMessage = "The object class. Either Application or ServicePrincipal")] [string] $ObjectClass, # The application id (for singular GET) [Parameter(Mandatory = $false, HelpMessage = "The application id or application principal id of the app/sp object to query")] [guid] $AppId, # The object id (for singular GET) [Parameter(Mandatory = $false, HelpMessage = "The object id of the app/sp object to query")] [guid] $ObjectId, # The url with skip token to continue from if necessary [Parameter(Mandatory = $false, HelpMessage = "The given ms graph url containing skip token to continue from")] [string] $SkipTokenUrl = $null, # The toggle for simple/verbose output [Parameter(Mandatory = $false, HelpMessage = "Toggle for additional object properties are to be printed in the output file")] [switch] $ExtendedOutputSchema, # The toggle for scanning single/all objects in the tenant [Parameter(Mandatory = $false, HelpMessage = "Toggle to scan all objects in the tenant")] [switch] $ScanAll, # $top passed to the List Applications or List ServicePrincipals call [int] $PageSize = 200, # How long to sleep (in seconds) between paginated calls [int] $SleepInterval = 2, # Max number of retries for List Applications or List ServicePrincipals MS Graph request [int] $MaxRetryLimit = 5, # Max page size [int] $MaxPageSize = 500 ) # Validate page size Validate-PageSize -PageSize $PageSize -MaxPageSize $MaxPageSize # Get ms graph endpoint resource url $resourceUrl = Get-MSGraphEndpoint -Env $Env # Output warning message if scanning all objects # Otherwise, check if either AppId or ObjectId is specified if ($ScanAll) { Write-Warning "Are you sure you want to run the commandlet for all $($ObjectClass)s in your tenant? The commandlet may take a long time to run, and requests for a large number of $($ObjectClass)s could be throttled." -WarningAction Inquire if ($AppId -or $ObjectId) { throw "You cannot specify -AppId or -ObjectId when running in -ScanAll mode" } } else { if (!$AppId -and !$ObjectId) { throw "When scanning a single application or service principal, you must provide either an -AppId or an -ObjectId" } if ($AppId -and $ObjectId) { throw "Please provide exactly one of the following: -AppId or -ObjectId" } } # Set flag to distinguish between default schema and extended schema for output if ($ExtendedOutputSchema) { $IncludeExtendedProperties = $true } # Information about installing Az.Accounts module Write-Warning "This script requires the powershell module 'Az.Accounts' to installed." Write-Warning "If this is not installed, you will be asked to install the module." Write-Warning "Please refer: https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-6.5.0" # Check if the Az Module is installed and imported if(!(Get-Module Az.Accounts)){ try{Import-Module -Name Az.Accounts -ErrorAction Stop} catch{Install-Module -Name Az.Accounts -AllowClobber -Confirm -Scope CurrentUser} } # Connect to AAD Write-Host "`nConnecting to AAD tenant..." Connect-AzAccount -Tenant $TenantId -Environment $Env -ErrorAction Stop | out-null Write-Host "Connected to $($TenantId)`n" -ForegroundColor Green # Get access token $accessToken = (Get-AzAccessToken -ResourceUrl $resourceUrl).Token # Get total number of application/service prinicpals to scan if scanning all objects in the tenant if ($ScanAll) { $totalObjectCount = Get-TotalObjectCount -AccessToken $accessToken -ObjectClass $ObjectClass -Resource $resourceUrl } # Generate the url for corresponding Microsoft Graph API endpoint $url = Generate-Url -ObjectClass $ObjectClass -AppId $AppId -ObjectId $ObjectId -Resource $resourceUrl -PageSize $PageSize if ($SkipTokenUrl) { $url = Trim-Url -Url $SkipTokenUrl } # Start key credential scan Write-Host "Starting scan..." $stopwatch = [System.Diagnostics.Stopwatch]::new() $stopwatch.Start() $scannedObjectCount = 0 $keyWithExtenededValueCount = 0 while ($null -ne $url) { $result = Make-MSGraphRequest -Url $url -ObjectClass $ObjectClass -AccessToken $accessToken -MaxRetryLimit $MaxRetryLimit # If no results are returned, then exit if (($null -eq $result) -or ($null -eq $result.Value) -or ($result.Value.Length -eq 0)) { Write-Error "No resource of type $($ObjectClass) found in tenant $($TenantId). Exiting." return } $result.Value | ForEach-Object { $reg = $_ $scannedObjectCount += 1 $displayName = $reg.displayName $appID = $reg.appId $objectID = $reg.Id Write-Verbose "Scanning key credentials for: $displayName" $reg.keyCredentials | ForEach-Object { $cred = $_ if (($cred.hasExtendedValue) -and ($cred.type -eq 'AsymmetricX509Cert') -and ($cred.usage -ne 'Sign')) { $keyWithExtenededValueCount += 1 $isExpired = $false $currentDateTime = Get-Date if ($currentDateTime -gt $cred.endDateTime) { $isExpired = $true } $out = [pscustomobject][ordered] @{ "ObjectClass" = $ObjectClass "AppId" = $appId "ObjectId" = $objectID "DisplayName" = $displayName "KeyId" = $cred.KeyId "Usage" = $cred.usage "StartDateTime" = $cred.startDateTime "EndDateTime" = $cred.endDateTime "HasExtendedValue" = $cred.hasExtendedValue "IsExpired" = $isExpired } if ($IncludeExtendedProperties) { $identifierUrisStr = "" if ($ObjectClass -eq "application") { $identifierUrisStr = $reg.identifierUris -Join "," } $ownersStr = Parse-Owners -Owners $reg.owners $homePageUrl = "" if ($reg.web) { $homePageUrl = $reg.web.homePageUrl } $out = [pscustomobject][ordered] @{ "ObjectClass" = $ObjectClass "AppId" = $appId "ObjectId" = $objectID "DisplayName" = $displayName "KeyId" = $cred.KeyId "Usage" = $cred.usage "StartDateTime" = $cred.startDateTime "EndDateTime" = $cred.endDateTime "HasExtendedValue" = $cred.hasExtendedValue "IsExpired" = $isExpired "IdentifierUris" = $identifierUrisStr "Owners" = $ownersStr "ObjectCreatedDateTime" = $reg.createdDateTime "SignInAudience" = $reg.signInAudience "HomePageUrl" = $homePageUrl } } Write-Verbose $out #output the object into the pipeline $out } } } if ($ScanAll) { $pcomplete = ($scannedObjectCount / $totalObjectCount) * 100 Write-Progress -Activity "Scanning $($ObjectClass)s" -Status "$($scannedObjectCount) of $($totalObjectCount)" -PercentComplete $pcomplete } Write-Verbose "[In-Progress] Scanned $($scannedObjectCount) $($ObjectClass)s so far. # of $($ObjectClass)s with KeyCredentials containing extended value: $($keyWithExtenededValueCount)" Start-Sleep -s $SleepInterval $url = $null if ($result | Get-Member -name '@odata.nextLink' -Membertype Properties) { $url = $result.'@odata.nextLink' } } Write-Verbose "[Finished] Scanned a total of $($scannedObjectCount) $($ObjectClass)s. # of $($ObjectClass)s with KeyCredentials containing extended value: $($keyWithExtenededValueCount)" $stopwatch.Stop() Write-Verbose "Time elapsed: $($stopwatch.ELAPSED)" Write-Host "$($ObjectClass) key credentials successfully scanned." -ForegroundColor Green } Export-ModuleMember -Function Get-AffectedKeyCredentials # Sample usage: # Import-Module AffectedKeyCredentials.psm1 # Get-AffectedKeyCredentials -TenantId <guid> -ObjectClass <String> [-AppId <guid>] [-ObjectId <guid>] [-Env <String>] [-SkipTokenUrl <String>] [-ExtendedOutputSchema] [-ScanAll] [-Verbose] # SIG # Begin signature block # MIInQAYJKoZIhvcNAQcCoIInMTCCJy0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDuCfhf+9pwcP68 # sfN7OZJkhuT+EmknnXEyQuXlbbMbHqCCEXkwggiJMIIHcaADAgECAhM2AAABfv9v # /QSkJVgSAAIAAAF+MA0GCSqGSIb3DQEBCwUAMEExEzARBgoJkiaJk/IsZAEZFgNH # QkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxFTATBgNVBAMTDEFNRSBDUyBDQSAwMTAe # Fw0yMTA5MDkwMTI2MjZaFw0yMjA5MDkwMTI2MjZaMCQxIjAgBgNVBAMTGU1pY3Jv # c29mdCBBenVyZSBDb2RlIFNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQCQh1zMc6GVq9fygCskp/O9g6jS0ilJ3idmz+2JkE+9AarM0AiJ1/CDQETS # X56JOh9Vm8kdffjdqJfD2NoSV2lO1eKAFKETKyiJKvbcW38H7JhH1h+yCBjajiWy # wcAZ/ipRX3sMYM5nXl5+GxEZpGQbLIsrLj24Zi9dj2kdHc0DxqbemzlCySiB+n9r # HFdi9zEn6XzuTf/3i6XM36lUPZ+xt6Zckupu0CAnu4dZr1XiwHvbJvqq3RcXOU5j # p1m/AKk4Ov+9jaEKOnYiHJbnpC+vKx/Zv8aZajhPyVY3fXb/tygGOyb607EYn7F2 # v4AcJL5ocPTT3BGWtve1KuOwRRs3AgMBAAGjggWVMIIFkTApBgkrBgEEAYI3FQoE # HDAaMAwGCisGAQQBgjdbAQEwCgYIKwYBBQUHAwMwPQYJKwYBBAGCNxUHBDAwLgYm # KwYBBAGCNxUIhpDjDYTVtHiE8Ys+hZvdFs6dEoFgg93NZoaUjDICAWQCAQwwggJ2 # BggrBgEFBQcBAQSCAmgwggJkMGIGCCsGAQUFBzAChlZodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpaW5mcmEvQ2VydHMvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1F # JTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBSBggrBgEFBQcwAoZGaHR0cDovL2NybDEu # YW1lLmdibC9haWEvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUy # MDAxKDIpLmNydDBSBggrBgEFBQcwAoZGaHR0cDovL2NybDIuYW1lLmdibC9haWEv # QlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBS # BggrBgEFBQcwAoZGaHR0cDovL2NybDMuYW1lLmdibC9haWEvQlkyUEtJQ1NDQTAx # LkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBSBggrBgEFBQcwAoZG # aHR0cDovL2NybDQuYW1lLmdibC9haWEvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1F # JTIwQ1MlMjBDQSUyMDAxKDIpLmNydDCBrQYIKwYBBQUHMAKGgaBsZGFwOi8vL0NO # PUFNRSUyMENTJTIwQ0ElMjAwMSxDTj1BSUEsQ049UHVibGljJTIwS2V5JTIwU2Vy # dmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1BTUUsREM9R0JM # P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0 # aG9yaXR5MB0GA1UdDgQWBBRufMhNVeWweAyGzdFbxkxa8y1WjDAOBgNVHQ8BAf8E # BAMCB4AwUAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRp # b25zIFB1ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzYxNjcrNDY3OTc0MIIB5gYDVR0f # BIIB3TCCAdkwggHVoIIB0aCCAc2GP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w # a2lpbmZyYS9DUkwvQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNybIYxaHR0cDovL2Ny # bDEuYW1lLmdibC9jcmwvQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNybIYxaHR0cDov # L2NybDIuYW1lLmdibC9jcmwvQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNybIYxaHR0 # cDovL2NybDMuYW1lLmdibC9jcmwvQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNybIYx # aHR0cDovL2NybDQuYW1lLmdibC9jcmwvQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNy # bIaBvWxkYXA6Ly8vQ049QU1FJTIwQ1MlMjBDQSUyMDAxKDIpLENOPUJZMlBLSUNT # Q0EwMSxDTj1DRFAsQ049UHVibGljJTIwS2V5JTIwU2VydmljZXMsQ049U2Vydmlj # ZXMsQ049Q29uZmlndXJhdGlvbixEQz1BTUUsREM9R0JMP2NlcnRpZmljYXRlUmV2 # b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2lu # dDAfBgNVHSMEGDAWgBSWUYTga297/tgGq8PyheYprmr51DAfBgNVHSUEGDAWBgor # BgEEAYI3WwEBBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAU1RmrZsQtaYx # 8dBu9zC6w4TXEtumd3O0ArP7W0Co7nNFCDTv8pxqOM2bz/pH49DXdnzcXCTjUjci # o03V+QPO3Ql8xOMqm8bE9Kcof+fPk4DyDY5y+YzxQyk49URn4ea3WhihAJkg/xnF # LiKnbWW8iyqxie+B44u9dPfbsWrxcgedzSnH0aXwfIt29IKCpGHL74rBDbKHXdL0 # pEjf9c2YA6OiS1IH7X/suBjEFa4LEYPTSFK2AJXpgM7q9dmSvta4CyudRoYf1BXP # KR+CzNT9XL5ZJX8LUuC5LrZgbt7LzjlW+1Umo2OsmUO3YA7/s5vH6Tqc6uZ9isIw # sit0XfouHTCCCOgwggbQoAMCAQICEx8AAABR6o/2nHMMqDsAAAAAAFEwDQYJKoZI # hvcNAQELBQAwPDETMBEGCgmSJomT8ixkARkWA0dCTDETMBEGCgmSJomT8ixkARkW # A0FNRTEQMA4GA1UEAxMHYW1lcm9vdDAeFw0yMTA1MjExODQ0MTRaFw0yNjA1MjEx # ODU0MTRaMEExEzARBgoJkiaJk/IsZAEZFgNHQkwxEzARBgoJkiaJk/IsZAEZFgNB # TUUxFTATBgNVBAMTDEFNRSBDUyBDQSAwMTCCASIwDQYJKoZIhvcNAQEBBQADggEP # ADCCAQoCggEBAMmaUgl9AZ6NVtcqlzIU+gVJSWVqWuKd8RXokxzuL5tkOgv2s0ec # cMZ8mB65Ehg7Utj/V/igxOuFdtJphEJLm8ZzzXjlZxNkb3TxsYMJavgYUtzjXVbE # D4+/au14BzPR4cwffqpNDwvSjdc5vaf7HsokUuiRdXWzqkX9aVJexQFcZoIghYFf # IRyG/6wz14oOxQ4t0tMhMdglA1aSKvIxIRvGp1BRNVmMTPp4tEuSh8MCjyleKshg # 6AzvvQJg6JmtwocruVg5VuXHbal01rBjxN7prZ1+gJpZXVBS5rODlUeILin/p+Sy # AQgum04qHH1z6JqmI2EysewBjH2lS2ml5oUCAwEAAaOCBNwwggTYMBIGCSsGAQQB # gjcVAQQFAgMCAAIwIwYJKwYBBAGCNxUCBBYEFBJoJEIhR8vUa74xzyCkwAsjfz9H # MB0GA1UdDgQWBBSWUYTga297/tgGq8PyheYprmr51DCCAQQGA1UdJQSB/DCB+QYH # KwYBBQIDBQYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3FAIBBgkrBgEEAYI3 # FQYGCisGAQQBgjcKAwwGCSsGAQQBgjcVBgYIKwYBBQUHAwkGCCsGAQUFCAICBgor # BgEEAYI3QAEBBgsrBgEEAYI3CgMEAQYKKwYBBAGCNwoDBAYJKwYBBAGCNxUFBgor # BgEEAYI3FAICBgorBgEEAYI3FAIDBggrBgEFBQcDAwYKKwYBBAGCN1sBAQYKKwYB # BAGCN1sCAQYKKwYBBAGCN1sDAQYKKwYBBAGCN1sFAQYKKwYBBAGCN1sEAQYKKwYB # BAGCN1sEAjAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw # EgYDVR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBQpXlFeZK40ueusnA2njHUB # 0QkLKDCCAWgGA1UdHwSCAV8wggFbMIIBV6CCAVOgggFPhjFodHRwOi8vY3JsLm1p # Y3Jvc29mdC5jb20vcGtpaW5mcmEvY3JsL2FtZXJvb3QuY3JshiNodHRwOi8vY3Js # Mi5hbWUuZ2JsL2NybC9hbWVyb290LmNybIYjaHR0cDovL2NybDMuYW1lLmdibC9j # cmwvYW1lcm9vdC5jcmyGI2h0dHA6Ly9jcmwxLmFtZS5nYmwvY3JsL2FtZXJvb3Qu # Y3JshoGqbGRhcDovLy9DTj1hbWVyb290LENOPUFNRVJvb3QsQ049Q0RQLENOPVB1 # YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRp # b24sREM9QU1FLERDPUdCTD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/ # b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwggGrBggrBgEFBQcBAQSC # AZ0wggGZMEcGCCsGAQUFBzAChjtodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp # aW5mcmEvY2VydHMvQU1FUm9vdF9hbWVyb290LmNydDA3BggrBgEFBQcwAoYraHR0 # cDovL2NybDIuYW1lLmdibC9haWEvQU1FUm9vdF9hbWVyb290LmNydDA3BggrBgEF # BQcwAoYraHR0cDovL2NybDMuYW1lLmdibC9haWEvQU1FUm9vdF9hbWVyb290LmNy # dDA3BggrBgEFBQcwAoYraHR0cDovL2NybDEuYW1lLmdibC9haWEvQU1FUm9vdF9h # bWVyb290LmNydDCBogYIKwYBBQUHMAKGgZVsZGFwOi8vL0NOPWFtZXJvb3QsQ049 # QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNv # bmZpZ3VyYXRpb24sREM9QU1FLERDPUdCTD9jQUNlcnRpZmljYXRlP2Jhc2U/b2Jq # ZWN0Q2xhc3M9Y2VydGlmaWNhdGlvbkF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOC # AgEAUBAjt08P6N9e0a3e8mnanLMD8dS7yGMppGkzeinJrkbehymtF3u91MdvwEN9 # E34APRgSZ4MHkcpCgbrEc8jlNe4iLmyb8t4ANtXcLarQdA7KBL9VP6bVbtr/vnaE # wif4vhm7LFV5IGl/B/uhDhhJk+Hr6eBm8EeB8FpXPg73/Bx/D3VANmdOAr3MCH3J # EoqWzZvOI8SfF45kxU1rHJXS/XnY9jbGOohp8iRSMrq9j0u1UWMld6dVQCafdYI9 # Y0ULVhMggfD+YPZxN8/LtADWlP4Y8BEAq3Rsq2r1oJ39ibRvm09umAKJG3PJvt9s # 1LV0TvjSt7QI4TrthXbBt6jaxeLHO8t+0fwvuz3G/3BX4bbarIq3qWYouMUrXIzD # g2Ll8xptyCbNG9KMBxuqCne2Thrx6ZpofSvPwy64g/7KvG1EQ9dKov8LlvMzOyKS # 4Nb3EfXSCtpnNKY+OKXOlF9F27bT/1RCYLt5U9niPVY1rWio8d/MRPcKEjMnpD0b # c08IH7srBfQ5CYrK/sgOKaPxT8aWwcPXP4QX99gx/xhcbXktqZo4CiGzD/LA7pJh # Kt5Vb7ljSbMm62cEL0Kb2jOPX7/iSqSyuWFmBH8JLGEUfcFPB4fyA/YUQhJG1KEN # lu5jKbKdjW6f5HJ+Ir36JVMt0PWH9LHLEOlky2KZvgKAlCUxghUdMIIVGQIBATBY # MEExEzARBgoJkiaJk/IsZAEZFgNHQkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxFTAT # BgNVBAMTDEFNRSBDUyBDQSAwMQITNgAAAX7/b/0EpCVYEgACAAABfjANBglghkgB # ZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3 # AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgwgBPlo8pHLELJrVK # 08K2XXu6ocKwDu4fOOMBq4a80uswQgYKKwYBBAGCNwIBDDE0MDKgFIASAE0AaQBj # AHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTANBgkqhkiG # 9w0BAQEFAASCAQA2X289tKS7zLRV8isUD8gE6oC+irIKv1L/DnIXJg9WcMRE1TeS # KcRYmIk6dVFde+jTYZ13TyDe5b+t1hLhyKvZ7W8Cai4iVDa0J6xk65tzc1xJvVbY # 3JfTnNh7dnSLSHCXESj4ErzqWw+xrOxYt8X6/3VHo4nhJkb2YHfQZTJzAWdHLNpn # oo3EoJOHCfp7SXQxmw/p+2YCSJCgRW8Br2PBOp/9nBTsL8Fq1qnyeajMH/85X74O # 9KdWP0g3vp5TYprJQubhZHfZaC0qticL7LZ0tkMRh8geJxSpV+tJ8vdZluv+bDW8 # EEkXJtyA2u6LOeXJAVQa75u0gAxyT7bFz8onoYIS5TCCEuEGCisGAQQBgjcDAwEx # ghLRMIISzQYJKoZIhvcNAQcCoIISvjCCEroCAQMxDzANBglghkgBZQMEAgEFADCC # AVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMBMDEwDQYJ # YIZIAWUDBAIBBQAEIDHraMJsb6VX55m0qs861SCxXtsC4dfq7gaxelvOfpRBAgZh # kGjTrCkYEzIwMjExMTE2MjI1MzQwLjE4OVowBIACAfSggdCkgc0wgcoxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29m # dCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkFF # MkMtRTMyQi0xQUZDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2 # aWNloIIOPDCCBPEwggPZoAMCAQICEzMAAAFIoohFVrwvgL8AAAAAAUgwDQYJKoZI # hvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEm # MCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjAxMTEy # MTgyNTU2WhcNMjIwMjExMTgyNTU2WjCByjELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0 # aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046QUUyQy1FMzJCLTFBRkMxJTAj # BgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQD3/3ivFYSK0dGtcXaZ8pNLEARbraJewryi/Jgb # aKlq7hhFIU1EkY0HMiFRm2/Wsukt62k25zvDxW16fphg5876+l1wYnClge/rFlrR # 2Uu1WwtFmc1xGpy4+uxobCEMeIFDGhL5DNTbbOisLrBUYbyXr7fPzxbVkEwJDP5F # G2n0ro1qOjegIkLIjXU6qahduQxTfsPOEp8jgqMKn++fpH6fvXKlewWzdsfvhiZ4 # H4Iq1CTOn+fkxqcDwTHYkYZYgqm+1X1x7458rp69qjFeVP3GbAvJbY3bFlq5uyxr # iPcZxDZrB6f1wALXrO2/IdfVEdwTWqJIDZBJjTycQhhxS3i1AgMBAAGjggEbMIIB # FzAdBgNVHQ4EFgQUhzLwaZ8OBLRJH0s9E63pIcWJokcwHwYDVR0jBBgwFoAU1WM6 # XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5t # aWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENBXzIwMTAt # MDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0w # MS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG # 9w0BAQsFAAOCAQEAZhKWwbMnC9Qywcrlgs0qX9bhxiZGve+8JED27hOiyGa8R9nq # zHg4+q6NKfYXfS62uMUJp2u+J7tINUTf/1ugL+K4RwsPVehDasSJJj+7boIxZP8A # U/xQdVY7qgmQGmd4F+c5hkJJtl6NReYE908Q698qj1mDpr0Mx+4LhP/tTqL6HpZE # URlhFOddnyLStVCFdfNI1yGHP9n0yN1KfhGEV3s7MBzpFJXwOflwgyE9cwQ8jjOT # VpNRdCqL/P5ViCAo2dciHjd1u1i1Q4QZ6xb0+B1HdZFRELOiFwf0sh3Z1xOeSFcH # g0rLE+rseHz4QhvoEj7h9bD8VN7/HnCDwWpBJTCCBnEwggRZoAMCAQICCmEJgSoA # AAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1NVow # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUA # A4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18aEss # X8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgI # s0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHE # pl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2KQk1A # UdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZzTzn # L0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQBgkr # BgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJ # KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF # MAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8w # TTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBK # BggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N # aWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJ # KwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwA # ZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0G # CSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn+ # +ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbMQEBB # m9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmp # tWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rb # V0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S4Zz5 # Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoG # ig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf5zEH # pJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpU # ObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJzxlB # TeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB0d4w # wP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/edIhJ # EqGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRp # b25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpBRTJDLUUzMkItMUFGQzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa # AxUAhyuClrocWf4SIcRafAEX1Rhs6zmggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOU+MwIwIhgPMjAyMTExMTYy # MTM5MTRaGA8yMDIxMTExNzIxMzkxNFowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA # 5T4zAgIBADAKAgEAAgIQkgIB/zAHAgEAAgIRTTAKAgUA5T+EggIBADA2BgorBgEE # AYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYag # MA0GCSqGSIb3DQEBBQUAA4GBAD4IhsH7VGEZq3Et8NzhOCvICoWIyY64XnKWVMz5 # dvsKi//vfT2mX6mbOE3knjHzrGrvDAm3Kj+jbkoXLu4yKODjhIV6tXfcWeBtg0hb # 5dUWmRidX8nkQpPzDCVUbCqjm3aP3cp4NHbfEBdT4M0YjxTVSDkUT7GSo7xRXJJc # 8rg8MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC # EzMAAAFIoohFVrwvgL8AAAAAAUgwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3 # DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgKoW4Cm5HKxG1c7SQ # dE3HFX6LSG3bNVukGLbTMKKohEcwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9 # BCCpkBrqjHmhvyYf5tTcTvD5Y4a+V79TwVV6T1aAwdto2DCBmDCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABSKKIRVa8L4C/AAAAAAFIMCIE # IG/qeIp6mbVd8Dr+EThpnN/6w78fmvUoISM5Bb/WyvbkMA0GCSqGSIb3DQEBCwUA # BIIBAOZ5Si1j3uIZCB4kK/fNwMIXekGCwIFdUG1hi/tEHBllgjp/cSp7oTOdc8bv # X9k5horUQJMZN6Bf4x2uUhMW1VMld+oM49g2C52atPYRlggUyMyisZEM6cIt3Yby # MZqTEa4Am+BRUJonkDr+ig4PERUr6R8OxXbKSVacG5ugAcXUA7dtWIdkvDBLO75S # ZZiepsy/KcdrK4vGKH/ySrLaXbJ+I+E4hCQa9+CNBuKxbqTqbQEDykphbEOet/5E # Dsgm3Lo/nUaGW9B9lPDXANZA873j0jC6YHEsm1czzJBU5x+OgFi2zJmdhfmpRniL # NMbHF8axH9LfihtpE2XsKhpsTw0= # SIG # End signature block |