# # 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 }).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: # # # \Dependencies # .appx # \x86 # .appx # \x64 # .appx # \arm # .appx # 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" } # 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" } } 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" } } 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" } } } 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 { # List all .appx files in the script directory $PackagePath = Get-ChildItem (Join-Path $ScriptDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" } # List all .appxbundle files in the script directory $BundlePath = Get-ChildItem (Join-Path $ScriptDir "*.appxbundle") | Where-Object { $_.Mode -NotMatch "d" } $NumberOfPackages = $PackagePath.Count + $BundlePath.Count; # There must be exactly 1 package/bundle if ($NumberOfPackages -lt 1) { PrintMessageAndExit $UiStrings.ErrorNoPackageFound $ErrorCodes.NoPackageFound } elseif ($NumberOfPackages -gt 1) { PrintMessageAndExit $UiStrings.ErrorManyPackagesFound $ErrorCodes.ManyPackagesFound } if ($PackagePath.Count -eq 1) { $DeveloperPackagePath = $PackagePath Write-Host ($UiStrings.PackageFound -f $DeveloperPackagePath.FullName) } else { $DeveloperPackagePath = $BundlePath Write-Host ($UiStrings.BundleFound -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" } # There must be exactly 1 certificate if ($DeveloperCertificatePath.Count -lt 1) { PrintMessageAndExit $UiStrings.ErrorNoCertificateFound $ErrorCodes.NoCertificateFound } elseif ($DeveloperCertificatePath.Count -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 # MIIiBAYJKoZIhvcNAQcCoIIh9TCCIfECAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDr6TQeGU6q8Gc6 # 4UyJ3BErCSyDb5bOOr9muAPuxnPhgKCCC4MwggULMIID86ADAgECAhMzAAAAe6KB # C4cRq+f8AAAAAAB7MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTAwHhcNMTQxMDAxMTgwNjM4WhcNMTYwMTAxMTgwNjM4WjCBgzEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q # UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAvQcxxth2EB1eNeHpmIf/xHv5hBJHStPFJ2Zp22w2 # nNOhA3CXNR09AyJfXIEL50eYNUaSPv035IjaKWFTJMd9iXVt9WcwDtqZ40mvXhVE # IyRFFgVmHcDac0t+/HxA4SPZKYlGHAvdNz8gd97MD7tIaeAXcx/qOxYqkhsujIJJ # OKAqkfCh2w/3fWqRLgVrimZlJghs5DJOT5DL6br8nBYxqgjIsp4c8QsCsBZdlXxK # m+7DAolGOmGJ8GwTtg1m3hcURkrTyXnyx34N2x22kH5gItwtcNicDAMnN6xc8ydI # WviJIaR0Gj4Da8UiqTd9s2dNxuScWaOIuy/Ev/9DUf1zPQIDAQABo4IBejCCAXYw # HwYDVR0lBBgwFgYIKwYBBQUHAwMGCisGAQQBgjc9BgEwHQYDVR0OBBYEFE8LE+6Q # o6azvj+qlES88piRTzIdMFEGA1UdEQRKMEikRjBEMQ0wCwYDVQQLEwRNT1BSMTMw # MQYDVQQFEyozODA3Nis2OGQyZjhiYi0wYTNiLTQwYjQtOWYyNC1lYjdlOTQxOWQx # NjAwHwYDVR0jBBgwFoAU5vxfe7siAFjkck619CF0IzLm76wwVgYDVR0fBE8wTTBL # oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljQ29kU2lnUENBXzIwMTAtMDctMDYuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr # BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWND # b2RTaWdQQ0FfMjAxMC0wNy0wNi5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0B # AQsFAAOCAQEAL1QbJw93iIl4XlutFtrDwEs8JT/kMaquI7SOYeIwgVH+SI71MCMZ # TcveYIhzmIcEQz3gi+6gC28dVa6Y0GeuADG+vu3thnUKkko63nE4tGH3wDvhMRbx # /L8wYXp13zaEV3R/HaYCiukoMfWMkN+qWdGepYUmp+PAXFTl3dVd/4oxMTdbLf4t # lx1QqOyGSAyRrbJmPB57RWEJrGfhfYInTwr8Sy7UF/mVMxF7qCYBPJJD5pngvKsK # TXNRW3YSyiaJ1TXHutME/dy4jipqbcZXUUA9qjRIICpPnoFxyk1VhBpKePeG96WJ # MV0lBi+tzLVoWe5RSzxIKyI2qxzQho7SiTCCBnAwggRYoAMCAQICCmEMUkwAAAAA # AAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1 # dGhvcml0eSAyMDEwMB4XDTEwMDcwNjIwNDAxN1oXDTI1MDcwNjIwNTAxN1owfjEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWlj # cm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQAD # ggEPADCCAQoCggEBAOkOZFB5Z7XE4/0JAEyelKz3VmjqRNjPxVhPqaV2fG1FutM5 # krSkHvn5ZYLkF9KP/UScCOhlk84sVYS/fQjjLiuoQSsYt6JLbklMaxUH3tHSwoke # cZTNtX9LtK8I2MyI1msXlDqTziY/7Ob+NJhX1R1dSfayKi7VhbtZP/iQtCuDdMor # sztG4/BGScEXZlTJHL0dxFViV3L4Z7klIDTeXaallV6rKIDN1bKe5QO1Y9OyFMjB # yIomCll/B+z/Du2AEjVMEqa+Ulv1ptrgiwtId9aFR9UQucboqu6Lai0FXGDGtCpb # nCMcX0XjGhQebzfLGTOAaolNo2pmY3iT1TDPlR8CAwEAAaOCAeMwggHfMBAGCSsG # AQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTm/F97uyIAWORyTrX0IXQjMubvrDAZBgkr # BgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw # AwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBN # MEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0 # cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoG # CCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01p # Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBnQYDVR0gBIGVMIGSMIGPBgkrBgEE # AYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9Q # S0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcA # YQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZI # hvcNAQELBQADggIBABp071dPKXvEFoV4uFDTIvwJnayCl/g0/yosl5US5eS/z7+T # yOM0qduBuNweAL7SNW+v5X95lXflAtTx69jNTh4bYaLCWiMa8IyoYlFFZwjjPzwe # k/gwhRfIOUCm1w6zISnlpaFpjCKTzHSY56FHQ/JTrMAPMGl//tIlIG1vYdPfB9XZ # cgAsaYZ2PVHbpjlIyTdhbQfdUxnLp9Zhwr/ig6sP4GubldZ9KFGwiUpRpJpsyLcf # ShoOaanX3MF+0Ulwqratu3JHYxf6ptaipobsqBBEm2O2smmJBsdGhnoYP+jFHSHV # e/kCIy3FQcu/HUzIFu+xnH/8IktJim4V46Z/dlvRU3mRhZ3V0ts9czXzPK5UslJH # asCqE5XSjhHamWdeMoz7N4XR3HWFnIfGWleFwr/dDY+Mmy3rtO7PJ9O1Xmn6pBYE # AackZ3PPTU+23gVWl3r36VJN9HcFT4XG2Avxju1CCdENduMjVngiJja+yrGMbqod # 5IXaRzNij6TJkTNfcR5Ar5hlySLoQiElihwtYNk3iUGJKhYP12E8lGhgUu/WR5mg # gEDuFYF3PpzgUxgaUB04lZseZjMTJzkXeIc2zk7DX7L1PUdTtuDl2wthPSrXkizO # N1o+QEIxpB8QCMJWnL8kXVECnWp50hfT2sGUjgd7JXFEqwZq5tTG3yOalnXFMYIV # 1zCCFdMCAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMAITMwAA # AHuigQuHEavn/AAAAAAAezANBglghkgBZQMEAgEFAKCBwjAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQgg/pZZeE/1C2B77fYHkoDT3MzZQ2kNQTNDAPsgcHASjMwVgYK # KwYBBAGCNwIBDDFIMEagLIAqAEEAZABkAC0AQQBwAHAARABlAHYAUABhAGMAawBh # AGcAZQAuAHAAcwAxoRaAFGh0dHA6Ly9taWNyb3NvZnQuY29tMA0GCSqGSIb3DQEB # AQUABIIBAKDQ+pcbeSwP16osA/t0G2lHZOz4OTxlSF1Zz6lJdmVbZsllR71Ilu9f # cawPoLXB2btJ1LG+Zdv2gy7pKPauY6Lj/LX3OPDErxeNx7PbQyBU4TXy4sxlz+ID # 3E+g9IkghAHWEtvcHYSme9zX36ror8PEki6/fZpOXSEj60hy/tfxqJbQrvO5acxa # TdJFUjJ0CxfxvWOXJrV12rdmCHadYClHekXfzs+TIFCNe4numqSUFioKtThD2kN5 # sMAZV42cA7oq7vZL0Y5QQtEAW7xl4ILB85BEfiZ0WoYaXiFmct/f2ksjmECD6X76 # uLI++TsuALzUhiZM8dbXeJsTxgVI0Y6hghNNMIITSQYKKwYBBAGCNwMDATGCEzkw # ghM1BgkqhkiG9w0BBwKgghMmMIITIgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYL # KoZIhvcNAQkQAQSgggEsBIIBKDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgB # ZQMEAgEFAAQga3GLIhcT+cLJR1a9038fzP+iV0W2u/5VFNdNCAkiFIMCBlV8Mf53 # rhgTMjAxNTA3MDcwNzI3MTYuMjAyWjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUG # A1UECxMebkNpcGhlciBEU0UgRVNOOjMxQzUtMzBCQS03QzkxMSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIO0DCCBnEwggRZoAMCAQICCmEJ # gSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1 # NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEB # AQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18 # aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdN # uDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NM # ksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2K # Qk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZ # zTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQ # BgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUw # GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB # /wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0f # BE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJv # ZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4w # TDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0 # cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCB # jwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAd # AEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAd # MA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F # 4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbM # QEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mB # ZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7ti # X5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S # 4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3ai # caoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf # 5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsb # iSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJ # zxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB # 0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/e # dIhJEjCCBNowggPCoAMCAQICEzMAAABVbp3aWqbzHFsAAAAAAFUwDQYJKoZIhvcN # AQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTUwMzIwMTcz # MjI4WhcNMTYwNjIwMTczMjI4WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0Ug # RVNOOjMxQzUtMzBCQS03QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1/j0bMVw # Mfdo8G+OG7hP8ABk900fyishbHIEvu1LeWVAAcxS+06/sZTTeegnxAg1E6U+67+V # V3hlntvik+5zJk4mHpiCXIN1EXfPKYHaybbypADOdOwv/d3A6VnwmgXy7njIFkmu # LI9/gu73mRNVVO0XH0fY/Q3KEjxIfopZQcodSGQTBb1auzNgT9jV3kBv3hEJk2Rq # EPhH6TguzZCsEl2VrHfUAqn1OQHF2TYhEo3Hdc5J5tkuk2RPhtA27ioIgFFGsXFz # K8E6Ox+QWJ7lJQkPBZuGCAMelxxjtV9xB81KWnJJx1BLavTQUtcXsyn/wGft79It # MpMhiuWJKiF8hQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFNh/BAqfB0hvgXCe2iFn # ZGwg8fFuMB8GA1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRP # ME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 # Y3RzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEww # SgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv # TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0l # BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAE3KrSoIJv63eF2rk5BI # zqMNoLWz3Zq8ZmpWt74xtWn/mRCPdPpzp5FU7UHRQPWTi0KfPdCyIzL/7aT2xwD1 # DsKHWq5cZM8wk148WdNcL5lzaSUYV1Pa+N1k8PHKW6twShJNwemdXpAs6Qf42qG8 # zdT1Q1oMTVD/htsEZMgXsAYbLcPt2qObj+hBeBjrwXincK5+OP9IDZwfilDjwrMy # xgEYslXK5FV/20OsDXXdd04Cy4iER2/6ZQZbDw2jVsBBXojHovcha9Mo1rF/LRho # Cu/8+2gZRhO5XFBlnG5HHzhu6FfLiY9QqAWrPQzlCXL57cgwDuAEE2EFglNrZhxq # bsihggN5MIICYQIBATCB46GBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgT # Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBE # U0UgRVNOOjMxQzUtMzBCQS03QzkxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T # dGFtcCBTZXJ2aWNloiUKAQEwCQYFKw4DAhoFAAMVAFHpRafJtX+xSpIXeJVCW0wt # TWbOoIHCMIG/pIG8MIG5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NTdG # Ni1DMUUwLTU1NEMxKzApBgNVBAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0 # ZXIgQ2xvY2swDQYJKoZIhvcNAQEFBQACBQDZRZrdMCIYDzIwMTUwNzA3MDAyNTAx # WhgPMjAxNTA3MDgwMDI1MDFaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFANlFmt0C # AQAwCgIBAAICBQ4CAf8wBwIBAAICGGQwCgIFANlG7F0CAQAwNgYKKwYBBAGEWQoE # AjEoMCYwDAYKKwYBBAGEWQoDAaAKMAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkq # hkiG9w0BAQUFAAOCAQEAEHwQjcul/gWoMwu1rC5yuuS5nKZOPSzxwXrea7NZuW5Q # wLej7PJZD9BKIFRBMB4neHuDrztS1j6yO2Xiz5L+v81Qo1QbJo9y2HZ+qsmGW0tD # AhVEj9osTqOr/lAQaSdzNkDLwpeXZ2p+IqkR9bqNZHU1+5HLVDSMUWp/vpLcBM8c # 1ZUpoDO4cfF2hYZFoatfVTaZm1E1hAgwwTGrjYe1HXfhAMYNHldvQY8K25fD6qX0 # QVIAK8bauoR5DmLEy7JkgcPLuBJkuhVWf61BdRlpRLXGvtjKaORM1GeonCEiQMGx # epOmIXdc3Yi8evU7SfU8tGY5Uz3KPTDqdCOMBQd5FDGCAvUwggLxAgEBMIGTMHwx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p # Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAVW6d2lqm8xxbAAAAAABV # MA0GCWCGSAFlAwQCAQUAoIIBMjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQw # LwYJKoZIhvcNAQkEMSIEIC1j9DNnBuluvmahADk9fGuYYc43oe0b/jS33DaxpIJL # MIHiBgsqhkiG9w0BCRACDDGB0jCBzzCBzDCBsQQUUelFp8m1f7FKkhd4lUJbTC1N # Zs4wgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAFVu # ndpapvMcWwAAAAAAVTAWBBSYIfH+TGx9K25tT4ewfGEfRSTMiDANBgkqhkiG9w0B # AQsFAASCAQCbE0yZfHlpYX+qONo65NWGRclIcQoZdLgofXWyCrKLs/wN1S4tPeRR # xWV+ehi7CVdpbh3aqg2YEdhzTv78trYbJdRh/xNIrGkXtYYxtAYVbUQahee56EQk # ajnTeLvC37+KzLnvQBC22V9Sze32MRVdBAPea3t5WwdIBmiYyp/9XkMcBFfieecz # wd+PFLfsphBmB7vYriY/ncx3gtY2/R0i4uSXaCC45PCFu6V2bU0AJNU+/5J8KpEz # oVcfzSlPf1JApyaGOG9mhwqwtK+VjyDuWIQcJnl/SHt8VJdcmmRrPiFSjJ59b4DC # gtDErCEDM1/xIa7qaBg3S1hh1KJTHIyg # SIG # End signature block