ConfigureWVDSSO.ps1
<#PSScriptInfo
.VERSION 1.0.0.1 .GUID f093f861-273c-4436-a189-efe0ccd5bc2b .AUTHOR Microsoft Corporation .COMPANYNAME Microsoft Corporation .COPYRIGHT (c) 2020 Microsoft Corporation. All rights reserved. .TAGS .LICENSEURI http://www.microsoftvolumelicensing.com/DocumentSearch.aspx?Mode=3&DocumentTypeId=31 .PROJECTURI https://www.microsoft.com/en-us/microsoft-365/modern-desktop/enterprise/windows-virtual-desktop .ICONURI .EXTERNALMODULEDEPENDENCIES ADFS .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES #> <# .SYNOPSIS Configures ADFS for a SSO with WVD deployment .DESCRIPTION This script creates creates a Relying Party and two ADFS clients. The Relying Party is used for issueing claims that can be used for cerificate logon. The first client is created with ADFSClientId parameter as it's client Id which allows the RD client to get a token directly from ADFS. Later this token is exchanged using the second ADFS client with ResourceBaseAddress parameter as its ClientId. The second ADFS client is also confidential client, the secret generated for that client is used by the Rds deploymenent to sign a user certificate in exchange to the token acqiured from the first client. .PARAMETER WvdClientAppApplicationID Required Guid of the Resource's ClientId .PARAMETER WvdWebAppAppIDUri Required The URL for which the RDS deployment is configured. This equals IdentityAuthority setting in the globalSettings table .PARAMETER RelyingPartyClientName Optional Name of the Relying Part .PARAMETER ADFSAuthority Required The URL where ADFS can be found. Usually it has the form of "https://<FQDN of ADFS>/adfs" .PARAMETER RdWebURL Required The URL that points to RD web service. This Url is used to add the appropriate RedirectUri to the clients .EXAMPLE .\ConfigureWVDSSO.ps1 -WvdWebAppAppIDUri "https://www.wvd.microsoft.com" -WvdClientAppApplicationID "a85cf173-4192-42f8-81fa-777a763e6e2c" -RelyingPartyClientName "Windows Virtual Desktop ADFS Logon SSO" -ADFSAuthority "https://sts.contoso.com/adfs" -RdWebURL "https://rdweb.wvd.microsoft.com" #> #Requires -Version 4.0 Param( [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $WvdWebAppAppIDUri="https://www.wvd.microsoft.com", [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $WvdClientAppApplicationID="a85cf173-4192-42f8-81fa-777a763e6e2c", [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $RelyingPartyClientName="Windows Virtual Desktop ADFS Logon SSO", [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [string] $ADFSAuthority, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $RdWebURL="https://rdweb.wvd.microsoft.com", [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $RdWebAliasURL, [Parameter ()] [switch] $UseCert, [Parameter(Mandatory=$False)] [ValidateNotNullOrEmpty()] [string] $CertPath ) # Windows (Desktop) Microsoft Remote Desktop client, with the Client App ID $OOBRedirectUri = "ms-appx-web://Microsoft.AAD.BrokerPlugin/$($WvdClientAppApplicationID)" # Microsoft Remote Desktop Preview application in Microsoft Store $URDCPreviewRedirectUri = "ms-appx-web://Microsoft.AAD.BrokerPlugin/S-1-15-2-1675141024-1003855329-915403128-1652244152-3721706001-1815751561-448073730" # Microsoft Remote Desktop application in Microsoft Store $URDCReleaseRedirectUri = "ms-appx-web://Microsoft.AAD.BrokerPlugIn/S-1-15-2-1351064824-1747509879-3343020337-936898558-3022384538-2153969374-721259951" # Calculate ring rdweb URLs $RdWebURLR0 = $RdWebURL.Replace("rdweb", "rdweb-r0") $RdWebURLR1 = $RdWebURL.Replace("rdweb", "rdweb-r1") # Calculate the rdweb alias URI if ($RdWebAliasURL.Length -ne 0) { $RdWebWWWUri = $RdWebAliasURL } else { $RdWebWWWUri = $RdWebURL.Replace("rdweb", "www") } # Windows Virtual Desktop web client $webClientRedirectUri = $RdWebURL + "/webclient/sso.html" $webClientRedirectUriR0 = $RdWebURLR0 + "/webclient/sso.html" $webClientRedirectUriR1 = $RdWebURLR1 + "/webclient/sso.html" $webClientWWWRedirectUri = $RdWebWWWUri + "/webclient/sso.html" $webClientARMRedirectUri = $RdWebURL + "/arm/webclient/sso.html" $webClientARMRedirectUriR0 = $RdWebURLR0 + "/arm/webclient/sso.html" $webClientARMRedirectUriR1 = $RdWebURLR1 + "/arm/webclient/sso.html" $webClientARMWWWRedirectUri = $RdWebWWWUri + "/arm/webclient/sso.html" $ADFSRedirectUri = @($WvdWebAppAppIDUri, $OOBRedirectUri, $URDCPreviewRedirectUri, $URDCReleaseRedirectUri, $webClientRedirectUri, $webClientRedirectUriR0, $webClientRedirectUriR1, $webClientWWWRedirectUri, $webClientARMRedirectUri, $webClientARMRedirectUriR0, $webClientARMRedirectUriR1, $webClientARMWWWRedirectUri) $SSORedirectUri = @(($RdWebURL + "/sso"), ($RdWebURLR0 + "/sso"), ($RdWebURLR1 + "/sso")) if($UseCert.IsPresent) { $cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 try{ $cer.Import($CertPath) } catch { Write-Host "Certificate Path is invalid, exiting" $error_object = New-Object -TypeName PSObject return $error_object } } # first add Implicit UPN claim if none exists $impupnclaim = Get-AdfsClaimDescription -ClaimType http://schemas.xmlsoap.org/ws/2005/05/identity/claims/implicitupn if ($impupnclaim -eq $null) { Write-Host "Adding Implicit UPN claim" Add-AdfsClaimDescription -Name "Implicit UPN" -ClaimType "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/implicitupn" -ShortName "Implicit UPN" -IsAccepted $true -IsOffered $true Write-Host "Implicit UPN claim added" } else { Write-Host "Implicit UPN claim already exists" } Write-Host "Adding RP trust for SSO" $rp = Get-AdfsRelyingPartyTrust -Identifier $WvdWebAppAppIDUri if ($rp -eq $null) { Write-Host "Adding Relying Party Trust" Add-AdfsRelyingPartyTrust -Name $RelyingPartyClientName -Identifier $WvdWebAppAppIDUri -IssuanceAuthorizationRules '=> issue(Type = "http://schemas.microsoft.com/authorization/claims/permit", Value = "true");' -IssuanceTransformRules '@RuleTemplate = "PassThroughClaims" @RuleName = "UPN" c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"] => issue(claim = c); @RuleTemplate = "PassThroughClaims" @RuleName = "Group" c:[Type == "http://schemas.xmlsoap.org/claims/Group"] => issue(claim = c); @RuleTemplate = "PassThroughClaims" @RuleName = "Primary SID" c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"] => issue(claim = c); @RuleName = "Anchor Claim Type" EXISTS([Type == "http://schemas.microsoft.com/ws/2014/01/identity/claims/anchorclaimtype"]) => issue(Type = "http://schemas.microsoft.com/ws/2014/01/identity/claims/anchorclaimtype", Value = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"); @RuleTemplate = "PassThroughClaims" @RuleName = "Implicit UPN" c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/implicitupn"] => issue(claim = c); @RuleTemplate = "PassThroughClaims" @RuleName = "Windows Account Name" c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"] => issue(claim = c); ' Write-Host "Relying Party Trust added" } else { Write-Host "Relying Party Trust already exists" if (-not $rp.IssuanceTransformRules.Contains("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn") ` -or -not $rp.IssuanceTransformRules.Contains("http://schemas.xmlsoap.org/claims/Group") ` -or -not $rp.IssuanceTransformRules.Contains("http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid") ` -or -not $rp.IssuanceTransformRules.Contains("http://schemas.microsoft.com/ws/2014/01/identity/claims/anchorclaimtype")` -or -not $rp.IssuanceTransformRules.Contains("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/implicitupn")) { Write-Host "Relying Party with Identifier $WvdWebAppAppIDUri has missing claims. Delete it and run this script again" return; } } $adfsClient = Get-AdfsClient -ClientId $WvdClientAppApplicationID if ($adfsClient -eq $null) { Write-Host "Adding ADFS client with ClientId: $WvdClientAppApplicationID" Add-AdfsClient -ClientId $WvdClientAppApplicationID -Name $RelyingPartyClientName -RedirectUri $ADFSRedirectUri Write-Host "ADFS client added" } else { Write-Host "ADFS client with ClientId: $WvdClientAppApplicationID already exists" } $perm = Get-AdfsApplicationPermission|where-object {$_.ClientRoleIdentifier -eq $WvdClientAppApplicationID -and $_.ServerRoleIdentifier -eq $WvdWebAppAppIDUri -and $_.ScopeNames[0] -eq "openid" -and $_.ScopeNames[1] -eq "logon_cert"} if ($perm -eq $null) { Write-Host "Granting logon_cert access to $WvdClientAppApplicationID" Grant-AdfsApplicationPermission -ServerRoleIdentifier $WvdWebAppAppIDUri -ClientRoleIdentifier $WvdClientAppApplicationID -ScopeNames "openid","logon_cert" Write-Host "Access granted" } else { Write-Host "Access logon_cert already granted for $WvdClientAppApplicationID" } $adfsClient = Get-AdfsClient -ClientId $WvdWebAppAppIDUri if ($adfsClient -eq $null) { if ($UseCert.IsPresent) { Write-Host "Adding confidential ADFS client (with JWT signing certificate authentication) with ClientId: $WvdWebAppAppIDUri" $adfsClient = Add-AdfsClient -ClientId $WvdWebAppAppIDUri -Name ($RelyingPartyClientName + " SSO") -RedirectUri $SSORedirectUri -ClientType Confidential -JWTSigningCertificate $cer -JWTSigningCertificateRevocationCheck CheckChainExcludeRoot Write-Host "ADFS client added" } else { Write-Host "Adding confidential ADFS client (with shared client secret authentication) with ClientId: $WvdWebAppAppIDUri" $adfsClient = Add-AdfsClient -ClientId $WvdWebAppAppIDUri -Name ($RelyingPartyClientName + " SSO") -RedirectUri $SSORedirectUri -ClientType Confidential -GenerateClientSecret Write-Host "ADFS client added" } } else { Write-Host "Confidential ADFS client with ClientId: $WvdWebAppAppIDUri already exists" } $clientSecret = $null $perm = Get-AdfsApplicationPermission|where-object {$_.ClientRoleIdentifier -eq $WvdWebAppAppIDUri -and $_.ServerRoleIdentifier -eq $WvdWebAppAppIDUri -and $_.ScopeNames[0] -eq "openid" -and $_.ScopeNames[1] -eq "logon_cert"} if ($perm -eq $null) { Write-Host "Granting logon_cert access to $WvdWebAppAppIDUri" Grant-AdfsApplicationPermission -ServerRoleIdentifier $WvdWebAppAppIDUri -ClientRoleIdentifier $WvdWebAppAppIDUri -ScopeNames "openid","logon_cert" if (!($UseCert.IsPresent)) { $clientSecret = $adfsClient.ClientSecret } Write-Host "Access granted" } else { Write-Host "Access logon_cert already granted for $WvdWebAppAppIDUri" if (!($UseCert.IsPresent)) { Write-Warning "Can't get secret for ADFS client. Need to recreate it using UnConfigureWVDSSO.ps1" } } $return_object = New-Object -TypeName PSObject if ($clientSecret -ne $null) { Add-Member -InputObject $return_object -NotePropertyName SSOClientSecret -NotePropertyValue $clientSecret | Out-Null Write-Warning "Ready to execute: Update-AzWvdHostPool -Name <Host Pool Name> -SsoadfsAuthority $ADFSAuthority -SsoClientId $WvdWebAppAppIDUri -SsoSecretType SharedKeyInKeyVault -SsoClientSecretKeyVaultPath <KeyVault secret URL>" } else { Add-Member -InputObject $return_object -NotePropertyName SSOCertPath -NotePropertyValue $CertPath | Out-Null Write-Warning "Please run the following commands to set up SSO on your RDSTenant:" Write-Host '$PFX = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2' Write-Host '$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable' Write-Host '$PFX.Import(<pfxpath>,<password>,$flags)' Write-Warning "Ready to execute: Update-AzWvdHostPool -Name <Host Pool Name> -SsoadfsAuthority $ADFSAuthority -SsoClientId $WvdWebAppAppIDUri -SsoSecretType CertificateInKeyVault -SsoClientSecretKeyVaultPath <KeyVault secret URL>" } Write-Warning "Also remember to set tag on the secret in Azure Key Vault to the subscription id of the host pool" Add-Member -InputObject $return_object -NotePropertyName SSOADFSAuthority -NotePropertyValue $ADFSAuthority | Out-Null Add-Member -InputObject $return_object -NotePropertyName SSOClientId -NotePropertyValue $WvdWebAppAppIDUri | Out-Null return $return_object # SIG # Begin signature block # MIIjlgYJKoZIhvcNAQcCoIIjhzCCI4MCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBL3WuzkGg8yN3q # YCoLcTemMy+8gpOClvBI1YbuHKNlzqCCDYUwggYDMIID66ADAgECAhMzAAABiK9S # 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 # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJnj # 7skVqopty8fHcAoGwkzK5J5NDFsnPMTw6tR9G3uaMEIGCisGAQQBgjcCAQwxNDAy # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20wDQYJKoZIhvcNAQEBBQAEggEAhy49nmKrlHSBIKAQwZqhAH4CK2BF9NGofEcK # DfaFQIQEsHPb2S/44jbjntPM5Jfja80vp2Avrt12cL6IW1RtbJcKPEtpq0vNbPBS # h1K/vL7TTJYzwa4l27FuqCEVqQCMDL8mLmIgoKk4PYS13clEou1X6N26VOMkRELJ # r+rCczKY2NXZkpgcRrN28qwB+e34FYrR8l/BQ9v4oaziGCThAeUermZOEOX4ZDKj # v3ybvKT0t6mYF85RtUoAUQJF9tIjTnu1scPS9ld5PtAz5cItqIBqyYpupwggQFQc # ZxMqssg9/DYGgM9fSmgD9fSX9fGYmawpA0W2PBHcu8O8vYyiM6GCEvEwghLtBgor # BgEEAYI3AwMBMYIS3TCCEtkGCSqGSIb3DQEHAqCCEsowghLGAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE # WQoDATAxMA0GCWCGSAFlAwQCAQUABCAPnicALt0z3HRawo6lXXSCLc2NftBg+m6j # NOyDWxOB3wIGX7vTt2YBGBMyMDIwMTIwOTEyMjYzNi42ODVaMASAAgH0oIHUpIHR # MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL # EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh # bGVzIFRTUyBFU046Nzg4MC1FMzkwLTgwMTQxJTAjBgNVBAMTHE1pY3Jvc29mdCBU # aW1lLVN0YW1wIFNlcnZpY2Wggg5EMIIE9TCCA92gAwIBAgITMwAAASigDoHhNtVP # wgAAAAABKDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMDAeFw0xOTEyMTkwMTE1MDBaFw0yMTAzMTcwMTE1MDBaMIHOMQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg # T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046 # Nzg4MC1FMzkwLTgwMTQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl # cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdkbHW91Tbhj7N # vw4KXPYLe+yxtCT5A+FVk5RCS5Ks50yZfkaGX4jsDeolnz7uJP5I/J8GO6by7NTr # AcuPeMrrIOKxy8BzVCT7cNU3OeDDi4HXKLAODcZIu93w8qlsA7YznZOh+5DXMwT6 # gAw+gffKLe+/8EgAgSSMZvagFLnarkuX3MwhdPvmllGrw7uOlN3L+hxIyHVdmXSU # 1CoOFlCHU2DEFyNPNvqkrOOVgWY3CvfP7SH8fLqvKvJLFhffs1IxkxYjGih4Z+3E # gqBI+xNbVZltPCEqUuu/FhT9vgNDkMGlnCSjQAivifi2uy89mxrqQonThs+Vw3sH # NZQ/Zyz3AgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUhOLyg/F+tTeb1AHDTnR/UATL # pvIwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBL # oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr # BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU # aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAK # BggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAi19mRxiFC4A5P4nHB1lMsIw8 # gLAR5YZJrZgaeJZXcC93TaNG12WR4kmQfnNis7z6mOuAZRAo0vz6rq9pvVGk9TdA # XlKMER0E/PMHc3feIGKil5iw21UfMnlYAZHD/yYlVm13UM3M9REx4Fq4frswPAcF # IAGhycPp12HHCLg4DyTNVE3jZfUeTr3/us0dhOWSOA6yKr0uIx+ELKDD059uwIze # 1WbeGpqEcTCxHEAEu7z09SVFGkRaRR5pFGFZZ9WDLMP//+vevGkb8t3JgpUuOLsZ # JGiC24YdYdPXo2Yx4axJ/pPTHFZFormO9uIyf+e7cpTOwP48yFjY9RfFZYZMsjCC # 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 # U046Nzg4MC1FMzkwLTgwMTQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w # IFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVADE9SxvygBI9F7Ii/Z+5sZl9Wn2boIGD # MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEF # BQACBQDjer/kMCIYDzIwMjAxMjA5MDcxNTQ4WhgPMjAyMDEyMTAwNzE1NDhaMHcw # PQYKKwYBBAGEWQoEATEvMC0wCgIFAON6v+QCAQAwCgIBAAICMMECAf8wBwIBAAIC # LiMwCgIFAON8EWQCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAK # MAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQBCxGBJXPw0 # 2Pgq0cOf+UlQkYlpxyCEG3LwXuTazvcO1e7C0jeoG1ic7oVMBEjqDQ19xd8NGvkg # xTN5pb6Vp2L+YsYHGU0sbPjN82a4tKY1Fnqwb8Zf4PJ/lfy5ntqqUG9+ryLDTg+E # mSVzSOdMGVPxUGVxh8TUblVv6bfmWnGyXjGCAw0wggMJAgEBMIGTMHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABKKAOgeE21U/CAAAAAAEoMA0GCWCG # SAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZI # hvcNAQkEMSIEILeGa3VYgmLmgWf3WTv52NL0RcqlJARkbmQaJJMCVL3lMIH6Bgsq # hkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgvEVqi68FUnfv3BsQ3wakuG9bT14aDxaw # uteb1dboFNowgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAIT # MwAAASigDoHhNtVPwgAAAAABKDAiBCD1rItT7sAp4xE6hWudjrvp+0vVSyA3sb// # +eKYkV40qjANBgkqhkiG9w0BAQsFAASCAQA021T1C1gyVImsYWNE5DfsxPDu5ghm # qn8pfI3bLyQ/jpJLlt6qOZOzoeuWj1aghPxil/0bQkS1tf8yg+aabRnyMlTCNRuY # TGWxuqnRGAzTXEzvrwbcua5RmgCiyLHsqkp5OOpwemtkDOJsqFVlKeNzG3V7e7ct # njjmEvb7jr83jpeHoerfsni8L5Xih6ecrQhuCmUFcaQcn0CzJNwLcc5yMnxmWxS5 # gClTiHAlQq42i0kC96yzEkM7VLK9HPrT95VF11ISxpY3Wt25AVbgR0R+kx1/kmMg # oQN73Jk5Kt7TdBLjEZfvWQr1+/f7573X3W22cjD3O2rpgwR5potVMpWx # SIG # End signature block |