commands/Get-VITrustedCertificate.ps1

using module VMware.PowerCLI.VCenter.Types.CertificateManagement
using namespace VMware.VimAutomation.ViCore.Types.V1
using namespace VMware.VimAutomation.ViCore.Types.V1.Inventory
using namespace System.Security.Cryptography.X509Certificates
using namespace VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet
using namespace VMware.VimAutomation.Sdk.Types.V1
using namespace VMware.VimAutomation.ViCore.Types.V1

. (Join-Path $PSScriptRoot "../utils/Connection.ps1")
. (Join-Path $PSScriptRoot "../utils/Report-CommandUsage.ps1")
. (Join-Path $PSScriptRoot "../utils/Get-ConnectionUid.ps1")
. (Join-Path $PSScriptRoot "../types/builders/New-TrustedCertificateInfo.ps1")

<#
.SYNOPSIS
 
This cmdlet retrieves information about the trusted certificates in a vCenter Server environment.
 
.DESCRIPTION
 
This cmdlet retrieves information about the certificates trusted by a vCenter Server instance and/or its connected ESXi hosts.
The returned object is a pair of the certificate and the vCenter Server or ESXi entity that trusts the certificate. The cmdlet might return more than one certificate/entity object.
If you pass no parameters, the command returns all certificate/entity pairs trusted by the vCenter Server instance and the connected ESXi hosts.
 
.PARAMETER Id
Specifies the UIDs of the trusted certificates you want to retrieve.
 
Note: If you specify multiple UIDs, the system returns a certificate/entity object for each UID.
 
.PARAMETER VCenterOnly
Specifies that the result includes only the certificates trusted by the vCenter Server instance.
 
.PARAMETER EsxOnly
Specifies that the result includes only the certificates trusted by the connected ESXi hosts.
 
.PARAMETER VMHost
Specifies one or more ESXi hosts whose trusted certificates you want to retrieve.
 
.PARAMETER Server
Specifies the vCenter Server systems on which you want to run the cmdlet.
If no value is provided or $null value is passed to this parameter, the command runs on the default server.
For more information about default servers, see the description of the Connect-VIServer cmdlet.
 
.EXAMPLE
PS C:\> Get-VITrustedCertificate
 
Retrieves the certificates trusted by the vCenter Server instance and the connected ESXi hosts.
 
.EXAMPLE
PS C:\> Get-VITrustedCertificate -VCenterOnly
 
Retrieves the certificates trusted by the vCenter Server system only.
 
.EXAMPLE
PS C:\> Get-VITrustedCertificate -EsxOnly
 
Retrieves the certificates trusted by the connected ESXi hosts only.
 
.EXAMPLE
PS C:\> Get-VITrustedCertificate -VMHost 'MyHost'
 
Retrieves the certificates trusted by the specified ESXi host(s) only.
 
.OUTPUTS
Zero or more TrustedCertificateInfo objects
 
.LINK
 
https://developer.vmware.com/docs/powercli/latest/vmware.powercli.vcenter/commands/get-vitrustedcertificate
 
 
#>

