ファイルを選択
  1. base64hub
  2. 各言語での Base64
  3. PowerShell
コード

PowerShell での Base64

PowerShell には Base64 用のコマンドレットがなく、.NET の [Convert] クラスを直接呼び出します。注意点は正しいテキストエンコーディングを選ぶことです。以下の出力は Linux 上の PowerShell 7.4 のものです。

文字列のエンコードとデコード

エンコーディングを明示して文字列をバイト列に変換し、[Convert] を呼び出します。

$enc = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("héllo wörld"))
$enc                                                     # aMOpbGxvIHfDtnJsZA==

[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($enc))   # héllo wörld

出力はエンコーディングで決まります。[Text.Encoding]::Unicode は UTF-16LE なので、同じ 2 文字でも結果が変わります。

[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("hi"))      # aGk=
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("hi"))   # aABpAA==

Web API、設定ファイル、他の言語に渡すものには UTF-8 を使ってください。Unicode を使うのは -EncodedCommand のときだけです。デコードした文字列で 1 文字ごとに隙間が空いていたら、それは UTF-16 です。Base64 から UTF-16 ツールでデコードできます。

REST 呼び出しの Basic 認証ヘッダーなら次のとおりです。

$token = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("user:pass"))
$headers = @{ Authorization = "Basic $token" }
$headers.Authorization                                   # Basic dXNlcjpwYXNz
Invoke-RestMethod -Uri $url -Headers $headers

PowerShell 6 以降では、Invoke-RestMethod -Authentication Basic -Credential $cred でヘッダーを組み立てさせることもでき、パスワードを平文の文字列として持たずに済みます。

-EncodedCommand

powershell.exe と pwsh は、-EncodedCommand(短縮形は -enc、-e)で Base64 のスクリプトを受け付けます。引用符や特殊文字を含むスクリプトを、エスケープの問題なしに cmd.exe、スケジューラー、リモートツール経由で渡すための機能です。テキストは UTF-16LE でなければなりません。

$cmd = 'Write-Output "hi from base64"'
$b = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))
$b
# VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAiAGgAaQAgAGYAcgBvAG0AIABiAGEAcwBlADYANAAiAA==

pwsh -NoProfile -EncodedCommand $b                       # hi from base64

攻撃者も、ざっと見ただけではわからないようスクリプトを隠すために -enc を使うため、セキュリティツールはこれを検知対象にしています。実行せずに中身を読むには次のようにします。

[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($b))
# Write-Output "hi from base64"

PowerShell エンコードコマンド ツールは両方向の変換をブラウザで行い、スクリプトを実行することはありません。

URLセーフな Base64

[Convert] には URLセーフのモードが組み込まれていません。文字とパディングを自分で置き換えます。

$u = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("<<???>>"))
$u.TrimEnd('=').Replace('+', '-').Replace('/', '_')      # PDw_Pz8-Pg

$s = "PDw_Pz8-Pg".Replace('-', '+').Replace('_', '/')
$s = $s.PadRight($s.Length + (4 - $s.Length % 4) % 4, '=')
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s))    # <<???>>

PowerShell 7.5 は .NET 9 上で動作するので、[System.Buffers.Text.Base64Url]::EncodeToString($bytes) も使えます。7.4 と Windows PowerShell 5.1 では、上記の置き換えを使ってください。それぞれのバリアントが使われる場面は Base64 と Base64URL の違い をご覧ください。

ファイルのエンコードとデコード

[Convert]::ToBase64String([IO.File]::ReadAllBytes("$PWD/hello.txt"))   # aGVsbG8K

[IO.File]::WriteAllBytes("$PWD/copy.txt", [Convert]::FromBase64String("aGVsbG8K"))

$PWD/ の接頭辞が必要なのは、.NET のメソッドが相対パスをプロセスの作業ディレクトリを基準に解決し、Set-Location ではこれが変わらないためです。/tmp で pwsh を起動し、Set-Location /w を実行してから [IO.File]::ReadAllBytes("hello.txt") を呼ぶと、ファイルがそこにあっても FileNotFoundException がスローされます。

