Jaifroid a62d03cc19 Signed app packages for Kiwix JS Windows 1.6.0
Former-commit-id: ad9c9ac82aa9ca9def238b20bb56877fb5746d31 [formerly 252e6012d20658d3ed830b9395f33a4a4e686a7b [formerly e6cf52cd9d212002a665e6d3570fda45b6117d76]]
Former-commit-id: 3ee24a6857f318b12aa6b0de34c442f157cb9784
Former-commit-id: cd14773001222e707d68c4fde761bf3ca3026383
2021-07-29 10:03:59 +01:00

777 lines
32 KiB
PowerShell

#
# Add-AppxDevPackage.ps1 is a PowerShell script designed to install app
# packages created by Visual Studio for developers. To run this script from
# Explorer, right-click on its icon and choose "Run with PowerShell".
#
# Visual Studio supplies this script in the folder generated with its
# "Prepare Package" command. The same folder will also contain the app
# package (a .appx file), the signing certificate (a .cer file), and a
# "Dependencies" subfolder containing all the framework packages used by the
# app.
#
# This script simplifies installing these packages by automating the
# following functions:
# 1. Find the app package and signing certificate in the script directory
# 2. Prompt the user to acquire a developer license and to install the
# certificate if necessary
# 3. Find dependency packages that are applicable to the operating system's
# CPU architecture
# 4. Install the package along with all applicable dependencies
#
# All command line parameters are reserved for use internally by the script.
# Users should launch this script from Explorer.
#
# .Link
# http://go.microsoft.com/fwlink/?LinkId=243053
param(
[switch]$Force = $false,
[switch]$GetDeveloperLicense = $false,
[string]$CertificatePath = $null
)
$ErrorActionPreference = "Stop"
# The language resources for this script are placed in the
# "Add-AppDevPackage.resources" subfolder alongside the script. Since the
# current working directory might not be the directory that contains the
# script, we need to create the full path of the resources directory to
# pass into Import-LocalizedData
$ScriptPath = $null
try
{
$ScriptPath = (Get-Variable MyInvocation).Value.MyCommand.Path
$ScriptDir = Split-Path -Parent $ScriptPath
}
catch {}
if (!$ScriptPath)
{
PrintMessageAndExit $UiStrings.ErrorNoScriptPath $ErrorCodes.NoScriptPath
}
$LocalizedResourcePath = Join-Path $ScriptDir "Add-AppDevPackage.resources"
Import-LocalizedData -BindingVariable UiStrings -BaseDirectory $LocalizedResourcePath
$ErrorCodes = Data {
ConvertFrom-StringData @'
Success = 0
NoScriptPath = 1
NoPackageFound = 2
ManyPackagesFound = 3
NoCertificateFound = 4
ManyCertificatesFound = 5
BadCertificate = 6
PackageUnsigned = 7
CertificateMismatch = 8
ForceElevate = 9
LaunchAdminFailed = 10
GetDeveloperLicenseFailed = 11
InstallCertificateFailed = 12
AddPackageFailed = 13
ForceDeveloperLicense = 14
CertUtilInstallFailed = 17
CertIsCA = 18
BannedEKU = 19
NoBasicConstraints = 20
NoCodeSigningEku = 21
InstallCertificateCancelled = 22
BannedKeyUsage = 23
ExpiredCertificate = 24
'@
}
function PrintMessageAndExit($ErrorMessage, $ReturnCode)
{
Write-Host $ErrorMessage
if (!$Force)
{
Pause
}
exit $ReturnCode
}
#
# Warns the user about installing certificates, and presents a Yes/No prompt
# to confirm the action. The default is set to No.
#
function ConfirmCertificateInstall
{
$Answer = $host.UI.PromptForChoice(
"",
$UiStrings.WarningInstallCert,
[System.Management.Automation.Host.ChoiceDescription[]]@($UiStrings.PromptYesString, $UiStrings.PromptNoString),
1)
return $Answer -eq 0
}
#
# Validates whether a file is a valid certificate using CertUtil.
# This needs to be done before calling Get-PfxCertificate on the file, otherwise
# the user will get a cryptic "Password: " prompt for invalid certs.
#
function ValidateCertificateFormat($FilePath)
{
# certutil -verify prints a lot of text that we don't need, so it's redirected to $null here
certutil.exe -verify $FilePath > $null
if ($LastExitCode -lt 0)
{
PrintMessageAndExit ($UiStrings.ErrorBadCertificate -f $FilePath, $LastExitCode) $ErrorCodes.BadCertificate
}
# Check if certificate is expired
$cert = Get-PfxCertificate $FilePath
if (($cert.NotBefore -gt (Get-Date)) -or ($cert.NotAfter -lt (Get-Date)))
{
PrintMessageAndExit ($UiStrings.ErrorExpiredCertificate -f $FilePath) $ErrorCodes.ExpiredCertificate
}
}
#
# Verify that the developer certificate meets the following restrictions:
# - The certificate must contain a Basic Constraints extension, and its
# Certificate Authority (CA) property must be false.
# - The certificate's Key Usage extension must be either absent, or set to
# only DigitalSignature.
# - The certificate must contain an Extended Key Usage (EKU) extension with
# Code Signing usage.
# - The certificate must NOT contain any other EKU except Code Signing and
# Lifetime Signing.
#
# These restrictions are enforced to decrease security risks that arise from
# trusting digital certificates.
#
function CheckCertificateRestrictions
{
Set-Variable -Name BasicConstraintsExtensionOid -Value "2.5.29.19" -Option Constant
Set-Variable -Name KeyUsageExtensionOid -Value "2.5.29.15" -Option Constant
Set-Variable -Name EkuExtensionOid -Value "2.5.29.37" -Option Constant
Set-Variable -Name CodeSigningEkuOid -Value "1.3.6.1.5.5.7.3.3" -Option Constant
Set-Variable -Name LifetimeSigningEkuOid -Value "1.3.6.1.4.1.311.10.3.13" -Option Constant
$CertificateExtensions = (Get-PfxCertificate $CertificatePath).Extensions
$HasBasicConstraints = $false
$HasCodeSigningEku = $false
foreach ($Extension in $CertificateExtensions)
{
# Certificate must contain the Basic Constraints extension
if ($Extension.oid.value -eq $BasicConstraintsExtensionOid)
{
# CA property must be false
if ($Extension.CertificateAuthority)
{
PrintMessageAndExit $UiStrings.ErrorCertIsCA $ErrorCodes.CertIsCA
}
$HasBasicConstraints = $true
}
# If key usage is present, it must be set to digital signature
elseif ($Extension.oid.value -eq $KeyUsageExtensionOid)
{
if ($Extension.KeyUsages -ne "DigitalSignature")
{
PrintMessageAndExit ($UiStrings.ErrorBannedKeyUsage -f $Extension.KeyUsages) $ErrorCodes.BannedKeyUsage
}
}
elseif ($Extension.oid.value -eq $EkuExtensionOid)
{
# Certificate must contain the Code Signing EKU
$EKUs = $Extension.EnhancedKeyUsages.Value
if ($EKUs -contains $CodeSigningEkuOid)
{
$HasCodeSigningEKU = $True
}
# EKUs other than code signing and lifetime signing are not allowed
foreach ($EKU in $EKUs)
{
if ($EKU -ne $CodeSigningEkuOid -and $EKU -ne $LifetimeSigningEkuOid)
{
PrintMessageAndExit ($UiStrings.ErrorBannedEKU -f $EKU) $ErrorCodes.BannedEKU
}
}
}
}
if (!$HasBasicConstraints)
{
PrintMessageAndExit $UiStrings.ErrorNoBasicConstraints $ErrorCodes.NoBasicConstraints
}
if (!$HasCodeSigningEKU)
{
PrintMessageAndExit $UiStrings.ErrorNoCodeSigningEku $ErrorCodes.NoCodeSigningEku
}
}
#
# Performs operations that require administrative privileges:
# - Prompt the user to obtain a developer license
# - Install the developer certificate (if -Force is not specified, also prompts the user to confirm)
#
function DoElevatedOperations
{
if ($GetDeveloperLicense)
{
Write-Host $UiStrings.GettingDeveloperLicense
if ($Force)
{
PrintMessageAndExit $UiStrings.ErrorForceDeveloperLicense $ErrorCodes.ForceDeveloperLicense
}
try
{
Show-WindowsDeveloperLicenseRegistration
}
catch
{
$Error[0] # Dump details about the last error
PrintMessageAndExit $UiStrings.ErrorGetDeveloperLicenseFailed $ErrorCodes.GetDeveloperLicenseFailed
}
}
if ($CertificatePath)
{
Write-Host $UiStrings.InstallingCertificate
# Make sure certificate format is valid and usage constraints are followed
ValidateCertificateFormat $CertificatePath
CheckCertificateRestrictions
# If -Force is not specified, warn the user and get consent
if ($Force -or (ConfirmCertificateInstall))
{
# Add cert to store
certutil.exe -addstore TrustedPeople $CertificatePath
if ($LastExitCode -lt 0)
{
PrintMessageAndExit ($UiStrings.ErrorCertUtilInstallFailed -f $LastExitCode) $ErrorCodes.CertUtilInstallFailed
}
}
else
{
PrintMessageAndExit $UiStrings.ErrorInstallCertificateCancelled $ErrorCodes.InstallCertificateCancelled
}
}
}
#
# Checks whether the machine is missing a valid developer license.
#
function CheckIfNeedDeveloperLicense
{
$Result = $true
try
{
$Result = (Get-WindowsDeveloperLicense | Where-Object { $_.IsValid } | Measure-Object).Count -eq 0
}
catch {}
return $Result
}
#
# Launches an elevated process running the current script to perform tasks
# that require administrative privileges. This function waits until the
# elevated process terminates, and checks whether those tasks were successful.
#
function LaunchElevated
{
# Set up command line arguments to the elevated process
$RelaunchArgs = '-ExecutionPolicy Unrestricted -file "' + $ScriptPath + '"'
if ($Force)
{
$RelaunchArgs += ' -Force'
}
if ($NeedDeveloperLicense)
{
$RelaunchArgs += ' -GetDeveloperLicense'
}
if ($NeedInstallCertificate)
{
$RelaunchArgs += ' -CertificatePath "' + $DeveloperCertificatePath.FullName + '"'
}
# Launch the process and wait for it to finish
try
{
$AdminProcess = Start-Process "$PsHome\PowerShell.exe" -Verb RunAs -ArgumentList $RelaunchArgs -PassThru
}
catch
{
$Error[0] # Dump details about the last error
PrintMessageAndExit $UiStrings.ErrorLaunchAdminFailed $ErrorCodes.LaunchAdminFailed
}
while (!($AdminProcess.HasExited))
{
Start-Sleep -Seconds 2
}
# Check if all elevated operations were successful
if ($NeedDeveloperLicense)
{
if (CheckIfNeedDeveloperLicense)
{
PrintMessageAndExit $UiStrings.ErrorGetDeveloperLicenseFailed $ErrorCodes.GetDeveloperLicenseFailed
}
else
{
Write-Host $UiStrings.AcquireLicenseSuccessful
}
}
if ($NeedInstallCertificate)
{
$Signature = Get-AuthenticodeSignature $DeveloperPackagePath -Verbose
if ($Signature.Status -ne "Valid")
{
PrintMessageAndExit ($UiStrings.ErrorInstallCertificateFailed -f $Signature.Status) $ErrorCodes.InstallCertificateFailed
}
else
{
Write-Host $UiStrings.InstallCertificateSuccessful
}
}
}
#
# Finds all applicable dependency packages according to OS architecture, and
# installs the developer package with its dependencies. The expected layout
# of dependencies is:
#
# <current dir>
# \Dependencies
# <Architecture neutral dependencies>.appx\.msix
# \x86
# <x86 dependencies>.appx\.msix
# \x64
# <x64 dependencies>.appx\.msix
# \arm
# <arm dependencies>.appx\.msix
#
function InstallPackageWithDependencies
{
$DependencyPackagesDir = (Join-Path $ScriptDir "Dependencies")
$DependencyPackages = @()
if (Test-Path $DependencyPackagesDir)
{
# Get architecture-neutral dependencies
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" }
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "*.msix") | Where-Object { $_.Mode -NotMatch "d" }
# Get architecture-specific dependencies
if (($Env:Processor_Architecture -eq "x86" -or $Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $DependencyPackagesDir "x86")))
{
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x86\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x86\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
}
if (($Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $DependencyPackagesDir "x64")))
{
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x64\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x64\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
}
if (($Env:Processor_Architecture -eq "arm") -and (Test-Path (Join-Path $DependencyPackagesDir "arm")))
{
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
}
}
Write-Host $UiStrings.InstallingPackage
$AddPackageSucceeded = $False
try
{
if ($DependencyPackages.FullName.Count -gt 0)
{
Write-Host $UiStrings.DependenciesFound
$DependencyPackages.FullName
Add-AppxPackage -Path $DeveloperPackagePath.FullName -DependencyPath $DependencyPackages.FullName -ForceApplicationShutdown
}
else
{
Add-AppxPackage -Path $DeveloperPackagePath.FullName -ForceApplicationShutdown
}
$AddPackageSucceeded = $?
}
catch
{
$Error[0] # Dump details about the last error
}
if (!$AddPackageSucceeded)
{
if ($NeedInstallCertificate)
{
PrintMessageAndExit $UiStrings.ErrorAddPackageFailedWithCert $ErrorCodes.AddPackageFailed
}
else
{
PrintMessageAndExit $UiStrings.ErrorAddPackageFailed $ErrorCodes.AddPackageFailed
}
}
}
#
# Main script logic when the user launches the script without parameters.
#
function DoStandardOperations
{
# Check for an .appx or .msix file in the script directory
$PackagePath = Get-ChildItem (Join-Path $ScriptDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" }
if ($PackagePath -eq $null)
{
$PackagePath = Get-ChildItem (Join-Path $ScriptDir "*.msix") | Where-Object { $_.Mode -NotMatch "d" }
}
$PackageCount = ($PackagePath | Measure-Object).Count
# Check for an .appxbundle or .msixbundle file in the script directory
$BundlePath = Get-ChildItem (Join-Path $ScriptDir "*.appxbundle") | Where-Object { $_.Mode -NotMatch "d" }
if ($BundlePath -eq $null)
{
$BundlePath = Get-ChildItem (Join-Path $ScriptDir "*.msixbundle") | Where-Object { $_.Mode -NotMatch "d" }
}
$BundleCount = ($BundlePath | Measure-Object).Count
# Check for an .eappx or .emsix file in the script directory
$EncryptedPackagePath = Get-ChildItem (Join-Path $ScriptDir "*.eappx") | Where-Object { $_.Mode -NotMatch "d" }
if ($EncryptedPackagePath -eq $null)
{
$EncryptedPackagePath = Get-ChildItem (Join-Path $ScriptDir "*.emsix") | Where-Object { $_.Mode -NotMatch "d" }
}
$EncryptedPackageCount = ($EncryptedPackagePath | Measure-Object).Count
# Check for an .eappxbundle or .emsixbundle file in the script directory
$EncryptedBundlePath = Get-ChildItem (Join-Path $ScriptDir "*.eappxbundle") | Where-Object { $_.Mode -NotMatch "d" }
if ($EncryptedBundlePath -eq $null)
{
$EncryptedBundlePath = Get-ChildItem (Join-Path $ScriptDir "*.emsixbundle") | Where-Object { $_.Mode -NotMatch "d" }
}
$EncryptedBundleCount = ($EncryptedBundlePath | Measure-Object).Count
$NumberOfPackages = $PackageCount + $EncryptedPackageCount
$NumberOfBundles = $BundleCount + $EncryptedBundleCount
# There must be at least one package or bundle
if ($NumberOfPackages + $NumberOfBundles -lt 1)
{
PrintMessageAndExit $UiStrings.ErrorNoPackageFound $ErrorCodes.NoPackageFound
}
# We must have exactly one bundle OR no bundle and exactly one package
elseif ($NumberOfBundles -gt 1 -or
($NumberOfBundles -eq 0 -and $NumberOfpackages -gt 1))
{
PrintMessageAndExit $UiStrings.ErrorManyPackagesFound $ErrorCodes.ManyPackagesFound
}
# First attempt to install a bundle or encrypted bundle. If neither exists, fall back to packages and then encrypted packages
if ($BundleCount -eq 1)
{
$DeveloperPackagePath = $BundlePath
Write-Host ($UiStrings.BundleFound -f $DeveloperPackagePath.FullName)
}
elseif ($EncryptedBundleCount -eq 1)
{
$DeveloperPackagePath = $EncryptedBundlePath
Write-Host ($UiStrings.EncryptedBundleFound -f $DeveloperPackagePath.FullName)
}
elseif ($PackageCount -eq 1)
{
$DeveloperPackagePath = $PackagePath
Write-Host ($UiStrings.PackageFound -f $DeveloperPackagePath.FullName)
}
elseif ($EncryptedPackageCount -eq 1)
{
$DeveloperPackagePath = $EncryptedPackagePath
Write-Host ($UiStrings.EncryptedPackageFound -f $DeveloperPackagePath.FullName)
}
# The package must be signed
$PackageSignature = Get-AuthenticodeSignature $DeveloperPackagePath
$PackageCertificate = $PackageSignature.SignerCertificate
if (!$PackageCertificate)
{
PrintMessageAndExit $UiStrings.ErrorPackageUnsigned $ErrorCodes.PackageUnsigned
}
# Test if the package signature is trusted. If not, the corresponding certificate
# needs to be present in the current directory and needs to be installed.
$NeedInstallCertificate = ($PackageSignature.Status -ne "Valid")
if ($NeedInstallCertificate)
{
# List all .cer files in the script directory
$DeveloperCertificatePath = Get-ChildItem (Join-Path $ScriptDir "*.cer") | Where-Object { $_.Mode -NotMatch "d" }
$DeveloperCertificateCount = ($DeveloperCertificatePath | Measure-Object).Count
# There must be exactly 1 certificate
if ($DeveloperCertificateCount -lt 1)
{
PrintMessageAndExit $UiStrings.ErrorNoCertificateFound $ErrorCodes.NoCertificateFound
}
elseif ($DeveloperCertificateCount -gt 1)
{
PrintMessageAndExit $UiStrings.ErrorManyCertificatesFound $ErrorCodes.ManyCertificatesFound
}
Write-Host ($UiStrings.CertificateFound -f $DeveloperCertificatePath.FullName)
# The .cer file must have the format of a valid certificate
ValidateCertificateFormat $DeveloperCertificatePath
# The package signature must match the certificate file
if ($PackageCertificate -ne (Get-PfxCertificate $DeveloperCertificatePath))
{
PrintMessageAndExit $UiStrings.ErrorCertificateMismatch $ErrorCodes.CertificateMismatch
}
}
$NeedDeveloperLicense = CheckIfNeedDeveloperLicense
# Relaunch the script elevated with the necessary parameters if needed
if ($NeedDeveloperLicense -or $NeedInstallCertificate)
{
Write-Host $UiStrings.ElevateActions
if ($NeedDeveloperLicense)
{
Write-Host $UiStrings.ElevateActionDevLicense
}
if ($NeedInstallCertificate)
{
Write-Host $UiStrings.ElevateActionCertificate
}
$IsAlreadyElevated = ([Security.Principal.WindowsIdentity]::GetCurrent().Groups.Value -contains "S-1-5-32-544")
if ($IsAlreadyElevated)
{
if ($Force -and $NeedDeveloperLicense)
{
PrintMessageAndExit $UiStrings.ErrorForceDeveloperLicense $ErrorCodes.ForceDeveloperLicense
}
if ($Force -and $NeedInstallCertificate)
{
Write-Warning $UiStrings.WarningInstallCert
}
}
else
{
if ($Force)
{
PrintMessageAndExit $UiStrings.ErrorForceElevate $ErrorCodes.ForceElevate
}
else
{
Write-Host $UiStrings.ElevateActionsContinue
Pause
}
}
LaunchElevated
}
InstallPackageWithDependencies
}
#
# Main script entry point
#
if ($GetDeveloperLicense -or $CertificatePath)
{
DoElevatedOperations
}
else
{
DoStandardOperations
PrintMessageAndExit $UiStrings.Success $ErrorCodes.Success
}
# SIG # Begin signature block
# MIIiTwYJKoZIhvcNAQcCoIIiQDCCIjwCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD5BdpRggDM/3NT
# wBDQFaER9aOD9SdM3CUzPDQe6F3dFqCCC3YwggT+MIID5qADAgECAhMzAAACz20s
# xXyqZabYAAAAAALPMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTAwHhcNMTkwNTAyMjEyNTQyWhcNMjAwNTAyMjEyNTQyWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC5WvO1XVj5Qw+VAXn6dOWwyrU48JLK7JuZQh066rWPdXWDqRs2/0CWjtd1CtGW
# rsezdxI5gtnuSHU9R61TOTxp0crY8D8hWkw66Chnw30gU0uci7yT+SBG8FCCym9q
# Tlbx1EOu2Onbvnx3fUZzAzpk6fYtyyVRDZDJFlkpDBUgY6i4T3AknQHlaZRJydZY
# qJuJuC/gbhunzJ11CKbIMWUPo+291saTjGYiv0hXHYyhweatkIEpK4pi9MKh28jB
# eSN0NwiSZeNXAeRPoQr1aJbWCYBKBdyuMNexWvsd+YTeksvywPjqJm7Tc37FZqUF
# rMTrENoMXHw2mZxZicaUR/a7AgMBAAGjggF9MIIBeTAfBgNVHSUEGDAWBgorBgEE
# AYI3PQYBBggrBgEFBQcDAzAdBgNVHQ4EFgQUN/NN7E2vLAQxqRCIC1jAbwO8VQgw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwODY1KzQ1NDI0NDAfBgNVHSMEGDAW
# gBTm/F97uyIAWORyTrX0IXQjMubvrDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8v
# Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDb2RTaWdQQ0Ff
# MjAxMC0wNy0wNi5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNpZ1BDQV8yMDEw
# LTA3LTA2LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCqMI8I
# dLEoiAqBDMw46rHAR5p98xk1puiL6RYE5MMy4Sn3zLjwxsMC1bCNC0H+XKkDhiX1
# JJ+wBMRpCieUBLIJ3BmvuwGLkuTYcgQHcoHTicrXeZhB5aAp+5WR2fiMQCNwmrL2
# ywp1C70cctf/mCAB3sDN0NOl/2sMi/mFaSIVCddO8sHp1Hin0eNG9j2pBmYRo54v
# 7+bTxYApyPrTt5gN3KTOhGjrm/sUgyhtIleaRr43ccTs7TiPTW4TVDmHxvQeQd+6
# hyjawsACpYBSPL2a4mbAXsNW6GUdzNTs9U0FrvOblnvyqTuu4Ls7QpvsILrqHG30
# aiSPMQE8igpYYWdYMIIGcDCCBFigAwIBAgIKYQxSTAAAAAAAAzANBgkqhkiG9w0B
# AQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG
# A1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAw
# HhcNMTAwNzA2MjA0MDE3WhcNMjUwNzA2MjA1MDE3WjB+MQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT
# aWduaW5nIFBDQSAyMDEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
# 6Q5kUHlntcTj/QkATJ6UrPdWaOpE2M/FWE+ppXZ8bUW60zmStKQe+fllguQX0o/9
# RJwI6GWTzixVhL99COMuK6hBKxi3oktuSUxrFQfe0dLCiR5xlM21f0u0rwjYzIjW
# axeUOpPOJj/s5v40mFfVHV1J9rIqLtWFu1k/+JC0K4N0yiuzO0bj8EZJwRdmVMkc
# vR3EVWJXcvhnuSUgNN5dpqWVXqsogM3Vsp7lA7Vj07IUyMHIiiYKWX8H7P8O7YAS
# NUwSpr5SW/Wm2uCLC0h31oVH1RC5xuiq7otqLQVcYMa0KlucIxxfReMaFB5vN8sZ
# M4BqiU2jamZjeJPVMM+VHwIDAQABo4IB4zCCAd8wEAYJKwYBBAGCNxUBBAMCAQAw
# HQYDVR0OBBYEFOb8X3u7IgBY5HJOtfQhdCMy5u+sMBkGCSsGAQQBgjcUAgQMHgoA
# UwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQY
# MBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6
# Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1
# dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3J0MIGdBgNVHSAEgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9Bggr
# BgEFBQcCARYxaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9k
# ZWZhdWx0Lmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABp
# AGMAeQBfAFMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEA
# GnTvV08pe8QWhXi4UNMi/AmdrIKX+DT/KiyXlRLl5L/Pv5PI4zSp24G43B4AvtI1
# b6/lf3mVd+UC1PHr2M1OHhthosJaIxrwjKhiUUVnCOM/PB6T+DCFF8g5QKbXDrMh
# KeWloWmMIpPMdJjnoUdD8lOswA8waX/+0iUgbW9h098H1dlyACxphnY9UdumOUjJ
# N2FtB91TGcun1mHCv+KDqw/ga5uV1n0oUbCJSlGkmmzItx9KGg5pqdfcwX7RSXCq
# tq27ckdjF/qm1qKmhuyoEESbY7ayaYkGx0aGehg/6MUdIdV7+QIjLcVBy78dTMgW
# 77Gcf/wiS0mKbhXjpn92W9FTeZGFndXS2z1zNfM8rlSyUkdqwKoTldKOEdqZZ14y
# jPs3hdHcdYWch8ZaV4XCv90Nj4ybLeu07s8n07VeafqkFgQBpyRnc89NT7beBVaX
# evfpUk30dwVPhcbYC/GO7UIJ0Q124yNWeCImNr7KsYxuqh3khdpHM2KPpMmRM19x
# HkCvmGXJIuhCISWKHC1g2TeJQYkqFg/XYTyUaGBS79ZHmaCAQO4VgXc+nOBTGBpQ
# HTiVmx5mMxMnORd4hzbOTsNfsvU9R1O24OXbC2E9KteSLM43Wj5AQjGkHxAIwlac
# vyRdUQKdannSF9PawZSOB3slcUSrBmrm1MbfI5qWdcUxghYvMIIWKwIBATCBlTB+
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9N
# aWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEwAhMzAAACz20sxXyqZabYAAAA
# AALPMA0GCWCGSAFlAwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE
# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBD
# AerId9KHdHY2YUo2JgxX4DBOYZn/OL23opQ4Y8smAjBCBgorBgEEAYI3AgEMMTQw
# MqAUgBIATQBpAGMAcgBvAHMAbwBmAHShGoAYaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tMA0GCSqGSIb3DQEBAQUABIIBADGTPfGCerJY3hyXU9uXgMgXbhJWidsAnI4d
# MAwoI/yBxmlg+1Yq4gAFR0wFDqTPbXYTBvQ5OdKjqhxOmS9f0Qf3PY0NYrwdLaQP
# IjeSrAF3YFbhiyUokgLR78Iy1XQeJxhEquOuH5TA7rvSt4kFkZIlAdwSARSul7JN
# VXegfxt+PV/tV+P21pqbkYvvjNgBpbVJBO4WY74vPNc/GWI5juH807Lnb3/AGx5D
# Mg5AGXJnO0sgRrLDxAensCAEmPBDj+ej1mnhhqD86xPQtTvqaPQ/TkFkZfT4AtJR
# m5eMPwcbjSpCHr/XqKv3R5SyQYnCu4PiLaboMf7JzaYrulkqHAmhghO5MIITtQYK
# KwYBBAGCNwMDATGCE6UwghOhBgkqhkiG9w0BBwKgghOSMIITjgIBAzEPMA0GCWCG
# SAFlAwQCAQUAMIIBVwYLKoZIhvcNAQkQAQSgggFGBIIBQjCCAT4CAQEGCisGAQQB
# hFkKAwEwMTANBglghkgBZQMEAgEFAAQgwBrd2L0fVeiLCvS9/WD0rS16JiEiejGB
# agB4Lm83Q5oCBl56b8E7rhgSMjAyMDA0MDkyMDU4MDQuMzFaMAcCAQGAAgH0oIHU
# pIHRMIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYD
# VQQLEyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMd
# VGhhbGVzIFRTUyBFU046QkJFQy0zMENBLTJEQkUxJTAjBgNVBAMTHE1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFNlcnZpY2Wggg8iMIIE9TCCA92gAwIBAgITMwAAAP8vnmsF
# EdNrlwAAAAAA/zANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMDAeFw0xOTA5MDYyMDQxMDlaFw0yMDEyMDQyMDQxMDlaMIHOMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv
# ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBF
# U046QkJFQy0zMENBLTJEQkUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFNlcnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuCOuILcP+
# LfUEqGIrXGvCr+mEFRpy20gxNNMkZI+kViE/rsYH3eItmQfawIndfP1bKp+1J2b3
# mXXJ7qqwMYlflpc4qTc/HgOByIa3tFmUEWN/jDBKzE9FucFHKgPT8gtEnVGOg9h3
# ES3zIJj18d+sVVcbpgmutvJKopGl1u9vTNwLxdH3S4t+ov1SgOgsTF0CblJiu/zd
# iwsQzstk+3nQPjk+DlwfEC5kwOricPV0QMaC/q+Buj+4rBUAvDKb/NvBqRtJptmT
# 9S5HZOXidFMm2i2SJVK0sEamOw69Jaj1mB5bksyMP0PFttGYVxh02dTBDG6AbmTC
# Er9bKrgPBjWxAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUzhnW8N+xfFZ8o+3bxTZy
# MEBX1gEwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8w
# TTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj
# dHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBK
# BggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N
# aWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUE
# DDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEApAnaQljuFW4UjErMgHn4
# LWHVod53qtw/9FERINhT6tH/Ed6n6AzDcfeBdr4taaxo5dB+juWM6nDQwd99kInu
# Kk7fqRlxd7ZmJlkFQ6MAdLzdJDGXSkS7I3aR7E/pKnBV1Y6gbICuPeM1CTPYSO26
# ryfWM36/PGu1Iv1JGNkJOG82X2J3SiACPFIzCaexAK4P9bEPc8GhqdHdxc7x0fVe
# Ow+EvBanHFa82hmz2HC/JMaI4yR7JtRgKjZZTuOIWBqkXmDFcFaTgfFl4jJLdwQC
# dkMb4wfLgVdc/Jym4nJrqBZFTb0mkAYb6OkK1h6gNobk69mFO5yjDTiw8hPGZRor
# kjCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jv
# c29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIx
# MzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX
# 9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkT
# jnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG
# 8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGK
# r0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6
# Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEF
# TyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6
# XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD
# VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxi
# aNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3Nv
# ZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMu
# Y3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNy
# b3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQw
# gaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFo
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRt
# MEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0
# AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/
# gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtU
# VwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9
# Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9
# BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOd
# eyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1
# JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4Ttx
# Cd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5
# u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9U
# JyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Z
# ta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa
# 7wknHNWzfjUeCLraNtvTX4/edIhJEqGCA7AwggKYAgEBMIH+oYHUpIHRMIHOMQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNy
# b3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRT
# UyBFU046QkJFQy0zMENBLTJEQkUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIFNlcnZpY2WiJQoBATAJBgUrDgMCGgUAAxUAAjbENWjPNg9MFMx7uLG8Osg6
# dx2ggd4wgdukgdgwgdUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMScw
# JQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NERFOS0wQzVFLTNFMDkxKzApBgNVBAMT
# Ik1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJKoZIhvcNAQEF
# BQACBQDiORpNMCIYDzIwMjAwNDA5MTE1MTQxWhgPMjAyMDA0MTAxMTUxNDFaMHcw
# PQYKKwYBBAGEWQoEATEvMC0wCgIFAOI5Gk0CAQAwCgIBAAICGdwCAf8wBwIBAAIC
# GqwwCgIFAOI6a80CAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAaAK
# MAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkqhkiG9w0BAQUFAAOCAQEABnBMppi9
# x3/eTqCHWKBAzZuCVn0vSxjDJ8mUsWB3mXddZ/TDcfA2MTqQnwW/xiDx6HNlTdRt
# Ii/tjOegAiYl1HfPViMQTadFFA3+v38/dWLFPtiZHv4tc9P1E2SQ9IxkDx/2T5zd
# bLQynB/5HvkGv3PoFp7uT6XmwnM+E7NXGaKPEieId69dlxmrujXD9PdyP4zVTD2A
# cS70aUYUVJb0yyePQnFP5nbWJLY9l0mZ/4ypxNnlx1GYO7D6z3YW8CBtHvncKEoF
# sMHdvZSa4uqxxixty1j5ckjPjzEmK94glEetxaRsxfSKYUDmGyM+dRgOhCzQgkjW
# EWoyoxk1SKmC5TGCAvUwggLxAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwAhMzAAAA/y+eawUR02uXAAAAAAD/MA0GCWCGSAFlAwQCAQUAoIIBMjAa
# BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEINF+cv0h
# z00XM+ToBhlb8dz+wZuCJW0KI9hnUR0ujeLXMIHiBgsqhkiG9w0BCRACDDGB0jCB
# zzCBzDCBsQQUAjbENWjPNg9MFMx7uLG8Osg6dx0wgZgwgYCkfjB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAP8vnmsFEdNrlwAAAAAA/zAWBBQN4cP3
# Lty5AfjYZ0mpFsU2dlUhQTANBgkqhkiG9w0BAQsFAASCAQCOVuvdo4Ol6alhUPSP
# rhSzI2HiILRARtD8pv1hIaaZBTHhGGTMlqeqQJNsuWqBqFPrfkFSVGsFv0aWfO/S
# Z2c6hTKvj2JfxoxDpBgfbNBe53CDKI3zKBDqHX/jMs6H+wI1ebDzXROkmp4G7h2R
# ZyW/0yi9kj8m4mkL8sDNcS/KABoIUxUPyfC+wHN1Tgujpzu6ZeaMJ0vPptI3vX3w
# hLf6jmMleMS/iMG6iYDfdIEnbeM4OC1X9YC9+CrCnIAyHkjQxBW/NI94oV1e7YGF
# 4A1Qt3Q+A3kC1Asua6owY2oL/ecKYeGzwsbP2/kU6SWerMObKfbd++xalbf/eCyD
# xEZ+
# SIG # End signature block