HP.Private.psm1
# HP CONFIDENTIAL # __________________ # # Copyright (C)2018 HP Inc # All Rights Reserved. # # NOTICE: All information contained herein is, and remains the property of HP Inc. # # The intellectual and technical concepts contained herein are proprietary to HP Inc # and may be covered by U.S. and Foreign Patents, patents in process, and are protected by # trade secret or copyright law. Dissemination of this information or reproduction of this material # is strictly forbidden unless prior written permission is obtained from HP Inc. $ErrorActionPreference = 'Stop' Add-Type -TypeDefinition @' using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct arr4k_t { [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 4096)] public byte[] raw; }; public static class X509Utilities { [DllImport("dfmbios32.dll", EntryPoint = "get_public_key_from_pem", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] public static extern int get_public_key_from_pem32([In] string file, [In,Out] ref arr4k_t modulus, [In,Out] ref UInt32 modulus_size, [In,Out] ref UInt32 exponent); [DllImport("dfmbios64.dll", EntryPoint = "get_public_key_from_pem", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] public static extern int get_public_key_from_pem64([In] string file, [In,Out] ref arr4k_t modulus, [In,Out] ref UInt32 modulus_size, [In,Out] ref UInt32 exponent); }; '@ function Get-HPPrivateReadINI { [CmdletBinding()] param( [Parameter(Mandatory = $True)] $file, [Parameter(Mandatory = $False)] [int]$maxRetries = 0 ) Write-Verbose "Reading INI style file '$file'" [System.IO.StreamReader]$streamReader = $null $CommentCount = 0 $name = $null try { if ($file.StartsWith("http://") -or $file.StartsWith("https://") -or $file.StartsWith("ftp://")) { Write-Verbose ("Reading network file: $file") $webClient = New-Object System.Net.WebClient if ($file.StartsWith("ftp://")) { $webClient.Credentials = new-object -type System.Net.NetworkCredential -ArgumentList "anonymous","anonymous" } else { $webClient.UseDefaultCredentials = $true; } $webClient.Proxy = [System.Net.WebRequest]::GetSystemWebProxy() [int]$retries = $maxRetries do { try { Write-Verbose "Downloading CVA file $file, try $($maxRetries-$retries) / $maxRetries" $data = $webClient.DownloadData($file) $retries = 0 } catch { $retries = $retries - 1 Write-Verbose ("Download failed: $($_.Exception)") if ($retries -le 0) { throw $_ } Start-Sleep 5 } } while ($retries -gt 0) $memoryStream = New-Object System.IO.MemoryStream ($data, [System.Text.Encoding]::Default) $streamReader = New-Object System.IO.StreamReader ($memoryStream) } else { Write-Verbose ("Reading filesystem file: $file") $streamReader = New-Object -TypeName System.IO.StreamReader -ArgumentList $file } $ini = @{ } while (($line = $streamReader.ReadLine()) -ne $null) { switch -regex ($line) { "^\[(.+)\]$" { # Section $section = $matches[1] $ini[$section] = @{ } $CommentCount = 0 } "^(;.*)$" { # Comment if (!(Test-Path variable:\section)) { $section = "No-Section" $ini[$section] = @{ } } $value = $matches[1] $CommentCount = $CommentCount + 1 $name = "Comment" + $CommentCount $ini[$section][$name] = $value } "(.+?)\s*=\s*(.*)" { # Key if (!($section)) { $section = "No-Section" $ini[$section] = @{ } } $name, $value = $matches[1..2] $ini[$section][$name] = $value continue } "^(?!(.*[=])).*" { # section text block if (!($section)) { $section = "No-Section" $ini[$section] = @{ } } if ($ini[$section]["_body"] -eq $null) { $ini[$section]["_body"] = @() } $ini[$section]["_body"] += ($matches.Values | Where-Object { $_.StartsWith("[") -eq $false }) } } } } finally { if ($streamReader) { $streamReader.Close() $streamReader.Dispose() $streamReader = $null } if ($memoryStream) { $memoryStream.Close() $memoryStream.Dispose() $memoryStream = $null } if ($webClient) { $webClient.Dispose() $webClient = $null } } return $ini } # this is what the downloaded filename will be function Get-HPPrivateTemporaryFileName ($filename, [System.IO.DirectoryInfo]$cacheDir = [System.IO.Path]::GetTempPath() + "hp") { $cacheDir = Join-Path -Path $cacheDir -ChildPath $filename $cacheDir.FullName } function getLockedStreamForWrite { [CmdletBinding()] param([string]$target, [int]$maxRetries = 10) Write-Verbose "Opening exclusive access to file $target with maximum retries of $maxRetries" $lock_wait = $false do { try { $lock_wait = $false $result = New-Object -TypeName System.IO.FileStream -ArgumentList $target, Create, Write, None } catch { Write-Verbose ("******* $($_ | fl)") $lock_wait = $true if ($maxRetries -gt 0) { Start-Sleep -Seconds 30 $maxRetries = $maxRetries - 1 } else { throw "Could not obtain exclusive access to file '$target' and all retries were exhausted." } } } while ($lock_wait -eq $true) $result } # check for collision with other processes function getSharedFileInformation { [CmdletBinding()] param($file, [string]$mode, [switch]$wait, [int]$maxRetries, [switch]$progress, [switch]$skipSignatureCheck) $return = $true $length = 0 $sig = $false Write-Verbose ("Getting file information for file $file with access rights=$mode, wait = $wait, maxRetries=$maxRetries, skipAuthenticode=$($skipSignatureCheck.IsPresent)") if (-not $wait.IsPresent) { Write-Verbose ("This operation will not be retried.") $maxRetries = 0 } do { # file length try { $length = (Get-ChildItem -File $file -ErrorAction Stop).length } catch { return (-1, $true, $skipSignatureCheck.IsPresent) } Write-Verbose ("Target file length on disk is $length bytes") try { $fs = [System.IO.File]::Open($file, "Open", $mode) $return = $true $fs.Close() $fs.Dispose() Write-Verbose "Able to read from file '$file', it doesn't seem locked." if ($skipSignatureCheck.IsPresent) { Write-Verbose "Not checking Authenticode signature for file $file" $sig = $true } else { $sig = Get-HPPrivateCheckSignature -File $file } break } catch [System.IO.FileNotFoundException] { return (-1, $true, $skipSignatureCheck.IsPresent) } catch { Write-Verbose "Internal error: $_.Message" $return = $false if ($maxRetries -gt 0) { if ($progress) { Write-Progress -Activity "Blocked by another process, will retry for ($maxRetries) tries" } Write-Verbose ("Sleeping for 30 seconds since someone else has '$file' locked") Start-Sleep -Seconds 30 Write-Verbose ("Woke up") } $maxRetries = $maxRetries - 1 } } while ($maxRetries -gt 0) ($length, $return, $sig) } # download a file function Invoke-HPPrivateDownloadFile { [CmdletBinding()] param ( [string]$url, [string]$target, [bool]$progress, [string]$noclobber, [switch]$panic, [int]$maxRetries = 0, [switch]$skipSignatureCheck ) Write-Verbose ("Requesting to download $url to $target with progress: $progress and signatureCheckSkip: $skipSignatureCheck") [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols try { if (Test-Path $target -PathType Leaf) { # target file exists switch ($noclobber) { "no" { if ($panic.IsPresent) { throw "File $target already exists, will not overwrite." } Write-Host -ForegroundColor Magenta "File $target already exists, will not overwrite." return } "yes" { if ($progress -eq $true) { Write-Verbose "Overwriting existing file $target" } } "skip" { } } } else { #create lead directory if needed $lead = Split-Path $target if (!(Test-Path $lead)) { Write-Verbose "Creating directory '$lead'" $tmp = New-Item -ItemType Directory -Force -Path $lead } } $uri = New-Object "System.Uri" "$url" $retries = $maxRetries $request = [System.Net.HttpWebRequest]::Create($uri) $request.set_Timeout(30000) if (-not $request -is [System.Net.FtpWebRequest]) { $request.set_UserAgent($LIBRARY_UA); } do { try { Write-Verbose "Executing query on $uri, try $($maxRetries-$retries) / $maxRetries" $response = $request.GetResponse() $retries = 0 } catch { $retries = $retries - 1 Write-Verbose ("Query failed $($_.Exception)") if ($retries -le 0) { throw $_ } Start-Sleep 5 } } while ($retries -gt 0) $totalLength = [System.Math]::Floor($response.get_ContentLength() / 1024) # Someone else may be downloading this file at this time, so we'll wait until they release the # lock and then we check the size Write-Verbose ("Target file is $target") $r = getSharedFileInformation -File $target -Mode "Read" -Wait -maxRetries $maxRetries -progress:$progress -skipSignatureCheck:$skipSignatureCheck if ($noclobber -eq "skip") { if (($r[0] -eq $response.get_ContentLength()) -and ($r[2] -eq $true)) { Write-Verbose "File already exists or another process has finished downloading this file for us." return } else { Write-Verbose ("Existing file $target doesn't seem correct (size=$($r[0]) vs expected $($response.get_ContentLength()), signature_check=$($r[2]), deleting it.") } } $responseStream = $response.GetResponseStream() $targetStream = getLockedStreamForWrite -maxRetries $maxRetries -target $target #try { $buffer = New-Object byte[] 10KB $count = $responseStream.Read($buffer, 0, $buffer.length) $downloadedBytes = $count while ($count -gt 0) { $targetStream.Write($buffer, 0, $count) $count = $responseStream.Read($buffer, 0, $buffer.length) $downloadedBytes = $downloadedBytes + $count if ($progress -eq $true) { Write-Progress -Activity "Downloading file '$($url.split('/') | Select -Last 1)'" -Status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes / 1024)) / $totalLength) * 100) } } if ($progress -eq $true) { Write-Verbose ("Finished downloading '$($url.split('/') | Select -Last 1)'") Write-Progress -Activity "Finished downloading file '$($url.split('/') | Select -Last 1)'" } } finally { if ($targetStream) { $targetStream.Flush() $targetStream.Close() $targetStream.Dispose() } if ($responseStream) { $responseStream.Close() $responseStream.Dispose() } if ($response) { $response.Close() } } } function Get-HPPrivateAllowedHttpsProtocols { $c = [System.Net.SecurityProtocolType]([System.Net.SecurityProtocolType].GetEnumNames() | Where-Object { $_ -ne "Ssl3" -and $_ -ne "Tls" }) Write-Verbose "Removing obsolete protocols SSL3 and TLS, now supporting: $c" $c } function Get-HPPrivateCacheDirPath { [CmdletBinding()] param([System.IO.DirectoryInfo]$seed) if (-not $seed) { $seed = [System.IO.Path]::GetTempPath() + "hp" } Join-Path -Path $seed -ChildPath "cache" Write-Verbose "Local caching path is: $seed" } # check authenticode signature # check CVA and Softpaq hash to determine download of the Softpaq # # tests (remove these comments once we are happy with the function) # # PASS: Get-HPPrivateCheckSignature -file C:\windows\System32\notepad.exe -signedBy "Microsoft Windows" -Verbose # PASS: Get-HPPrivateCheckSignature -file C:\windows\System32\notepad.exe -Verbose # PASS: Get-HPPrivateCheckSignature -file .\sp99062.exe -CVAfile .\sp99062.cva -Verbose # PASS: Get-HPPrivateCheckSignature -file .\sp99062.exe -CVAfile .\sp99062.cva -Verbose -signedBy "HP Inc." function Get-HPPrivateCheckSignature { param( [Parameter(Position = 0, Mandatory = $true)] [string]$file, [Parameter(Mandatory = $false, Position = 1)] [string]$CVAfile = $null, [Parameter(Mandatory = $false, Position = 2)] [string]$signedBy = $null ) try { $c = Get-AuthenticodeSignature -FilePath $file if ($c.Status -ne "Valid") { Write-Verbose ("$file is not signed or certificate is invalid.") return $false } if ($signedBy) { $signer = $c.SignerCertificate.Subject.split(",")[0].trim().split("=")[1] if ($signer -ne $signedBy) { Write-Verbose ("$file is not signed by $signedBy, it is signed by $signer, failing.") return $false } else { Write-Verbose ("$file is signed by $signedBy.") # return $true } } if ($CVAfile) { Write-Verbose "Verifying '$file' using '$CVAFile'" $targetFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file) $targetCVA = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($CVAFile) # Getting hash value of CVA $read_file = Get-HPPrivateReadINI -file $targetCVA $CVA_SHA256 = $read_file | Out-SoftpaqField -field SoftPaqSHA256 $CVA_MD5 = $read_file | Out-SoftpaqField -field SoftPaqMD5 Write-Verbose "CVA has MD5 hash '$CVA_MD5' and sha256 hash '$CVA_SHA256'" if ($CVA_SHA256 -ne $null) { return $CVA_SHA256 -eq (Get-FileHash -path $targetFile -Algorithm SHA256).hash } elseif ($CVA_MD5 -ne $null) { return $CVA_MD5 -eq (Get-FileHash -path $targetFile -Algorithm MD5).hash } else { return $false } } # When only file is passed and it has valid signature return $true } catch { Write-Verbose "During signature check, had exception $_.Exception" return $false } } function Invoke-HPPrivateDeleteCachedItem ([Parameter(Mandatory = $true)] $cab) { Invoke-HPPrivateSafeRemove -Path $cab Invoke-HPPrivateSafeRemove -Path "$cab.dir" -Recurse } function Invoke-HPPrivateSafeRemove { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string[]]$path, [Parameter(Mandatory = $false)] [switch]$recurse ) foreach ($p in $path) { if (Test-Path $p) { Write-Verbose "Removing $p" Remove-Item $p -Recurse:$recurse } } } function Invoke-HPPrivateExpandCAB { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $cab, [Parameter(Mandatory = $true)] $expectedFile ) Write-Verbose "Expanding CAB $cab to $cab.dir" $target = "$cab.dir" Invoke-HPPrivateSafeRemove -Path $target -Recurse Write-Verbose "Expanding $cab to $target" $result = New-Item -Force $target -ItemType Directory Write-Verbose "Created folder $result" $shell = New-Object -ComObject "Shell.Application" try { if (!$?) { $(throw "unable to create $comObject object") } $sourceCab = $shell.Namespace($cab).items() $DestinationFolder = $shell.Namespace($target) $DestinationFolder.CopyHere($sourceCab) } finally { [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$shell) | Out-Null [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } $downloadedOk = Test-Path $expectedFile if ($downloadedOk -eq $false) { throw "Invalid cab file, did not find $expectedFile in contents" } return $expectedFile } # check if a download is needed, based on file existence and the remote last-modified time function Test-HPPrivateIsDownloadNeeded { [CmdletBinding()] param([Parameter(Mandatory = $true)] $url, [Parameter(Mandatory = $true)] $file) Write-Verbose "Checking if we need a new copy of $file" # $c = [System.Net.ServicePointManager]::SecurityProtocol Write-Verbose ("Allowed HTTPS protocols: $c") [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols $headers = (Invoke-WebRequest -Uri $url -Method HEAD -UseBasicParsing).Headers [datetime]$offered = $headers["Last-Modified"] Write-Verbose "File on server has timestamp $offered" $exists = Test-Path -Path $file -PathType leaf if ($exists -eq $false) { Write-Verbose "Cached file $file does not exist, will need to download new file" $offered $true } else { [datetime]$have = (Get-Item $file).CreationTime $r = ($have -lt $offered) Write-Verbose "Cached file exist and it has timestamp $offered. Need to download: $r" $offered $r } } # check if the downloaded xml file is corrupted. function Test-HPPrivateIsValidXmlFile { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $file ) if (-not (Test-Path -Path $file)) { Write-Verbose "File $file does not exist." return $false } # Check for Load or Parse errors when loading the XML file. $xml = New-Object System.Xml.XmlDocument try { $xml.Load($file) return $true } catch [System.Xml.XmlException] { Write-Verbose "Invalid Xml file $file" return $false } } function Get-HPPrivateUserAgent () { "hpcmsl ($($MyInvocation.MyCommand.Module.Name); $($MyInvocation.MyCommand.Module.Version))" } function runParallel { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [System.Management.Automation.ScriptBlock]$scriptBlock, [ValidateRange(1, 16)] [Parameter(Mandatory = $false, Position = 1)] [int]$maxInstances = [int]$env:NUMBER_OF_PROCESSORS + 1 ) $pool = [runspacefactory]::CreateRunspacePool(1, $maxInstances) $pool.ApartmentState = "MTA" $pool.Open() $jobs = New-Object System.Collections.ArrayList Write-Progress -Activity "Running tasks" -Id 1 -PercentComplete 0 -CurrentOperation "Initializing" try { 1..$maxInstances | ForEach-Object { $psi = [powershell]::Create() $psi.RunspacePool = $pool $psi.AddScript($scriptBlock).AddArgument($obj) | Out-Null $obj = New-Object -TypeName PSObject -Property @{ ChildID = $_ Handle = $psi.BeginInvoke() Instance = $psi Item = "?" PercentComplete = 0 Status = "?" } $jobs.Add($obj) | Out-Null } $currentcount = 0; $totalcount = ($jobs | Measure-Object).Count Write-Verbose (?Available runspaces (unused) : { 0 } Where-Object -f $pool.GetAvailableRunspaces()) $allCompleted = $true while ($jobs.Handle.IsCompleted -eq $False) { Write-Host "." -NoNewline Start-Sleep -Milliseconds 300 $jobs | ForEach-Object { Write-Progress -ParentId 1 -Id (1 + $_.ChildID) -Activity $_.Item -Status $_.Status -PercentComplete $_.PercentComplete } } $return = $jobs | ForEach-Object { $_.Instance.EndInvoke($_.Handle) $_.Instance.Dispose() } $jobs.Clear() } finally { if ($pool) { $pool.Close(); $pool.Dispose() } } } function validateWmiResult { [CmdletBinding()] param([int]$code, [int]$category = 0xff) Write-Verbose "Validating error code $code for facility $category" switch ($code) { 0 { } 0xea { } 6 { throw [NotSupportedException]"Operation could not be completed. Please ensure this is a supported HP system." } 5 { throw [ArgumentException]"Method called with invalid parameters." } 4 { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." } default { validateWmiResultInCategory -Category $category -code $code } } } function validateWmiResultInCategory { [CmdletBinding()] param([int]$category, [int]$code) switch ($category) { 1 { switch ($code) { 0x40 { throw [NotSupportedException]"This system does not support firmware logs." } 0x41 { throw [System.TimeoutException]"Call has timed out." } default { throw [SystemException]"An unknown error $code has occured." } } } 2 { switch ($code) { 0x0b { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." } 0x0e { throw [UnauthorizedAccessException]"The operation could not be completed, possibly due to a bios password mismatch?" } 0x0010 { throw [SystemException]"Invalid flash offset." } 0x0012 { throw [SystemException]"Invalid flash checksum" } 0x0013 { throw [InvalidOperationException]"Flash-in-progress error" } 0x0014 { throw [InvalidOperationException]"Flash-in-progress not set" } default { throw [SystemException]"An unknown error $code has occured." } } } 3 { switch ($code) { # this facility doesn't define specific codes default { throw [SystemException]"An unknown error $code has occured." } } } 4 { switch ($code) { 0x0b { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." } 0x03 { throw [NotSupportedException]"This system does not support HP Secure Platform or a hardware option is missing." } 0x1c { throw [SystemException]"The request was not accepted by the BIOS." } 0x1000 { throw [SystemException]"HP Secure Platform is not provisioned." } 0x1001 { throw [SystemException]"HP Secure Platform is already provisioned." } 0x1002 { throw [SystemException]"HP Secure Platform is in use. Deprovision all features that use the HP Secure Platform first." } default { throw [SystemException]"An unknown error $code has occured." } } } default { throw [SystemException]"An unknown error $code has occured." } } } function Test-HPPrivateCustomResult { [CmdletBinding()] param([int]$result, [int]$mi_result, [int]$category) Write-Verbose ("Checking result={0:x8}, mi_result={1:x8}, category={2:x4}" -f $result, $mi_result, $category) switch ($result) { 0 { Write-Verbose ("Operation succeeded.") } 0x80000711 { validateWmiResult -code $mi_result -Category $category } # E_DFM_FAILED_WITH_EXTENDED_ERROR 0x80000710 { throw [NotSupportedException]"Current platform does not support this operation." } # E_DFM_FEATURE_NOT_SUPPORTED 0x8000070b { throw [System.IO.IOException]"Firmware file could not be read." } # E_DFM_FILE_ACCESS_FAILURE 0x8000070e { throw [InvalidOperationException]"Firmware file is too long for expected flash type." } # E_DFM_FLASH_BUR_INPUT_DATA_TOO_LARGE 0x80000712 { throw [InvalidOperationException]"The firmware does not mach the target platform." } # E_DFM_WRONG_FLASH_FILE 0x80000714 { throw [OutOfMemoryException]"A memory allocation failed. The system may be out of memory." } # E_DFM_ALLOC_FAILED 0x80000715 { throw [InvalidOperationException]"Password length is not valid." } # E_DFM_PASSWORD_SIZE_INVALID 0x8000071a { throw [System.ArgumentException]"Invalid parameter for HP Sure View API" } 1392 { throw [System.IO.IOException]"Could not copy the file to the system partition." } # ERROR_FILE_CORRUPT 234 { Write-Verbose ("Operation succeeded.") } # MORE_DATA default { throw [ComponentModel.Win32Exception]$result } } } function Convert-HPPrivateObjectToBytes { [CmdletBinding()] param($obj) $mem = $null $length = 0 $bytes = $() Write-Verbose "Converting object of type $($obj.Gettype()) to byte array" try { $length = [System.Runtime.InteropServices.Marshal]::SizeOf($obj) $bytes = New-Object byte[] $length Write-Verbose "Converting object of type $($obj.Gettype()) is $length bytes" $mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($length) [System.Runtime.InteropServices.Marshal]::StructureToPtr($obj, $mem, $true) [System.Runtime.InteropServices.Marshal]::Copy($mem, $bytes, 0, $length) ($bytes, $length) } finally { # Free the memory we allocated for the struct value if ($mem) { Write-Verbose "Freeing allocated memory" [System.Runtime.InteropServices.Marshal]::FreeHGlobal($mem) } } Write-Verbose "Conversion complete." } #region Cryptography function Get-HPPrivatePublicKeyCoalesce { [CmdletBinding()] param( [System.IO.FileInfo]$file, [PSObject]$key ) if ($file) { $efile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file) Write-Verbose "Coalescing to FILE PEM $efile" $modulus = New-Object arr4k_t $modulus_size = 4096 $exponent = 0 $c = '[X509Utilities]::get_public_key_from_pem' + (Test-OSBitness) + '($efile,[ref]$modulus, [ref]$modulus_size, [ref]$exponent);' $result = Invoke-Expression -Command $c Test-HPPrivateCustomResult -result $result -mi_result $mi_result -Category 0x04 New-Object -TypeName PSObject -Property @{ Modulus = $modulus.raw[0..($modulus_size - 1)] Exponent = $exponent } } else { Write-Verbose "Coalescing to binary PEM" $key } } function Get-HPPrivateX509CertCoalesce { [CmdletBinding()] param( [System.IO.FileInfo]$file, [System.Security.Cryptography.X509Certificates.X509Certificate2]$cert, [string]$password ) $param = @{ } if ($password) { $param.Add("Password", (ConvertTo-SecureString -AsPlainText -Force $password)) } if ($file) { $efile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file) Write-Verbose "Coalescing to FILE certificate $efile" $param.Add("FileName", $efile) Get-HPPrivatePublicKeyCertificateFromPFX @param -Verbose:$VerbosePreference } else { Write-Verbose "Coalescing to binary certificate" $key = [System.Security.Cryptography.RSACryptoServiceProvider ]$cert.PublicKey.Key $parameters = $key.ExportParameters($false); $mod_reversed = $parameters.Modulus [array]::Reverse($mod_reversed) New-Object -TypeName PSObject -Property @{ Full = $Cert Certificate = $cert.Export('Cert') Modulus = $mod_reversed Exponent = $parameters.Exponent } } } # get the PK from a PFX file function Get-HPPrivatePublicKeyCertificateFromPFX { [CmdletBinding(DefaultParameterSetName = "FF")] param( [Parameter(Mandatory = $true, Position = 0)] [string]$FileName, [Parameter(Mandatory = $false, Position = 1)] [securestring]$Password ) $certfile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FileName) if (-not (Test-Path -PathType leaf -Path $certfile)) { throw [System.IO.FileNotFoundException] "Certificate file '$certfile' could not be found" } Write-Verbose "Extracting public key from '$certfile'." try { $cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import($certfile, $Password, 'DefaultKeySet') $key = [System.Security.Cryptography.RSACryptoServiceProvider ]$cert.PublicKey.Key $parameters = $key.ExportParameters($false); $mod_reversed = $parameters.Modulus [array]::Reverse($mod_reversed) New-Object -TypeName PSObject -Property @{ Full = $cert Certificate = $cert.Export('Cert') Modulus = $mod_reversed Exponent = $parameters.Exponent } } finally { #$cert.Dispose(); #$cert = $null } } # hash and sign a byte array with the given certificate function hashAndSign { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [byte[]]$data, [Parameter(Mandatory = $true, Position = 1)] [System.Security.Cryptography.X509Certificates.X509Certificate2]$signer ) $csp = $null $hasher = $null try { $hasher = New-Object System.Security.Cryptography.SHA256Managed $hash = $hasher.ComputeHash($data) Write-Verbose "Calculated hash $hash" # this seems to provide a bit more flexibility [System.Security.Cryptography.CspParameters]$cspParams = New-Object System.Security.Cryptography.CspParameters [System.Security.Cryptography.RSACryptoServiceProvider]$sn = $signer.PrivateKey $cspParams.KeyContainerName = $sn.CspKeyContainerInfo.KeyContainerName if ($sn.CspKeyContainerInfo.KeyNumber -eq [System.Security.Cryptography.KeyNumber]::Exchange) { $cspParams.KeyNumber = 1 } else { $cspParams.KeyNumber = 2 } [System.Security.Cryptography.RSACryptoServiceProvider]$csp = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList $cspParams $csp.PersistKeyInCsp = $false $csp.SignHash($hash, "SHA256"); } finally { if ($hasher) { $hasher.Dispose() } if ($csp) { $csp.Dispose() } } } # sign a byte array with a certificate provided in $Filename function Invoke-HPPrivateSignData { [CmdletBinding(DefaultParameterSetName = "FF")] param( [Parameter(ParameterSetName = "FF", Mandatory = $true, Position = 0)] [System.IO.FileInfo]$FileName, [Parameter(ParameterSetName = "FB", Mandatory = $true, Position = 0)] [ System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, [Parameter(ParameterSetName = "FF")] [Parameter(ParameterSetName = "FB")] [Parameter(Mandatory = $false, Position = 1)] [string]$Password, [Parameter(ParameterSetName = "FF")] [Parameter(ParameterSetName = "FB")] [Parameter(Mandatory = $true, Position = 2)] [byte[]]$data ) if ($Password) { $pwd = ConvertTo-SecureString -AsPlainText -force $Password } else { $pwd = $null } $cert = Get-HPPrivateX509CertCoalesce -File $FileName -cert $Certificate -password $pwd $result = hashAndSign -data $data -signer $cert.Full [array]::Reverse($result) $result } function Get-HPPrivateHash { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [byte[]]$data, [Parameter(Mandatory = $false, Position = 1)] [string]$algo = "SHA256" ) $cp = [System.Security.Cryptography.HashAlgorithm]::Create($algo) try { $result = $cp.ComputeHash($data) } finally { $cp.Dispose() } $result } # Downloads files for when OfflineCacheMode is Enable # If -platform is present : Downloads Advisory Data Files (XXXX_cds.cab) where XXXX is platform ID. # also downloads the platform List function Get-HPPrivateOfflineCacheFiles { [CmdletBinding()] param( [string]$url, [string]$filename, [System.IO.DirectoryInfo]$cacheDirOffline = [System.IO.Path]::GetTempPath() + "hp", [switch]$expand ) $file = Get-HPPrivateTemporaryFileName -filename $filename -cacheDir $cacheDirOffline $filename = $filename.replace("cab","xml") $downloadedFile = "$file.dir\$filename" Write-Verbose "Checking if $url is available locally." try { $result = Test-HPPrivateIsDownloadNeeded -url $url -File $file -Verbose:$VerbosePreference } catch { throw [System.Net.WebException] "Could not find a data file for this platform." } if ($result[1] -eq $true) { Write-Verbose "$url is not local, or is out of date, will download." Write-Verbose "Cleaning cached data and downloading the data file." Invoke-HPPrivateDeleteCachedItem -cab $file Invoke-HPPrivateDownloadFile -url $url -target $file -Verbose:$VerbosePreference (Get-Item $file).CreationTime = ($result[0]) (Get-Item $file).LastWriteTime = ($result[0]) } if ($expand.IsPresent) { # Need to make sure that the expanded data file exists and is not corrupted. # Otherwise, expand the cab file. if (-not (Test-Path $downloadedFile) -or (-not (Test-HPPrivateIsValidXmlFile -file $downloadedFile))) { Write-Verbose "Extracting the data file, looking for $downloadedFile." $file = Invoke-HPPrivateExpandCAB -cab $file -expectedFile $downloadedFile } } return $downloadedFile } # build URL for a remote item function Get-HPPrivateItemUrl ([int]$number,[string]$ext,[string]$url) { if ($url) { return "$url/sp$number.$ext" } [string]$baseNumber = $number.ToString() [int] $last3Value = [int]($baseNumber.Substring($baseNumber.Length - 3)) [int]$blockStart = [int]($baseNumber.Substring(0, $baseNumber.Length - 3)) [string]$block = "" [int]$blockEnd = $blockStart if ($last3Value -gt 500) { $blockEnd += 1 $block = "$($blockStart)501-$($blockEnd)000" } else { if ($last3Value -eq 0) { $blockStart -= 1 $block = "$($blockStart)501-$($blockEnd)000" } else { $block = "$($blockStart)001-$($blockStart)500" } } return "https://ftp.hp.com/pub/softpaq/sp$block/sp$number.$ext" } #endregion # SIG # Begin signature block # MIIcOAYJKoZIhvcNAQcCoIIcKTCCHCUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDpAJIit4/3cxLM # w+aHV0eRIjAb7SDZ6YfJ2Ck4YsmR1KCCCo0wggU2MIIEHqADAgECAhAM1s71mz4i # 3j/UnuaI4vzeMA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xNTAzBgNV # BAMTLERpZ2lDZXJ0IFNIQTIgSGlnaCBBc3N1cmFuY2UgQ29kZSBTaWduaW5nIENB # MB4XDTE5MDQyMjAwMDAwMFoXDTIwMDQyOTEyMDAwMFowdTELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVBhbG8gQWx0bzEQMA4GA1UE # ChMHSFAgSW5jLjEZMBcGA1UECxMQSFAgQ3liZXJzZWN1cml0eTEQMA4GA1UEAxMH # SFAgSW5jLjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEwuTFpw7fQ # 3Ds5fvexal46Gg9TNMvdiJu7qMqDZnDJNl7ECdEPyLxsioGS7/yomOS9RXdXMJOm # tyV4/wIPbBaGC8E2tbLTbQQ4IJbgvC+Vc46vbo+sI8YTG6qBICOovFw9VhUNXXEy # SwHMoBNk8JS8R1slPpJKmNGB10HSatMGaHja0Lbqos0QuEx/tx2OXe+mzepIo66T # dtSv2MfPy2tcVcXIdiJGn7f4otxoj6T9X7hVIl78r5Y2XWHYtDK8KaV1E/qkiNXK # 1Xw5S53zv2VsZl6i1LZwt3d1Q9pUmm1AZe2YdhSGvwMP2LYBJGXIBbyLYnxS4HKB # R7MYZyz7H2kCAwEAAaOCAb8wggG7MB8GA1UdIwQYMBaAFGedDyAJDMyKOuWCRnJi # /PHMkOVAMB0GA1UdDgQWBBSnSAWgK15kcBLxsg4XNsT7ncH29zAOBgNVHQ8BAf8E # BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwbQYDVR0fBGYwZDAwoC6gLIYqaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItaGEtY3MtZzEuY3JsMDCgLqAshipo # dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1jcy1nMS5jcmwwTAYDVR0g # BEUwQzA3BglghkgBhv1sAwswKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln # aWNlcnQuY29tL0NQUzAIBgZngQwBBAEwgYgGCCsGAQUFBwEBBHwwejAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFIGCCsGAQUFBzAChkZodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEySGlnaEFzc3VyYW5j # ZUNvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD # ggEBAJQblkFw+UYKYSY2M/CIEpJxZDnf+cDhodKAy+goI3XfExRHhyLu3Gc2ibFB # Y4wyz/sJSfHehtNPYckXxR9k/FB/GfYtEACug9xXxJ+iLxWUNQ4KPt3bXY/kmDxW # D1QXJFLbW5Dop3w/K0DL3fxnjOfYCcxsYodbeEiCJprCdNi3zd6x/J8Y35GDbLA5 # p7RfIAzKrmBLPHFGDWr/jWTfwPfUNz6jYJ51m0Ba9j81kzpxNUD0yBIZXBkVvSkx # A09KxzMSSvxvV9DSqSezQBVgWnl9TbElouYUQwk64i0GzL4lTsphK4rQJJ2uuKtH # wN4E0ibpm0uIqbLhgk+3ic8fHTIwggVPMIIEN6ADAgECAhALfhCQPDhJD/ovZ5qH # oae5MA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp # Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRp # Z2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAw # WhcNMjgxMDIyMTIwMDAwWjB2MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl # cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTUwMwYDVQQDEyxEaWdp # Q2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIENvZGUgU2lnbmluZyBDQTCCASIwDQYJ # KoZIhvcNAQEBBQADggEPADCCAQoCggEBALRKXn0HD0HexPV2Fja9cf/PP09zS5zR # Df5Ky1dYXoUW3QIVVJnwjzwvTQJ4EGjI2DVLP8H3Z86YHK4zuS0dpApUk8SFot81 # sfXxPKezNPtdSMlGyWJEvEiZ6yhJU8M9j8AO3jWY6WJR3z1rQGHuBEHaz6dcVpbR # +Uy3RISHmGnlgrkT5lW/yJJwkgoxb3+LMqvPa1qfYsQ+7r7tWaRTfwvxUoiKewpn # JMuQzezSTTRMsOG1n5zG9m8szebKU3QBn2c13jhJLc7tOUSCGXlOGrK1+7t48Elm # p8/6XJZ1kosactn/UJJTzD7CQzIJGoYTaTz7gTIzMmR1cygmHQgwOwcCAwEAAaOC # AeEwggHdMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMH8GCCsGAQUFBwEBBHMwcTAkBggrBgEFBQcwAYYYaHR0 # cDovL29jc3AuZGlnaWNlcnQuY29tMEkGCCsGAQUFBzAChj1odHRwOi8vY2FjZXJ0 # cy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0 # MIGPBgNVHR8EgYcwgYQwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9j # cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5j # cmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBz # Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFGed # DyAJDMyKOuWCRnJi/PHMkOVAMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9j # ZCvDMA0GCSqGSIb3DQEBCwUAA4IBAQBqDv9+E3wGpUvALoz5U2QJ4rpYkTBQ7Myf # 4dOoL0hGNhgp0HgoX5hWQA8eur2xO4dc3FvYIA3tGhZN1REkIUvxJ2mQE+sRoQHa # /bVOeVl1vTgqasP2jkEriqKL1yxRUdmcoMjjTrpsqEfSTtFoH4wCVzuzKWqOaiAq # ufIAYmS6yOkA+cyk1LqaNdivLGVsFnxYId5KMND66yRdBsmdFretSkXTJeIM8ECq # XE2sfs0Ggrl2RmkI2DK2gv7jqVg0QxuOZ2eXP2gxFjY4lT6H98fDr516dxnZ3pO1 # /W4r/JT5PbdMEjUsML7ojZ4FcJpIE/SM1ucerDjnqPOtDLd67GftMYIRATCCEP0C # AQEwgYowdjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG # A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTE1MDMGA1UEAxMsRGlnaUNlcnQgU0hBMiBI # aWdoIEFzc3VyYW5jZSBDb2RlIFNpZ25pbmcgQ0ECEAzWzvWbPiLeP9Se5oji/N4w # DQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQg3jGg3FBa9FJ5oxrZDtSmv6mcWG/L+BmWFJQZfcXAD94wDQYJ # KoZIhvcNAQEBBQAEggEAbEWccIVbdWWOGY9JjkyEJkUNiSDP7mkkkvQcAmHyqzhP # xJjgGMd/UlK4npkk01uWX0cxhFN91zu6Siw3kbTPrOhtEMATPqFnvKT3eQxO+LWS # bAarx5MiZP03jF2ZPyEjCLqyooQeA1xeJIHuoyKmZV2zEzbSYmhmcHTU9xszgX1d # ZuUC6aOw0N2xVBZH4CTmOLoLBJSyplKGnmrAHZpzl8sCijGEpSlZGLIli2WwPeZM # 0j9JhctKd4mKrNr2Ioob+w0rLbwvhzuO2sq8/Nu0gblkoz6gMWfPWMOYaZVFnKE5 # WRybXS8aqIQzp/2o14XqDjxQutrhm+0nVDXByZvP56GCDskwgg7FBgorBgEEAYI3 # AwMBMYIOtTCCDrEGCSqGSIb3DQEHAqCCDqIwgg6eAgEDMQ8wDQYJYIZIAWUDBAIB # BQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFl # AwQCAQUABCAGhPu/AtwtXlOoULj9vfskfgjS0K7zv/bABNkhPJluWwIRAJgv28au # DXXTd7X948oWL0MYDzIwMjAwMjE0MjI1MTQxWqCCC7swggaCMIIFaqADAgECAhAE # zT+FaK52xhuw/nFgzKdtMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUw # EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x # MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcg # Q0EwHhcNMTkxMDAxMDAwMDAwWhcNMzAxMDE3MDAwMDAwWjBMMQswCQYDVQQGEwJV # UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJDAiBgNVBAMTG1RJTUVTVEFNUC1T # SEEyNTYtMjAxOS0xMC0xNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB # AOlkNZz6qZhlZBvkF9y4KTbMZwlYhU0w4Mn/5Ts8EShQrwcx4l0JGML2iYxpCAQj # 4HctnRXluOihao7/1K7Sehbv+EG1HTl1wc8vp6xFfpRtrAMBmTxiPn56/UWXMbT6 # t9lCPqdVm99aT1gCqDJpIhO+i4Itxpira5u0yfJlEQx0DbLwCJZ0xOiySKKhFKX4 # +uGJcEQ7je/7pPTDub0ULOsMKCclgKsQSxYSYAtpIoxOzcbVsmVZIeB8LBKNcA6P # isrg09ezOXdQ0EIsLnrOnGd6OHdUQP9PlQQg1OvIzocUCP4dgN3Q5yt46r8fcMbu # QhZTNkWbUxlJYp16ApuVFKMCAwEAAaOCAzgwggM0MA4GA1UdDwEB/wQEAwIHgDAM # BgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIB # tjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3 # dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAg # AHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAg # AGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABv # AGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBu # AGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBl # AGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBs # AGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBk # ACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCG # SAGG/WwDFTAfBgNVHSMEGDAWgBT0tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4E # FgQUVlMPwcYHp03X2G5XcoBQTOTsnsEwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDov # L2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggr # BgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv # bTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsF # AAOCAQEALoOhRAVKBOO5MlL62YHwGrv4CY0juT3YkqHmRhxKL256PGNuNxejGr9Y # I7JDnJSDTjkJsCzox+HizO3LeWvO3iMBR+2VVIHggHsSsa8Chqk6c2r++J/BjdEh # jOQpgsOKC2AAAp0fR8SftApoU39aEKb4Iub4U5IxX9iCgy1tE0Kug8EQTqQk9Eec # 3g8icndcf0/pOZgrV5JE1+9uk9lDxwQzY1E3Vp5HBBHDo1hUIdjijlbXST9X/Aqf # I1579JSN3Z0au996KqbSRaZVDI/2TIryls+JRtwxspGQo18zMGBV9fxrMKyh7eRH # TjOeZ2ootU3C7VuXgvjLqQhsUwm09zCCBTEwggQZoAMCAQICEAqhJdbWMht+QeQF # 2jaXwhUwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp # Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMb # RGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTE2MDEwNzEyMDAwMFoXDTMx # MDEwNzEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQg # U0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQTCCASIwDQYJKoZIhvcNAQEB # BQADggEPADCCAQoCggEBAL3QMu5LzY9/3am6gpnFOVQoV7YjSsQOB0UzURB90Pl9 # TWh+57ag9I2ziOSXv2MhkJi/E7xX08PhfgjWahQAOPcuHjvuzKb2Mln+X2U/4Jvr # 40ZHBhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oiPhisEeTwmQNtO4V8CdPuXciaC1Tj # qAlxa+DPIhAPdc9xck4Krd9AOly3UeGheRTGTSQjMF287DxgaqwvB8z98OpH2YhQ # Xv1mblZhJymJhFHmgudGUP2UKiyn5HU+upgPhH+fMRTWrdXyZMt7HgXQhBlyF/EX # Bu89zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2srOlW/5MCAwEAAaOCAc4wggHKMB0G # A1UdDgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1bjAfBgNVHSMEGDAWgBRF66Kv9JLL # gjEtUYunpyGd823IDzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB # hjATBgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUH # MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDov # L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNy # dDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBQBgNVHSAESTBH # MDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl # cnQuY29tL0NQUzALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggEBAHGVEulR # h1Zpze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9xafDDiBCLK938ysfDCFaKrcFNB1q # rpn4J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIlHsS6HHssIeLWWywUNUMEaLLbdQLg # cseY1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8pieV4H9YLFKWA1xJHcLN11ZOFk362 # kmf7U2GJqPVrlsD0WGkNfMgBsbkodbeZY4UijGHKeZR+WfyMD+NvtQEmtmyl7odR # IeRYYJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8QkIoxhhWz0E0tmZdtnR79VYzIi8i # NrJLokqV2PWmjlIxggJNMIICSQIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMGA1UE # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYD # VQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBAhAE # zT+FaK52xhuw/nFgzKdtMA0GCWCGSAFlAwQCAQUAoIGYMBoGCSqGSIb3DQEJAzEN # BgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjAwMjE0MjI1MTQxWjArBgsq # hkiG9w0BCRACDDEcMBowGDAWBBQDJb1QXtqWMC3CL0+gHkwovig0xTAvBgkqhkiG # 9w0BCQQxIgQg1965ZC99q/SICT1w2VfN48Ysni06Qbp5Ey2HT2PzkNUwDQYJKoZI # hvcNAQEBBQAEggEAR5wqSmWS/KFVOk7lfGipEBXtwpNjumcCSCmwgtcIWMv+E2Hp # 7+EbSxhfjCpEaLDnMItG1waHcn/2IrOzVKXTYbkgl7a68QiOYEEmIuRXz4KZGLEC # fu+BOuMTRRcdAwR/8EX1gR8x5X4bKVDc6pc4Cf41DCPV+NxRUiY1BkVuNfrPtiR/ # +zpk3qoCkMBsvOTbrA2Ri9UijEtWTJ/DZ+6wpKNQSoxzT9Cv4JilkDvNgCsZztQG # vdlxxVSZwbw1dQBkQWXbu8h+C9bfI4isIN8CttoyuNVOlW4UvO0JnU7fnsGp4BeO # KSL6QM/hVW4sV9VYsnsWCzEHU7X3rltPG9Mhtw== # SIG # End signature block |