コマンドレットでもバイト列を読めますが、パラメーターがバージョンによって異なります。

# PowerShell 7+
[Convert]::ToBase64String((Get-Content hello.txt -AsByteStream -Raw))

# Windows PowerShell 5.1
[Convert]::ToBase64String((Get-Content hello.txt -Encoding Byte -Raw))

-Raw を付けないと、Get-Content は 1 バイトごとに 1 つのオブジェクトを返すため、大きなファイルでは非常に遅くなります。

大きなファイル

ReadAllBytes はファイル全体を読み込みます。数百 MB のファイルなら、.NET の ToBase64Transform を通してストリーム処理してください。

$in  = [IO.File]::OpenRead((Resolve-Path big.bin))
$out = [IO.File]::Create("$PWD/big.b64")
$b64 = [Security.Cryptography.CryptoStream]::new(
    $out, [Security.Cryptography.ToBase64Transform]::new(), 'Write')
$in.CopyTo($b64)
$b64.Dispose(); $in.Dispose()

出力を使う前に CryptoStream を Dispose してください。そうしないと最後のブロックとパディングが欠けます。300 KB のテストファイルでは、ToBase64String と同じ文字列が得られました。デコードも、FromBase64Transform を 'Read' モードで使えば同じようにできます。そのバージョンは C# での Base64 にあります。

Windows では certutil -encode in.bin out.txt と certutil -decode も使えます。ただし -encode は出力を -----BEGIN CERTIFICATE----- 行で囲むので、ほかで使う前に取り除いてください。

MIME の改行

InsertLineBreaks を渡すと、CRLF で区切られた 76 文字の行になります。

[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("x" * 60), 'InsertLineBreaks')
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4

エラーと検証

不正な入力では .NET の System.FormatException が発生し、PowerShell はそれを MethodInvocationException で包みます。

[Convert]::FromBase64String("aGk")
# Exception calling "FromBase64String" with "1" argument(s): "The input is not a
# valid Base-64 string as it contains a non-base 64 character, more than two
# padding characters, or an illegal character among the padding characters."

aGk はパディングが不足しているため失敗します。URLセーフの文字、data: 接頭辞、文字列と一緒にコピーされた引用符でも同じメッセージが出ます。簡単なテスト関数は次のとおりです。

function Test-Base64([string]$s) {
    try { $null = [Convert]::FromBase64String($s); $true } catch { $false }
}
Test-Base64 "aGk="    # True
Test-Base64 "aGk"     # False

ログやメールから取り出した文字列がどうしてもデコードできないときは、Base64 の修復 が引用符と改行を取り除き、パディングを直します。原因は Base64 のエラーの解説 で説明しています。

バージョンごとの注意

  • [Convert]::ToBase64String / FromBase64String:Windows PowerShell 5.1 を含むすべての PowerShell バージョン
  • Get-Content -AsByteStream:PowerShell 6 以降。同時に -Encoding Byte は削除
  • Base64Url クラス:PowerShell 7.5 以降(.NET 9 ベース)

よくある質問

-EncodedCommand はどのエンコーディングを想定していますか?

UTF-16LE で、.NET では [Text.Encoding]::Unicode と呼ばれます。UTF-8 のバイト列からエンコードしたコマンドは意味不明な文字列にデコードされ、構文エラーで失敗します。

怪しい -enc コマンドを安全にデコードするには?

実行せずにデコードします:[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($b))。または、スクリプトを表示するだけの PowerShell エンコードコマンドデコーダー に貼り付けてください。

ConvertTo-Base64 のような Base64 用コマンドレットはありますか?

PowerShell 本体にはありません。追加するモジュールもありますが、[Convert]::ToBase64String と [Convert]::FromBase64String なら何もインストールせずにどこでも使えます。

[IO.File]::ReadAllBytes でファイルが見つからないと言われるのはなぜですか?

.NET は相対パスをプロセスの作業ディレクトリを基準に解決しますが、Set-Location ではこれが変わりません。代わりに (Resolve-Path file.bin) か "$PWD/file.bin" を渡してください。

続けて読む