function Get-VITrustedCertificate {
    [CmdletBinding(
       ConfirmImpact = "None",
       DefaultParameterSetName = "Default")]
    [OutputType([TrustedCertificateInfo])]
    Param (
      [Parameter(
         Mandatory = $true,
         ParameterSetName = "ById"
      )]
      [string[]]
      $Id,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "VCenterOnly"
        )]
        [switch]
        $VCenterOnly,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "EsxOnly"
        )]
        [switch]
        $EsxOnly,

        [Parameter(
            Mandatory = $true,
            Position = 0,
            ParameterSetName = "PerEsx",
            ValueFromPipeline = $true
        )]
        [ObnArgumentTransformation([VMHost])]
        [VMHost[]]
        $VMHost,

        [Parameter(ParameterSetName = "Default")]
        [Parameter(ParameterSetName = "VCenterOnly")]
        [Parameter(ParameterSetName = "EsxOnly")]
        [Parameter(ParameterSetName = "PerEsx")]
        [ObnArgumentTransformation([VIServer], Critical = $true)]
        [VIServer]
        $Server
    )

   Begin {
      Report-CommandUsage $MyInvocation
      
      if ($Id) {
         # ById parameter set
         $Id | % {
            if (![DistinguishedName]::IsDistinguishedName($_)) {
               Write-PowerCLIError `
                  -ErrorObject "Id '$($_)' is invalid trusted certificate Uid." `
                  -ErrorId "PowerCLI_VITrustedCertificate_InvalidUid" `
                  -Terminating
            }

            $parentDn = [DistinguishedName]::GetParentDn($_)
            if ($parentDn -eq $null) {
               Write-PowerCLIError `
                  -ErrorObject "Id '$($_)' is invalid trusted certificate Uid." `
                  -ErrorId "PowerCLI_VITrustedCertificate_InvalidTrustedCertificateUid_TopLevelUid" `
                  -Terminating
            }
            $parentDnKey = [DistinguishedName]::GetRdnKey($parentDn)
            if ($parentDnKey -ne [DnKeyListSdk]::VIServer -and `
               $parentDnKey -ne [DnKeyListViCore]::VMHost) {
                  Write-PowerCLIError `
                     -ErrorObject "Id '$($_)' is invalid trusted certificate Uid." `
                     -ErrorId "PowerCLI_VITrustedCertificate_InvalidTrustedCertificateUid_NotVmHostOrViServerParent" `
                     -Terminating
               }
         }
      } else {
         # All other parameter sets
         # Handle Server obn first
         if($Server) {
            $resolvedServer = Resolve-ObjectByName -Object $Server `
               -Type ([VIServer]) `
               -OneObjectExpected

            $Server = [VIServer] $resolvedServer
         }

         $activeServer = GetActiveServer($Server)
         ValidateApiVersionSupported -server $activeServer -major 6 -minor 7 -ErrorAction:Stop

         # Handle OBN
         if($VMHost) {
            $resolvedVMHosts = Resolve-ObjectByName -Object $VMHost `
               -Type ([VMHost]) `
               -CollectorCmdlet 'Get-VMHost' `
               -OneOrMoreObjectsExpected `
               -Server $activeServer

            $VMHost = [VMHost[]] $resolvedVMHosts
         }
      }
   }

   Process {
      if ($Id) {
         # ById parameter set
         $foundIds = [System.Collections.ArrayList]::new()

         $Id | ? {
            [DistinguishedName]::GetRdnKey([DistinguishedName]::GetParentDn($_)) -eq [DnKeyListSdk]::VIServer
         } | % {
            $currentServer = $_ | Get-ConnectionUid | Get-ServerByUid
            $currentApiServer = GetApiServer($currentServer)

            ValidateApiVersionSupported -server $currentServer -major 6 -minor 7 -ErrorAction:Stop
            $result = $null
            $result = [DistinguishedName]::GetRdnValue($_) | Get-ViCenterTrustedCertificate `
               -ApiServer $currentApiServer `
               -Server $currentServer
            if($result) {
               $foundIds.Add($result.Uid) | Out-Null
               $result
            }
         }

         $vmhostIds = $id | % {
            [DistinguishedName]::GetParentDn($_)
         } | ? {
            [DistinguishedName]::GetRdnKey($_) -eq [DnKeyListViCore]::VMHost
         } | Get-Unique

         if ($vmhostIds) {
            $vmhostIds | % {
               $vmhostServer = $_ | Get-ConnectionUid | Get-ServerByUid
               Get-VMHost -Id $_ -Server $vmhostServer | `
                  Get-VMHostTrustedCertificate -Server $vmhostServer | ? {
                     $Id -contains $_.Uid
                  } | % {
                     $foundIds.Add($_.Uid) | Out-Null
                     $_
                  }
            }
         }

         foreach ($currentId in $Id) {
            if (-not $foundIds.Contains($currentId)) {
               Write-PowerCLIError `
                  -ErrorObject "[VITrustedCertificate] with Uid '$currentId' not found." `
                  -ErrorId "PowerCLI_VITrustedCertificate_IdNotFound" `
                  -ErrorCategory ([System.Management.Automation.ErrorCategory]::ObjectNotFound)
            }
         }
      } else {
         # all other parameter sets

         # Validate all objects are from the same server
         if($VMHost) {
            $VMHost | ValidateSameServer -ExpectedServer $activeServer
         }

         $collectVc = $PsCmdlet.ParameterSetName -eq 'Default' -or `
            ($PsCmdlet.ParameterSetName -eq 'VCenterOnly' -and $VCenterOnly.ToBool())

         $collectEsx = $PsCmdlet.ParameterSetName -eq 'Default' -or `
            ($PsCmdlet.ParameterSetName -eq 'EsxOnly' -and $EsxOnly.ToBool())

         if ($collectVc) {
            $apiServer = GetApiServer($activeServer)
            
            try {
               $trustedChainIds =
                  Invoke-ListCertificateManagementTrustedRootChains -Server $apiServer -ErrorAction:Stop | ForEach-Object { $_.chain }

               foreach($trustedChainId in $trustedChainIds) {
                  $trustedChainId | Get-ViCenterTrustedCertificate -ApiServer $apiServer -Server $activeServer | Write-Output
               }
            } catch {
               Write-PowerCLIError `
                  -ErrorObject $_ `
                  -ErrorId "PowerCLI_VITrustedCertificate_FailedToListVcTrustChains"
            }
         }

         if ($collectEsx) {
            $tempVMHost = Get-VMHost -Server $activeServer
            if ($tempVMHost) {
               $VMHost = $tempVMHost
            }
         }

         if ($VMHost) {
            foreach ($currentVMHost in $VMHost) {
               $currentVMHost | Get-VMHostTrustedCertificate -Server $activeServer | Write-Output
            }
         }
      }
   }
}

