Private/Add-ADFSTkSPRelyingPartyTrust.ps1

function Add-ADFSTkSPRelyingPartyTrust {
    param (
        [Parameter(Mandatory = $true,
            Position = 0)]
        $sp
    )
    
    $Continue = $true
    
    ### EntityId
    $entityID = $sp.entityID

    $rpParams = @{
        Identifier                           = $sp.entityID
        EncryptionCertificateRevocationCheck = 'None'
        SigningCertificateRevocationCheck    = 'None'
        ClaimsProviderName                   = @("Active Directory")
        ErrorAction                          = 'Stop'
        SignatureAlgorithm                   = Get-ADFSTkSecureHashAlgorithm -EntityId $entityID -CertificateSignatureAlgorithm $SigningCertificate.SignatureAlgorithm.Value
        SamlResponseSignature                = Get-ADFSTkSamlResponseSignature -EntityId $entityID
    }

    Write-ADFSTkLog (Get-ADFSTkLanguageText addRPAddingRP -f $entityId) -EntryType Information -EventID 41
     
    ### Name, DisplayName
    $Name = (Split-Path $sp.entityID -NoQualifier).TrimStart('/') -split '/' | select -First 1


    #region Token Encryption Certificate
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPGettingEncryptionert)
    
    $CertificateString = ($sp.SPSSODescriptor.KeyDescriptor | ? use -eq "encryption"  | select -ExpandProperty KeyInfo).X509Data.X509Certificate
    
    if ($CertificateString -eq $null) {
        #Check if any certificates without 'use'. Should we use this?
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPEncryptionCertNotFound)
        
        $CertificateString = ($sp.SPSSODescriptor.KeyDescriptor | ? use -ne "signing"  | select -ExpandProperty KeyInfo).X509Data.X509Certificate #or shoud 'use' not be present?
    }
    
    if ($CertificateString -ne $null) {
        $rpParams.EncryptionCertificate = $null
        try {
            #May be more certificates!
            #If more than one, choose the one with furthest end date.

            $CertificateString | % {
                Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPConvertingEncrytionCert)
                $EncryptionCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
    
                $CertificateBytes = [system.Text.Encoding]::UTF8.GetBytes($_)
                $EncryptionCertificate.Import($CertificateBytes)
                
                
                if ($rpParams.EncryptionCertificate -eq $null) {
                    $rpParams.EncryptionCertificate = $EncryptionCertificate
                }
                elseif ($rpParams.EncryptionCertificate.NotAfter -lt $EncryptionCertificate.NotAfter) {
                    $rpParams.EncryptionCertificate = $EncryptionCertificate
                }
                Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPConvertionEncryptionCertDone)
            }

            if ($CertificateString -is [Object[]]) {
                #Just for logging!
                Write-ADFSTkLog (Get-ADFSTkLanguageText addRPMultipleEncryptionCertsFound -f $EncryptionCertificate.Thumbprint)  -EntryType Warning -EventID 30
            }
        }
        catch {
            Write-ADFSTkLog (Get-ADFSTkLanguageText addRPCouldNotImportEncrytionCert) -EntryType Error -EventID 21
            $Continue = $false
        }
    }
    #endregion

    #region Token Signing Certificate

    #Add all signing certificates if there are more than one
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPGetSigningCert)
    
    #$rpParams.SignatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
    
    $CertificateString = ($sp.SPSSODescriptor.KeyDescriptor | ? use -eq "signing"  | select -ExpandProperty KeyInfo).X509Data.X509Certificate
    if ($CertificateString -eq $null) {
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPSigningCertNotFound)
        $CertificateString = ($sp.SPSSODescriptor.KeyDescriptor | ? use -ne "encryption"  | select -ExpandProperty KeyInfo).X509Data.X509Certificate #or shoud 'use' not be present?
    }
    
    if ($CertificateString -ne $null) {
        #foreach insted create $SigningCertificates array
        try {
            $rpParams.RequestSigningCertificate = @()

            $CertificateString | % {

                Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPConvertingSigningCert)

                $CertificateBytes = [system.Text.Encoding]::UTF8.GetBytes($_)
                
                $SigningCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2                
                $SigningCertificate.Import($CertificateBytes)

                $rpParams.RequestSigningCertificate += $SigningCertificate

                #if ($SigningCertificate.SignatureAlgorithm.Value -eq '1.2.840.113549.1.1.11') #Check if Signature Algorithm is SHA256
                #{
                # $rpParams.SignatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
                #}
            }
            
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPConvertionSigningCertDone)
        }
        catch {
            Write-ADFSTkLog (Get-ADFSTkLanguageText addRPCouldNotImportSigningCert) -EntryType Error -EventID 22
            $Continue = $false
        }
    }
    #endregion

    #region Get SamlEndpoints
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPGetSamlEndpoints)
    $rpParams.SamlEndpoint = @()
    $rpParams.SamlEndpoint += $sp.SPSSODescriptor.AssertionConsumerService | % {
        if ($_.Binding -eq "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST") {  
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPHTTPPostFound)
            New-ADFSSamlEndpoint -Binding POST -Protocol SAMLAssertionConsumer -Uri $_.Location -Index $_.index 
        }
        elseif ($_.Binding -eq "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact") {
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPHTTPArtifactFound)
            New-ADFSSamlEndpoint -Binding Artifact -Protocol SAMLAssertionConsumer -Uri $_.Location -Index $_.index 
        }
        elseif ($_.Binding -eq "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect") {
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPHTTPRedirectFound)
            New-ADFSSamlEndpoint -Binding Redirect -Protocol SAMLAssertionConsumer -Uri $_.Location -Index $_.index
        }
        else {
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPUnhandledEndpointFound -f $_.Binding, $entityID)
        }
    } 

    if ($rpParams.SamlEndpoint.Count -eq 0) {
        Write-ADFSTkLog (Get-ADFSTkLanguageText addRPNoSamlEndpointsFound) -EntryType Error -EventID 23
        $Continue = $false
    }
    #endregion

    #region Get LogoutEndpoints
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPGetLogoutEndpoints) 
    $rpParams.SamlEndpoint += $sp.SPSSODescriptor.SingleLogoutService | % {
        if ($_.Binding -eq "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST") {  
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPLogoutPostFound)
            New-ADFSSamlEndpoint -Binding POST -Protocol SAMLLogout -ResponseUri $_.Location -Uri ("https://{0}/adfs/ls/?wa=wsignout1.0" -f $Settings.configuration.staticValues.ADFSExternalDNS)
        }
        elseif ($_.Binding -eq "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect") {
            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPLogoutRedirectFound)
            New-ADFSSamlEndpoint -Binding Redirect -Protocol SAMLLogout -ResponseUri $_.Location -Uri ("https://{0}/adfs/ls/?wa=wsignout1.0" -f $Settings.configuration.staticValues.ADFSExternalDNS)
        }
    } 
    #endregion

    #region Get Issuance Transform Rules from Entity Categories
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPGetEntityCategories)
    $EntityCategories = @()
    $EntityCategories += $sp.Extensions.EntityAttributes.Attribute | ? Name -eq "http://macedir.org/entity-category" | select -ExpandProperty AttributeValue | % {
        if ($_ -is [string]) {
            $_
        }
        elseif ($_ -is [System.Xml.XmlElement]) {
            $_."#text"
        }
    }
    
    Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPFollowingECFound -f ($EntityCategories -join ','))

    if ($ForcedEntityCategories) {
        $EntityCategories += $ForcedEntityCategories
        Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPAddedForcedEC -f ($ForcedEntityCategories -join ','))
    }

    $rpParams.IssuanceTransformRules = Get-ADFSTkIssuanceTransformRules $EntityCategories -EntityId $entityID `
        -RequestedAttribute $sp.SPSSODescriptor.AttributeConsumingService.RequestedAttribute `
        -RegistrationAuthority $sp.Extensions.RegistrationInfo.registrationAuthority `
        -NameIdFormat $sp.SPSSODescriptor.NameIDFormat
    #endregion

    #region Add MFA Access Policy and extra rules if needed

    $mfaRules = Get-ADFSTkMFAConfiguration -EntityId $entityID

    if ([string]::IsNullOrEmpty($mfaRules)) {
        $rpParams.IssuanceAuthorizationRules = Get-ADFSTkIssuanceAuthorizationRules -EntityId $entityID
    }
    else {
        $rpParams.AccessControlPolicyName = 'ADFSTk:Permit everyone and force MFA'
        $rpParams.IssuanceTransformRules += $mfaRules
    }
    #endregion

    if ((Get-ADFSRelyingPartyTrust -Identifier $entityID) -eq $null) {
        $NamePrefix = $Settings.configuration.MetadataPrefix 
        $Sep = $Settings.configuration.MetadataPrefixSeparator      
        $NameWithPrefix = "$NamePrefix$Sep$Name"

        if ((Get-ADFSRelyingPartyTrust -Name $NameWithPrefix) -ne $null) {
            $n = 1
            Do {
                $n++
                $NameWithPrefix = "$NamePrefix$Sep$Name ($n)"
            }
            Until ((Get-ADFSRelyingPartyTrust -Name $NameWithPrefix) -eq $null)

            Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPRPAlreadyExistsChangingNameTo -f $NameWithPrefix)
        }

        $rpParams.Name = $NameWithPrefix
        
        if ($Continue) {
            try {
                Write-ADFSTkVerboseLog (Get-ADFSTkLanguageText addRPAddingRP -f $entityID)
                
                # Invoking the following command leverages 'splatting' for passing the switches for commands
                # for details, see: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting?view=powershell-6
                # (that's what it's @rpParams and not $rpParams)

                Add-ADFSRelyingPartyTrust @rpParams

                Write-ADFSTkLog (Get-ADFSTkLanguageText addRPSuccefullyAddedRP -f $entityId) -EntryType Information -EventID 42
                Add-ADFSTkEntityHash -EntityID $entityId
            }
            catch {
                Write-ADFSTkLog (Get-ADFSTkLanguageText addRPCouldNotAddRP -f $entityId, $_) -EntryType Error -EventID 24
                Add-ADFSTkEntityHash -EntityID $entityId
            }
        }
        else {
            #There were some error with certificate or endpoints with this SP. Let's only try again if it changes...
            Add-ADFSTkEntityHash -EntityID $entityId
        }
    }
    else {
        Write-ADFSTkLog (Get-ADFSTkLanguageText addRPRPAlreadyExists -f $entityId) -EntryType Warning -EventID 25
    }                
}
# SIG # Begin signature block
# MIId/gYJKoZIhvcNAQcCoIId7zCCHesCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD2Cb79qcxZMrUi
# lQf/yEzVZsS1IVtbbVZFAaYJxjVkZKCCGKwwggR9MIIDZaADAgECAgMb5xUwDQYJ
# KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRk
# eSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZp
# Y2F0aW9uIEF1dGhvcml0eTAeFw0xNDAxMDEwNzAwMDBaFw0zMTA1MzAwNzAwMDBa
# MIGDMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2Nv
# dHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xMTAvBgNVBAMTKEdv
# IERhZGR5IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/cWII8fpZNPcbyRij94BJWOkigxOmxSBD
# ATuE8eaFSZ8n6vaEG06gtNtwmMcyAbEFPgdO7vT6Ty9ZMCLnqxlWa+KAB/zzFnWA
# OVF75fk1tnROqY2CE+S2P6kDg/qivooVan/eC8O2GRQFyurDqASUO0Z8Mg3zAGYi
# yI1pbTaMERi307IcYLQ4+gKMztPdRgfeCj7rXXzIfPuwK1OkkmJpUSUFYRpEgYws
# qUOWI9+sOoGaDinFHKnpXR62np4wCjnO8YiA+0tdzDLshWJDJTQCVicBkbQ7cCo/
# brHonIgBfZ/U+dtTbWCdvyznWKu4X0b8zsQbAzwJ60kxXGlGs+BHAgMBAAGjggEX
# MIIBEzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
# OpqFBxBnKLbv9r0FQW4gwZTaD94wHwYDVR0jBBgwFoAU0sSw0pHUTBFxs2HLPaH+
# 3ahq1OMwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5n
# b2RhZGR5LmNvbS8wMgYDVR0fBCswKTAnoCWgI4YhaHR0cDovL2NybC5nb2RhZGR5
# LmNvbS9nZHJvb3QuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIB
# FiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3
# DQEBCwUAA4IBAQBZC1O9koYRpyR77Vsxzx0fbHDFuG6+Trv2vpdQ4TB/uihcYpTC
# 434z9/tCdoXblRyMIlh1CQyIZWc5ChYJxaA4l6TFI5M/tBimAQZEkeOnaSe0WiV/
# Orcyzd2E/yo4KTOk3Weyhf6hiCAcUInI3Cr2QgM3TOaI39WvJPKxw9/MtezgmV63
# SVQgPJQYDMccUhhJpG3hs1gLydjs2a4cMo4ocA3i/qYXnoQPvVdws1rpH6CGU7vv
# fP9pC+BIw7eTC8gKVMSsXRRnN2zKpS8xCDeqbm+MvJviV10kga+Xl5yErWysN0xm
# 82GRESDkvjCfeqQpCbDhNF9kdxhAUd+MMKavMIIE0DCCA7igAwIBAgIBBzANBgkq
# hkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzAR
# BgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMTEw
# LwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy
# MB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3MDAwMFowgbQxCzAJBgNVBAYTAlVT
# MRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQK
# ExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFk
# ZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzDBNliF44v/z5lz4/OYuY8UhzaFkVL
# Vat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOvK/6AYZ15V8TPLvQ/MDxdR/yaFrzD
# N5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23ecSZHjzhHU9FGHbTj3ADqRay9vHHZ
# qm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HYpDNO6rPWJ0+tJYqlxvTV0KaudAVk
# V4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7neTOvDCAHf+jfBDnCaQJsY1L6d8Eb
# yHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMBAAGjggEaMIIBFjAPBgNVHRMBAf8E
# BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUQMK9J47MNIMwojPX+2yz
# 8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv9r0FQW4gwZTaD94wNAYIKwYBBQUH
# AQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wNQYD
# VR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZHJvb3QtZzIu
# Y3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVodHRwczovL2Nl
# cnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQAI
# fmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz91cxG7685C/b+LrTW+C05+Z5Yg4M
# otdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2RJ17LJ3lXubvDGGqv+QqG+6EnriD
# fcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawiDsoXiWJYRBuriSUBAA/NxBti21G0
# 0w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11GIo/ikGQI31bS/6kA1ibRrLDYGCD
# +H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2xLXY2JtwE65/3YR8V3Idv7kaWKK2h
# Jn0KCacuBKONvPi8BDABMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TAN
# BgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg
# SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2Vy
# dCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAw
# MFoXDTMxMDEwNjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFC
# vQp1dU2UtAxQtSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in4
# 3Stwhd4CGPN4bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6
# t2HWc+xObTOKfF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0
# TXhG4wODMSlKXAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK0
# 2/xWVLwfoYervnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEA
# AaOCAbgwggG0MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB
# /wQMMAoGCCsGAQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYB
# BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0
# tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNq
# erwwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3No
# YTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVz
# dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5n
# Rv1CclF0CiNHo6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2
# uHDPYuj1UUp4eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmD
# gvoaU+2QzI2hF3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QR
# cucbZEnYIpp1FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4b
# TxMd90oNcX6Xt/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aK
# LzCCBRwwggQEoAMCAQICCGXB0JJJvDvXMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD
# VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa
# MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0
# cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj
# dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTIxMDExOTE4MzczNloX
# DTIyMDMwODE4NTgwMFowXjELMAkGA1UEBhMCQ0ExEDAOBgNVBAgTB09udGFyaW8x
# DzANBgNVBAcTBk90dGF3YTEVMBMGA1UEChMMQ0FOQVJJRSBJbmMuMRUwEwYDVQQD
# EwxDQU5BUklFIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ
# hfCjFqiTmN1uLoySixnwaOjf/ZAL9P6SvjlCaBA2mutoorEgnzUP8HnOIcvMRgEM
# PmpaZ8egM93Bmx9d41xoarsQpCN3DhYOo+b3fWnPucVtpxbul2OFePv63mw/uvr+
# dqkv4b/f3Tg+ilQbpsNonbvh9MKEFv8Pn9koj0ySV+qxz34PxTVAe6g//pel3/3i
# 9fqilCnIEcx4zg/+NKBeOWROSs4oXo3IvBjVrunmz+YuieSr78TqIE6hD8JF2q1w
# KwfMB3+x7dEXZAus9WtIU/qITATtEfO9QAgrrYL4F1MLN+osSp8my5eCOjnLTQc4
# 7q574V3zQhsIHW7yBXLdAgMBAAGjggGFMIIBgTAMBgNVHRMBAf8EAjAAMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDA1BgNVHR8ELjAsMCqgKKAm
# hiRodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkaWcyczUtNi5jcmwwXQYDVR0gBFYw
# VDBIBgtghkgBhv1tAQcXAjA5MDcGCCsGAQUFBwIBFitodHRwOi8vY2VydGlmaWNh
# dGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMAgGBmeBDAEEATB2BggrBgEFBQcB
# AQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggr
# BgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0
# b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAd
# BgNVHQ4EFgQUUPnMg2nmYS8l7rmax3weVkrgz5AwDQYJKoZIhvcNAQELBQADggEB
# AGabJLu09gdYHt7ZMbpJ4048ZIiXwVLE/HNcnApTghNaHnSSiMI2xTsmbrM/lYsm
# pwFuws1c2fMBvyDRgkzR/4+RIjoQJpLrHy1QABYlWAIKMqdFmfqty0QApgIkGN2+
# scMxKMWJGND8qp3KM+5C8TNTsO0gPVfdaarX2TmLM6yIQcgxD8YZMd0mqdR7rcCe
# bgMeAdHLYPQu/HM0Cj3qtzFx/CZzz93CAlh8Dx5woqeNJixQMLK28MhU8y6NSN5o
# KnD/8EESudRzXyoowZ2N4YJzyye5UL9pxhniDKs444w1r5XcjQYDo11G8Y4up4XW
# 1cFtLNulHYcKhAnQ7XHswxMwggUxMIIEGaADAgECAhAKoSXW1jIbfkHkBdo2l8IV
# MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD
# ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcx
# MjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX
# BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIg
# QXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
# DwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2
# oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYa
# VX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvg
# zyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM/fDqR9mIUF79Zm5W
# YScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfxFwbvPc3W
# Te8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4E
# FgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGL
# p6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYD
# VR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEG
# A1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYDVR0gBEkwRzA4Bgpg
# hkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv
# bS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v
# 3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+Cei
# Zr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8
# ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3CzddWThZN+tpJn+1Nh
# iaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6HUSHkWGCb
# ugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JK
# ldj1po5SMYIEqDCCBKQCAQEwgcEwgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdB
# cml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNv
# bSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9z
# aXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0
# aG9yaXR5IC0gRzICCGXB0JJJvDvXMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQB
# gjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK
# KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEICXcVdfA
# Oa9ogtyILutPyj1rs7xxV3IVvQoGZq98rqH5MA0GCSqGSIb3DQEBAQUABIIBAJjP
# pyXd0cLBqIyiWqgjoOU9yQyNrtynPWSF0rLF3WehqBUxWnWNOH8bJ2iV6aQMUKsm
# cVMEzS6PHU5UjG9+RYeqwOgwK65iEjWchbY1+hHlS/trWFmmo0/jWEDuhnw7w9JH
# 0dEdfi5g/ijnJglX57Ys/mrKc8yBbc2+P2IdTjItioBXGtuqc5HsXyDWoByVb6LS
# MJHK7tJyy0yFFXciZY0IhrkLTZq94ebVHwqphQU7xXJ43ABPafsro2MGZAvrkH4+
# FqocbhUXEwi9a7SkazcfVCwUDJ/BL3odgKunq2O89372ADXtbMC+RA35/lzgd8WZ
# ThsoZWiRxHd7FsTM/Q2hggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYw
# cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk
# IElEIFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQME
# AgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
# DTIyMDIwMTIyMTEwMVowLwYJKoZIhvcNAQkEMSIEIKrSbBohiK0mTwy5sIz3JQ3K
# +5FDSPMoO+szpTLJtsj7MA0GCSqGSIb3DQEBAQUABIIBAFupoM7CgnidGnHKoVTY
# 48gCORpNhh/2s4/tIe/g8gPG4EMwLRFYrGYxnxFpxxTkGvnmWp27iMz9VFNE1UiE
# KqDWxJK/Wbg3xl3QB2PUpXkUQG7iGT1ndvFS+BN9mMKFyCslxwreI9Cho9w/rGvl
# NWb01tWAmkewuhA8DapMWPt2+thxn3KG/VA/SBuWSNaRnQNgmwAu+GjTtZu5SOx/
# oslyZ+w+rUlWjljuljETC4Wu8rPcRDEZnBC3XFugTbhGslmJKOZQlKH/ZVNOdPFC
# lIBdrihqtVbtT0oMU1rxAyFHssdznhjZ+90Bl8soW+8iTWJFnIvtmilP3pBWFJq9
# byA=
# SIG # End signature block