# # 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: # # # \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" } $PackageCount = ($PackagePath | Measure-Object).Count # List all .appxbundle files in the script directory $BundlePath = Get-ChildItem (Join-Path $ScriptDir "*.appxbundle") | Where-Object { $_.Mode -NotMatch "d" } $BundleCount = ($BundlePath | Measure-Object).Count # List all .eappx files in the script directory $EncryptedPackagePath = Get-ChildItem (Join-Path $ScriptDir "*.eappx") | Where-Object { $_.Mode -NotMatch "d" } $EncryptedPackageCount = ($EncryptedPackagePath | Measure-Object).Count # List all .eappxbundle files in the script directory $EncryptedBundlePath = Get-ChildItem (Join-Path $ScriptDir "*.eappxbundle") | Where-Object { $_.Mode -NotMatch "d" } $EncryptedBundleCount = ($EncryptedBundlePath | Measure-Object).Count $NumberOfPackagesAndBundles = $PackageCount + $BundleCount + $EncryptedPackageCount + $EncryptedBundleCount # There must be exactly 1 package/bundle if ($NumberOfPackagesAndBundles -lt 1) { PrintMessageAndExit $UiStrings.ErrorNoPackageFound $ErrorCodes.NoPackageFound } elseif ($NumberOfPackagesAndBundles -gt 1) { PrintMessageAndExit $UiStrings.ErrorManyPackagesFound $ErrorCodes.ManyPackagesFound } if ($PackageCount -eq 1) { $DeveloperPackagePath = $PackagePath Write-Host ($UiStrings.PackageFound -f $DeveloperPackagePath.FullName) } elseif ($BundleCount -eq 1) { $DeveloperPackagePath = $BundlePath Write-Host ($UiStrings.BundleFound -f $DeveloperPackagePath.FullName) } elseif ($EncryptedPackageCount -eq 1) { $DeveloperPackagePath = $EncryptedPackagePath Write-Host ($UiStrings.EncryptedPackageFound -f $DeveloperPackagePath.FullName) } elseif ($EncryptedBundleCount -eq 1) { $DeveloperPackagePath = $EncryptedBundlePath Write-Host ($UiStrings.EncryptedBundleFound -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 # MIIiBQYJKoZIhvcNAQcCoIIh9jCCIfICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAnYmBUpv/sjF9s # UpSJeaz8bsgho4m0HYf/wsPXcJL9raCCC4QwggUMMIID9KADAgECAhMzAAABT+fG # YslG9Kl/AAAAAAFPMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTAwHhcNMTYxMTE3MjE1OTE0WhcNMTgwMjE3MjE1OTE0WjCBgzEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q # UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAtImQinYMrMU9obyB6NdQCLtaaaeux8y4W704DyFR # Rggj0b0imXO3KO/3B6sr+Uj3pRQFqU0kG21hlpyDnTPALHmZ8F3z7NVE36XNWfp2 # rQY/xkoD5uotlBDCZm/9YtBQitEikSOXZTShxJoCXpLiuHwoeMJe40b3yu84V4is # VgZYypgbx6jXXjaumkUw47a3PRjCpyeweU1T2DLmdqNQKvY/urtBHiSGTZibep72 # LOK8kGBl+5Zp+uATaOKJKi51GJ3Cbbgh9JleKn8xoKcNzO9PEW7+SUJOYd43yyue # QO/Oq15wCHOlcnu3Rs5bMlNdijlRb7DXqHjdoyhvXu5CHwIDAQABo4IBezCCAXcw # HwYDVR0lBBgwFgYKKwYBBAGCNz0GAQYIKwYBBQUHAwMwHQYDVR0OBBYEFJIOoRFx # ti9VDcMP9MlcdC5aDGq/MFIGA1UdEQRLMEmkRzBFMQ0wCwYDVQQLEwRNT1BSMTQw # MgYDVQQFEysyMzA4NjUrYjRiMTI4NzgtZTI5My00M2U5LWIyMWUtN2QzMDcxOWQ0 # NTJmMB8GA1UdIwQYMBaAFOb8X3u7IgBY5HJOtfQhdCMy5u+sMFYGA1UdHwRPME0w # S6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz # L01pY0NvZFNpZ1BDQV8yMDEwLTA3LTA2LmNybDBaBggrBgEFBQcBAQROMEwwSgYI # KwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWlj # Q29kU2lnUENBXzIwMTAtMDctMDYuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN # AQELBQADggEBABHAuWpDNf6FsTiADbh0dSyNcUm4PEHtLb3iBjaQdiuJ5baB6Ybj # GIyWkzJCp6f2tzQlOdDGekPq23dwzNTpQuuoxVUCdXie2BC+BxvKlGP7PA9x7tRV # Z9cp9mq/B7zlj4Lq+KHiczM/FJJeobplVzdFhYBc1izGizxqh6MHEcvs2XE4IDUk # PVS9zFWJ9HcQm+WZqg+uxjyOn9oAT8994bPAIPdSMfciSNVhjX8mAhl9g8xhkyrd # uNziCLOn3+EEd2DI9Kw1yzHlbHVRxTd7E2pOlWuPQJ7ITT6uvVnFINbCeK23ZFs7 # 0MAVcDQU5cWephzH9P/2y0jB4o3zbs6qtKAwggZwMIIEWKADAgECAgphDFJMAAAA # AAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBB # dXRob3JpdHkgMjAxMDAeFw0xMDA3MDYyMDQwMTdaFw0yNTA3MDYyMDUwMTdaMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUA # A4IBDwAwggEKAoIBAQDpDmRQeWe1xOP9CQBMnpSs91Zo6kTYz8VYT6mldnxtRbrT # OZK0pB75+WWC5BfSj/1EnAjoZZPOLFWEv30I4y4rqEErGLeiS25JTGsVB97R0sKJ # HnGUzbV/S7SvCNjMiNZrF5Q6k84mP+zm/jSYV9UdXUn2siou1YW7WT/4kLQrg3TK # K7M7RuPwRknBF2ZUyRy9HcRVYldy+Ge5JSA03l2mpZVeqyiAzdWynuUDtWPTshTI # wciKJgpZfwfs/w7tgBI1TBKmvlJb9aba4IsLSHfWhUfVELnG6Krui2otBVxgxrQq # W5wjHF9F4xoUHm83yxkzgGqJTaNqZmN4k9Uwz5UfAgMBAAGjggHjMIIB3zAQBgkr # BgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU5vxfe7siAFjkck619CF0IzLm76wwGQYJ # KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF # MAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8w # TTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBK # BggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N # aWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgZ0GA1UdIASBlTCBkjCBjwYJKwYB # BAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # UEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBn # AGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqG # SIb3DQEBCwUAA4ICAQAadO9XTyl7xBaFeLhQ0yL8CZ2sgpf4NP8qLJeVEuXkv8+/ # k8jjNKnbgbjcHgC+0jVvr+V/eZV35QLU8evYzU4eG2GiwlojGvCMqGJRRWcI4z88 # HpP4MIUXyDlAptcOsyEp5aWhaYwik8x0mOehR0PyU6zADzBpf/7SJSBtb2HT3wfV # 2XIALGmGdj1R26Y5SMk3YW0H3VMZy6fWYcK/4oOrD+Brm5XWfShRsIlKUaSabMi3 # H0oaDmmp19zBftFJcKq2rbtyR2MX+qbWoqaG7KgQRJtjtrJpiQbHRoZ6GD/oxR0h # 1Xv5AiMtxUHLvx1MyBbvsZx//CJLSYpuFeOmf3Zb0VN5kYWd1dLbPXM18zyuVLJS # R2rAqhOV0o4R2plnXjKM+zeF0dx1hZyHxlpXhcK/3Q2PjJst67TuzyfTtV5p+qQW # BAGnJGdzz01Ptt4FVpd69+lSTfR3BU+FxtgL8Y7tQgnRDXbjI1Z4IiY2vsqxjG6q # HeSF2kczYo+kyZEzX3EeQK+YZcki6EIhJYocLWDZN4lBiSoWD9dhPJRoYFLv1keZ # oIBA7hWBdz6c4FMYGlAdOJWbHmYzEyc5F3iHNs5Ow1+y9T1HU7bg5dsLYT0q15Is # zjdaPkBCMaQfEAjCVpy/JF1RAp1qedIX09rBlI4HeyVxRKsGaubUxt8jmpZ1xTGC # FdcwghXTAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTACEzMA # AAFP58ZiyUb0qX8AAAAAAU8wDQYJYIZIAWUDBAIBBQCggcIwGQYJKoZIhvcNAQkD # MQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJ # KoZIhvcNAQkEMSIEIM6sGgl9EI3BKHGPaKRgrF1O/bVv8y7tQZuqLVzy8rFiMFYG # CisGAQQBgjcCAQwxSDBGoCyAKgBBAGQAZAAtAEEAcABwAEQAZQB2AFAAYQBjAGsA # YQBnAGUALgBwAHMAMaEWgBRodHRwOi8vbWljcm9zb2Z0LmNvbTANBgkqhkiG9w0B # AQEFAASCAQB+h3AYSy8MV5bb3ZfjI9xGXZgMGVKhmRo9yYG3wIelD9RULoRtCN4J # ScCz++xjjxhUEdJ57ZeRdobQGnjEKyepHccHJsnxiFXUqd8gHWN56LxVKXLK6lgH # RnSmMm63Z/s6/qA7XdgHKjUGqG0MsZgFiX0DBfVQUQnPPPgkicc2xwWI1G4ZTDVn # Qoi9zUNb1bvJJhcN1BlPcvKVrVOqBLLfsSDsYGMmLcrh0v9QkMPBefLexqGxoibs # fvzJiwiCSRTRiC29aUrAP3s6EazYYP866d7F94qZfF5Qy8fy3RnnjErxD1PpgEVx # EZus2pc23sNrQzNokH1A7q3beEKTKal8oYITTTCCE0kGCisGAQQBgjcDAwExghM5 # MIITNQYJKoZIhvcNAQcCoIITJjCCEyICAQMxDzANBglghkgBZQMEAgEFADCCAT0G # CyqGSIb3DQEJEAEEoIIBLASCASgwggEkAgEBBgorBgEEAYRZCgMBMDEwDQYJYIZI # AWUDBAIBBQAEIJPdm/WIYp5J5+KJJMbxhHziKQ+l8HJ9bF3jCZXkxTgaAgZY1UYJ # UwIYEzIwMTcwNDIwMDAzMzU5LjY0NFowBwIBAYACAfSggbmkgbYwgbMxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAl # BgNVBAsTHm5DaXBoZXIgRFNFIEVTTjpCOEVDLTMwQTQtNzE0NDElMCMGA1UEAxMc # TWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCDtAwggZxMIIEWaADAgECAgph # CYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp # Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3MDEyMTQ2 # NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWlCgCChfvt # fGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/FgiIRUQwzX # Tbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeRX4FUsc+T # TJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/XcfPfBXday9 # ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogINeh4HLDp # mc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB5jCCAeIw # EAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvFM2hahW1V # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8EgZUwgZIw # gY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5taWNyb3Nv # ZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcCAjA0HjIg # HQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUAbgB0AC4g # HTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Prpsz1Mb7P # BeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOMzPRgEop2 # zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCvOA8X9S95 # gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v/rbljjO7 # Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99lmqQeKZt # 0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1klD3ouOVd2 # onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQHm+98eEA # 3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30uIUBHoD7 # G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp25ayp0Ki # yc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HSxVXjad5X # wdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi62jbb01+P # 3nSISRIwggTaMIIDwqADAgECAhMzAAAAn2fytagjBlt7AAAAAACfMA0GCSqGSIb3 # DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTE2MDkwNzE3 # NTY0N1oXDTE4MDkwNzE3NTY0N1owgbMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAsTHm5DaXBoZXIgRFNF # IEVTTjpCOEVDLTMwQTQtNzE0NDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkI8SOc # 3cQCLwKFoaMnl2T5A5wSVD9Tglq4Put9bhjFcsEn1XApDPCWS9aPhMcWOWKe+7EN # I4Si4zD30nVQC9PZ0NDu+pK9XV83OfjGchFkKzOBRddOhpsQkxFgMF3RfLTNXAEq # ffnNaReXwtVUkiGEJvW6KmABixzP0aeUVmJ6MHnJnmo+TKZdoVl7cg6TY6LCoze/ # F6rhOXmi/P3X/K3jHtmAaxL9Ou53jjDgO5Rjxt6ZEamdEsGF2SWZ6wH6Dmg9G6iZ # Pxgw+mjODwReL6jwh7H2XhsvzoFMrSERMzIIf2eJGAM9C0GR0BZHyRti17QqL5Ta # CuWPjMxTKXX4DlkCAwEAAaOCARswggEXMB0GA1UdDgQWBBT9ixsiw30jR3amHt/g # ZtRS6bb5oDAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8E # TzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9k # dWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBM # MEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRz # L01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBlEMFsa88VHq8PSDbr # 3y0LvAAA5pFmGlCWZbkxD2WMqfF0y8fnlvgb874z8sz8QZzByCmY1jHyHTc98Zek # z7L2Y5SANUIa8jyU36c64Ck5fY6Pe9hUA1RG/1zP+eq080chUPCF2zezhfwuz9Ob # 0obO64BwW0GZgYYz1hjsq+DBkSCBRV59ryFpzgKRwhWF8quXtHDpimiJx+ds2VZS # wEVk/QRY7pLuUvedN8P5DNuLaaRw3oJcs2Wxh2jWS5T8Y3JevUo3K3VTtHPi2IBW # ISkEG7TOnNEUcUXDMGSOeZ27kuPFzKkDVbtzvwEVepkGrsZ1W+1xuDYPQ1b3BMG8 # C79HoYIDeTCCAmECAQEwgeOhgbmkgbYwgbMxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIxJzAlBgNVBAsTHm5DaXBoZXIg # RFNFIEVTTjpCOEVDLTMwQTQtNzE0NDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZaIlCgEBMAkGBSsOAwIaBQADFQBs0ycI8vnZqMv5Gd6SS0qt # 2xmjwaCBwjCBv6SBvDCBuTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBOVFMgRVNOOjU3 # RjYtQzFFMC01NTRDMSswKQYDVQQDEyJNaWNyb3NvZnQgVGltZSBTb3VyY2UgTWFz # dGVyIENsb2NrMA0GCSqGSIb3DQEBBQUAAgUA3KJ+BDAiGA8yMDE3MDQyMDAwMjMz # MloYDzIwMTcwNDIxMDAyMzMyWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDcon4E # AgEAMAoCAQACAgIpAgH/MAcCAQACAhpTMAoCBQDco8+EAgEAMDYGCisGAQQBhFkK # BAIxKDAmMAwGCisGAQQBhFkKAwGgCjAIAgEAAgMW42ChCjAIAgEAAgMHoSAwDQYJ # KoZIhvcNAQEFBQADggEBAMNu8xSY3BVCszbhzYVReDYtQq5osuZNWMIQG3z0VMBy # 2CesXSvJhJ3FRfaH8T1oXKkKtpMKWzYRvoSN2QFLxsrja3QAWFpK+L2tc3SZn0Fh # oAbA3wrGX+PPZc+7KCe1011rLLU3jpU5p8WyXhoLNc0YpGT3Rf50P+J4mK0NCOQt # weBieStXSi7fvfxd7AyOnd/Sl/qVXaVWc7RWrHOxwyb3VMgFwPy73WWELCmHmtDa # zmZE+kX8uwFCweFCTZraxXf/QCZ6S6qxWsUbmgL3og4DWk3KHUFLaXUAJCbYLkv2 # 9/K2km3/D/pMnEuA97lLPkaTmS2qjPZ6OIwsefeQSvsxggL1MIIC8QIBATCBkzB8 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N # aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAJ9n8rWoIwZbewAAAAAA # nzANBglghkgBZQMEAgEFAKCCATIwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE # MC8GCSqGSIb3DQEJBDEiBCDOKIBEM8xfNhB0LPgIdV4IVpvEHSpJJY1OElQEf/7o # 8TCB4gYLKoZIhvcNAQkQAgwxgdIwgc8wgcwwgbEEFGzTJwjy+dmoy/kZ3pJLSq3b # GaPBMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAACf # Z/K1qCMGW3sAAAAAAJ8wFgQUuddYY/HPCoksb/xOBDjdPXlDGMEwDQYJKoZIhvcN # AQELBQAEggEAO+i4nAivWpc98DvCdqDpFvPa1+YQ+w4w59An08W8QDzLFvr4nDLz # SdXYQKnWFQaSI6qf81pe2tHfc4uehs/nbfFRK9CiVi321Ao7B31szkLCLWnClG7l # Ar+FQv0vFx6cIe2fOZeobsm1GDsiAroe6MGz3wAM4LzuZ3FkO1i9K1RlrBrN+28C # bjXmHksJHQNMBcGOLbRDrmpfrc6ijKjheepQekxBxfboGc0B57MM+LRIL2GC8mdn # Q6Iv/mcJnYW1pjLzlnRRqW32fjjKa/iygkVsU+aNrj3XxLU4GyDsV5NWBLl5LJ7z # dd+w7o1/PK5+wn4z52qQvpLt6luLRdHq9Q== # SIG # End signature block