function Get-VMHostTrustedCertificate {
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
      [VMHost]
      $VMHost,

      [Parameter(Mandatory = $true)]
      [VIServer]
      $Server
   )

   try {
      $certificateManager = Get-View $VMHost.ExtensionData.ConfigManager.CertificateManager -Server $Server
      $trustedCertificates = $certificateManager.ListCACertificates()
      foreach($trustedCertificate in $trustedCertificates) {
         try {
            $certificate = ConvertTo-X509Certificate -CertificatePEM $trustedCertificate
            New-TrustedCertificateInfo `
               -IdWithinStore $certificate.Thumbprint `
               -Certificate $certificate `
               -TargetESXi $VMHost | Write-Output
         } catch {
            Write-PowerCLIError `
               -ErrorObject $_ `
               -ErrorId "PowerCLI_VITrustedCertificate_FailedEsxParseCertificate"
         }
      }
   } catch {
      Write-PowerCLIError `
         -ErrorObject $_ `
         -ErrorId "PowerCLI_VITrustedCertificate_FailedToListEsxTrustChains"
   }
}

function Get-ViCenterTrustedCertificate {
   param(
      [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
      [string]
      $TrustedChainId,

      [Parameter(Mandatory = $true)]
      [PSTypeName('vSphereServerConfiguration')]
      $ApiServer,

      [Parameter(Mandatory = $true)]
      [VIServer]
      $Server
   )

   $trustedChain = $null
   try {
      $trustedChain = Invoke-GetChainCertificateManagementTrustedRootChains `
         -Chain $TrustedChainId `
         -Server $ApiServer `
         -ErrorAction:Stop
   } catch {
      $serverError = $_.Exception.ServerError

      if($serverError.error_type -ne "NOT_FOUND") {
         Write-PowerCLIError `
            -ErrorObject $_ `
            -ErrorId "PowerCLI_VITrustedCertificate_Invoke_GetChainCertificateManagementTrustedRootChains"
      }
   }

   if($trustedChain) {
      if($trustedChain.cert_chain.cert_chain.Length -gt 1) {
         throw "Unexpected API response - chain contains more than one entity."
      }
      $chain = $trustedChain.cert_chain.cert_chain[0]
      try {
         New-TrustedCertificateInfo `
            -IdWithinStore $TrustedChainId `
            -Certificate (Resolve-TrustChain -TrustChainPem $chain) `
            -TargetVC $Server | `
            Write-Output
      } catch {
         Write-PowerCLIError `
            -ErrorObject $_ `
            -ErrorId "PowerCLI_VITrustedCertificate_FailedVcParseCertificate"
      }
   }
}

# SIG # Begin signature block
# MIIexgYJKoZIhvcNAQcCoIIetzCCHrMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDXL8S5Vx8dqlXx
# 4tkIVzmK4JYtX7CTtSI0G2LgNhlXh6CCDdowggawMIIEmKADAgECAhAIrUCyYNKc
# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z
# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0
# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr
# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF
# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F
# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh
# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ
# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay
# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI
# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp
# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro
# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB
# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+
# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED
# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql
# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF
# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h
# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw
# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld
# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw
# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP
# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE
# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn
# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji
# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq
# yK+p/pQd52MbOoZWeE4wggciMIIFCqADAgECAhAOxvKydqFGoH0ObZNXteEIMA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjEwODEwMDAwMDAwWhcNMjMwODEw
# MjM1OTU5WjCBhzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQ
# BgNVBAcTCVBhbG8gQWx0bzEVMBMGA1UEChMMVk13YXJlLCBJbmMuMRUwEwYDVQQD
# EwxWTXdhcmUsIEluYy4xITAfBgkqhkiG9w0BCQEWEm5vcmVwbHlAdm13YXJlLmNv
# bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMD6lJG8OWkM12huIQpO
# /q9JnhhhW5UyW9if3/UnoFY3oqmp0JYX/ZrXogUHYXmbt2gk01zz2P5Z89mM4gqR
# bGYC2tx+Lez4GxVkyslVPI3PXYcYSaRp39JsF3yYifnp9R+ON8O3Gf5/4EaFmbeT
# ElDCFBfExPMqtSvPZDqekodzX+4SK1PIZxCyR3gml8R3/wzhb6Li0mG7l0evQUD0
# FQAbKJMlBk863apeX4ALFZtrnCpnMlOjRb85LsjV5Ku4OhxQi1jlf8wR+za9C3DU
# ki60/yiWPu+XXwEUqGInIihECBbp7hfFWrnCCaOgahsVpgz8kKg/XN4OFq7rbh4q
# 5IkTauqFhHaE7HKM5bbIBkZ+YJs2SYvu7aHjw4Z8aRjaIbXhI1G+NtaNY7kSRrE4
# fAyC2X2zV5i4a0AuAMM40C1Wm3gTaNtRTHnka/pbynUlFjP+KqAZhOniJg4AUfjX
# sG+PG1LH2+w/sfDl1A8liXSZU1qJtUs3wBQFoSGEaGBeDQIDAQABo4ICJTCCAiEw
# HwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFIhC+HL9
# QlvsWsztP/I5wYwdfCFNMB0GA1UdEQQWMBSBEm5vcmVwbHlAdm13YXJlLmNvbTAO
# BgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCB
# qjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3Rl
# ZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0
# dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu
# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQB
# MCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYI
# KwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
# LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEACQAYaQI6Nt2KgxdN
# 6qqfcHB33EZRSXkvs8O9iPZkdDjEx+2fgbBPLUvk9A7T8mRw7brbcJv4PLTYJDFo
# c5mlcmG7/5zwTOuIs2nBGXc/uxCnyW8p7kD4Y0JxPKEVQoIQ8lJS9Uy/hBjyakeV
# ef982JyzvDbOlLBy6AS3ZpXVkRY5y3Va+3v0R/0xJ+JRxUicQhiZRidq2TCiWEas
# d+tLL6jrKaBO+rmP52IM4eS9d4Yids7ogKEBAlJi0NbvuKO0CkgOlFjp1tOvD4sQ
# taHIMmqi40p4Tjyf/sY6yGjROXbMeeF1vlwbBAASPWpQuEIxrNHoVN30YfJyuOWj
# zdiJUTpeLn9XdjM3UlhfaHP+oIAKcmkd33c40SFRlQG9+P9Wlm7TcPxGU4wzXI8n
# Cw/h235jFlAAiWq9L2r7Un7YduqsheJVpGoXmRXJH0T2G2eNFS5/+2sLn98kN2Cn
# J7j6C242onjkZuGL2/+gqx8m5Jbpu9P4IAeTC1He/mX9j6XpIu+7uBoRVwuWD1i0
# N5SiUz7Lfnbr6Q1tHMXKDLFdwVKZos2AKEZhv4SU0WvenMJKDgkkhVeHPHbTahQf
# P1MetR8tdRs7uyTWAjPK5xf5DLEkXbMrUkpJ089fPvAGVHBcHRMqFA5egexOb6sj
# tKncUjJ1xAAtAExGdCh6VD2U5iYxghBCMIIQPgIBATB9MGkxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1
# c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEA7G
# 8rJ2oUagfQ5tk1e14QgwDQYJYIZIAWUDBAIBBQCggZYwGQYJKoZIhvcNAQkDMQwG
# CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwKgYKKwYB
# BAGCNwIBDDEcMBqhGIAWaHR0cDovL3d3dy52bXdhcmUuY29tLzAvBgkqhkiG9w0B
# CQQxIgQg4ZOliDAHc7tLrXE7MVzilakuj3XO/kcW9ikaRBRLP2QwDQYJKoZIhvcN
# AQEBBQAEggGApOrYcGjrHBw9eKf/a/V9lx7Op1O9Eb+MavTFWD5pcABYT5ZbDgu3
# VB47nytmnj8Te09TZppJq71KX7ZDSBZqCgxHv36OdRCTK9XyPAkEGBDVffuCdo+F
# NIMhCk6lAse+iqpVbaSg/w6wMEGPSZnJXPDnmUnu3mN2lSjAw2XLgtLSIUg+1Dx7
# HJ+v+DkImXjYJmi85NXMhTmuuAy0OmH3wBhDVEbpwxLrMGmk5rtQ66mYoLzxN/hX
# XgAaFErKSVRAEMHkIgs0BFJbaq0yaFiMfpfvjNDxTDO0kdMtCB/tnbJXgsNLO761
# leus/ccsTr8DMSWLrS+HBYrmi8ju8WVU3/2PHa3t1Kv3beFwg4IrbLIgPZS3QH04
# /54AuSl9/fFEq/pBZp/39lZGm9mJpZsbxj8RgxFWFnNBAAvGuUXAE/6INNerT2VZ
# QsSAyqM0ZsWFGeVhwINDesaHiiNzjy30aHtOiW1GwsocmRolzIyQpI4tbhhhjn0S
# oZ1GDT2uwIx9oYINfTCCDXkGCisGAQQBgjcDAwExgg1pMIINZQYJKoZIhvcNAQcC
# oIINVjCCDVICAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0BCRABBKBoBGYw
# ZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIG2Orhyru2gh0jMdHRSH
# Tso/JaUvrwmsHofmnpR5YgdcAhA+dmMvFJ9F6988XPXT69YvGA8yMDIxMTAwNDEx
# NTkyM1qgggo3MIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG
# 9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw
# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy
# IEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMx
# MDEwNjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZI
# hvcNAQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2U
# tAxQtSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4C
# GPN4bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xO
# bTOKfF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wOD
# MSlKXAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwf
# oYervnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgw
# ggG0MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoG
# CCsGAQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEW
# G2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4p
# rtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYD
# VR0fBGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNz
# dXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEy
# LWFzc3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0
# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2Vy
# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGlu
# Z0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0
# CiNHo6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1
# UUp4eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2Q
# zI2hF3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnY
# Ipp1FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oN
# cX6Xt/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBTEw
# ggQZoAMCAQICEAqhJdbWMht+QeQF2jaXwhUwDQYJKoZIhvcNAQELBQAwZTELMAkG
# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
# Z2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENB
# MB4XDTE2MDEwNzEyMDAwMFoXDTMxMDEwNzEyMDAwMFowcjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGlu
# ZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3QMu5LzY9/3am6
# gpnFOVQoV7YjSsQOB0UzURB90Pl9TWh+57ag9I2ziOSXv2MhkJi/E7xX08PhfgjW
# ahQAOPcuHjvuzKb2Mln+X2U/4Jvr40ZHBhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oi
# PhisEeTwmQNtO4V8CdPuXciaC1TjqAlxa+DPIhAPdc9xck4Krd9AOly3UeGheRTG
# TSQjMF287DxgaqwvB8z98OpH2YhQXv1mblZhJymJhFHmgudGUP2UKiyn5HU+upgP
# hH+fMRTWrdXyZMt7HgXQhBlyF/EXBu89zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2s
# rOlW/5MCAwEAAaOCAc4wggHKMB0GA1UdDgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1
# bjAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzASBgNVHRMBAf8ECDAG
# AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB5Bggr
# BgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
# bTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDov
# L2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6
# oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNybDBQBgNVHSAESTBHMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcC
# ARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sBwEwDQYJ
# KoZIhvcNAQELBQADggEBAHGVEulRh1Zpze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9
# xafDDiBCLK938ysfDCFaKrcFNB1qrpn4J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIl
# HsS6HHssIeLWWywUNUMEaLLbdQLgcseY1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8p
# ieV4H9YLFKWA1xJHcLN11ZOFk362kmf7U2GJqPVrlsD0WGkNfMgBsbkodbeZY4Ui
# jGHKeZR+WfyMD+NvtQEmtmyl7odRIeRYYJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8
# QkIoxhhWz0E0tmZdtnR79VYzIi8iNrJLokqV2PWmjlIxggKGMIICggIBATCBhjBy
# MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
# d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQg
# SUQgVGltZXN0YW1waW5nIENBAhANQkrgvjqI/2BAIc4UAPDdMA0GCWCGSAFlAwQC
# AQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUx
# DxcNMjExMDA0MTE1OTIzWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTh14Ko4ZG+
# 72vKFpG1qrSUpiSb8zAvBgkqhkiG9w0BCQQxIgQgNNcaKzR0dgjlflz+YJBnj7Cg
# Sk30YC5/eouKU//p560wNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgsxCQBrwK2YMH
# kVcp4EQDQVyD4ykrYU8mlkyNNXHs9akwDQYJKoZIhvcNAQEBBQAEggEAVjfby+S9
# jDW7aL6jo++5QNzoaTaYFqSudgAx/utiZsy0HjBsuAAFoKebz8VTrPx34wPsxd78
# EXCh2xaFkqTd6Nsa6/2VRjlM9jb4lvJEhGJ203eU0Bt6EfQ7v2e4znYbmqIUmbfo
# oyCLj0tADrYdWUeeTYWTyP0GLrzqf2ovj4zFl61/5teJbi8bS+AQ49ueKlEnZZKA
# P2R4922jJF7cWcWR0Xkzu6V9qkmv0mhIhvEZjQV0onsbc1aLHacwrapWmqCILi6r
# 4404N4TkQZos6WfL7MTimA7oKduNyhdBWcw6pM3AdScnR8qJ28xwGfFmI+ql/Vk3
# l9kWQESXY+7A3g==
# SIG # End signature block