2024年5月1日 星期三

PowerShell 如何接收外部程式返回的二進制值

PowerShell 如何接收外部程式返回的二進制值

這個超坑人阿 PowerShell 預設會自動把收到的數值編碼,導致二進制數值讀進來的瞬間就因為轉換的關係資料遺失了

舉個例子來說如果要用 OpenSSL 簽名,簽名有點複雜在簡化一點產生一個隨機數值並返回二進位數值

openssl rand -out - 8

拿到的值就是爛掉的因為轉換編碼的過程就發生不可逆的資料丟失了
查了一下還好是有解的

shell - How to get original binary data from external command output in PowerShell? - Stack Overflow

只能說真愛生命不要用 PowerShell 管道傳輸非字串的東西…




完成的函式

代碼我把他弄好成一個函式了

function Invoke-CommandAndGetBinaryOutput {
    [CmdletBinding()]
    Param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [string]$CommandLine
    )
    Process {
        # 用空白分割命令行,並分配文件名與參數
        $parts = $CommandLine -split ' ', 2
        $fileName = $parts[0]
        $arguments = $parts[1]

        # 建立 ProcessStartInfo 對象並配置
        $processInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
            FileName = $fileName
            Arguments = $arguments
            RedirectStandardOutput = $true
            RedirectStandardError = $true
            UseShellExecute = $false
            CreateNoWindow = $true
        }

        # 開始進程
        $process = New-Object System.Diagnostics.Process
        $process.StartInfo = $processInfo
        $process.Start() | Out-Null
        $process.WaitForExit()

        # 使用 MemoryStream 讀取二進制數據
        $memoryStream = New-Object System.IO.MemoryStream
        try {
            $process.StandardOutput.BaseStream.CopyTo($memoryStream)
            $binaryOutput = $memoryStream.ToArray()

            # 返回二進制數據
            return $binaryOutput
        } finally {
            $memoryStream.Dispose()
        }
    }
} # Write-Host (Invoke-CommandAndGetBinaryOutput "openssl rand -out - 8")

使用範例就是

Write-Host (Invoke-CommandAndGetBinaryOutput "openssl rand -out - 8")

拿到的是二進位數值,這個不要隨便亂成任意編碼(UTF-8)會丟失信息的。

想要安全的攜帶大概就是走base64的路子了,轉法下面有。




驗證

就能拿到正確的數值了,不過 OpenSSL 只有簽名的時候結果才是固定的這邊有點難驗證就是了,大概寫一下可驗證的辦法

走檔案路線的不會受到影響結果一定是對的

# 調用 OpenSSL 進行簽名 (走檔案路線)
$toSignData | Set-Content $toSignDataPath -NoNewline
openssl dgst -sha512 -sign $privatekeyPath -binary -out $signaturePath $toSignDataPath
$base64Signature = [System.Web.HttpServerUtility]::UrlTokenEncode([System.IO.File]::ReadAllBytes($signaturePath)) -replace('[012]$')

然後是副程式的用法

$toSignData | Set-Content $toSignDataPath -NoNewline
$bytes = Invoke-CommandAndGetBinaryOutput "OpenSSL dgst -sha512 -binary -sign `"$privatekeyPath`" `"$toSignDataPath`""
$base64Signature = [System.Web.HttpServerUtility]::UrlTokenEncode($bytes) -replace('[012]$')

寫個大概而已自己在琢磨一下 OpenSSL 怎麼使用




沒有留言:

張貼留言